@kevisual/cnb 0.0.65 → 0.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/agent/cli.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { app } from './index.ts';
2
- import { parse } from '@kevisual/router/src/commander.ts';
3
-
4
- await parse({ app: app, description: 'CNB控制台命令行工具', parse: true, exitOnEnd: true })
2
+ import { parse } from '@kevisual/router/commander';
3
+ import { program } from './program.ts'
4
+ import './cmds/quick-cmd.ts'
5
+ await parse({ program, app: app, description: 'CNB控制台命令行工具', parse: true, exitOnEnd: true })
5
6
 
6
7
  console.log('命令行工具已启动,输入 help 查看可用命令', 'end');
@@ -0,0 +1,8 @@
1
+ import { app } from '../app.ts';
2
+ import { parse } from '@kevisual/router/commander';
3
+ import '../routes/auth/index.ts';
4
+
5
+ await parse({
6
+ app: app,
7
+ exitOnEnd: false
8
+ })
@@ -1,4 +1,4 @@
1
- import { createKeepAlive } from '../src/workspace/keep-live.ts';
1
+ import { createKeepAlive } from '../../src/workspace/keep-live.ts';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { Command, program } from 'commander';
@@ -0,0 +1,33 @@
1
+ import { program } from '../program.ts'
2
+ import { app } from '../index.ts';
3
+
4
+ program.command('release')
5
+ .description('上传发布文件到指定仓库')
6
+ .option("-r, --repo <string>", "仓库名称,格式为 owner/repo")
7
+ .option("-v, --version <string>", "版本号,例如1.0.0,默认为1.0.0")
8
+ .option("-d, --dir <string>", "相对执行路径的文件夹名称,默认为 dist")
9
+ .option("--dot", "是否包含点文件,默认为 false")
10
+ .option("-c, --cwd <string>", "执行命令的当前工作目录,默认为 process.cwd()")
11
+ .action(async (opts) => {
12
+ const repo = opts.repo;
13
+ const version = opts.version || '1.0.0';
14
+ const dir = opts.dir || 'dist';
15
+ const cwd = opts.cwd || process.cwd();
16
+ const dot = !!opts.dot;
17
+ if (!repo) {
18
+ console.error('repo 参数必填');
19
+ process.exit(1);
20
+ }
21
+
22
+ const res = await app.run({
23
+ path: 'cnb-deploy',
24
+ key: 'page',
25
+ payload: {
26
+ repo,
27
+ version,
28
+ dir,
29
+ cwd,
30
+ dot
31
+ }
32
+ }, { appId: app.appId })
33
+ })
@@ -48,7 +48,8 @@ export class CNBManager {
48
48
  constructor() {
49
49
  setInterval(() => {
50
50
  this.clearExpiredCNB()
51
- }, 1000 * 60 * 30) // 每30分钟清理一次过期的 CNB 实例
51
+ // unref 定时器,避免阻止进程退出
52
+ }, 1000 * 60 * 30).unref() // 每30分钟清理一次过期的 CNB 实例
52
53
  }
