feat(client): add routes for version, time, system info, and restart functionality
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
],
|
||||
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||
"license": "MIT",
|
||||
"packageManager": "pnpm@10.28.1",
|
||||
"packageManager": "pnpm@10.28.2",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -41,19 +41,21 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kevisual/ai": "^0.0.22",
|
||||
"@kevisual/api": "^0.0.26",
|
||||
"@inquirer/prompts": "^8.2.0",
|
||||
"@kevisual/ai": "^0.0.24",
|
||||
"@kevisual/api": "^0.0.35",
|
||||
"@kevisual/cnb": "^0.0.13",
|
||||
"@kevisual/load": "^0.0.6",
|
||||
"@kevisual/local-app-manager": "^0.1.32",
|
||||
"@kevisual/logger": "^0.0.4",
|
||||
"@kevisual/query": "0.0.38",
|
||||
"@kevisual/query-login": "0.0.7",
|
||||
"@kevisual/router": "^0.0.62",
|
||||
"@kevisual/router": "^0.0.64",
|
||||
"@kevisual/types": "^0.0.12",
|
||||
"@kevisual/use-config": "^1.0.28",
|
||||
"@opencode-ai/plugin": "^1.1.36",
|
||||
"@types/bun": "^1.3.6",
|
||||
"@types/node": "^25.0.10",
|
||||
"@opencode-ai/plugin": "^1.1.44",
|
||||
"@types/bun": "^1.3.8",
|
||||
"@types/node": "^25.1.0",
|
||||
"@types/send": "^1.2.1",
|
||||
"@types/ws": "^8.18.1",
|
||||
"chalk": "^5.6.2",
|
||||
@@ -62,7 +64,6 @@
|
||||
"dayjs": "^1.11.19",
|
||||
"dotenv": "^17.2.3",
|
||||
"get-port": "^7.1.0",
|
||||
"inquirer": "^13.2.1",
|
||||
"nanoid": "^5.1.6",
|
||||
"send": "^1.2.1",
|
||||
"supports-color": "^10.2.2",
|
||||
@@ -76,16 +77,16 @@
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.975.0",
|
||||
"@aws-sdk/client-s3": "^3.978.0",
|
||||
"@kevisual/ha-api": "^0.0.6",
|
||||
"@kevisual/js-filter": "^0.0.5",
|
||||
"@kevisual/oss": "^0.0.16",
|
||||
"@kevisual/video-tools": "^0.0.13",
|
||||
"@opencode-ai/sdk": "^1.1.36",
|
||||
"@opencode-ai/sdk": "^1.1.44",
|
||||
"es-toolkit": "^1.44.0",
|
||||
"eventemitter3": "^5.0.4",
|
||||
"lowdb": "^7.0.1",
|
||||
"lru-cache": "^11.2.4",
|
||||
"lru-cache": "^11.2.5",
|
||||
"pm2": "^6.0.14",
|
||||
"unstorage": "^1.17.4",
|
||||
"zod": "^4.3.6"
|
||||
|
||||
15
assistant/pnpm-lock.yaml
generated
15
assistant/pnpm-lock.yaml
generated
@@ -84,9 +84,6 @@ importers:
|
||||
get-port:
|
||||
specifier: ^7.1.0
|
||||
version: 7.1.0
|
||||
inquirer:
|
||||
specifier: ^12.6.3
|
||||
version: 12.6.3(@types/node@22.15.29)
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
@@ -2136,18 +2133,6 @@ snapshots:
|
||||
|
||||
ini@1.3.8: {}
|
||||
|
||||
inquirer@12.6.3(@types/node@22.15.29):
|
||||
dependencies:
|
||||
'@inquirer/core': 10.1.13(@types/node@22.15.29)
|
||||
'@inquirer/prompts': 7.5.3(@types/node@22.15.29)
|
||||
'@inquirer/type': 3.0.7(@types/node@22.15.29)
|
||||
ansi-escapes: 4.3.2
|
||||
mute-stream: 2.0.0
|
||||
run-async: 3.0.0
|
||||
rxjs: 7.8.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.15.29
|
||||
|
||||
ip-address@9.0.5:
|
||||
dependencies:
|
||||
jsbn: 1.1.0
|
||||
|
||||
@@ -24,9 +24,15 @@ export const assistantQuery = useContextKey('assistantQuery', () => {
|
||||
return new AssistantQuery(assistantConfig);
|
||||
});
|
||||
|
||||
export const runtime = useContextKey('runtime', () => {
|
||||
type Runtime = {
|
||||
type: 'client' | 'server';
|
||||
isServer?: boolean;
|
||||
}
|
||||
export const runtime: Runtime = useContextKey('runtime', () => {
|
||||
console.log('Runtime detected:', manualParse);
|
||||
return {
|
||||
type: 'client',
|
||||
isServer: manualParse.isServer,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ const command = new Command('server')
|
||||
shellCommands.push(`-e ${options.interpreter}`);
|
||||
}
|
||||
const basename = _interpreter.split('/').pop();
|
||||
|
||||
if (basename.includes('bun')) {
|
||||
console.log(`Assistant server shell command: bun src/run-server.ts server ${shellCommands.join(' ')}`);
|
||||
const child = spawnSync(_interpreter, ['src/run-server.ts', ...shellCommands], {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { program, Command } from '@/program.ts';
|
||||
import { AssistantInit } from '@/services/init/index.ts';
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import inquirer from 'inquirer';
|
||||
import { confirm } from '@inquirer/prompts';
|
||||
import chalk from 'chalk';
|
||||
|
||||
type InitCommandOptions = {
|
||||
@@ -41,17 +41,11 @@ const removeCommand = new Command('remove')
|
||||
const assistantDir = path.join(configDir, 'assistant-app');
|
||||
|
||||
if (fs.existsSync(assistantDir)) {
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
confirm({
|
||||
message: `确定要删除助手配置文件吗?\n助手配置文件路径:${assistantDir}`,
|
||||
default: false,
|
||||
},
|
||||
])
|
||||
.then((answers) => {
|
||||
if (answers.confirm) {
|
||||
}).then((confirmed) => {
|
||||
if (confirmed) {
|
||||
fs.rmSync(assistantDir, { recursive: true, force: true });
|
||||
console.log(chalk.green('助手配置文件已删除'));
|
||||
} else {
|
||||
|
||||
@@ -85,6 +85,7 @@ type AuthPermission = {
|
||||
username?: string; // 用户名
|
||||
admin?: string[];
|
||||
};
|
||||
type AssistantRoutes = { type: "npm" | "file", path: string } | string
|
||||
export type AssistantConfigData = {
|
||||
app?: {
|
||||
/**
|
||||
@@ -117,6 +118,7 @@ export type AssistantConfigData = {
|
||||
base?: boolean;
|
||||
lightcode?: boolean;
|
||||
}
|
||||
routes?: AssistantRoutes[],
|
||||
/**
|
||||
* API 代理配置, 比如,api开头的,v1开头的等等
|
||||
*/
|
||||
@@ -418,10 +420,27 @@ export const parseHomeArg = (homedir?: string) => {
|
||||
}
|
||||
const checkUrl = ['.opencode', 'bin/opencode', 'opencode.exe']
|
||||
const isOpencode = checkUrl.some((item) => execPath.includes(item))
|
||||
let isServer = false;
|
||||
// 如果args包含 server 则认为是服务端运行。其中config中server必须存在
|
||||
console.log('parseHomeArg args:', args);
|
||||
if (args.includes('server') || args.includes('assistant-server')) {
|
||||
let isDaemon = false;
|
||||
// 判断 --daemon 参数, 如果有则认为是守护进程运行
|
||||
if (args.includes('--daemon') || args.includes('-d')) {
|
||||
isDaemon = true;
|
||||
}
|
||||
if (!isDaemon) {
|
||||
// 判断 -s 或者 --start 参数
|
||||
if (args.includes('-s') || args.includes('--start')) {
|
||||
isServer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
isOpencode,
|
||||
options,
|
||||
configDir: _configDir,
|
||||
isServer
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -211,4 +211,22 @@ export class AssistantApp extends Manager {
|
||||
}
|
||||
}
|
||||
}
|
||||
async initRoutes() {
|
||||
// TODO 初始化应用内置路由
|
||||
const routes = this.config.getConfig().routes || [];
|
||||
for (const route of routes) {
|
||||
try {
|
||||
if (typeof route === 'string') {
|
||||
await import(route);
|
||||
console.log('安装路由', route);
|
||||
} else if (typeof route === 'object' && route.path) {
|
||||
const routePath = route.path;
|
||||
await import(routePath);
|
||||
console.log('安装路由', routePath);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('初始化路由失败', route, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChildProcess, fork, ForkOptions } from 'child_process';
|
||||
import { ChildProcess, fork, ForkOptions } from 'node:child_process';
|
||||
class BaseProcess {
|
||||
private process: ChildProcess;
|
||||
status: 'running' | 'stopped' | 'error' = 'stopped';
|
||||
|
||||
84
assistant/src/routes/client/index.ts
Normal file
84
assistant/src/routes/client/index.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
import { app, assistantConfig } from '../../app.ts';
|
||||
import { createSkill } from '@kevisual/router';
|
||||
import os from 'node:os';
|
||||
import { runCommand } from '@/services/app/index.ts';
|
||||
app
|
||||
.route({
|
||||
path: 'client',
|
||||
key: 'version',
|
||||
description: '获取客户端版本号',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
ctx.body = 'v1.0.0';
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'client',
|
||||
key: 'time',
|
||||
description: '获取当前时间',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
ctx.body = {
|
||||
time: new Date().getTime(),
|
||||
date: new Date().toLocaleDateString(),
|
||||
};
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
// 调用 path: client key: system
|
||||
app
|
||||
.route({
|
||||
path: 'client',
|
||||
key: 'system',
|
||||
description: '获取系统信息',
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
skill: 'view-system-info',
|
||||
title: '查看系统信息',
|
||||
summary: '获取服务器操作系统平台、架构和版本信息',
|
||||
})
|
||||
}
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { platform, arch, release } = os;
|
||||
ctx.body = {
|
||||
platform: platform(),
|
||||
arch: arch(),
|
||||
release: release(),
|
||||
};
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
|
||||
app.route({
|
||||
path: 'client',
|
||||
key: 'restart',
|
||||
description: '重启客户端',
|
||||
middleware: ['admin-auth'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
skill: 'restart-client',
|
||||
title: '重启客户端',
|
||||
summary: '重启当前运行的客户端应用程序',
|
||||
})
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const cmd = 'pm2 restart assistant-server --update-env';
|
||||
try {
|
||||
runCommand(cmd, []);
|
||||
ctx.body = {
|
||||
message: '客户端重启命令已执行',
|
||||
};
|
||||
} catch (error) {
|
||||
ctx.status = 500;
|
||||
ctx.body = {
|
||||
message: '重启客户端失败',
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}).addTo(app);
|
||||
@@ -11,9 +11,8 @@ import './opencode/index.ts';
|
||||
import './remote/index.ts';
|
||||
import './kevisual/index.ts'
|
||||
|
||||
import os from 'node:os';
|
||||
import { authCache } from '@/module/cache/auth.ts';
|
||||
import { createSkill } from '@kevisual/router';
|
||||
|
||||
import { logger } from '@/module/logger.ts';
|
||||
const getTokenUser = async (token: string) => {
|
||||
const query = assistantConfig.query
|
||||
@@ -135,53 +134,3 @@ app
|
||||
}
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'client',
|
||||
key: 'version',
|
||||
description: '获取客户端版本号',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
ctx.body = 'v1.0.0';
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'client',
|
||||
key: 'time',
|
||||
description: '获取当前时间',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
ctx.body = {
|
||||
time: new Date().getTime(),
|
||||
date: new Date().toLocaleDateString(),
|
||||
};
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
// 调用 path: client key: system
|
||||
app
|
||||
.route({
|
||||
path: 'client',
|
||||
key: 'system',
|
||||
description: '获取系统信息',
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
skill: 'view-system-info',
|
||||
title: '查看系统信息',
|
||||
summary: '获取服务器操作系统平台、架构和版本信息',
|
||||
})
|
||||
}
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { platform, arch, release } = os;
|
||||
ctx.body = {
|
||||
platform: platform(),
|
||||
arch: arch(),
|
||||
release: release(),
|
||||
};
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useContextKey } from '@kevisual/context';
|
||||
import { app, assistantConfig } from './app.ts';
|
||||
import { app, assistantConfig, runtime } from './app.ts';
|
||||
import { proxyRoute, proxyWs } from './services/proxy/proxy-page-index.ts';
|
||||
import './routes/index.ts';
|
||||
import './routes-simple/index.ts';
|
||||
@@ -58,6 +58,9 @@ export const runServer = async (port: number = 51515, listenPath = '127.0.0.1')
|
||||
});
|
||||
manager.initRemoteApp()
|
||||
manager.initRouterApp()
|
||||
if (runtime.isServer) {
|
||||
manager.initRoutes();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { checkFileExists, AssistantConfig } from '@/module/assistant/index.ts';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import inquirer from 'inquirer';
|
||||
import { confirm } from '@inquirer/prompts';
|
||||
|
||||
import { spawnSync } from 'child_process';
|
||||
export const runCommand = (command: string, args: string[]) => {
|
||||
@@ -91,15 +91,10 @@ export class AppDownload {
|
||||
return runCommand(command, args);
|
||||
}
|
||||
async confirm(message?: string) {
|
||||
const { confirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
return await confirm({
|
||||
message: message || '是否继续删除应用?',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
return confirm;
|
||||
});
|
||||
}
|
||||
async deleteApp(opts: DeleteAppOptions) {
|
||||
const { id, type = 'web', yes = false } = opts;
|
||||
|
||||
@@ -136,11 +136,11 @@ export class AssistantInit extends AssistantConfig {
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.975.0",
|
||||
"@aws-sdk/client-s3": "^3.978.0",
|
||||
"@kevisual/oss": "^0.0.16",
|
||||
"@kevisual/query": "^0.0.38",
|
||||
"eventemitter3": "^5.0.4",
|
||||
"@kevisual/router": "^0.0.62",
|
||||
"@kevisual/router": "^0.0.64",
|
||||
"@kevisual/use-config": "^1.0.28",
|
||||
"ioredis": "^5.9.2",
|
||||
"minio": "^8.0.6",
|
||||
@@ -157,16 +157,18 @@ export class AssistantInit extends AssistantConfig {
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kevisual/types": "^0.0.12",
|
||||
"@types/bun": "^1.3.6",
|
||||
"@types/bun": "^1.3.8",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/node": "^25.0.10"
|
||||
"@types/node": "^25.1.0"
|
||||
}
|
||||
}
|
||||
`,
|
||||
);
|
||||
console.log(chalk.green('助手 package.json 文件创建成功, 正在安装依赖...'));
|
||||
installDeps({ appPath: path.dirname(packagePath), isProduction: true }).then(() => {
|
||||
console.log('------------------------------------------------');
|
||||
console.log(chalk.green('助手依赖安装完成'));
|
||||
console.log('------------------------------------------------');
|
||||
});
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.975.0",
|
||||
"@aws-sdk/client-s3": "^3.978.0",
|
||||
"@kevisual/oss": "^0.0.16",
|
||||
"@kevisual/query": "^0.0.38",
|
||||
"eventemitter3": "^5.0.4",
|
||||
"@kevisual/router": "^0.0.62",
|
||||
"@kevisual/router": "^0.0.64",
|
||||
"@kevisual/use-config": "^1.0.28",
|
||||
"ioredis": "^5.9.2",
|
||||
"minio": "^8.0.6",
|
||||
@@ -33,8 +33,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kevisual/types": "^0.0.12",
|
||||
"@types/bun": "^1.3.6",
|
||||
"@types/bun": "^1.3.8",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/node": "^25.0.10"
|
||||
"@types/node": "^25.1.0"
|
||||
}
|
||||
}
|
||||
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kevisual/cli",
|
||||
"version": "0.0.96",
|
||||
"version": "0.0.97",
|
||||
"description": "envision 命令行工具",
|
||||
"type": "module",
|
||||
"basename": "/root/cli",
|
||||
@@ -49,13 +49,13 @@
|
||||
"@kevisual/auth": "^2.0.3",
|
||||
"@kevisual/context": "^0.0.4",
|
||||
"@kevisual/use-config": "^1.0.28",
|
||||
"@opencode-ai/sdk": "^1.1.36",
|
||||
"@opencode-ai/sdk": "^1.1.44",
|
||||
"@types/busboy": "^1.5.4",
|
||||
"busboy": "^1.6.0",
|
||||
"eventemitter3": "^5.0.4",
|
||||
"jose": "^6.1.3",
|
||||
"lowdb": "^7.0.1",
|
||||
"lru-cache": "^11.2.4",
|
||||
"lru-cache": "^11.2.5",
|
||||
"micromatch": "^4.0.8",
|
||||
"pm2": "latest",
|
||||
"semver": "^7.7.3",
|
||||
@@ -67,11 +67,11 @@
|
||||
"@kevisual/logger": "^0.0.4",
|
||||
"@kevisual/query": "0.0.38",
|
||||
"@kevisual/query-login": "0.0.7",
|
||||
"@types/bun": "^1.3.6",
|
||||
"@types/bun": "^1.3.8",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/micromatch": "^4.0.10",
|
||||
"@types/node": "^25.0.10",
|
||||
"@types/node": "^25.1.0",
|
||||
"@types/semver": "^7.7.1",
|
||||
"chalk": "^5.6.2",
|
||||
"commander": "^14.0.2",
|
||||
@@ -82,7 +82,7 @@
|
||||
"ignore": "^7.0.5",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"pm2": "^6.0.14",
|
||||
"tar": "^7.5.6",
|
||||
"tar": "^7.5.7",
|
||||
"zustand": "^5.0.10"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
972
pnpm-lock.yaml
generated
972
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user