update add update

This commit is contained in:
2025-11-28 02:49:52 +08:00
parent 01c253e553
commit b2fcc84321
10 changed files with 162 additions and 81 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@kevisual/cli",
"version": "0.0.64",
"version": "0.0.65",
"description": "envision 命令行工具",
"type": "module",
"basename": "/root/cli",
@@ -29,6 +29,7 @@
"dev": "bun src/run.ts ",
"dev:tsx": "tsx src/run.ts ",
"build": "rimraf dist && bun run bun.config.mjs",
"deploy": "ev pack -u -p -m no",
"pub:me": "npm publish --registry https://npm.xiongxiao.me --tag beta",
"postbuild": "cd assistant && pnpm build ",
"dts": "dts-bundle-generator --external-inlines=@types/jsonwebtoken src/index.ts -o dist/index.d.ts "
@@ -39,8 +40,10 @@
],
"author": "abearxiong",
"dependencies": {
"@kevisual/context": "^0.0.4",
"micromatch": "^4.0.8",
"pm2": "^6.0.14"
"pm2": "^6.0.14",
"semver": "^7.7.3"
},
"devDependencies": {
"@kevisual/dts": "^0.0.3",
@@ -53,6 +56,7 @@
"@types/jsonwebtoken": "^9.0.10",
"@types/micromatch": "^4.0.10",
"@types/node": "^24.10.1",
"@types/semver": "^7.7.1",
"chalk": "^5.6.2",
"commander": "^14.0.2",
"crypto-js": "^4.2.0",

55
pnpm-lock.yaml generated
View File

@@ -8,12 +8,18 @@ importers:
.:
dependencies:
'@kevisual/context':
specifier: ^0.0.4
version: 0.0.4
micromatch:
specifier: ^4.0.8
version: 4.0.8
pm2:
specifier: ^6.0.14
version: 6.0.14(supports-color@10.2.2)
semver:
specifier: ^7.7.3
version: 7.7.3
devDependencies:
'@kevisual/dts':
specifier: ^0.0.3
@@ -45,6 +51,9 @@ importers:
'@types/node':
specifier: ^24.10.1
version: 24.10.1
'@types/semver':
specifier: ^7.7.1
version: 7.7.1
chalk:
specifier: ^5.6.2
version: 5.6.2
@@ -336,8 +345,8 @@ packages:
'@kevisual/cache@0.0.3':
resolution: {integrity: sha512-BWEck69KYL96/ywjYVkML974RHjDJTj2ITQND1zFPR+hlBV1H1p55QZgSYRJCObg3EAV1S9Zic/fR2T4pfe8yg==}
'@kevisual/cache@0.0.3':
resolution: {integrity: sha512-BWEck69KYL96/ywjYVkML974RHjDJTj2ITQND1zFPR+hlBV1H1p55QZgSYRJCObg3EAV1S9Zic/fR2T4pfe8yg==}
'@kevisual/context@0.0.4':
resolution: {integrity: sha512-HJeLeZQLU+7tCluSfOyvkgKLs0HjCZrdJlZgEgKRSa8XTwZfMAUt6J7qZTbrZAHBlPtX68EPu/PI8JMCeu3WAQ==}
'@kevisual/dts@0.0.3':
resolution: {integrity: sha512-4T/m2LqhtwWEW+lWmg7jLxKFW7VtIAftsWFDDZvh10bZunqFf8iXxChHcVSQWikghJb4cq1IkWzPkvc2l+Asdw==}
@@ -357,11 +366,6 @@ packages:
peerDependencies:
'@kevisual/query': ^0
'@kevisual/query-login@0.0.7':
resolution: {integrity: sha512-oOyPIz337cdTt7WncFj7Wr7nxUHh0pBB6KSAJlas+lQiWBPwQEZhpEd7YciydCRlMc9IJMcZRV1Bw3qgy8FFqQ==}
peerDependencies:
'@kevisual/query': ^0
'@kevisual/query@0.0.29':
resolution: {integrity: sha512-rQZk0J073UuC1QGzuyq+pb4Y0hu8/Qx/xYHs9NbsmslM+RuMnd1zpXmvhXNj7Kn1MdYTH90ng2MlFLBkkQFaIg==}
@@ -560,9 +564,6 @@ packages:
'@types/bun@1.3.3':
resolution: {integrity: sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g==}
'@types/bun@1.3.3':
resolution: {integrity: sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g==}
'@types/crypto-js@4.2.2':
resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==}
@@ -593,6 +594,9 @@ packages:
'@types/resolve@1.20.2':
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
'@types/semver@7.7.1':
resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==}
'@types/send@1.2.1':
resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==}
@@ -673,9 +677,6 @@ packages:
bun-types@1.3.3:
resolution: {integrity: sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ==}
bun-types@1.3.3:
resolution: {integrity: sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ==}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
@@ -1442,13 +1443,13 @@ packages:
engines: {node: '>=10'}
hasBin: true
semver@7.6.3:
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
semver@7.7.2:
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
engines: {node: '>=10'}
hasBin: true
semver@7.7.2:
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
semver@7.7.3:
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
engines: {node: '>=10'}
hasBin: true
@@ -1846,9 +1847,7 @@ snapshots:
dependencies:
idb-keyval: 6.2.1
'@kevisual/cache@0.0.3':
dependencies:
idb-keyval: 6.2.1
'@kevisual/context@0.0.4': {}
'@kevisual/dts@0.0.3(typescript@5.8.2)':
dependencies:
@@ -2085,10 +2084,6 @@ snapshots:
'@types/braces@3.0.5': {}
'@types/bun@1.3.3':
dependencies:
bun-types: 1.3.3
'@types/bun@1.3.3':
dependencies:
bun-types: 1.3.3
@@ -2125,6 +2120,8 @@ snapshots:
'@types/resolve@1.20.2': {}
'@types/semver@7.7.1': {}
'@types/send@1.2.1':
dependencies:
'@types/node': 24.10.1
@@ -2186,10 +2183,6 @@ snapshots:
buffer-from@1.1.2: {}
bun-types@1.3.3:
dependencies:
'@types/node': 24.10.1
bun-types@1.3.3:
dependencies:
'@types/node': 24.10.1
@@ -2621,7 +2614,7 @@ snapshots:
lodash.isstring: 4.0.1
lodash.once: 4.1.1
ms: 2.1.3
semver: 7.6.3
semver: 7.7.3
jwa@1.4.1:
dependencies:
@@ -3009,10 +3002,10 @@ snapshots:
dependencies:
lru-cache: 6.0.0
semver@7.6.3: {}
semver@7.7.2: {}
semver@7.7.3: {}
send@1.2.0(supports-color@10.2.2):
dependencies:
debug: 4.4.0(supports-color@10.2.2)

