重构 CLI 代码,删除不必要的命令和文件,更新路由配置,优化构建设置

This commit is contained in:
xiongxiao
2026-03-26 21:30:42 +08:00
committed by cnb
parent 4e2ae49372
commit 33459259ad
19 changed files with 28 additions and 938 deletions

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env node
import { runParser } from '../dist/envision.js';
runParser(process.argv);

View File

@@ -1,5 +1,4 @@
import { buildWithBun } from '@kevisual/code-builder'; import { buildWithBun } from '@kevisual/code-builder';
await buildWithBun({ naming: 'envision', entry: 'src/oldindex.ts',});
await buildWithBun({ naming: 'cli', entry: 'src/cli.ts' }); await buildWithBun({ naming: 'cli', entry: 'src/cli.ts' });

View File

@@ -13,9 +13,8 @@
] ]
}, },
"bin": { "bin": {
"envision": "bin/envision.js", "envision": "bin/cli.js",
"ev": "bin/envision.js", "ev": "bin/cli.js",
"ev-next": "bin/cli.js",
"assistant": "bin/assistant.js", "assistant": "bin/assistant.js",
"assistant-server": "bin/assistant-server.js", "assistant-server": "bin/assistant-server.js",
"asst": "bin/assistant.js", "asst": "bin/assistant.js",

View File

@@ -1,4 +1,5 @@
import { app } from './index.ts'; import { app } from './index.ts';
import { program } from './oldindex.ts';
import { parse } from "@kevisual/router/commander" import { parse } from "@kevisual/router/commander"
parse({ app }); parse({ app, program });

View File

@@ -1,41 +0,0 @@
import { program, Command } from '@/program.ts';
import { app } from '../ai/index.ts';
import util from 'util';
import { chalk } from '@/module/chalk.ts';
import { logger } from '@/module/logger.ts';
const aiCmd = new Command('ai')
.description('AI 相关命令')
.action(async (opts) => {
});
const runCmd = async (cmd: string) => {
const res = await app.router.call({ path: 'cmd-run', payload: { cmd } });
const { body } = res;
const steps = body?.steps || [];
for (const step of steps) {
logger.debug(chalk.blue(`\n==== 步骤: ${step.cmd || '结束'} ====`));
logger.debug(step.result || 'No result');
}
}
const aiRun = new Command('run')
.description('执行 AI 命令')
.option('-c, --cmd <cmd>', '要执行的 CMD 命令')
.action(async (opts) => {
if (opts.cmd) {
await runCmd(opts.cmd);
} else {
console.log('请提供要执行的 CMD 命令');
}
});
const aiRunDeploy = new Command('deploy')
.description('部署 AI 后端应用')
.action(async (opts) => {
const cmd = 'ev pack -p -u';
const res = await runCmd(cmd);
});
aiCmd.addCommand(aiRun);
aiCmd.addCommand(aiRunDeploy);
program.addCommand(aiCmd);

View File

@@ -1,85 +0,0 @@
import { createKeepAlive } from '@kevisual/cnb/keep'
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { program, Command } from '@/program.ts';
const cnbCmd = new Command('cnb')
.description('CNB 相关命令')
.action(async (opts) => {
console.log('CNB 命令');
});
// cnb live -j '{"wss":"wss://...","cookie":"...","url":"..."}'
// # 或
// cnb live --json '{"wss":"wss://...","cookie":"...","url":"..."}'
const liveCmd = new Command('live')
.description('启动 CNB Keep Alive 服务')
.option('-j, --json <string>', 'JSON数据')
.option('-c, --config <string>', '配置文件路径 (优先级高于 JSON 参数), 默认keep.json')
.action(async (opts) => {
let config: { wss: string; cookie: string; url?: string };
let configPath = opts.config || 'keep.json'
if (configPath.startsWith('/')) {
configPath = path.resolve(configPath)
} else {
configPath = path.join(process.cwd(), configPath)
}
try {
let jsonString = opts.json;
if (!jsonString) {
jsonString = readFileSync(configPath, 'utf-8').trim();
}
config = JSON.parse(jsonString);
} catch (error) {
console.error('JSON 解析错误: 请检查输入的 JSON 格式是否正确');
process.exit(1);
}
if (!config.wss || !config.cookie) {
console.error('配置错误: 必须包含 wss 和 cookie 字段');
process.exit(1);
}
createKeepAlive({
wsUrl: config.wss,
cookie: config.cookie,
onDisconnect: (code) => {
console.log(`与 CNB 服务器断开连接,代码: ${code}`);
},
debug: true
});
});
cnbCmd.addCommand(liveCmd);
const workspaceCmd = new Command('workspace')
.alias('w')
.description('工作区live保活默认执行 ev cnb live -c /workspace/live/keep.json')
.action(() => {
try {
const configPath = path.join('/workspace/live/keep.json')
const json = readFileSync(configPath, 'utf-8').trim();
let config = JSON.parse(json);
if (!config.wss || !config.cookie) {
console.error('配置错误: 必须包含 wss 和 cookie 字段');
process.exit(1);
}
createKeepAlive({
wsUrl: config.wss,
cookie: config.cookie,
onDisconnect: (code) => {
console.log(`与 CNB 服务器断开连接,代码: ${code}`);
},
debug: true
});
} catch (e) {
console.error('error', e)
}
})
program.addCommand(workspaceCmd)
program.addCommand(cnbCmd);

View File

@@ -1,7 +1,6 @@
import { program, Command } from '@/program.ts'; import { program, Command } from '@/program.ts';
import { chalk } from '../../module/chalk.ts'; import { chalk } from '../../module/chalk.ts';
import path from 'node:path'; import path from 'node:path';
import { spawn } from 'node:child_process';
import { useKey } from '@kevisual/use-config'; import { useKey } from '@kevisual/use-config';
import os from 'node:os' import os from 'node:os'
import fs from 'node:fs'; import fs from 'node:fs';

View File

@@ -1,96 +0,0 @@
import { program, Command } from '@/program.ts';
import { query } from '@/module/query.ts';
import { QueryConfig } from '@kevisual/api/query-config';
import { showMore } from '@/uitls/show-more.ts';
import fs from 'node:fs';
import path from 'node:path';
const queryConfig = new QueryConfig({ query: query as any });
const command = new Command('remote-config')
.alias('rc').description('获取或设置远程配置');
const getCommand = new Command('get')
.option('-k, --key <key>', '配置键名')
.action(async (options) => {
const { key } = options || {};
if (!key) {
console.log('Please provide a key using -k or --key option.');
return;
}
const res = await queryConfig.getConfigByKey(key);
console.log('res Config Result:', showMore(res.data));
})
const listCommand = new Command('list')
.description('列出所有配置')
.action(async () => {
const res = await queryConfig.listConfig();
console.log('Remote Configs:', res);
if (res.code === 200) {
const list = res.data?.list || [];
list.forEach(item => {
console.log(item.id, item.key, item.data);
});
}
});
const updateCommand = new Command('update')
.description('更新远程配置')
.option('-k, --key <key>', '配置键名')
.option('-v, --value <value>', '配置值')
.option('-f, --file <file>', '从文件读取配置值')
.action(async (options) => {
const { key, value, file } = options || {};
if (!key) {
console.log('请提供配置键名,使用 -k 或 --key 选项。', options);
return;
}
try {
let data: any = {}
const filePath = path.resolve(process.cwd(), file);
const hasFile = fs.existsSync(filePath);
if (value) {
data = JSON.parse(value);
} else if (file || hasFile) {
// 从文件读取配置值
if (!hasFile) {
console.log('指定的文件不存在:', filePath);
return;
}
data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
} else {
console.log('请提供配置值,使用 -v 或 --value 选项,或使用 -f 或 --file 从文件读取。');
return;
}
const res = await queryConfig.updateConfig({
key,
data,
});
console.log('Update Config Result:', showMore(res.data));
} catch (error) {
console.log('Error parsing JSON:');
}
});
const deleteCommand = new Command('delete')
.description('删除远程配置')
.option('-i, --id <id>', '配置ID')
.option('-k, --key <key>', '配置键名')
.action(async (options) => {
const { key, id } = options || {};
if (!key && !id) {
console.log('请提供配置键名或配置ID使用 -k 或 --key 选项,或 -i 或 --id 选项。');
return;
}
const res = await queryConfig.deleteConfig({ key, id });
console.log('Delete Config Result:', showMore(res));
});
command.addCommand(listCommand);
command.addCommand(getCommand);
command.addCommand(updateCommand);
command.addCommand(deleteCommand);
program.addCommand(command);

View File

@@ -1,78 +0,0 @@
import { program, Command } from '@/program.ts';
import { query } from '@/module/query.ts';
import { QueryConfig } from '@kevisual/api/query-secret';
import { showMore } from '@/uitls/show-more.ts';
import fs from 'node:fs';
import path from 'node:path';
const queryConfig = new QueryConfig({ query: query as any });
const command = new Command('remote-secret')
.alias('rs').description('获取或设置远程配置');
const getCommand = new Command('get')
.option('-k, --key <key>', '配置键名')
.action(async (options) => {
const { key } = options || {};
if (!key) {
console.log('Please provide a key using -k or --key option.');
return;
}
const res = await queryConfig.getItem({ id: key });
console.log('res Config Result:', showMore(res.data));
})
const listCommand = new Command('list')
.description('列出所有配置')
.action(async () => {
const res = await queryConfig.listItems();
if (res.code === 200) {
const list = res.data?.list || [];
list.forEach(item => {
console.log(item.id, item.key, showMore(item));
});
} else {
console.log('获取错误:', res.message);
}
});
const updateCommand = new Command('update')
.description('更新远程配置')
.option('-i, --id <id>', '配置ID')
.option('-t, --title <title>', '配置值')
.option('-d, --description <description>', '配置数据JSON格式')
.action(async (options) => {
const { id, title, description } = options || {};
let updateData: any = {};
if (title) {
updateData.title = title;
}
if (description) {
updateData.description = description;
}
if (id) {
updateData.id = id;
}
const res = await queryConfig.updateItem(updateData);
console.log('修改结果:', showMore(res));
});
const deleteCommand = new Command('delete')
.description('删除远程配置')
.option('-i, --id <id>', '配置ID')
.option('-k, --key <key>', '配置键名')
.action(async (options) => {
const { key, id } = options || {};
if (!key && !id) {
console.log('请提供配置键名或配置ID使用 -k 或 --key 选项,或 -i 或 --id 选项。');
return;
}
const res = await queryConfig.deleteItem({ key, id });
console.log('Delete Config Result:', showMore(res));
});
command.addCommand(listCommand);
command.addCommand(getCommand);
command.addCommand(updateCommand);
command.addCommand(deleteCommand);
program.addCommand(command);

View File

@@ -1,104 +0,0 @@
import { program, Command } from '@/program.ts';
import { chalk } from '../module/chalk.ts';
import path from 'node:path';
import { spawn } from 'node:child_process';
import { useKey } from '@kevisual/use-config';
import os from 'node:os'
import fs from 'node:fs';
import { select } from '@inquirer/prompts';
// 执行 docker指令
// docker login --username=${DOCKER_USERNAME} ${DOCKER_REGISTRY} --password ${DOCKER_PASSWORD}
// docker login -u cnb docker.cnb.cool --password ${CNB_TOKEN}
// helm registry login -u cnb helm.cnb.cool --password ${CNB_TOKEN}
const dockerCommand = new Command('docker')
.description('Docker 相关指令')
.action(async () => {
console.log(chalk.green('Docker command executed'));
});
const login = new Command('login')
.description('登录 Docker 镜像仓库')
.option('-r , --registry <registry>', 'Docker 镜像仓库地址', 'default')
.action(async (options) => {
const { registry = 'default' } = options;
let DOCKER_USERNAME = useKey('DOCKER_USERNAME') as string;
let DOCKER_PASSWORD = useKey('DOCKER_PASSWORD') as string;
let DOCKER_REGISTRY = useKey('DOCKER_REGISTRY') as string;
if (registry !== 'default') {
DOCKER_USERNAME = 'cnb';
DOCKER_PASSWORD = useKey('CNB_TOKEN') as string;
DOCKER_REGISTRY = 'docker.cnb.cool';
}
if (!DOCKER_USERNAME || !DOCKER_PASSWORD) {
console.log(chalk.red('请先配置 DOCKER_USERNAME 和 DOCKER_PASSWORD'));
return;
}
const loginProcess = spawn('docker', [
'login',
'--username',
DOCKER_USERNAME,
DOCKER_REGISTRY,
'--password-stdin'
], {
stdio: ['pipe', 'inherit', 'inherit']
});
loginProcess.stdin.write(DOCKER_PASSWORD + '\n');
loginProcess.stdin.end();
loginProcess.on('close', (code) => {
if (code === 0) {
console.log(chalk.green('登录成功'));
} else {
console.log(chalk.red(`登录失败,退出码:${code}`));
}
});
});
dockerCommand.addCommand(login);
const helmLogin = new Command('helm')
.description('Helm 登录镜像仓库')
.action(async () => {
});
const helmLoginCommand = new Command('login')
.description('登录 Helm 镜像仓库')
.action(async () => {
let DOCKER_USERNAME = 'cnb';
let DOCKER_PASSWORD = useKey('CNB_TOKEN') as string;
if (!DOCKER_PASSWORD) {
console.log(chalk.red('请先配置 CNB_TOKEN'));
return;
}
const helmLoginProcess = spawn('helm', [
'registry',
'login',
'--username',
DOCKER_USERNAME,
'--password-stdin',
'helm.cnb.cool'
], {
stdio: ['pipe', 'inherit', 'inherit']
});
helmLoginProcess.stdin.write(DOCKER_PASSWORD + '\n');
helmLoginProcess.stdin.end();
helmLoginProcess.on('close', (code) => {
if (code === 0) {
console.log(chalk.green('Helm 登录成功'));
} else {
console.log(chalk.red(`Helm 登录失败,退出码:${code}`));
}
});
});
helmLogin.addCommand(helmLoginCommand);
program.addCommand(dockerCommand);
program.addCommand(helmLogin);

View File

@@ -1,74 +0,0 @@
import { generate } from '@kevisual/auth'
import { program, Command } from '@/program.ts';
import fs from 'node:fs';
import path from 'node:path';
export const getPath = async (dir: string) => {
const JWKS_PATH = path.join(dir, 'jwks.json');
const PRIVATE_JWK_PATH = path.join(dir, 'privateKey.json');
const PRIVATE_KEY_PATH = path.join(dir, 'privateKey.txt');
const PUBLIC_KEY_PATH = path.join(dir, 'publicKey.txt');
return {
JWKS_PATH,
PRIVATE_JWK_PATH,
PRIVATE_KEY_PATH,
PUBLIC_KEY_PATH,
}
}
const jwksCmd = new Command('jwks')
.description('JWKS 相关命令')
.action(async (opts) => {
});
const jwksGenerate = new Command('generate')
.alias('gen')
.option('-d , --dir <dir>', '指定保存目录,默认当前目录下 jwt 文件夹', 'jwt')
.description('生成 JWKS 密钥对')
.action(async (opts) => {
const dir = path.isAbsolute(opts.dir) ? opts.dir : path.join(process.cwd(), opts.dir);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const { JWKS_PATH, PRIVATE_JWK_PATH, PRIVATE_KEY_PATH, PUBLIC_KEY_PATH } = await getPath(dir);
const { jwks, privateJWK, privatePEM, publicPEM } = await generate();
fs.writeFileSync(PUBLIC_KEY_PATH, publicPEM);
fs.writeFileSync(PRIVATE_KEY_PATH, privatePEM);
fs.writeFileSync(PRIVATE_JWK_PATH, JSON.stringify(privateJWK, null, 2));
fs.writeFileSync(JWKS_PATH, JSON.stringify(jwks, null, 2));
console.log(`Keys have been saved to directory: ${dir}`);
});
jwksCmd.addCommand(jwksGenerate);
const getJWKS = new Command('get')
.description('获取 JWKS 内容')
.option('-d , --dir <dir>', '指定 JWKS 所在目录,默认当前目录下 jwt 文件夹', 'jwt')
.option('-t, --type <type>', '指定获取类型jwks 或 privateJWK', 'jwks')
.action(async (opts) => {
const dir = path.isAbsolute(opts.dir) ? opts.dir : path.join(process.cwd(), opts.dir);
const { JWKS_PATH, PRIVATE_JWK_PATH } = await getPath(dir);
const type = opts.type || 'jwks';
if (type !== 'jwks') {
if (!fs.existsSync(PRIVATE_JWK_PATH)) {
console.error(`Private JWK file not found in directory: ${dir}`);
return;
}
const privateJWKContent = fs.readFileSync(PRIVATE_JWK_PATH, 'utf-8');
console.log('Private JWK:\n');
console.log(privateJWKContent);
return;
}
if (!fs.existsSync(JWKS_PATH)) {
console.error(`JWKS file not found in directory: ${dir}`);
return;
}
const jwksContent = fs.readFileSync(JWKS_PATH, 'utf-8');
console.log('PublicJWKS:\n');
console.log(jwksContent);
});
jwksCmd.addCommand(getJWKS);
program.addCommand(jwksCmd);

View File

@@ -1,240 +0,0 @@
import { program as app, Command } from '@/program.ts';
import { getConfig, getEnvToken, writeConfig } from '@/module/index.ts';
import { queryLogin, storage } from '@/module/query.ts';
import { input } from '@inquirer/prompts';
import { Kevisual } from '@/module/kevisual.ts';
import { showMore } from '@/uitls/show-more.ts';
function isNumeric(str: string) {
return /^-?\d+\.?\d*$/.test(str);
}
const showList = (list: string[]) => {
if (list.length === 0) {
console.log('expand baseURLList is empty');
return;
}
const config = getConfig();
console.log('----current baseURL:' + config.baseURL + '----\n');
list.forEach((item, index) => {
console.log(`${index + 1}: ${item}`);
});
};
const token = new Command('token')
.option('-e, --env', 'show token in env')
.description('show token')
.action(async (opts) => {
const token = storage.getItem('token');
if (opts.env) {
console.log('token in env', getEnvToken());
} else {
console.log('token', token);
}
});
const tokenList = new Command('list')
.description('show token list')
// .option('-r --remove <number>', 'remove token by number')
.action(async (opts) => {
console.log('show token list');
queryLogin.cacheStore.init();
// const res = await queryLogin.cacheStore.cache.get('token');
console.log(queryLogin.cacheStore.cacheData);
// console.log(util.inspect(res, { colors: true, depth: 4 }));
});
token.addCommand(tokenList);
const createToken = new Command('create')
.description('create jwks token')
.action(async (opts) => {
const kevisual = new Kevisual();
const res = await kevisual.getAdminToken();
if (res.code === 200) {
const jwtToken = res.data?.accessToken;
console.log('============jwt token============\n\n');
console.log(jwtToken);
} else {
console.log('create token failed', showMore(res));
}
});
token.addCommand(createToken);
app.addCommand(token);
const baseURL = new Command('baseURL')
.alias('base')
.description('show baseURL')
.option('-a, --add <baseURL>', 'add baseURL')
.option('-r, --remove <number>', 'remove baseURL number')
.option('-s, --set <number|string>', 'set current baseURL, use number to set from list or string to set')
.option('-l, --list', 'list baseURL')
.option('-c, --clear', 'clear baseURL')
.action(async (opts) => {
let config = getConfig();
let list = (config.baseURLList as Array<string>) || [];
if (!config.baseURL) {
list = ['https://kevisual.cn'];
writeConfig({ ...config, baseURL: 'https://kevisual.cn', baseURLList: list });
config = getConfig();
}
const quineList = (list: string[]) => {
const newList = new Set(list);
return Array.from(newList);
};
if (opts.add || opts.set) {
let change = false;
if (opts.add) {
change = true;
list.push(opts.add);
} else if (opts.set) {
if (!isNumeric(opts.set)) {
change = true;
list.push(opts.set);
writeConfig({ ...config, baseURL: opts.set });
config = getConfig();
}
}
if (change) {
list = quineList(list);
writeConfig({ ...config, baseURLList: list });
config = getConfig();
showList(list);
}
}
if (opts.remove) {
const index = Number(opts.remove) - 1;
if (index < 0 || index >= list.length) {
console.log('index out of range');
return;
}
const removeBase = list.splice(index, 1);
list = quineList(list);
showList(list);
writeConfig({ ...config, baseURLList: list });
removeBase[0];
return;
}
if (opts.set) {
const isNumber = isNumeric(opts.set);
let baseURL = '';
if (isNumber) {
const index = Number(opts.set) - 1;
if (index < 0 || index >= list.length) {
console.log('index out of range');
return;
}
baseURL = list[index];
writeConfig({ ...config, baseURL: list[index] });
showList(list);
} else {
baseURL = opts.set;
}
return;
}
if (opts.list) {
showList(list);
return;
}
if (opts.clear) {
writeConfig({ ...config, baseURLList: [] });
return;
}
if (!config.baseURL) {
config = getConfig();
writeConfig({ ...config, baseURL: 'https://kevisual.cn' });
config = getConfig();
}
console.log('current baseURL:', config.baseURL);
});
app.addCommand(baseURL);
const setBaseURL = new Command('set')
.option('-b, --baseURL <baseURL>', 'set baseURL')
.description('set baseURL')
.action(async (opt) => {
const config = getConfig();
let baseURL = opt.baseURL;
if (!baseURL) {
baseURL = await input({
message: `Enter your baseURL:(current: ${config.baseURL})`,
});
if (!baseURL) {
console.log('baseURL is required');
return;
}
}
writeConfig({ ...config, baseURL });
});
baseURL.addCommand(setBaseURL);
// const showQueryURL = new Command('showQueryURL').description('show query URL').action(async () => {
// console.log("url", query.url);
// });
// app.addCommand(showQueryURL);
const rvm = new Command('registry')
.alias('reg')
.description('registry manager')
.option('-l, --list', 'list registry')
.option('-s, --set <registry>', 'set registry')
.action(async (opts) => {
const config = getConfig();
const defaultRegistry = ['https://kevisual.cn', 'https://kevisual.silkyai.cn', 'https://kevisual.xiongxiao.me', 'http://localhost:3005'];
if (opts.list) {
showList(defaultRegistry);
return;
}
if (opts.set) {
const isNumber = isNumeric(opts.set);
if (isNumber) {
const index = Number(opts.set) - 1;
if (index < 0 || index >= defaultRegistry.length) {
console.log('index out of range');
return;
}
writeConfig({ ...config, baseURL: defaultRegistry[index] });
console.log('set registry', defaultRegistry[index]);
} else {
writeConfig({ ...config, baseURL: opts.set });
console.log('set registry', opts.set);
}
}
});
app.addCommand(rvm);
const silky = new Command('silky').description('silky registry').action(async (opts) => {
console.log('silky registry');
const config = getConfig();
const defaultRegistry = ['https://kevisual.silkyai.cn'];
writeConfig({ ...config, baseURL: defaultRegistry[0] });
showList(defaultRegistry);
});
baseURL.addCommand(silky);
const xiongxiao = new Command('me').description('xiongxiao registry').action(async (opts) => {
console.log('xiongxiao registry');
const config = getConfig();
const defaultRegistry = ['https://kevisual.xiongxiao.me'];
writeConfig({ ...config, baseURL: defaultRegistry[0] });
showList(defaultRegistry);
});
baseURL.addCommand(xiongxiao);
const local = new Command('local').description('local registry').action(async (opts) => {
console.log('local registry');
const config = getConfig();
const defaultRegistry = ['http://localhost:3005'];
writeConfig({ ...config, baseURL: defaultRegistry[0] });
showList(defaultRegistry);
});
baseURL.addCommand(local);
const kv = new Command('kevisual').description('kevisual registry').action(async (opts) => {
console.log('kevisual registry');
const config = getConfig();
const defaultRegistry = ['https://kevisual.cn'];
writeConfig({ ...config, baseURL: defaultRegistry[0] });
showList(defaultRegistry);
});
baseURL.addCommand(kv);

View File

@@ -1,2 +0,0 @@
// TODO: 对 .opencode/plugin/agent.ts 的内容进行管理
// 例如添加、删除、列出等操作

View File

@@ -1,30 +0,0 @@
import { program, Command } from '@/program.ts';
import { chalk } from '@/module/chalk.ts';
const command = new Command('proxy')
.description('执行代理相关的命令')
.option('-s, --start', '启动代理')
.option('-u, --unset', '关闭代理')
.action((options) => {
// TODO: 代理相关的逻辑, 进行配置
const proxyShell = 'export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890';
const unProxyShell = 'unset https_proxy http_proxy all_proxy';
if (options.start) {
console.log(chalk.green('启动代理'));
console.log(chalk.green('执行以下命令以启用代理:'));
console.log(`\n ${chalk.yellow(proxyShell)}\n`);
console.log(`请运行以下命令应用代理:`);
console.log(chalk.cyan(`eval "$(${process.argv[1]} proxy -s)"`));
} else if (options.unset) {
console.log(chalk.green('关闭代理'));
console.log(chalk.green('执行以下命令以禁用代理:'));
console.log(`\n ${chalk.yellow(unProxyShell)}\n`);
console.log(`请运行以下命令取消代理:`);
console.log(chalk.cyan(`eval "$(${process.argv[1]} proxy -u)"`));
} else {
console.log(chalk.red('请提供选项 -s 或 -u'));
}
});
program.addCommand(command);

View File

@@ -1,46 +0,0 @@
import { program, Command } from '@/program.ts';
import { input } from '@inquirer/prompts';
import { query } from '../module/index.ts';
import chalk from 'chalk';
import util from 'util';
const router = new Command('router').description('router get');
program.addCommand(router);
// web 开发模块
const command = new Command('service')
.description('router services get')
.option('-p, --path <path>', '第一路径 path')
.option('-k, --key <key>', '第二路径 key')
.action(async (options) => {
let { path, key } = options;
// 如果没有传递参数,则通过交互式输入
if (!path) {
path = await input({
message: 'Enter your path:',
});
}
if (!key) {
key = await input({
message: 'Enter your key:',
});
}
const res = await query.post({ path, key });
if (res?.code === 200) {
console.log('query success');
const _list = res.data?.list || res.data;
if (Array.isArray(_list)) {
const data = _list.map((item: any) => {
// return `id: ${item.id}, title: ${item.title}`;
return {
id: item.id,
title: item.title,
};
});
console.log(chalk.green(util.inspect(data, { colors: true, depth: 4 })));
}
} else {
console.log('error', res.message || '');
}
});
router.addCommand(command);

View File

@@ -1,110 +0,0 @@
import { program, Command } from '@/program.ts';
import { execSync } from 'node:child_process';
import path from 'node:path';
import fs from 'node:fs';
import { getConfig } from '@/module/get-config.ts';
import { fetchLink } from '@/module/download/install.ts';
import { fileIsExist } from '@/uitls/file.ts';
import { getHash, getBufferHash } from '@/uitls/hash.ts';
import { useContextKey } from '@kevisual/context'
import semver from 'semver'
const getRunFilePath = () => {
const c = process.argv[1]; // 例子: /home/ubuntu/kevisual/cli/bin/envision.js
const runFilePath = path.resolve(c);
const isJs = runFilePath.endsWith('.js');
let distDir = '';
if (isJs) {
const dir = path.dirname(runFilePath); // /home/ubuntu/kevisual/cli/bin
distDir = path.relative(dir, '../dist'); // /home/ubuntu/kevisual/cli
} else {
distDir = path.resolve(process.cwd(), 'dist');
}
return distDir;
}
const distFiles = ["assistant-server.js", "assistant.js", "envision.js"];
const downloadNewDistFiles = async (distDir: string) => {
const baseURL = getConfig().baseURL || 'https://kevisual.cn';
const newData = distFiles.map(file => {
const url = `${baseURL}/root/cli/dist/${file}`;
const filePath = path.join(distDir, file);
const exist = fileIsExist(filePath);
let hash = '';
hash = getHash(filePath);
return { url, filePath, exist, hash };
});
const promises = newData.map(async ({ url, filePath }) => {
return await fetchLink(url, { returnContent: true });
});
let isUpdate = false;
await Promise.all(promises).then(results => {
results.forEach((res, index) => {
const data = newData[index];
const filePath = data.filePath;
const newHash = getBufferHash(res.content);
if (data.hash === newHash) {
return;
}
console.log('更新文件:', filePath);
isUpdate = true;
if (data.exist) {
fs.writeFileSync(filePath, res.content, 'utf-8');
} else {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(filePath, res.content, 'utf-8');
}
});
if (isUpdate) {
console.log('更新完成,请重新运行命令');
} else {
console.log('检测完成');
}
}).catch(error => {
console.error('Error downloading files:', error);
});
}
const getVersion = async (force?: boolean) => {
const runFilePath = getRunFilePath();
if (force) {
await downloadNewDistFiles(runFilePath);
return;
}
const baseURL = getConfig().baseURL || 'https://kevisual.cn';
const file = 'package.json';
const url = `${baseURL}/root/cli/${file}`;
const res = await fetchLink(url, { returnContent: true });
const text = res.content.toString('utf-8');
const json = JSON.parse(text);
const latestVersion = json.version;
const version = useContextKey('version');
if (semver.lt(version, latestVersion)) {
console.log('当前版本:', version, '最新版本:', latestVersion, '正在更新...');
downloadNewDistFiles(runFilePath);
} else {
console.log('已经是最新版本', version);
}
}
const update = new Command('update')
.option('-g --global', 'update global')
.option('-n --npm', 'use npm to update', false)
.option('-f --force', 'force update', false)
.description('update cli')
.action((opts) => {
try {
if (opts.npm) {
const cmd = opts.global ? 'npm install -g @kevisual/envision-cli' : 'npm install -D @kevisual/envision-cli';
execSync(cmd, { stdio: 'inherit', encoding: 'utf-8' });
} else {
const force = opts.force ? true : false;
getVersion(force)
}
} catch (error) {
console.error('Error updating CLI:', error);
}
});
program.addCommand(update);

View File

@@ -1,21 +1,21 @@
import { app } from './app.ts' import { app } from './app.ts'
// 导入所有路由模块 // 导入所有路由模块
import './routes/app.ts'; import './routes/app.ts';
import './routes/cc.ts'; // import './routes/cc.ts';
import './routes/ccc.ts'; // import './routes/ccc.ts';
import './routes/config.ts'; import './routes/config.ts';
import './routes/deploy.ts'; // import './routes/deploy.ts';
import './routes/docker.ts'; import './routes/docker.ts';
import './routes/download.ts'; import './routes/download.ts';
import './routes/gist.ts'; import './routes/gist.ts';
import './routes/jwks.ts'; import './routes/jwks.ts';
import './routes/login.ts'; import './routes/login.ts';
import './routes/npm.ts'; // import './routes/npm.ts';
import './routes/proxy.ts'; import './routes/proxy.ts';
import './routes/publish.ts'; // import './routes/publish.ts';
import './routes/remote-config.ts'; import './routes/remote-config.ts';
import './routes/remote-secret.ts'; import './routes/remote-secret.ts';
import './routes/sync.ts'; // import './routes/sync.ts';
import './routes/token-ls.ts'; import './routes/token-ls.ts';
import './routes/update.ts'; import './routes/update.ts';

View File

@@ -1,28 +1,13 @@
import { program } from '@/program.ts'; import { program } from '@/program.ts';
import './command/login.ts'; import './command/login.ts';
import './command/ls-token.ts';
import './command/deploy.ts'; import './command/deploy.ts';
import './command/config.ts';
import './command/router.ts';
import './command/npm.ts'; import './command/npm.ts';
import './command/publish.ts'; import './command/publish.ts';
import './command/proxy.ts';
import './command/update.ts';
import './command/sync/sync.ts'; import './command/sync/sync.ts';
import './command/app/index.ts';
import './command/gist/index.ts';
import './command/config-remote.ts';
import './command/config-secret-remote.ts';
import './command/coding-plan/cc.ts' import './command/coding-plan/cc.ts'
import './command/coding-plan/oc.ts' import './command/coding-plan/oc.ts'
import './command/docker.ts';
import './command/jwks.ts';
import './command/cnb/index.ts';
import './command/download.ts';
// program.parse(process.argv); // program.parse(process.argv);
@@ -31,3 +16,5 @@ export const runParser = async (argv: string[]) => {
// console.log('argv', argv); // console.log('argv', argv);
program.parse(argv); program.parse(argv);
}; };
export { program };

View File

@@ -199,7 +199,7 @@ app.route({
}).define(async (ctx) => { }).define(async (ctx) => {
const { list, set } = ctx.args; const { list, set } = ctx.args;
const config = getConfig(); const config = getConfig();
const defaultRegistry = ['https://kevisual.cn', 'https://kevisual.silkyai.cn', 'https://kevisual.xiongxiao.me', 'http://localhost:3005']; const defaultRegistry = ['https://kevisual.cn', 'https://kevisual.xiongxiao.me', 'http://localhost:3005'];
if (list) { if (list) {
showList(defaultRegistry); showList(defaultRegistry);
return; return;
@@ -236,6 +236,21 @@ app.route({
showList(defaultRegistry); showList(defaultRegistry);
}).addTo(app) }).addTo(app)
app.route({
path: 'baseURL',
key: 'me',
description: 'xiongxiao.me registry',
metadata: {
middleware: ['auth'],
args: {}
}
}).define(async () => {
const config = getConfig();
const defaultRegistry = ['https://kevisual.xiongxiao.me'];
writeConfig({ ...config, baseURL: defaultRegistry[0] });
showList(defaultRegistry);
}).addTo(app)
app.route({ app.route({
path: 'baseURL', path: 'baseURL',
key: 'local', key: 'local',