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:
2026-01-21 23:22:58 +08:00
parent a911334459
commit 028a6ac726
15 changed files with 469 additions and 276 deletions

View File

@@ -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开头的等等
*/

View File

@@ -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秒
}
}

View File

@@ -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;
}
};

View File

@@ -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;
}