up
This commit is contained in:
4
src/ai/ai.ts
Normal file
4
src/ai/ai.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { App } from '@kevisual/app/src/app.ts';
|
||||
import { storage } from '../module/query.ts';
|
||||
|
||||
export const app = new App({ token: storage.getItem('token') || '' });
|
||||
6
src/ai/index.ts
Normal file
6
src/ai/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { app } from './ai.ts'
|
||||
import './routes/cmd-run.ts'
|
||||
|
||||
export {
|
||||
app
|
||||
}
|
||||
54
src/ai/routes/cmd-run.ts
Normal file
54
src/ai/routes/cmd-run.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { app } from '../ai.ts';
|
||||
import { execSync } from 'node:child_process'
|
||||
const promptTemplate = `# CMD 结果判断器
|
||||
|
||||
分析上一条 CMD 命令的执行结果,判断是否需要执行下一条命令。
|
||||
|
||||
- 若结果中隐含或明确指示需继续执行 → 返回:\`{"cmd": "推断出的下一条命令", "type": "cmd"}\`
|
||||
- 若无后续操作 → 返回:\`{"type": "none"}\`
|
||||
|
||||
1. 仅输出合法 JSON,无任何额外文本。
|
||||
2. \`cmd\` 必须从执行结果中合理推断得出,非预设或猜测。
|
||||
3. 禁止解释、注释、换行或格式错误。`
|
||||
|
||||
app.router.route({
|
||||
path: 'cmd-run',
|
||||
description: '执行 CMD 命令并判断下一步操作, 参数是 cmd 字符串',
|
||||
}).define(async (ctx) => {
|
||||
const cmd = ctx.query.cmd || '';
|
||||
if (!cmd) {
|
||||
ctx.throw(400, 'cmd is required');
|
||||
}
|
||||
let result = '';
|
||||
ctx.state.steps = ctx.state?.steps || [];
|
||||
|
||||
try {
|
||||
result = execSync(cmd, { encoding: 'utf-8' });
|
||||
ctx.state.steps.push({ cmd, result });
|
||||
} catch (error: any) {
|
||||
result = error.message || '';
|
||||
ctx.state.steps.push({ cmd, result, error: true });
|
||||
ctx.body = {
|
||||
steps: ctx.state.steps,
|
||||
}
|
||||
return;
|
||||
}
|
||||
const prompt = `${promptTemplate}\n上一条命令:\n${cmd}\n执行结果:\n${result}\n`;
|
||||
const response = await app.ai.question(prompt);
|
||||
|
||||
const msg = app.ai.utils.extractJsonFromMarkdown(app.ai.responseText);
|
||||
try {
|
||||
const { cmd, type } = msg;
|
||||
if (type === 'cmd' && cmd) {
|
||||
await app.router.call({ path: 'cmd-run', payload: { cmd } }, { state: ctx.state });
|
||||
} else {
|
||||
ctx.state.steps.push({ type: 'none' });
|
||||
}
|
||||
} catch (error) {
|
||||
result = '执行错误,无法解析返回结果为合法 JSON' + app.ai.responseText
|
||||
ctx.state.steps.push({ cmd, result, parseError: true });
|
||||
}
|
||||
ctx.body = {
|
||||
steps: ctx.state.steps,
|
||||
}
|
||||
}).addTo(app.router);
|
||||
30
src/command/ai.ts
Normal file
30
src/command/ai.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { App } from '@kevisual/app/src/app.ts';
|
||||
import { storage } from '../module/query.ts';
|
||||
|
||||
export const runAIApp = async () => {
|
||||
const token = storage.getItem('token') || '';
|
||||
if (!token) {
|
||||
console.log('Please login first.');
|
||||
return;
|
||||
}
|
||||
const aiApp = new App({ token })
|
||||
await aiApp.loadAI();
|
||||
const router= aiApp.router;
|
||||
router.route({
|
||||
description: '今天的天气怎么样?',
|
||||
}).define(async (ctx) => {
|
||||
ctx.body = '今天的天气晴朗,适合外出活动!';
|
||||
}).addTo(router)
|
||||
|
||||
router.route({
|
||||
description: '当前时间是几点?',
|
||||
}).define(async (ctx) => {
|
||||
ctx.body = `当前时间是:${new Date().toLocaleTimeString()}`;
|
||||
}).addTo(router)
|
||||
|
||||
const chat = await aiApp.chat('今天的天气怎么样?');
|
||||
console.log('AI Response:', aiApp.ai.responseText);
|
||||
|
||||
console.log('chat', chat);
|
||||
}
|
||||
runAIApp();
|
||||
@@ -59,7 +59,7 @@ const command = new Command('deploy')
|
||||
if (!key && pkgInfo?.appKey) {
|
||||
key = pkgInfo?.appKey || '';
|
||||
}
|
||||
console.log('start deploy');
|
||||
logger.debug('start deploy');
|
||||
if (!version || !key) {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
@@ -124,7 +124,6 @@ const command = new Command('deploy')
|
||||
const uploadDirectory = isDirectory ? directory : path.dirname(directory);
|
||||
const res = await uploadFiles(_relativeFiles, uploadDirectory, { key, version, username: org, noCheckAppFiles: !noCheck, directory: options.directory });
|
||||
if (res?.code === 200) {
|
||||
console.log('File uploaded successfully!');
|
||||
res.data?.upload?.map?.((d) => {
|
||||
console.log(chalk.green('uploaded file', d?.name, d?.path));
|
||||
});
|
||||
@@ -139,7 +138,6 @@ const command = new Command('deploy')
|
||||
// const { id, data, ...rest } = res.data?.app || {};
|
||||
const { id, data, ...rest } = res2.data || {};
|
||||
if (id && !update) {
|
||||
console.log(chalk.green('id: '), id);
|
||||
if (!org) {
|
||||
console.log(chalk.green(`更新为最新版本: envision deploy-load ${id}`));
|
||||
} else {
|
||||
@@ -153,7 +151,7 @@ const command = new Command('deploy')
|
||||
logger.debug('deploy success', res2.data);
|
||||
if (id && showBackend) {
|
||||
console.log('\n');
|
||||
console.log(chalk.blue('服务端应用部署: '), 'envision pack-deploy', id);
|
||||
console.log(chalk.blue('服务端应用部署:\n'), 'envision pack-deploy', id);
|
||||
console.log('\n');
|
||||
}
|
||||
} else {
|
||||
@@ -180,8 +178,8 @@ const uploadFiles = async (files: string[], directory: string, opts: UploadFileO
|
||||
const filePath = path.join(directory, file);
|
||||
const hash = getHash(filePath);
|
||||
if (!hash) {
|
||||
console.error('文件', filePath, '不存在');
|
||||
console.error('请检查文件是否存在');
|
||||
logger.error('文件', filePath, '不存在');
|
||||
logger.error('请检查文件是否存在');
|
||||
}
|
||||
data.files.push({ path: file, hash: hash });
|
||||
}
|
||||
@@ -220,11 +218,11 @@ const uploadFiles = async (files: string[], directory: string, opts: UploadFileO
|
||||
const filePath = path.join(directory, file);
|
||||
const check = checkData.find((d) => d.path === file);
|
||||
if (check?.isUpload) {
|
||||
console.log('文件已经上传过了', file);
|
||||
logger.debug('文件已经上传过了', file);
|
||||
continue;
|
||||
}
|
||||
const filename = path.basename(filePath);
|
||||
console.log('upload file', file, filename);
|
||||
logger.debug('upload file', file, filename);
|
||||
form.append('file', fs.createReadStream(filePath), {
|
||||
filename: filename,
|
||||
filepath: file,
|
||||
@@ -232,7 +230,7 @@ const uploadFiles = async (files: string[], directory: string, opts: UploadFileO
|
||||
needUpload = true;
|
||||
}
|
||||
if (!needUpload) {
|
||||
console.log('所有文件都上传过了,不需要上传文件');
|
||||
logger.debug('所有文件都上传过了,不需要上传文件');
|
||||
return {
|
||||
code: 200,
|
||||
};
|
||||
@@ -260,16 +258,16 @@ const deployLoadFn = async (id: string, org?: string) => {
|
||||
},
|
||||
});
|
||||
if (res.code === 200) {
|
||||
console.log(chalk.green('deploy-load success. current version:', res.data?.version));
|
||||
logger.info(chalk.green('deploy-load success. current version:', res.data?.version));
|
||||
// /:username/:appName
|
||||
try {
|
||||
const { user, key } = res.data;
|
||||
const baseURL = getBaseURL();
|
||||
const deployURL = new URL(`/${user}/${key}/`, baseURL);
|
||||
console.log(chalk.blue('deployURL', deployURL.href));
|
||||
logger.info(chalk.blue('deployURL', deployURL.href));
|
||||
} catch (error) { }
|
||||
} else {
|
||||
console.error('deploy-load failed', res.message);
|
||||
logger.error('deploy-load failed', res.message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Logger } from '@kevisual/logger/node';
|
||||
|
||||
const level = process.env.LOG_LEVEL || 'info';
|
||||
export const logger = new Logger({
|
||||
level: level as any,
|
||||
|
||||
Reference in New Issue
Block a user