feat: restructure command for Claude models and add new remote routes
- Deleted the old cc.ts command and created a new cc.ts under src/command/claude for better organization. - Added support for a new model 'bailian' in the command. - Implemented remote app connection status and connection routes in assistant/src/routes/remote/index.ts. - Updated index.ts to reflect the new path for the cc command. - Added a placeholder for future management of plugin operations in src/command/opencode/plugin.ts.
This commit is contained in:
@@ -94,6 +94,15 @@ export type AssistantConfigData = {
|
||||
* 例子: { path: '/root/home', target: 'https://kevisual.cn', pathname: '/root/home' }
|
||||
*/
|
||||
proxy?: ProxyInfo[];
|
||||
/**
|
||||
* Router代理, 会自动获取 {path: 'router', key: 'list'}的路由信息,然后注入到整个router应用当中.
|
||||
* 例子: { proxy: [ { type: 'router', api: 'https://localhost:50002/api/router' } ] }
|
||||
* base: 是否使用 /api/router的基础路径,默认false
|
||||
*/
|
||||
router?: {
|
||||
proxy: ProxyInfo[];
|
||||
base?: boolean;
|
||||
}
|
||||
/**
|
||||
* API 代理配置, 比如,api开头的,v1开头的等等
|
||||
*/
|
||||
|
||||
@@ -7,12 +7,16 @@ import glob from 'fast-glob';
|
||||
import type { App } from '@kevisual/router';
|
||||
import { RemoteApp } from '@/module/remote-app/remote-app.ts';
|
||||
import { logger } from '@/module/logger.ts';
|
||||
import { getEnvToken } from '@/module/http-token.ts';
|
||||
import { initApi } from '@kevisual/api/proxy'
|
||||
import { Query } from '@kevisual/query';
|
||||
export class AssistantApp extends Manager {
|
||||
config: AssistantConfig;
|
||||
pagesPath: string;
|
||||
remoteIsConnected = false;
|
||||
attemptedConnectTimes = 0;
|
||||
remoteApp: RemoteApp | null = null;
|
||||
remoteUrl: string | null = null;
|
||||
constructor(config: AssistantConfig, mainApp?: App) {
|
||||
config.checkMounted();
|
||||
const appsPath = config?.configPath?.appsDir || path.join(process.cwd(), 'apps');
|
||||
@@ -71,11 +75,17 @@ export class AssistantApp extends Manager {
|
||||
return pagesParse;
|
||||
}
|
||||
|
||||
async initRemoteApp() {
|
||||
async initRemoteApp(opts?: { token?: string, enabled?: boolean }) {
|
||||
const config = this.config.getConfig();
|
||||
const share = config?.share;
|
||||
if (share && share.enabled !== false) {
|
||||
const token = config?.token;
|
||||
const enabled = opts?.enabled ?? share?.enabled ?? false;
|
||||
if (share && enabled !== false) {
|
||||
if (this.remoteApp) {
|
||||
this.remoteApp.ws?.close();
|
||||
this.remoteApp = null;
|
||||
this.remoteIsConnected = false;
|
||||
}
|
||||
const token = config?.token || opts?.token || getEnvToken() as string;
|
||||
const url = new URL(share.url || 'https://kevisual.cn/ws/proxy');
|
||||
const id = config?.app?.id;
|
||||
if (token && url && id) {
|
||||
@@ -99,12 +109,63 @@ export class AssistantApp extends Manager {
|
||||
}, 5 * 1000); // 第一次断开5秒后重连
|
||||
});
|
||||
logger.debug('链接到了远程应用服务器');
|
||||
const appId = id;
|
||||
const username = config?.auth.username || 'unknown';
|
||||
const url = new URL(`/${username}/v1/${appId}`, 'https://kevisual.cn/');
|
||||
this.remoteUrl = url.toString();
|
||||
console.log('远程地址', this.remoteUrl);
|
||||
} else {
|
||||
console.log('Not connected to remote app server');
|
||||
}
|
||||
this.remoteApp = remoteApp;
|
||||
} else {
|
||||
//
|
||||
if (!token) {
|
||||
logger.error('Token是远程应用连接必须的参数');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async initRouterApp() {
|
||||
const config = this.config.getConfig();
|
||||
const routerProxy = config.router.proxy || [];
|
||||
const base = config.router.base ?? false;
|
||||
if (base) {
|
||||
routerProxy.push({
|
||||
type: 'router',
|
||||
router: {
|
||||
url: `${this.config.getRegistry()}/api/router`,
|
||||
}
|
||||
})
|
||||
}
|
||||
if (routerProxy.length === 0) {
|
||||
return
|
||||
}
|
||||
for (const proxyInfo of routerProxy) {
|
||||
if (proxyInfo.type !== 'router') {
|
||||
console.warn('路由的type必须是"router"');
|
||||
continue;
|
||||
}
|
||||
const url = proxyInfo.router!.url;
|
||||
if (!url) {
|
||||
console.warn('路由的api地址不能为空', proxyInfo.router);
|
||||
continue;
|
||||
}
|
||||
const query = new Query({ url });
|
||||
try {
|
||||
initApi({
|
||||
router: this.mainApp,
|
||||
item: {
|
||||
type: 'api',
|
||||
api: {
|
||||
url,
|
||||
query: query as any,
|
||||
}
|
||||
},
|
||||
exclude: "WHERE path = 'auth' OR path = 'router' OR path = 'call'",
|
||||
})
|
||||
console.log('Router API 已初始化', url.toString());
|
||||
} catch (err) {
|
||||
console.error('Router API 初始化失败', url.toString(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,9 +180,10 @@ export class AssistantApp extends Manager {
|
||||
remoteApp.listenProxy();
|
||||
this.attemptedConnectTimes = 0;
|
||||
console.log('重新连接到了远程应用服务器');
|
||||
this.reconnectRemoteApp();
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.reconnectRemoteApp();
|
||||
this.initRouterApp()
|
||||
}, 30 * 1000 + this.attemptedConnectTimes * 10 * 1000); // 30秒后重连 + 每次增加10秒
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export type ProxyInfo = {
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
type?: 'file' | 'dynamic' | 'minio' | 'http' | 's3';
|
||||
type?: 'file' | 'dynamic' | 'minio' | 'http' | 's3' | 'router';
|
||||
/**
|
||||
* 目标的 pathname, 默认为请求的url.pathname, 设置了pathname,则会使用pathname作为请求的url.pathname
|
||||
* @default undefined
|
||||
@@ -41,6 +41,10 @@ export type ProxyInfo = {
|
||||
id?: string;
|
||||
indexPath?: string;
|
||||
rootPath?: string;
|
||||
},
|
||||
router?: {
|
||||
id?: string;
|
||||
url?: string;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useKey } from '@kevisual/use-config';
|
||||
import http from 'node:http';
|
||||
export const error = (msg: string, code = 500) => {
|
||||
return JSON.stringify({ code, message: msg });
|
||||
@@ -32,3 +33,7 @@ export const getToken = async (req: http.IncomingMessage) => {
|
||||
return { token };
|
||||
};
|
||||
|
||||
export const getEnvToken = () => {
|
||||
const envTokne = useKey('KEVISUAL_TOKEN') || '';
|
||||
return envTokne;
|
||||
}
|
||||
Reference in New Issue
Block a user