53
54
  getDefaultCNB() {
54
55
  const cnbItem = this.cnbMap.get('default')
@@ -0,0 +1,3 @@
1
+ import { program } from 'commander';
2
+
3
+ export { program }
@@ -0,0 +1,45 @@
1
+ import { app } from '../../app.ts';
2
+ /**
3
+ * 验证上下文中的 App ID 是否与指定的 App ID 匹配
4
+ * @param {any} ctx - 上下文对象,可能包含 appId 属性
5
+ * @param {string} appId - 需要验证的目标 App ID
6
+ * @returns {boolean} 如果 ctx 中包含 appId 且匹配则返回 true,否则返回 false
7
+ * @throws {Error} 如果 ctx 中包含 appId 但不匹配,则抛出 403 错误
8
+ */
9
+ const checkAppId = (ctx: any, appId: string) => {
10
+ const _appId = ctx?.app?.appId;
11
+ if (_appId) {
12
+ if (_appId !== appId) {
13
+ ctx.throw(403, 'Invalid App ID');
14
+ }
15
+ return true;
16
+ }
17
+ return false;
18
+ }
19
+
20
+ app.route({
21
+ rid: 'auth',
22
+ path: 'auth',
23
+ }).define(async (ctx) => {
24
+ // ctx.body = 'Auth Route';
25
+ if (checkAppId(ctx, app.appId)) {
26
+ ctx.state.tokenUser = {
27
+ username: 'default',
28
+ }
29
+ return;
30
+ }
31
+ }).addTo(app, { overwrite: false });
32
+
33
+ app.route({
34
+ rid: 'auth-admin',
35
+ path: 'auth-admin',
36
+ middleware: ['auth'],
37
+ }).define(async (ctx) => {
38
+ // ctx.body = 'Admin Auth Route';
39
+ if (checkAppId(ctx, app.appId)) {
40
+ ctx.state.tokenUser = {
41
+ username: 'default',
42
+ }
43
+ return;
44
+ }
45
+ }).addTo(app, { overwrite: false });
@@ -14,7 +14,9 @@ app.route({
14
14
  role: z.enum(['user', 'assistant']).describe('消息角色,user表示用户输入,assistant表示助手回复'),
15
15
  content: z.string().describe('消息内容')
16
16
  })).describe('对话消息列表,按照时间顺序排列,包含用户和助手的历史消息'),
17
- model: z.string().optional().describe('默认auto')
17
+ model: z.string().optional().describe('默认auto'),
18
+ isChatBot: z.boolean().optional().describe('纯对话模块, 不执行其他工具模块。'),
19
+ queryRouterFilter: z.string().optional().describe('查询路由过滤器,格式为SQL Where语法,例如"WHERE path="cnb" ",用于过滤可用的路由列表'),
18
20
  }
19
21
  }
