feat: add silky cli tools
This commit is contained in:
parent
75d181ef43
commit
6867de838e
4
assistant/.gitignore
vendored
4
assistant/.gitignore
vendored
@ -8,4 +8,6 @@ pack-dist
|
|||||||
assistant-app
|
assistant-app
|
||||||
|
|
||||||
.env*
|
.env*
|
||||||
!.env*example
|
!.env*example
|
||||||
|
|
||||||
|
libs
|
0
assistant/bin/assistant.js
Normal file → Executable file
0
assistant/bin/assistant.js
Normal file → Executable file
26
assistant/bun-lib.config.mjs
Normal file
26
assistant/bun-lib.config.mjs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import pkg from './package.json';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
// bun run src/index.ts --
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} p
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const w = (p) => path.join(__dirname, p);
|
||||||
|
await Bun.build({
|
||||||
|
target: 'node',
|
||||||
|
format: 'esm',
|
||||||
|
entrypoints: [w('./src/lib.ts')],
|
||||||
|
outdir: w('./libs'),
|
||||||
|
naming: {
|
||||||
|
entry: 'assistant-lib.mjs',
|
||||||
|
},
|
||||||
|
external: ['pm2'],
|
||||||
|
define: {
|
||||||
|
ENVISION_VERSION: JSON.stringify(pkg.version),
|
||||||
|
},
|
||||||
|
env: 'ENVISION_*',
|
||||||
|
});
|
@ -20,16 +20,27 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun run src/run.ts ",
|
"dev": "bun run src/run.ts ",
|
||||||
"dev:server": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 bun --watch src/run-server.ts",
|
"dev:server": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 bun --watch src/run-server.ts",
|
||||||
|
"build:lib": "bun run bun-lib.config.mjs",
|
||||||
|
"postbuild:lib": "dts -i src/lib.ts -o assistant-lib.d.ts -d libs -t",
|
||||||
"build": "rimraf dist && bun run bun.config.mjs"
|
"build": "rimraf dist && bun run bun.config.mjs"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"ev-assistant": "bin/assistant.js",
|
"ev-assistant": "bin/assistant.js",
|
||||||
"ev-asst": "bin/assistant.js"
|
"ev-asst": "bin/assistant.js"
|
||||||
},
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/assistant.mjs"
|
||||||
|
},
|
||||||
|
"./lib": {
|
||||||
|
"import": "./libs/assistant-lib.mjs",
|
||||||
|
"types": "./libs/assistant-lib.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kevisual/ai-center": "^0.0.3",
|
"@kevisual/ai-center": "^0.0.3",
|
||||||
"@kevisual/load": "^0.0.6",
|
"@kevisual/load": "^0.0.6",
|
||||||
"@kevisual/local-app-manager": "^0.1.16",
|
"@kevisual/local-app-manager": "^0.1.17",
|
||||||
"@kevisual/query": "0.0.17",
|
"@kevisual/query": "0.0.17",
|
||||||
"@kevisual/query-login": "0.0.5",
|
"@kevisual/query-login": "0.0.5",
|
||||||
"@kevisual/router": "^0.0.13",
|
"@kevisual/router": "^0.0.13",
|
||||||
@ -54,6 +65,7 @@
|
|||||||
"pino-pretty": "^13.0.0",
|
"pino-pretty": "^13.0.0",
|
||||||
"send": "^1.2.0",
|
"send": "^1.2.0",
|
||||||
"supports-color": "^10.0.0",
|
"supports-color": "^10.0.0",
|
||||||
|
"tslib": "^2.8.1",
|
||||||
"ws": "npm:@kevisual/ws",
|
"ws": "npm:@kevisual/ws",
|
||||||
"zustand": "^5.0.3"
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
@ -64,6 +76,7 @@
|
|||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"esbuild": "^0.25.3",
|
||||||
"pm2": "^6.0.5"
|
"pm2": "^6.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
3123
assistant/pnpm-lock.yaml
generated
Normal file
3123
assistant/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
2
assistant/pnpm-workspace.yaml
Normal file
2
assistant/pnpm-workspace.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
packages:
|
||||||
|
- 'tasks/**'
|
36
assistant/src/command/app/index.ts
Normal file
36
assistant/src/command/app/index.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { program, Command, assistantConfig } from '@/program.ts';
|
||||||
|
import { AppDownload } from '@/services/app/index.ts';
|
||||||
|
|
||||||
|
const appManagerCommand = new Command('app').description('本地的应用模块的安装和下载, 分为 app 和 web 两种类型');
|
||||||
|
program.addCommand(appManagerCommand);
|
||||||
|
|
||||||
|
const downloadCommand = new Command('download')
|
||||||
|
.description('下载应用')
|
||||||
|
.option('-i, --id <id>', '应用名称')
|
||||||
|
.option('-t, --type <type>', '应用类型', 'web')
|
||||||
|
.option('-r, --registry <registry>', '应用源 https://kevisual.cn')
|
||||||
|
.action(async (options) => {
|
||||||
|
const { id, type } = options;
|
||||||
|
assistantConfig.checkMounted();
|
||||||
|
const registry = options.registry || assistantConfig.getRegistry();
|
||||||
|
// console.log('registry', registry);
|
||||||
|
const app = new AppDownload(assistantConfig);
|
||||||
|
if (id) {
|
||||||
|
await app.downloadApp({ id, type, registry });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
appManagerCommand.addCommand(downloadCommand);
|
||||||
|
|
||||||
|
const deleteCommand = new Command('delete')
|
||||||
|
.description('删除应用')
|
||||||
|
.option('-i, --id <id>', '应用名称')
|
||||||
|
.option('-t, --type <type>', '应用类型', 'web')
|
||||||
|
.action(async (options) => {
|
||||||
|
const { id, type } = options;
|
||||||
|
const app = new AppDownload(assistantConfig);
|
||||||
|
if (id) {
|
||||||
|
await app.deleteApp({ id, type });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
appManagerCommand.addCommand(deleteCommand);
|
@ -2,6 +2,7 @@ import { program, runProgram } from '@/program.ts';
|
|||||||
import './command/config-manager/index.ts';
|
import './command/config-manager/index.ts';
|
||||||
import './command/app-manager/index.ts';
|
import './command/app-manager/index.ts';
|
||||||
import './command/asst-server/index.ts';
|
import './command/asst-server/index.ts';
|
||||||
|
import './command/app/index.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过命令行解析器解析参数
|
* 通过命令行解析器解析参数
|
||||||
|
2
assistant/src/lib.ts
Normal file
2
assistant/src/lib.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './module/assistant/index.ts';
|
||||||
|
export { AssistantInit } from './services/init/index.ts';
|
@ -60,8 +60,9 @@ export const initConfig = (configRootPath: string) => {
|
|||||||
};
|
};
|
||||||
export type ReturnInitConfigType = ReturnType<typeof initConfig>;
|
export type ReturnInitConfigType = ReturnType<typeof initConfig>;
|
||||||
|
|
||||||
type AssistantConfigData = {
|
export type AssistantConfigData = {
|
||||||
pageApi?: string; // https://kevisual.silkyai.cn
|
pageApi?: string; // https://kevisual.cn
|
||||||
|
registry?: string; // https://kevisual.cn
|
||||||
proxy?: ProxyInfo[];
|
proxy?: ProxyInfo[];
|
||||||
apiProxyList?: ProxyInfo[];
|
apiProxyList?: ProxyInfo[];
|
||||||
description?: string;
|
description?: string;
|
||||||
@ -135,6 +136,10 @@ export class AssistantConfig {
|
|||||||
}
|
}
|
||||||
return this.getConfig();
|
return this.getConfig();
|
||||||
}
|
}
|
||||||
|
getRegistry() {
|
||||||
|
const config = this.getCacheAssistantConfig();
|
||||||
|
return config?.registry || config?.pageApi;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 设置 assistant-config.json 配置
|
* 设置 assistant-config.json 配置
|
||||||
* @param config
|
* @param config
|
||||||
@ -211,19 +216,23 @@ export class AssistantConfig {
|
|||||||
* pem: 证书目录
|
* pem: 证书目录
|
||||||
* @param configDir
|
* @param configDir
|
||||||
*/
|
*/
|
||||||
static detectConfigDir(configDir?: string) {
|
static detectConfigDir(configDir?: string, deep = 3) {
|
||||||
const checkConfigDir = path.resolve(configDir || process.env.ASSISTANT_CONFIG_DIR || process.cwd());
|
const checkConfigDir = path.resolve(configDir || process.env.ASSISTANT_CONFIG_DIR || process.cwd());
|
||||||
const configPath = path.join(checkConfigDir, 'assistant-app');
|
const configPath = path.join(checkConfigDir, 'assistant-app');
|
||||||
if (checkFileExists(configPath)) {
|
if (checkFileExists(configPath)) {
|
||||||
return path.join(checkConfigDir);
|
return path.join(checkConfigDir);
|
||||||
}
|
}
|
||||||
const lastConfigPath = path.join(checkConfigDir, '..', 'assistant-app');
|
if (deep >= 2) {
|
||||||
if (checkFileExists(lastConfigPath)) {
|
const lastConfigPath = path.join(checkConfigDir, '..', 'assistant-app');
|
||||||
return path.join(checkConfigDir, '..');
|
if (checkFileExists(lastConfigPath)) {
|
||||||
|
return path.join(checkConfigDir, '..');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const lastConfigPath2 = path.join(checkConfigDir, '../..', 'assistant-app');
|
if (deep >= 3) {
|
||||||
if (checkFileExists(lastConfigPath2)) {
|
const lastConfigPath2 = path.join(checkConfigDir, '../..', 'assistant-app');
|
||||||
return path.join(checkConfigDir, '../..');
|
if (checkFileExists(lastConfigPath2)) {
|
||||||
|
return path.join(checkConfigDir, '../..');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 如果没有找到助手配置文件目录,则返回当前目录, 执行默认创建助手配置文件目录
|
// 如果没有找到助手配置文件目录,则返回当前目录, 执行默认创建助手配置文件目录
|
||||||
return checkConfigDir;
|
return checkConfigDir;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
export * from './install/index.ts';
|
|
||||||
export * from './config/index.ts';
|
export * from './config/index.ts';
|
||||||
export * from './file/index.ts';
|
export * from './file/index.ts';
|
||||||
|
|
||||||
|
@ -1,121 +0,0 @@
|
|||||||
import path from 'node:path';
|
|
||||||
import fs from 'node:fs';
|
|
||||||
import { checkFileExists } from '../file/index.ts';
|
|
||||||
|
|
||||||
type DownloadTask = {
|
|
||||||
downloadPath: string;
|
|
||||||
downloadUrl: string;
|
|
||||||
user: string;
|
|
||||||
key: string;
|
|
||||||
version: string;
|
|
||||||
};
|
|
||||||
export type Package = {
|
|
||||||
id: string;
|
|
||||||
name?: string;
|
|
||||||
version?: string;
|
|
||||||
description?: string;
|
|
||||||
title?: string;
|
|
||||||
user?: string;
|
|
||||||
key?: string;
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
type InstallAppOpts = {
|
|
||||||
appDir?: string;
|
|
||||||
kevisualUrl?: string;
|
|
||||||
/**
|
|
||||||
* 是否是客户端, 下载到 assistant-config的下面
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
export const installApp = async (app: Package, opts: InstallAppOpts = {}) => {
|
|
||||||
// const _app = demoData;
|
|
||||||
const { appDir = '', kevisualUrl = 'https://kevisual.cn' } = opts;
|
|
||||||
const _app = app;
|
|
||||||
try {
|
|
||||||
let files = _app.data.files || [];
|
|
||||||
const version = _app.version;
|
|
||||||
const user = _app.user;
|
|
||||||
const key = _app.key;
|
|
||||||
|
|
||||||
const downFiles = files.map((file: any) => {
|
|
||||||
const noVersionPath = file.path.replace(`/${version}`, '');
|
|
||||||
return {
|
|
||||||
...file,
|
|
||||||
downloadPath: path.join(appDir, noVersionPath),
|
|
||||||
downloadUrl: `${kevisualUrl}/${noVersionPath}`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const downloadTasks: DownloadTask[] = downFiles as any;
|
|
||||||
for (const file of downloadTasks) {
|
|
||||||
const downloadPath = file.downloadPath;
|
|
||||||
const downloadUrl = file.downloadUrl;
|
|
||||||
const dir = path.dirname(downloadPath);
|
|
||||||
if (!fs.existsSync(dir)) {
|
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
|
||||||
}
|
|
||||||
const res = await fetch(downloadUrl);
|
|
||||||
const blob = await res.blob();
|
|
||||||
fs.writeFileSync(downloadPath, Buffer.from(await blob.arrayBuffer()));
|
|
||||||
}
|
|
||||||
let indexHtml = files.find((file: any) => file.name === 'index.html');
|
|
||||||
if (!indexHtml) {
|
|
||||||
files.push({
|
|
||||||
name: 'index.html',
|
|
||||||
path: `${user}/${key}/index.html`,
|
|
||||||
});
|
|
||||||
fs.writeFileSync(path.join(appDir, `${user}/${key}/index.html`), JSON.stringify(app, null, 2));
|
|
||||||
}
|
|
||||||
_app.data.files = files;
|
|
||||||
return {
|
|
||||||
code: 200,
|
|
||||||
data: _app,
|
|
||||||
message: 'Install app success',
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return {
|
|
||||||
code: 500,
|
|
||||||
message: 'Install app failed',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export const checkAppDir = (appDir: string) => {
|
|
||||||
const files = fs.readdirSync(appDir);
|
|
||||||
if (files.length === 0) {
|
|
||||||
fs.rmSync(appDir, { recursive: true });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
type UninstallAppOpts = {
|
|
||||||
appDir?: string;
|
|
||||||
};
|
|
||||||
export const uninstallApp = async (app: Partial<Package>, opts: UninstallAppOpts = {}) => {
|
|
||||||
const { appDir = '' } = opts;
|
|
||||||
try {
|
|
||||||
const { user, key } = app;
|
|
||||||
const keyDir = path.join(appDir, user, key);
|
|
||||||
const parentDir = path.join(appDir, user);
|
|
||||||
if (!checkFileExists(appDir) || !checkFileExists(keyDir)) {
|
|
||||||
return {
|
|
||||||
code: 200,
|
|
||||||
message: 'uninstall app success',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// 删除appDir和文件
|
|
||||||
fs.rmSync(keyDir, { recursive: true });
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
checkAppDir(parentDir);
|
|
||||||
return {
|
|
||||||
code: 200,
|
|
||||||
message: 'Uninstall app success',
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return {
|
|
||||||
code: 500,
|
|
||||||
message: 'Uninstall app failed',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
0
assistant/src/module/index.ts
Normal file
0
assistant/src/module/index.ts
Normal file
@ -1 +1,124 @@
|
|||||||
// app downlaod
|
import { checkFileExists, AssistantConfig } from '@/module/assistant/index.ts';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import inquirer from 'inquirer';
|
||||||
|
|
||||||
|
import { spawnSync } from 'child_process';
|
||||||
|
export const runCommand = (command: string, args: string[]) => {
|
||||||
|
const result = spawnSync(command, args, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true,
|
||||||
|
});
|
||||||
|
if (result.error) {
|
||||||
|
console.error('Error executing command:', result.error);
|
||||||
|
throw result.error;
|
||||||
|
}
|
||||||
|
if (result.status !== 0) {
|
||||||
|
console.error('Command failed with status:', result.status);
|
||||||
|
throw new Error(`Command failed with status: ${result.status}`);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
export type appType = 'web' | 'app';
|
||||||
|
|
||||||
|
type DownloadAppOptions = {
|
||||||
|
/**
|
||||||
|
* 应用名称
|
||||||
|
* @type {string}
|
||||||
|
* @example user/app
|
||||||
|
* @description 应用名称
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
type?: appType;
|
||||||
|
registry?: string;
|
||||||
|
/**
|
||||||
|
* 应用名称,默认为 user/app的 app
|
||||||
|
*/
|
||||||
|
appName?: string;
|
||||||
|
};
|
||||||
|
type DeleteAppOptions = {
|
||||||
|
/**
|
||||||
|
* 应用名称
|
||||||
|
* @type {string}
|
||||||
|
* @example user/app
|
||||||
|
* @description 应用名称
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
type?: appType;
|
||||||
|
appName?: string;
|
||||||
|
};
|
||||||
|
export class AppDownload {
|
||||||
|
config: AssistantConfig;
|
||||||
|
constructor(config: AssistantConfig) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
async downloadApp(opts: DownloadAppOptions) {
|
||||||
|
const { id, type = 'web' } = opts;
|
||||||
|
const configDir = this.config.configDir;
|
||||||
|
this.config?.checkMounted();
|
||||||
|
const appsDir = this.config.configPath?.appsDir;
|
||||||
|
const pageDir = this.config.configPath?.pageDir;
|
||||||
|
if (!id) {
|
||||||
|
throw new Error('应用名称不能为空');
|
||||||
|
}
|
||||||
|
const command = 'ev';
|
||||||
|
const args = ['app', 'download'];
|
||||||
|
args.push('-i', id);
|
||||||
|
if (type) {
|
||||||
|
args.push('-t', type);
|
||||||
|
}
|
||||||
|
const appName = opts?.appName || id.split('/').pop();
|
||||||
|
if (type === 'web') {
|
||||||
|
args.push('-o', pageDir);
|
||||||
|
} else if (type === 'app') {
|
||||||
|
args.push('-o', path.join(appsDir, appName));
|
||||||
|
} else {
|
||||||
|
throw new Error('应用类型错误,只能是 web 或 app');
|
||||||
|
}
|
||||||
|
if (opts.registry) {
|
||||||
|
args.push('-r', opts.registry);
|
||||||
|
}
|
||||||
|
return runCommand(command, args);
|
||||||
|
}
|
||||||
|
async confirm(message?: string) {
|
||||||
|
const { confirm } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'confirm',
|
||||||
|
message: message || '是否继续删除应用?',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
return confirm;
|
||||||
|
}
|
||||||
|
async deleteApp(opts: DeleteAppOptions) {
|
||||||
|
const { id, type = 'web' } = opts;
|
||||||
|
const appName = opts?.appName || id.split('/').pop();
|
||||||
|
this.config?.checkMounted();
|
||||||
|
const appsDir = this.config.configPath?.appsDir;
|
||||||
|
const pageDir = this.config.configPath?.pageDir;
|
||||||
|
if (!id) {
|
||||||
|
throw new Error('应用名称不能为空');
|
||||||
|
}
|
||||||
|
let deletePath = '';
|
||||||
|
let isDelete = false;
|
||||||
|
if (type === 'web') {
|
||||||
|
// 直接删除路径就行
|
||||||
|
const pagePath = path.join(pageDir, id);
|
||||||
|
deletePath = pagePath;
|
||||||
|
} else if (type === 'app') {
|
||||||
|
const appPath = path.join(appsDir, appName);
|
||||||
|
deletePath = appPath;
|
||||||
|
}
|
||||||
|
if (deletePath && checkFileExists(deletePath)) {
|
||||||
|
const confirm = await this.confirm(`是否删除 ${deletePath} 应用?`);
|
||||||
|
if (!confirm) {
|
||||||
|
console.log('取消删除应用');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs.rmSync(deletePath, { recursive: true });
|
||||||
|
isDelete = true;
|
||||||
|
console.log(`删除应用成功: ${deletePath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { checkFileExists, AssistantConfig } from '@/module/assistant/index.ts';
|
import { checkFileExists, AssistantConfig, AssistantConfigData } from '@/module/assistant/index.ts';
|
||||||
import { chalk } from '@/module/chalk.ts';
|
import { chalk } from '@/module/chalk.ts';
|
||||||
import { HttpsPem } from '@/module/assistant/https/sign.ts';
|
import { HttpsPem } from '@/module/assistant/https/sign.ts';
|
||||||
export type AssistantInitOptions = {
|
export type AssistantInitOptions = {
|
||||||
@ -66,13 +66,16 @@ export class AssistantInit extends AssistantConfig {
|
|||||||
const assistantPath = this.configPath?.configPath;
|
const assistantPath = this.configPath?.configPath;
|
||||||
// 创建助手配置文件 assistant-config.json
|
// 创建助手配置文件 assistant-config.json
|
||||||
if (!checkFileExists(assistantPath, true)) {
|
if (!checkFileExists(assistantPath, true)) {
|
||||||
this.setConfig({
|
this.setConfig(this.getDefaultInitAssistantConfig());
|
||||||
description: '助手配置文件',
|
|
||||||
home: '/root/center',
|
|
||||||
proxy: [],
|
|
||||||
apiProxyList: [],
|
|
||||||
});
|
|
||||||
console.log(chalk.green('助手配置文件assistant-config.json创建成功'));
|
console.log(chalk.green('助手配置文件assistant-config.json创建成功'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
protected getDefaultInitAssistantConfig() {
|
||||||
|
return {
|
||||||
|
description: '助手配置文件',
|
||||||
|
home: '/root/center',
|
||||||
|
proxy: [],
|
||||||
|
apiProxyList: [],
|
||||||
|
} as AssistantConfigData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { execSync } from 'node:child_process';
|
|||||||
|
|
||||||
export const TaskCommandType = ['npm-install'] as const;
|
export const TaskCommandType = ['npm-install'] as const;
|
||||||
export type TaskCommand = {
|
export type TaskCommand = {
|
||||||
key?: string;
|
key?: any;
|
||||||
/**
|
/**
|
||||||
* 任务描述
|
* 任务描述
|
||||||
*/
|
*/
|
||||||
@ -30,10 +30,38 @@ export type TaskCommand = {
|
|||||||
* 如果没有这个文本,表示任务没有安装
|
* 如果没有这个文本,表示任务没有安装
|
||||||
*/
|
*/
|
||||||
beforeCheck?: string;
|
beforeCheck?: string;
|
||||||
|
tags?: string[];
|
||||||
|
meta?: any;
|
||||||
|
};
|
||||||
|
type RunTaskResult = {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: any;
|
||||||
|
output?: string;
|
||||||
|
task?: TaskCommand;
|
||||||
|
};
|
||||||
|
type TaskCommandOptions = {
|
||||||
|
isDebug?: boolean;
|
||||||
|
isLog?: boolean;
|
||||||
};
|
};
|
||||||
export class TasksCommand {
|
export class TasksCommand {
|
||||||
tasks: Map<string, TaskCommand> = new Map();
|
tasks: Map<string | number, TaskCommand> = new Map();
|
||||||
constructor() {}
|
isDebug: boolean = false;
|
||||||
|
isLog: boolean = false;
|
||||||
|
constructor(opts?: TaskCommandOptions) {
|
||||||
|
this.isDebug = opts?.isDebug ?? false;
|
||||||
|
this.isLog = opts?.isLog ?? false;
|
||||||
|
}
|
||||||
|
log(...args: any[]) {
|
||||||
|
if (this.isLog) {
|
||||||
|
console.log(...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug(...args: any[]) {
|
||||||
|
if (this.isDebug) {
|
||||||
|
console.log(...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
addTask(task: TaskCommand, run?: boolean) {
|
addTask(task: TaskCommand, run?: boolean) {
|
||||||
const key = task?.key || task?.description;
|
const key = task?.key || task?.description;
|
||||||
if (!key) {
|
if (!key) {
|
||||||
@ -45,12 +73,17 @@ export class TasksCommand {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
getTask(name: string) {
|
getTask(name: string | number) {
|
||||||
return this.tasks.get(name);
|
return this.tasks.get(name);
|
||||||
}
|
}
|
||||||
runTask(name: string) {
|
runTask(taskName: string | number | TaskCommand): RunTaskResult {
|
||||||
const task = this.getTask(name);
|
let task: TaskCommand | undefined;
|
||||||
const end = (data?: { code: number; [key: string]: any }) => {
|
if (typeof taskName === 'string' || typeof taskName === 'number') {
|
||||||
|
task = this.getTask(taskName);
|
||||||
|
} else if (typeof taskName === 'object') {
|
||||||
|
task = taskName;
|
||||||
|
}
|
||||||
|
const end = (data?: RunTaskResult) => {
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
task,
|
task,
|
||||||
@ -59,7 +92,8 @@ export class TasksCommand {
|
|||||||
if (!task) {
|
if (!task) {
|
||||||
return {
|
return {
|
||||||
code: 500,
|
code: 500,
|
||||||
message: `没有找到 ${name} 这个任务`,
|
message: `没有找到这个任务`,
|
||||||
|
task: task,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let { command, before, after, afterCheck, beforeCheck, type } = task;
|
let { command, before, after, afterCheck, beforeCheck, type } = task;
|
||||||
@ -68,8 +102,9 @@ export class TasksCommand {
|
|||||||
}
|
}
|
||||||
if (before) {
|
if (before) {
|
||||||
const res = this.runCommand(before);
|
const res = this.runCommand(before);
|
||||||
console.log('before', res, beforeCheck, this.checkForContainue(res, beforeCheck));
|
const checkForContainue = this.checkForContainue(res, beforeCheck);
|
||||||
if (!this.checkForContainue(res, beforeCheck)) {
|
this.debug('运行前检测', { runCommandResult: res, beforeCheck, checkForContainue });
|
||||||
|
if (!checkForContainue) {
|
||||||
return end({
|
return end({
|
||||||
code: 200,
|
code: 200,
|
||||||
message: `当前任务不需要执行, ${command}`,
|
message: `当前任务不需要执行, ${command}`,
|
||||||
@ -77,12 +112,14 @@ export class TasksCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const res = this.runCommand(command);
|
const res = this.runCommand(command);
|
||||||
console.log('runCommand', res);
|
this.debug(`执行任务:${command}`, { runCommandResult: res });
|
||||||
if (res.code !== 200) {
|
if (res.code !== 200) {
|
||||||
|
this.debug('当前任务执行失败:', { runCommandResult: res });
|
||||||
return end(res);
|
return end(res);
|
||||||
}
|
}
|
||||||
let checkText = res.data || '';
|
let checkText = res.data || '';
|
||||||
if (after) {
|
if (after) {
|
||||||
|
this.debug('执行后检测是否成功:', { after });
|
||||||
const res = this.runCommand(after);
|
const res = this.runCommand(after);
|
||||||
if (res.code !== 200) {
|
if (res.code !== 200) {
|
||||||
return end(res);
|
return end(res);
|
||||||
@ -91,10 +128,11 @@ export class TasksCommand {
|
|||||||
}
|
}
|
||||||
if (afterCheck) {
|
if (afterCheck) {
|
||||||
const isSuccess = checkText?.includes?.(afterCheck);
|
const isSuccess = checkText?.includes?.(afterCheck);
|
||||||
|
this.debug('执行后检测是否成功:', { afterCheck, checkText, isSuccess });
|
||||||
return end({
|
return end({
|
||||||
code: isSuccess ? 200 : 500,
|
code: isSuccess ? 200 : 500,
|
||||||
output: res.data,
|
output: res.data,
|
||||||
check: afterCheck,
|
data: afterCheck,
|
||||||
message: isSuccess ? `当前任务执行成功, ${command}` : `当前任务执行失败, ${command}`,
|
message: isSuccess ? `当前任务执行成功, ${command}` : `当前任务执行失败, ${command}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -139,4 +177,16 @@ export class TasksCommand {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
getTasksArray() {
|
||||||
|
const tasks = Array.from(this.tasks.values());
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 根据标签获取任务
|
||||||
|
* @param tag
|
||||||
|
*/
|
||||||
|
getTasksArrayByTag(tag?: string) {
|
||||||
|
const tasks = Array.from(this.tasks.values());
|
||||||
|
return tasks.filter((task) => !tag || task.tags?.includes(tag) || task.meta?.tags?.includes(tag) || task.key === tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@kevisual/task-command",
|
"name": "@kevisual/task-command",
|
||||||
"version": "0.0.2",
|
"version": "0.0.7",
|
||||||
"description": "",
|
"description": "",
|
||||||
"types": "mod.d.ts",
|
"types": "mod.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dts": "dts -i mod.ts -o mod.d.ts -d .",
|
"dts": "dts -i mod.ts -o mod.d.ts -d .",
|
||||||
"build": "bun run bun.config.mjs"
|
"build": "bun run bun.config.mjs",
|
||||||
|
"postbuild": "dts -i mod.ts -o mod.d.ts -d ."
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
2
assistant/tasks/silkyai-cli/.npmrc
Normal file
2
assistant/tasks/silkyai-cli/.npmrc
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
|
||||||
|
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
4
assistant/tasks/silkyai-cli/bin/silky.js
Executable file
4
assistant/tasks/silkyai-cli/bin/silky.js
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import { runParser } from '../dist/silky.mjs';
|
||||||
|
|
||||||
|
runParser(process.argv);
|
26
assistant/tasks/silkyai-cli/bun.config.mjs
Normal file
26
assistant/tasks/silkyai-cli/bun.config.mjs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import pkg from './package.json';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
// bun run src/index.ts --
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} p
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const w = (p) => path.join(__dirname, p);
|
||||||
|
await Bun.build({
|
||||||
|
target: 'node',
|
||||||
|
format: 'esm',
|
||||||
|
entrypoints: [w('./src/index.ts')],
|
||||||
|
outdir: w('./dist'),
|
||||||
|
naming: {
|
||||||
|
entry: 'silky.mjs',
|
||||||
|
},
|
||||||
|
external: ['pm2'],
|
||||||
|
define: {
|
||||||
|
ENVISION_VERSION: JSON.stringify(pkg.version),
|
||||||
|
},
|
||||||
|
env: 'ENVISION_*',
|
||||||
|
});
|
40
assistant/tasks/silkyai-cli/package.json
Normal file
40
assistant/tasks/silkyai-cli/package.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "@kevisual/silky-cli",
|
||||||
|
"version": "0.0.3",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bun run src/run.ts ",
|
||||||
|
"build": "bun run bun.config.mjs"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"silky": "./bin/silky.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||||
|
"license": "MIT",
|
||||||
|
"packageManager": "pnpm@10.9.0",
|
||||||
|
"type": "module",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"bin",
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"@kevisual/assistant-cli": "workspace:*",
|
||||||
|
"@kevisual/task-command": "^0.0.7",
|
||||||
|
"chalk": "^5.4.1",
|
||||||
|
"commander": "^13.1.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"dotenv": "^16.5.0",
|
||||||
|
"inquirer": "^12.6.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"nanoid": "^5.1.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"pm2": "^6.0.5"
|
||||||
|
}
|
||||||
|
}
|
2
assistant/tasks/silkyai-cli/src/command/check/index.ts
Normal file
2
assistant/tasks/silkyai-cli/src/command/check/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import './init.ts'
|
||||||
|
import './init-env.ts'
|
68
assistant/tasks/silkyai-cli/src/command/check/init-env.ts
Normal file
68
assistant/tasks/silkyai-cli/src/command/check/init-env.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { program, Command } from '@/program.ts';
|
||||||
|
|
||||||
|
import { TaskKey, silkyCommand } from '@/mdoules/talkshow.ts';
|
||||||
|
|
||||||
|
const description = `初始化运行环境,安装
|
||||||
|
1. @kevisual/cli
|
||||||
|
2. pm2
|
||||||
|
|
||||||
|
可选安装:
|
||||||
|
deno
|
||||||
|
bun
|
||||||
|
|
||||||
|
`;
|
||||||
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
const testEnv = new Command('init-env')
|
||||||
|
.description(description)
|
||||||
|
.option('-a --all')
|
||||||
|
.action((options) => {
|
||||||
|
let tag = options.all ? 'env' : 'must-env';
|
||||||
|
silkyCommand.isDebug = isDev ?? false;
|
||||||
|
const envTask = silkyCommand.getTasksArrayByTag(tag);
|
||||||
|
|
||||||
|
for (const value of envTask) {
|
||||||
|
try {
|
||||||
|
const res = silkyCommand.runTask(value);
|
||||||
|
if (res.code === 200) {
|
||||||
|
console.log('执行结果:', res.code, res.message);
|
||||||
|
} else {
|
||||||
|
console.log('执行结果错误:', res.task?.description, res.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('error', error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
program.addCommand(testEnv);
|
||||||
|
|
||||||
|
const appDownloadDesc = `下载安装talkshow的应用模块,
|
||||||
|
1. root/talkshow-admin
|
||||||
|
2. root/talkshow-code-center
|
||||||
|
3. root/center
|
||||||
|
`;
|
||||||
|
const appDownloadTask = new Command('init-talkshow')
|
||||||
|
.description(appDownloadDesc)
|
||||||
|
.option('-a --all', '下载所有应用模块或者只下载talkshow相关的')
|
||||||
|
.action((options) => {
|
||||||
|
//
|
||||||
|
let tag = options.all ? 'app' : 'talkshow';
|
||||||
|
const appTask = silkyCommand.getTasksArrayByTag(tag);
|
||||||
|
silkyCommand.isDebug = true;
|
||||||
|
for (const value of appTask) {
|
||||||
|
try {
|
||||||
|
const res = silkyCommand.runTask(value);
|
||||||
|
if (res.code === 200) {
|
||||||
|
console.log('执行结果:', res.task?.description, res.code);
|
||||||
|
} else {
|
||||||
|
console.log('执行结果错误:', res.task?.description, res.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('error', error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
program.addCommand(appDownloadTask);
|
25
assistant/tasks/silkyai-cli/src/command/check/init.ts
Normal file
25
assistant/tasks/silkyai-cli/src/command/check/init.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { program, Command } from '@/program.ts';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { AssistantInit } from '@/services/init/index.ts';
|
||||||
|
|
||||||
|
type InitCommandOptions = {
|
||||||
|
path?: string;
|
||||||
|
};
|
||||||
|
const Init = new Command('init')
|
||||||
|
.description('初始化一个助手客户端,生成配置文件。')
|
||||||
|
.option('-p --path <path>', '助手路径,默认为执行命令的目录,如果助手路径不存在则创建。')
|
||||||
|
.action((opts: InitCommandOptions) => {
|
||||||
|
// 如果path参数存在,检测path是否是相对路径,如果是相对路径,则转换为绝对路径
|
||||||
|
if (opts.path && !opts.path.startsWith('/')) {
|
||||||
|
opts.path = path.join(process.cwd(), opts.path);
|
||||||
|
} else if (opts.path) {
|
||||||
|
opts.path = path.resolve(opts.path);
|
||||||
|
}
|
||||||
|
const configDir = AssistantInit.detectConfigDir(opts.path);
|
||||||
|
const assistantInit = new AssistantInit({
|
||||||
|
path: configDir,
|
||||||
|
});
|
||||||
|
assistantInit.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
program.addCommand(Init);
|
7
assistant/tasks/silkyai-cli/src/config.ts
Normal file
7
assistant/tasks/silkyai-cli/src/config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { AssistantConfig } from '@kevisual/assistant-cli/lib';
|
||||||
|
|
||||||
|
export const configDir = AssistantConfig.detectConfigDir(null, 1);
|
||||||
|
export const assistantConfig = new AssistantConfig({
|
||||||
|
configDir,
|
||||||
|
init: false,
|
||||||
|
});
|
19
assistant/tasks/silkyai-cli/src/index.ts
Normal file
19
assistant/tasks/silkyai-cli/src/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { program, runProgram } from '@/program.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过命令行解析器解析参数
|
||||||
|
* args[0] 是执行的命令, example: node
|
||||||
|
* args[1] 是执行的脚本, example: index.ts
|
||||||
|
* @param argv
|
||||||
|
*/
|
||||||
|
export const runParser = async (argv: string[]) => {
|
||||||
|
// program.parse(process.argv);
|
||||||
|
// console.log('argv', argv);
|
||||||
|
try {
|
||||||
|
program.parse(argv);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('执行错误:', error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { runProgram };
|
116
assistant/tasks/silkyai-cli/src/mdoules/talkshow.ts
Normal file
116
assistant/tasks/silkyai-cli/src/mdoules/talkshow.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { TasksCommand } from '@kevisual/task-command/mod.ts';
|
||||||
|
export const silkyCommand = new TasksCommand();
|
||||||
|
export const enum TaskKey {
|
||||||
|
ev = 1,
|
||||||
|
pm2,
|
||||||
|
deno,
|
||||||
|
bun,
|
||||||
|
silkyInit,
|
||||||
|
appCenter,
|
||||||
|
appTalkshowAdmin,
|
||||||
|
appTalkshow,
|
||||||
|
}
|
||||||
|
export const init = {
|
||||||
|
description: '安装依赖',
|
||||||
|
key: TaskKey.ev,
|
||||||
|
command: 'npm install -g @kevisual/cli --registry=https://registry.npmmirror.com',
|
||||||
|
type: 'npm-install',
|
||||||
|
before: 'ev -v',
|
||||||
|
beforeCheck: '.',
|
||||||
|
tags: ['env', 'must-env'],
|
||||||
|
};
|
||||||
|
silkyCommand.addTask(init);
|
||||||
|
|
||||||
|
const init1 = {
|
||||||
|
description: '安装 pm2',
|
||||||
|
type: 'npm-install',
|
||||||
|
key: TaskKey.pm2,
|
||||||
|
command: 'npm install -g pm2 --registry=https://registry.npmmirror.com',
|
||||||
|
before: 'pm2 -v',
|
||||||
|
beforeCheck: '.',
|
||||||
|
tags: ['env', 'must-env'],
|
||||||
|
};
|
||||||
|
silkyCommand.addTask(init1);
|
||||||
|
|
||||||
|
const init2 = {
|
||||||
|
description: '安装 deno',
|
||||||
|
command: 'npm install -g deno --registry=https://registry.npmmirror.com',
|
||||||
|
key: TaskKey.deno,
|
||||||
|
type: 'npm-install',
|
||||||
|
before: 'deno -v',
|
||||||
|
beforeCheck: 'deno',
|
||||||
|
tags: ['env'],
|
||||||
|
};
|
||||||
|
silkyCommand.addTask(init2);
|
||||||
|
|
||||||
|
const init3 = {
|
||||||
|
description: '安装 bun',
|
||||||
|
type: 'npm-install',
|
||||||
|
key: TaskKey.bun,
|
||||||
|
command: 'npm install -g bun --registry=https://registry.npmmirror.com',
|
||||||
|
before: 'bun -v',
|
||||||
|
beforeCheck: '.',
|
||||||
|
tags: ['env'],
|
||||||
|
};
|
||||||
|
silkyCommand.addTask(init3);
|
||||||
|
|
||||||
|
// ===========================
|
||||||
|
// 安装talkshow的程序到本地
|
||||||
|
|
||||||
|
const talkInit = {
|
||||||
|
description: '初始化一个助手客户端,生成配置文件。',
|
||||||
|
key: TaskKey.silkyInit,
|
||||||
|
command: 'silky init',
|
||||||
|
};
|
||||||
|
silkyCommand.addTask(talkInit);
|
||||||
|
|
||||||
|
const task1 = {
|
||||||
|
description: '下载前端应用 root/center 应用',
|
||||||
|
key: TaskKey.appCenter,
|
||||||
|
command: 'asst app download -i root/center',
|
||||||
|
tags: ['app', 'root'],
|
||||||
|
};
|
||||||
|
silkyCommand.addTask(task1);
|
||||||
|
|
||||||
|
const task2 = {
|
||||||
|
description: '下载前端应用 root/talkshow-admin 应用',
|
||||||
|
key: TaskKey.appTalkshowAdmin,
|
||||||
|
command: 'asst app download -i root/talkshow-admin',
|
||||||
|
tags: ['app', 'talkshow'],
|
||||||
|
};
|
||||||
|
silkyCommand.addTask(task2);
|
||||||
|
|
||||||
|
const task3 = {
|
||||||
|
description: '安装后端应用 root/talkshow-code-center 应用',
|
||||||
|
key: TaskKey.appTalkshow,
|
||||||
|
command: 'asst app download -i root/talkshow-code-center -t app',
|
||||||
|
tags: ['app', 'talkshow'],
|
||||||
|
};
|
||||||
|
silkyCommand.addTask(task3);
|
||||||
|
|
||||||
|
// ===========================
|
||||||
|
const testTask = {
|
||||||
|
description: '测试',
|
||||||
|
command: 'npm i -g nodemon --registry=https://registry.npmmirror.com',
|
||||||
|
type: 'npm-install',
|
||||||
|
before: 'nodemon -v',
|
||||||
|
beforeCheck: '.',
|
||||||
|
key: 'test-task',
|
||||||
|
};
|
||||||
|
silkyCommand.addTask(testTask);
|
||||||
|
|
||||||
|
const runTask1 = async () => {
|
||||||
|
const tasksCommand = new TasksCommand();
|
||||||
|
tasksCommand.addTask(init2);
|
||||||
|
const res = tasksCommand.runTask(init2.description);
|
||||||
|
console.log(res);
|
||||||
|
};
|
||||||
|
// runTask1();
|
||||||
|
const runTestTask = async () => {
|
||||||
|
const tasksCommand = new TasksCommand();
|
||||||
|
tasksCommand.addTask(testTask);
|
||||||
|
const res = tasksCommand.runTask(testTask);
|
||||||
|
console.log(res);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
// runTestTask();
|
24
assistant/tasks/silkyai-cli/src/program.ts
Normal file
24
assistant/tasks/silkyai-cli/src/program.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { program, Command } from 'commander';
|
||||||
|
import fs from 'fs';
|
||||||
|
import './command/check/index.ts';
|
||||||
|
|
||||||
|
// 将多个子命令加入主程序中
|
||||||
|
let version = '0.0.1';
|
||||||
|
try {
|
||||||
|
// @ts-ignore
|
||||||
|
if (ENVISION_VERSION) version = ENVISION_VERSION;
|
||||||
|
} catch (e) {}
|
||||||
|
// @ts-ignore
|
||||||
|
program.name('silky').description('A CLI tool with silky').version(version, '-v, --version', 'output the current version');
|
||||||
|
|
||||||
|
export { program, Command };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在命令行中运行程序
|
||||||
|
* 当前命令参数去执行 其他的应用模块
|
||||||
|
* @param args
|
||||||
|
*/
|
||||||
|
export const runProgram = (args: string[]) => {
|
||||||
|
const [_app, _command] = process.argv;
|
||||||
|
program.parse([_app, _command, ...args]);
|
||||||
|
};
|
6
assistant/tasks/silkyai-cli/src/run.ts
Normal file
6
assistant/tasks/silkyai-cli/src/run.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { runParser } from './index.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test run parser
|
||||||
|
*/
|
||||||
|
runParser(process.argv);
|
12
assistant/tasks/silkyai-cli/src/services/init/index.ts
Normal file
12
assistant/tasks/silkyai-cli/src/services/init/index.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { AssistantInit as BaseAssistantInit } from '@kevisual/assistant-cli/lib';
|
||||||
|
|
||||||
|
export class AssistantInit extends BaseAssistantInit {
|
||||||
|
constructor(options: { path?: string }) {
|
||||||
|
super(options);
|
||||||
|
}
|
||||||
|
protected getDefaultInitAssistantConfig() {
|
||||||
|
const config = super.getDefaultInitAssistantConfig();
|
||||||
|
config.registry = 'https://kevisual.silkyai.cn';
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
14
assistant/tasks/silkyai-cli/tsconfig.json
Normal file
14
assistant/tasks/silkyai-cli/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "@kevisual/types/json/backend.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*",
|
||||||
|
],
|
||||||
|
}
|
@ -1,74 +0,0 @@
|
|||||||
import { TasksCommand } from 'https://esm.xiongxiao.me/@kevisual/task-command/mod.ts';
|
|
||||||
// import { TasksCommand } from '../../task-command/mod.ts';
|
|
||||||
const init = {
|
|
||||||
description: '安装依赖',
|
|
||||||
command: 'npm install -g @kevisual/cli --registry=https://registry.npmmirror.com',
|
|
||||||
afterCheck: 'added',
|
|
||||||
};
|
|
||||||
const init1 = {
|
|
||||||
description: '安装 pm2',
|
|
||||||
type: 'npm-install',
|
|
||||||
command: 'npm install -g pm2 --registry=https://registry.npmmirror.com',
|
|
||||||
before: 'pm2 -v',
|
|
||||||
};
|
|
||||||
const init2 = {
|
|
||||||
description: '安装 deno',
|
|
||||||
command: 'npm install -g deno --registry=https://registry.npmmirror.com',
|
|
||||||
type: 'npm-install',
|
|
||||||
before: 'deno -v',
|
|
||||||
beforeCheck: 'deno',
|
|
||||||
};
|
|
||||||
const init3 = {
|
|
||||||
description: '安装 bun',
|
|
||||||
type: 'npm-install',
|
|
||||||
command: 'npm install -g bun --registry=https://registry.npmmirror.com',
|
|
||||||
beforeCheck: 'bun -v',
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// 安装talkshow的程序到本地
|
|
||||||
const talk = {
|
|
||||||
description: '设置默认的 base registry 地址 为 https://kevisual.silkyai.cn',
|
|
||||||
command: 'ev base -s https://kevisual.silkyai.cn',
|
|
||||||
};
|
|
||||||
const talkInit = {
|
|
||||||
description: '初始化一个助手客户端,生成配置文件。',
|
|
||||||
command: 'asst init',
|
|
||||||
};
|
|
||||||
const task1 = {
|
|
||||||
description: '下载前端应用 root/center 应用',
|
|
||||||
command: 'ev app download -i root/center -o assistant-app/page',
|
|
||||||
};
|
|
||||||
|
|
||||||
const task2 = {
|
|
||||||
description: '下载前端应用 root/talkshow-admin 应用',
|
|
||||||
command: 'ev app download -i root/talkshow-admin -o assistant-app/page',
|
|
||||||
};
|
|
||||||
|
|
||||||
const task3 = {
|
|
||||||
description: '安装后端应用 root/talkshow-code-center 应用',
|
|
||||||
command: 'ev app download -i root/talkshow-code-center -t app -o assistant-app/apps/talkshow-code-center',
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================
|
|
||||||
|
|
||||||
const runTask1 = async () => {
|
|
||||||
const tasksCommand = new TasksCommand();
|
|
||||||
tasksCommand.addTask(init2);
|
|
||||||
const res = tasksCommand.runTask(init2.description);
|
|
||||||
console.log(res);
|
|
||||||
};
|
|
||||||
// runTask1();
|
|
||||||
const runTestTask = async () => {
|
|
||||||
const tasksCommand = new TasksCommand();
|
|
||||||
const task = {
|
|
||||||
description: 'test',
|
|
||||||
command: 'npm i -g rollup --registry=https://registry.npmmirror.com',
|
|
||||||
type: 'npm-install',
|
|
||||||
};
|
|
||||||
tasksCommand.addTask(task);
|
|
||||||
const res = tasksCommand.runTask(task.description);
|
|
||||||
console.log(res);
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
runTestTask();
|
|
@ -2,6 +2,10 @@
|
|||||||
"extends": "@kevisual/types/json/backend.json",
|
"extends": "@kevisual/types/json/backend.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"outDir": "./libs",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"noEmit": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": [
|
"@/*": [
|
||||||
"src/*"
|
"src/*"
|
||||||
|
0
bin/assistant.js
Normal file → Executable file
0
bin/assistant.js
Normal file → Executable file
@ -27,7 +27,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun run src/run.ts ",
|
"dev": "bun run src/run.ts ",
|
||||||
"build": "rimraf dist && bun run bun.config.mjs",
|
"build": "rimraf dist && bun run bun.config.mjs",
|
||||||
"dts": "dts-bundle-generator --external-inlines=@types/jsonwebtoken src/index.ts -o dist/index.d.ts "
|
"dts": "dts-bundle-generator --external-inlines=@types/jsonwebtoken src/index.ts -o dist/index.d.ts ",
|
||||||
|
"build:all": "rimraf dist && bun run bun.config.mjs && bun run assistant/bun.config.mjs"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"kevisual",
|
"kevisual",
|
||||||
|
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@ -92,8 +92,8 @@ importers:
|
|||||||
specifier: ^0.0.6
|
specifier: ^0.0.6
|
||||||
version: 0.0.6
|
version: 0.0.6
|
||||||
'@kevisual/local-app-manager':
|
'@kevisual/local-app-manager':
|
||||||
specifier: ^0.1.16
|
specifier: ^0.1.17
|
||||||
version: 0.1.16(@kevisual/router@0.0.13)(@kevisual/types@0.0.7)(@kevisual/use-config@1.0.11(dotenv@16.5.0))(pm2@6.0.5(supports-color@10.0.0))
|
version: 0.1.17(@kevisual/router@0.0.13)(@kevisual/types@0.0.7)(@kevisual/use-config@1.0.11(dotenv@16.5.0))(pm2@6.0.5(supports-color@10.0.0))
|
||||||
'@kevisual/query':
|
'@kevisual/query':
|
||||||
specifier: 0.0.17
|
specifier: 0.0.17
|
||||||
version: 0.0.17(@kevisual/ws@8.0.0)(encoding@0.1.13)
|
version: 0.0.17(@kevisual/ws@8.0.0)(encoding@0.1.13)
|
||||||
@ -535,8 +535,8 @@ packages:
|
|||||||
'@kevisual/load@0.0.6':
|
'@kevisual/load@0.0.6':
|
||||||
resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==}
|
resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==}
|
||||||
|
|
||||||
'@kevisual/local-app-manager@0.1.16':
|
'@kevisual/local-app-manager@0.1.17':
|
||||||
resolution: {integrity: sha512-PiXjL6bFuWyyEgzCaM6omDm7cFYeUMv7SFJ+Axg88RAamqpSHaIhRxvj2wfd+HAh089a16o8oRF9V62cWaURDQ==}
|
resolution: {integrity: sha512-0Ye+GwxPd9FwaICNJoG5avkScVZ9OnTtUfskFFA6UBiSJ7MT4ZBhS2dzwU4o2Yl6mV951M7rXN5Kbs08pYJWUg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@kevisual/router': ^0.0.6
|
'@kevisual/router': ^0.0.6
|
||||||
'@kevisual/types': ^0.0.1
|
'@kevisual/types': ^0.0.1
|
||||||
@ -2623,7 +2623,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
eventemitter3: 5.0.1
|
eventemitter3: 5.0.1
|
||||||
|
|
||||||
'@kevisual/local-app-manager@0.1.16(@kevisual/router@0.0.13)(@kevisual/types@0.0.7)(@kevisual/use-config@1.0.11(dotenv@16.5.0))(pm2@6.0.5(supports-color@10.0.0))':
|
'@kevisual/local-app-manager@0.1.17(@kevisual/router@0.0.13)(@kevisual/types@0.0.7)(@kevisual/use-config@1.0.11(dotenv@16.5.0))(pm2@6.0.5(supports-color@10.0.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@kevisual/router': 0.0.13
|
'@kevisual/router': 0.0.13
|
||||||
'@kevisual/types': 0.0.7
|
'@kevisual/types': 0.0.7
|
||||||
|
@ -9,6 +9,9 @@ import { checkAppDir, installApp, uninstallApp } from '@/module/download/install
|
|||||||
import { fileIsExist } from '@/uitls/file.ts';
|
import { fileIsExist } from '@/uitls/file.ts';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { getConfig } from '@/module/get-config.ts';
|
import { getConfig } from '@/module/get-config.ts';
|
||||||
|
import path from 'path';
|
||||||
|
import inquirer from 'inquirer';
|
||||||
|
import { baseURL, getUrl } from '@/module/query.ts';
|
||||||
export const appCommand = new Command('app').description('app 命令').action(() => {
|
export const appCommand = new Command('app').description('app 命令').action(() => {
|
||||||
console.log('app');
|
console.log('app');
|
||||||
});
|
});
|
||||||
@ -55,7 +58,6 @@ const downloadAppCommand = new Command('download')
|
|||||||
} else {
|
} else {
|
||||||
data.id = id;
|
data.id = id;
|
||||||
}
|
}
|
||||||
const res = await queryApp(data);
|
|
||||||
let registry = 'https://kevisual.cn';
|
let registry = 'https://kevisual.cn';
|
||||||
if (options?.registry) {
|
if (options?.registry) {
|
||||||
registry = new URL(options.registry).origin;
|
registry = new URL(options.registry).origin;
|
||||||
@ -63,6 +65,8 @@ const downloadAppCommand = new Command('download')
|
|||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
registry = new URL(config.baseURL).origin;
|
registry = new URL(config.baseURL).origin;
|
||||||
}
|
}
|
||||||
|
const res = await queryApp(data, { url: getUrl(registry) });
|
||||||
|
console.log('registry', registry, data);
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
const app = res.data;
|
const app = res.data;
|
||||||
let appType: 'app' | 'web' = 'web';
|
let appType: 'app' | 'web' = 'web';
|
||||||
@ -88,9 +92,36 @@ const downloadAppCommand = new Command('download')
|
|||||||
|
|
||||||
const uninstallAppCommand = new Command('uninstall')
|
const uninstallAppCommand = new Command('uninstall')
|
||||||
.alias('remove')
|
.alias('remove')
|
||||||
.description('卸载 app serve client的包')
|
.description('卸载 app serve client的包。 手动删除更简单。')
|
||||||
.option('-i, --id <id>', 'user/key')
|
.option('-i, --id <id>', 'user/key')
|
||||||
|
.option('-t, --type <type>', 'app,或者web, 默认为web', 'web')
|
||||||
|
.option('-p, --path <path>', '删除的路径, 如果存在,则优先执行,不会去判断 id 和 type 。')
|
||||||
.action(async (options) => {
|
.action(async (options) => {
|
||||||
|
if (options.path) {
|
||||||
|
const _path = path.resolve(options.path);
|
||||||
|
try {
|
||||||
|
const checkPath = fileIsExist(_path);
|
||||||
|
if (!checkPath) {
|
||||||
|
console.error(chalk.red('path is error, 请输入正确的路径'));
|
||||||
|
} else {
|
||||||
|
const answer = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'confirm',
|
||||||
|
message: `确定要删除 ${_path} 吗?`,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
if (answer.confirm) {
|
||||||
|
fs.rmSync(_path, { recursive: true });
|
||||||
|
console.log(chalk.green('删除成功', _path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(chalk.red('删除失败', e));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
const id = options.id || '';
|
const id = options.id || '';
|
||||||
if (!id) {
|
if (!id) {
|
||||||
console.error(chalk.red('id is required'));
|
console.error(chalk.red('id is required'));
|
||||||
@ -102,7 +133,7 @@ const uninstallAppCommand = new Command('uninstall')
|
|||||||
data.user = user;
|
data.user = user;
|
||||||
data.key = key;
|
data.key = key;
|
||||||
} else {
|
} else {
|
||||||
console.error(chalk.red('id is required'));
|
console.error(chalk.red('id is required user/key'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +144,7 @@ const uninstallAppCommand = new Command('uninstall')
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
appDir: '',
|
appDir: '',
|
||||||
|
type: options.type,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (result.code === 200) {
|
if (result.code === 200) {
|
||||||
|
@ -105,11 +105,17 @@ export const installApp = async (app: Package, opts: InstallAppOpts = {}) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 检查是否为空,如果为空则删除
|
||||||
|
* @param appDir
|
||||||
|
*/
|
||||||
export const checkAppDir = (appDir: string) => {
|
export const checkAppDir = (appDir: string) => {
|
||||||
const files = fs.readdirSync(appDir);
|
try {
|
||||||
if (files.length === 0) {
|
const files = fs.readdirSync(appDir);
|
||||||
fs.rmSync(appDir, { recursive: true });
|
if (files.length === 0) {
|
||||||
}
|
fs.rmSync(appDir, { recursive: true });
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
export const checkFileExists = (path: string) => {
|
export const checkFileExists = (path: string) => {
|
||||||
try {
|
try {
|
||||||
@ -121,9 +127,10 @@ export const checkFileExists = (path: string) => {
|
|||||||
};
|
};
|
||||||
type UninstallAppOpts = {
|
type UninstallAppOpts = {
|
||||||
appDir?: string;
|
appDir?: string;
|
||||||
|
type?: 'app' | 'web';
|
||||||
};
|
};
|
||||||
export const uninstallApp = async (app: Partial<Package>, opts: UninstallAppOpts = {}) => {
|
export const uninstallApp = async (app: Partial<Package>, opts: UninstallAppOpts = {}) => {
|
||||||
const { appDir = '' } = opts;
|
const { appDir = '', type = 'web' } = opts;
|
||||||
try {
|
try {
|
||||||
const { user, key } = app;
|
const { user, key } = app;
|
||||||
const keyDir = path.join(appDir, user, key);
|
const keyDir = path.join(appDir, user, key);
|
||||||
@ -140,7 +147,7 @@ export const uninstallApp = async (app: Partial<Package>, opts: UninstallAppOpts
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
checkAppDir(parentDir);
|
type === 'web' && checkAppDir(parentDir);
|
||||||
return {
|
return {
|
||||||
code: 200,
|
code: 200,
|
||||||
message: 'Uninstall app success',
|
message: 'Uninstall app success',
|
||||||
|
@ -45,3 +45,12 @@ export const queryLogin = new QueryLoginNode({
|
|||||||
// console.log('onLoad');
|
// console.log('onLoad');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getUrl = (url: string) => {
|
||||||
|
return new URL('/api/router', url).href;
|
||||||
|
};
|
||||||
|
@ -5,12 +5,15 @@ type QueryAppParams = {
|
|||||||
user?: string;
|
user?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
};
|
};
|
||||||
export const queryApp = async (params: QueryAppParams) => {
|
export const queryApp = async (params: QueryAppParams, opts?: any) => {
|
||||||
return await query.post({
|
return await query.post(
|
||||||
path: 'app',
|
{
|
||||||
key: 'getApp',
|
path: 'app',
|
||||||
data: {
|
key: 'getApp',
|
||||||
...params,
|
data: {
|
||||||
|
...params,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
opts,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user