View File

@@ -4,10 +4,8 @@ import path from 'path';
import fs from 'fs';
import FormData from 'form-data';
import { getBaseURL, query, storage } from '@/module/query.ts';
import { getConfig } from '@/module/index.ts';
import inquirer from 'inquirer';
import chalk from 'chalk';
import { installDeps } from '@/uitls/npm.ts';
import { upload } from '@/module/download/upload.ts';
import { getHash } from '@/uitls/hash.ts';
import { queryAppVersion } from '@/query/app-manager/query-app.ts';
@@ -96,7 +94,7 @@ const command = new Command('deploy')
dot,
absolute: true,
});
console.log('files', files);
// console.log('files', files);
// 添加一个工具函数来统一处理路径
const normalizeFilePath = (filePath: string) => {
return filePath.split(path.sep).join('/');
@@ -224,7 +222,6 @@ const uploadFiles = async (files: string[], directory: string, opts: UploadFileO
console.error('check failed', res);
return res;
}
console.log('res', res);
let needUpload = false;
for (const file of files) {
const filePath = path.join(directory, file);

View File

@@ -62,6 +62,8 @@ const loginCommand = new Command('login')
const res = await queryLogin.login({
username,
password,
}).catch((err) => {
return { code: 500, message: err.message || '' };
});
if (res.code === 200) {
console.log('welcome', username);

View File

@@ -36,8 +36,9 @@ async function collectFileInfo(filePath: string, baseDir = '.'): Promise<any[]>
* @param files 文件列表, 或者文件夹列表
* @param cwd 当前工作目录
* @param packDist 打包目录 pack-dist
* @param mergeDist 是否合并 dist 目录到 pack-dist 中
*/
export const copyFilesToPackDist = async (files: string[], cwd: string, packDist = 'pack-dist') => {
export const copyFilesToPackDist = async (files: string[], cwd: string, packDist = 'pack-dist', mergeDist = true) => {
const packDistPath = path.join(cwd, packDist);
if (!fileIsExist(packDistPath)) {
fs.mkdirSync(packDistPath, { recursive: true });
@@ -47,10 +48,12 @@ export const copyFilesToPackDist = async (files: string[], cwd: string, packDist
files.forEach((file) => {
const stat = fs.statSync(path.join(cwd, file));
let outputFile = file;
if (file.startsWith('dist/')) {
outputFile = file.replace(/^dist\//, '');
} else if (file === 'dist') {
outputFile = '';
if (mergeDist) {
if (file.startsWith('dist/')) {
outputFile = file.replace(/^dist\//, '');
} else if (file === 'dist') {
outputFile = '';
}
}
if (stat.isDirectory()) {
fs.cpSync(path.join(cwd, file), path.join(packDistPath, outputFile), { recursive: true });
@@ -93,9 +96,11 @@ ${filesString}
fs.writeFileSync(indexHtmlPath, indexHtmlContent);
}
};
export const pack = async (opts: { packDist?: string }) => {
export const pack = async (opts: { packDist?: string, mergeDist?: boolean }) => {
const cwd = process.cwd();
const collection: Record<string, any> = {};
const mergeDist = opts.mergeDist !== false;
const packageJsonPath = path.join(cwd, 'package.json');
if (!fileIsExist(packageJsonPath)) {
console.error('package.json not found');
@@ -150,7 +155,7 @@ export const pack = async (opts: { packDist?: string }) => {
console.log(`version: ${packageJson.version}`);
console.log(`total files: ${allFiles.length}`);
try {
copyFilesToPackDist(filesToInclude, cwd, opts.packDist);
copyFilesToPackDist(filesToInclude, cwd, opts.packDist, mergeDist);
} catch (error) {
console.error('Error creating tarball:', error);
}
@@ -173,17 +178,6 @@ export const getPackageInfo = async () => {
}
};
/**
* 打包应用
* @returns 打包结果
*/
export const packLib = async ({
packDist = 'pack-dist',
}: {
packDist?: string;
}): Promise<{ collection: Record<string, any>; dir: string }> => {
return await pack({ packDist });
};
const publishCommand = new Command('publish')
.description('发布应用')
.option('-k, --key <key>', '应用 key')
@@ -199,21 +193,6 @@ const deployLoadFn = async (id: string, fileKey: string, force = false, install
console.error(chalk.red('id is required'));
return;
}
// pkg: {
// name: 'mark',
// version: '0.0.2',
// description: '',
// main: 'dist/app.mjs',
// app: [Object],
// files: [Array],
// scripts: [Object],
// keywords: [Array],
// author: 'abearxiong <xiongxiao@xiongxiao.me>',
// license: 'MIT',
// type: 'module',
// devDependencies: [Object],
// dependencies: [Object]
// },
let appKey = '';
let version = '';
if (id && id.includes('/')) {
@@ -253,10 +232,13 @@ const packCommand = new Command('pack')
.option('-p, --publish', '打包并发布')
.option('-u, --update', '发布后显示更新命令, show command for deploy to server')
.option('-d, --packDist <dist>', '打包到的目录')
.option('-y, --yes', '确定,直接打包', true)
.option('-m, --mergeDist <mergeDist>', '合并 dist 目录到 pack-dist 中', "true")
.option('-y, --yes <yes>', '确定,直接打包', "true")
.option('-c, --clean', '清理 package.json中的 devDependencies')
.action(async (opts) => {
const packDist = opts.packDist || 'pack-dist';
const mergeDist = opts.mergeDist === "true";
const yes = opts.yes === "true";
const packageInfo = await getPackageInfo();
if (!packageInfo) {
console.error('Invalid package.json:');
@@ -297,8 +279,9 @@ const packCommand = new Command('pack')
]);
appKey = answers.appKey || appKey;
}
let value = await packLib({
let value = await pack({
packDist,
mergeDist
});
if (opts?.clean) {
const newPackageJson = { ...packageInfo };
@@ -317,7 +300,7 @@ const packCommand = new Command('pack')
if (opts.update) {
deployCommand.push('-s');
}
if (opts.yes) {
if (yes) {
deployCommand.push('-y', 'yes');
}
console.log(chalk.blue('deploy doing: '), deployCommand.slice(2).join(' '), '\n');

View File

@@ -1,13 +1,100 @@
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
}
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 () => {
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);
console.log('最新版本:', latestVersion);
downloadNewDistFiles(getRunFilePath());
} else {
console.log('已经是最新版本', version);
}
}
const update = new Command('update')
.option('-g --global', 'update global')
.option('-n --npm', 'use npm to update', false)
.description('update cli')
.action((opts) => {
try {
const cmd = opts.global ? 'npm install -g @kevisual/envision-cli' : 'npm install -D @kevisual/envision-cli';
execSync(cmd, { stdio: 'inherit', encoding: 'utf-8' });
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 {
getVersion()
}
} catch (error) {
console.error('Error updating CLI:', error);
}

View File

@@ -26,8 +26,14 @@ export type Package = {
};
type Options = {
check?: boolean;
/**
* 是否返回文本内容
*/
returnContent?: boolean;
setToken?: boolean;
/**
* 本地文件hash
*/
hash?: string;
[key: string]: any;
};

View File

@@ -66,7 +66,7 @@ type UploadOptions = {
* @param opts.meta meta
* @returns
*/
export const upload = (opts: UploadOptions): Promise<{ code?: number; message?: string; [key: string]: any }> => {
export const upload = (opts: UploadOptions): Promise<{ code?: number; message?: string;[key: string]: any }> => {
const form = opts?.form || new FormData();
if (!opts.form) {
let hash = '';

View File

@@ -1,11 +1,16 @@
import { program, Command } from 'commander';
import fs from 'fs';
import { useContextKey } from '@kevisual/context'
// 将多个子命令加入主程序中
let version = '0.0.1';
try {
// @ts-ignore
if (ENVISION_VERSION) version = ENVISION_VERSION;
} catch (e) {}
const version = useContextKey('version', () => {
let version = '0.0.64';
try {
// @ts-ignore
if (ENVISION_VERSION) version = ENVISION_VERSION;
} catch (e) { }
return version;
})
// @ts-ignore
program.name('app').description('A CLI tool with envison').version(version, '-V, --version');

View File

@@ -10,3 +10,7 @@ export const getHash = (file: string) => {
export const getBufferHash = (buffer: Buffer) => {
return MD5(buffer.toString()).toString();
};
export const getStringHash = (str: string) => {
return MD5(str).toString();
}