20
22
  }).define(async (ctx) => {
@@ -26,6 +28,8 @@ app.route({
26
28
  const model = ctx.args?.model || 'auto'
27
29
  const item = await cnbManager.getCNBItem(ctx);
28
30
  const cnbAi = item.cnbAi;
31
+ const isChatBot = ctx.args?.isChatBot ?? false;
32
+ const queryRouterFilter = ctx.args.queryRouterFilter;
29
33
  const messages = ctx.args.messages || [{
30
34
  role: 'user',
31
35
  content: ctx.args.question
@@ -40,9 +44,10 @@ app.route({
40
44
  const result = await runAgent({
41
45
  app,
42
46
  messages: messages,
43
- routes,
47
+ routes: isChatBot ? [] : routes,
44
48
  languageModel: cnbAi(model),
45
49
  token: '',
50
+ query: queryRouterFilter
46
51
  // token: ctx.query.token as string,
47
52
  });
48
53
  ctx.body = result;
@@ -0,0 +1,43 @@
1
+ import { app, cnbManager } from '../../app.ts';
2
+ import { z } from 'zod';
3
+ import fs from 'node:fs';
4
+
5
+ app.route({
6
+ path: 'cnb-deploy',
7
+ key: 'file-list',
8
+ description: '部署页面, 程序需要在自己的客户端中执行。',
9
+ middleware: ['auth'],
10
+ metadata: {
11
+ args: {
12
+ repo: z.string().describe('仓库名称,格式为 owner/repo'),
13
+ version: z.string().optional().describe('版本号,例如1.0.0,默认为1.0.0'),
14
+ // dir: z.string().optional().describe('相对执行路径的文件夹名称,默认为 dist'),
15
+ }
16
+ }
17
+ }).define(async (ctx) => {
18
+ const cnb = await cnbManager.getContext(ctx);
19
+ const version = ctx.args.version || '1.0.0';
20
+ const dir = ctx.args.dir || 'dist';
21
+ const pwd = process.cwd();
22
+ const repo = ctx.args.repo;
23
+ const dirPath = `${pwd}/${dir}`;
24
+ const dot = ctx.args.dot || false;
25
+ if (!repo) {
26
+ ctx.throw(400, 'repo 参数必填');
27
+ }
28
+ if (!fileIsExist(dirPath)) {
29
+ ctx.throw(400, `目录 ${dir} 不存在`);
30
+ }
31
+ const res = await cnb.release.list(repo, { page: 1, page_size: 50, query: version });
32
+ ctx.body = res;
33
+
34
+ }).addTo(app);
35
+
36
+ const fileIsExist = (filepath: string) => {
37
+ try {
38
+ fs.accessSync(filepath, fs.constants.F_OK);
39
+ } catch (e) {
40
+ return false;
41
+ }
42
+ return true;
43
+ }
@@ -0,0 +1,2 @@
1
+ import './upload.ts'
2
+ import './file-list.ts';
@@ -0,0 +1,88 @@
1
+ import { app, cnbManager } from '../../app.ts';
2
+ import { z } from 'zod';
3
+ import glob from 'fast-glob';
4
+ import fs from 'node:fs';
5
+
6
+ import { uploadReleaseFiles } from '../../../src/release/upload-release.ts'
7
+ import { DefaultIgnore } from '../../../src/upload/upload-base.ts';
8
+ app.route({
9
+ path: 'cnb-deploy',
10
+ key: 'page',
11
+ description: '部署页面,上传对应的文件的内容到指定的仓库,程序需要在自己的客户端中执行。',
12
+ middleware: ['auth-admin'],
13
+ metadata: {
14
+ args: {
15
+ repo: z.string().describe('仓库名称,格式为 owner/repo'),
16
+ version: z.string().optional().describe('版本号,例如1.0.0,默认为1.0.0'),
17
+ dir: z.string().optional().describe('相对执行路径的文件夹名称,默认为 dist'),
18
+ // filename: z.string().optional().describe('部署文件名称,默认为空,如果存在,dir 不具备作用'),
19
+ dot: z.boolean().optional().describe('是否包含点文件,默认为 false'),
20
+ cwd: z.string().optional().describe('执行命令的当前工作目录,默认为 process.cwd()')
21
+ }
22
+ }
23
+ }).define(async (ctx) => {
24
+ const cnb = await cnbManager.getContext(ctx);
25
+ const version = ctx.args.version || '1.0.0';
26
+ const dir = ctx.args.dir || 'dist';
27
+ const pwd = ctx.args.cwd || process.cwd();
28
+ const repo = ctx.args.repo;
29
+ let dirPath = `${pwd}/${dir}`;
30
+ const dot = ctx.args.dot || false;
31
+ if (!repo) {
32
+ ctx.throw(400, 'repo 参数必填');
33
+ }
34
+ let dirReg = '';
35
+ if (dirPath.includes('*') || dirPath.includes('?') || dirPath==='.') {
36
+ dirReg = dirPath;
37
+ } else {
38
+ dirReg = `${dirPath}/**/*`
39
+ }
40
+ const files = await glob(dirReg, {
41
+ cwd: pwd,
42
+ onlyFiles: true,
43
+ ignore: DefaultIgnore,
44
+ dot,
45
+ });
46
+ const res = await uploadReleaseFiles({
47
+ files: files.map(file => ({
48
+ type: 'file',
49
+ filepath: file,
50
+ name: file.replace(`${dirPath}/`, '')
51
+ })),
52
+ cnb,
53
+ repo: repo,
54
+ version
55
+ });
56
+ ctx.body = res;
57
+
58
+ }).addTo(app);
59
+
60
+ app.route({
61
+ path: 'cnb-deploy',
62
+ key: 'app',
63
+ description: '部署当前文件夹下的文件列表,根据 packages.json 进行部署,程序需要在自己的客户端中执行。basename 类似 /kevision/cnb, version类似 1.0.0,deploy.dir 类似 dist',
64
+ middleware: ['auth-admin'],
65
+ }).define(async (ctx) => {
66
+ try {
67
+ const pkgs = JSON.parse(fs.readFileSync(`${process.cwd()}/packages.json`, 'utf-8'));
68
+ let repo: string = pkgs.basename || '';
69
+ const version = pkgs.version || '1.0.0';
70
+ if (repo.startsWith('/')) {
71
+ repo = repo.slice(1);
72
+ }
73
+ const dir = pkgs.deploy?.dir || 'dist';
74
+ const res = await app.run({ path: 'cnb-deploy', key: 'page', payload: { repo, version, dir } });
75
+ ctx.forward(res);
76
+ } catch (e) {
77
+ ctx.throw(500, '部署失败,无法读取 packages.json 文件');
78
+ }
79
+ }).addTo(app);
80
+
81
+ const fileIsExist = (filepath: string) => {
82
+ try {
83
+ fs.accessSync(filepath, fs.constants.F_OK);
84
+ } catch (e) {
85
+ return false;
86
+ }
87
+ return true;
88
+ }
@@ -15,47 +15,5 @@ import './missions/index.ts';
15
15
  import './labels/index.ts';
16
16
  import './package/index.ts';
17
17
 
18
- /**
19
- * 验证上下文中的 App ID 是否与指定的 App ID 匹配
20
- * @param {any} ctx - 上下文对象,可能包含 appId 属性
21
- * @param {string} appId - 需要验证的目标 App ID
22
- * @returns {boolean} 如果 ctx 中包含 appId 且匹配则返回 true,否则返回 false
23
- * @throws {Error} 如果 ctx 中包含 appId 但不匹配,则抛出 403 错误
24
- */
25
- const checkAppId = (ctx: any, appId: string) => {
26
- const _appId = ctx?.app?.appId;
27
- if (_appId) {
28
- if (_appId !== appId) {
29
- ctx.throw(403, 'Invalid App ID');
30
- }
31
- return true;
32
- }
33
- return false;
34
- }
35
-
36
- app.route({
37
- rid: 'auth',
38
- path: 'auth',
39
- }).define(async (ctx) => {
40
- // ctx.body = 'Auth Route';
41
- if (checkAppId(ctx, app.appId)) {
42
- ctx.state.tokenUser = {
43
- username: 'default',
44
- }
45
- return;
46
- }
47
- }).addTo(app, { overwrite: false });
48
-
49
- app.route({
50
- rid: 'auth-admin',
51
- path: 'auth-admin',
52
- middleware: ['auth'],
53
- }).define(async (ctx) => {
54
- // ctx.body = 'Admin Auth Route';
55
- if (checkAppId(ctx, app.appId)) {
56
- ctx.state.tokenUser = {
57
- username: 'default',
58
- }
59
- return;
60
- }
61
- }).addTo(app, { overwrite: false });
18
+ import './deploy/index.ts';
19
+ import './auth/index.ts';
package/dist/a/a.txt ADDED
@@ -0,0 +1 @@
1
+ abc
package/dist/cli-live.js CHANGED
@@ -5172,7 +5172,7 @@ function createKeepAlive(config) {
5172
5172
  return client;
5173
5173
  }
5174
5174
 
5175
- // agent/live.ts
5175
+ // agent/clis/live.ts
5176
5176
  import { readFileSync } from "node:fs";
5177
5177
  import path from "node:path";
5178
5178
 
@@ -5192,7 +5192,7 @@ var {
5192
5192
  Help
5193
5193
  } = import__.default;
5194
5194
 
5195
- // agent/live.ts
5195
+ // agent/clis/live.ts
5196
5196
  program.addCommand(new Command("live").description("启动 CNB Keep Alive 服务").option("-j, --json <string>", "JSON数据").option("-c, --config <string>", "配置文件路径 (优先级高于 JSON 参数), 默认keep.json").action(async (opts) => {
5197
5197
  let config;
5198
5198
  let configPath = opts.config || "keep.json";