@kevisual/cnb 0.0.39 → 0.0.42

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/app.ts CHANGED
@@ -29,7 +29,6 @@ export const notCNBCheck = (ctx: any) => {
29
29
  const isCNB = useKey('CNB');
30
30
  if (!isCNB) {
31
31
  ctx.throw(400, '当前环境非 cnb-board 环境,无法获取 live 内容');
32
- return true;
33
32
  }
34
33
  return false;
35
34
  }
@@ -1,7 +1,10 @@
1
1
  import { Result } from '@kevisual/query';
2
2
  import { CNB } from '../../src/index.ts';
3
+ import { useKey } from '@kevisual/context';
3
4
  export const getConfig = async (opts: { token?: string }) => {
4
- const res = await fetch('https://kevisual.cn/api/router', {
5
+ const kevisualEnv = useKey('KEVISUAL_ENV')
6
+ const baseUrl = kevisualEnv === 'production' ? 'https://kevisual.cn/api/router' : 'https://kevisual.xiongxiao.me/api/router';
7
+ const res = await fetch(baseUrl, {
5
8
  method: 'POST',
6
9
  body: JSON.stringify({
7
10
  path: 'config',
@@ -51,6 +54,7 @@ export class CNBManager {
51
54
  cnbItem.runAt = Date.now()
52
55
  return cnbItem
53
56
  }
57
+
54
58
  const res = await getConfig({ token: opts?.kevisualToken })
55
59
  if (res.code === 200) {
56
60
  const cookie = res.data?.data?.CNB_COOKIE
@@ -0,0 +1,66 @@
1
+ import { execSync } from 'child_process';
2
+ import { app, notCNBCheck } from '../../app.ts'
3
+ import { z } from 'zod'
4
+ import { title } from 'process';
5
+
6
+ app.route({
7
+ path: 'cnb',
8
+ key: 'docker-sync',
9
+ middleware: ['auth'],
10
+ metadata: {
11
+ tag: ['opencode'],
12
+ skill: 'cnb-docker-sync',
13
+ title: 'CNB Docker 镜像同步',
14
+ args: {
15
+ image: z.string().describe('Docker 同步的具体的镜像名称.'),
16
+ toVersion: z.string().optional().describe('修改后的版本号.'),
17
+ }
18
+ }
19
+ }).define(async (ctx) => {
20
+ const { image, toVersion } = ctx.args;
21
+ notCNBCheck(ctx);
22
+ if (!image) {
23
+ ctx.body = {
24
+ message: '请提供 Docker 镜像名称.',
25
+ data: null,
26
+ }
27
+ return;
28
+ }
29
+ const config = {
30
+ registry: 'docker.cnb.cool/kevisual/dev-env',
31
+ dockers: [{ image, toVersion }]
32
+ }
33
+ // docker tag ghcr.io/esm-dev/esm.sh:v137 docker.cnb.cool/kevisual/dev-env/esm.sh:v137
34
+ // docker push docker.cnb.cool/kevisual/dev-env/esm.sh:v137
35
+ const run = async () => {
36
+ const dockers = config.dockers;
37
+ for (const { image, toVersion } of dockers) {
38
+ const imageName = image.split(':')[0].split('/').slice(-1)[0]
39
+ const tag = image.split(':')[1]
40
+ const newImage = `${config.registry}/${imageName}:${toVersion || tag}`
41
+ // console.log(`docker tag ${image} ${newImage}`)
42
+ // console.log(`docker push ${newImage}`)
43
+ const shell = `docker pull ${image} && docker tag ${image} ${newImage} && docker push ${newImage}`
44
+ console.log(shell)
45
+
46
+ console.log('\n-------------new---------------------------------\n')
47
+ console.log(`${newImage}`)
48
+ console.log('\n--------------------------------------------------\n')
49
+ try {
50
+ execSync(shell, { stdio: 'inherit' })
51
+ } catch (error) {
52
+ console.error(`Error: ${error}`)
53
+ }
54
+ }
55
+ }
56
+ run().then(() => {
57
+ // TODO: 通知用户同步完成
58
+ });;
59
+ ctx.body = {
60
+ message: 'Docker 镜像同步任务中,请稍后在目标仓库查看.',
61
+ data: {
62
+ registry: config.registry,
63
+ dockers: config.dockers,
64
+ }
65
+ }
66
+ }).addTo(app);
@@ -0,0 +1 @@
1
+ import './docker.ts'
@@ -9,6 +9,7 @@ import './issues/index.ts'
9
9
  import './cnb-board/index.ts';
10
10
  import './share/index.ts';
11
11
  import './cnb-manager/index.ts';
12
+ import './build/index.ts';
12
13
 
13
14
  /**
14
15
  * 验证上下文中的 App ID 是否与指定的 App ID 匹配
@@ -0,0 +1,167 @@
1
+ import { createSkill, tool } from '@kevisual/router';
2
+ import { app, cnbManager } from '../../app.ts';
3
+ import { useKey } from '@kevisual/context';
4
+
5
+ // 查询 Issue 评论列表
6
+ app.route({
7
+ path: 'cnb',
8
+ key: 'list-issue-comments',
9
+ description: '查询 Issue 评论列表, 参数 repo, issueNumber, page, page_size',
10
+ middleware: ['auth'],
11
+ metadata: {
12
+ tags: ['opencode'],
13
+ ...createSkill({
14
+ skill: 'list-issue-comments',
15
+ title: '查询 Issue 评论列表',
16
+ args: {
17
+ repo: tool.schema.string().optional().describe('代码仓库名称, 如 my-user/my-repo'),
18
+ issueNumber: tool.schema.number().describe('Issue 编号'),
19
+ page: tool.schema.number().optional().describe('分页页码,默认: 1'),
20
+ page_size: tool.schema.number().optional().describe('分页每页大小,默认: 30'),
21
+ },
22
+ summary: '查询 Issue 评论列表',
23
+ })
24
+ }
25
+ }).define(async (ctx) => {
26
+ const cnb = await cnbManager.getContext(ctx);
27
+ let repo = ctx.query?.repo || useKey('CNB_REPO_SLUG_LOWERCASE');
28
+ const issueNumber = ctx.query?.issueNumber;
29
+ const page = ctx.query?.page ? Number(ctx.query.page) : undefined;
30
+ const page_size = ctx.query?.page_size ? Number(ctx.query.page_size) : undefined;
31
+
32
+ if (!repo) {
33
+ ctx.throw(400, '缺少参数 repo');
34
+ }
35
+ if (!issueNumber) {
36
+ ctx.throw(400, '缺少参数 issueNumber');
37
+ }
38
+
39
+ const params: Record<string, any> = {};
40
+ if (page) params.page = page;
41
+ if (page_size) params.page_size = page_size;
42
+
43
+ const res = await cnb.issue.getCommentList(repo, issueNumber, params);
44
+ ctx.forward(res);
45
+ }).addTo(app);
46
+
47
+ // 创建 Issue 评论
48
+ app.route({
49
+ path: 'cnb',
50
+ key: 'create-issue-comment',
51
+ description: '创建 Issue 评论, 参数 repo, issueNumber, body',
52
+ middleware: ['auth'],
53
+ metadata: {
54
+ tags: ['opencode'],
55
+ ...createSkill({
56
+ skill: 'create-issue-comment',
57
+ title: '创建 Issue 评论',
58
+ args: {
59
+ repo: tool.schema.string().optional().describe('代码仓库名称, 如 my-user/my-repo'),
60
+ issueNumber: tool.schema.number().describe('Issue 编号'),
61
+ body: tool.schema.string().describe('评论内容'),
62
+ },
63
+ summary: '创建 Issue 评论',
64
+ })
65
+ }
66
+ }).define(async (ctx) => {
67
+ const cnb = await cnbManager.getContext(ctx);
68
+ let repo = ctx.query?.repo || useKey('CNB_REPO_SLUG_LOWERCASE');
69
+ const issueNumber = ctx.query?.issueNumber;
70
+ const body = ctx.query?.body;
71
+
72
+ if (!repo) {
73
+ ctx.throw(400, '缺少参数 repo');
74
+ }
75
+ if (!issueNumber) {
76
+ ctx.throw(400, '缺少参数 issueNumber');
77
+ }
78
+ if (!body) {
79
+ ctx.throw(400, '缺少参数 body');
80
+ }
81
+
82
+ const res = await cnb.issue.createComment(repo, issueNumber, body);
83
+ ctx.forward(res);
84
+ }).addTo(app);
85
+
86
+ // 获取 Issue 指定评论
87
+ app.route({
88
+ path: 'cnb',
89
+ key: 'get-issue-comment',
90
+ description: '获取 Issue 指定评论, 参数 repo, issueNumber, commentId',
91
+ middleware: ['auth'],
92
+ metadata: {
93
+ tags: ['opencode'],
94
+ ...createSkill({
95
+ skill: 'get-issue-comment',
96
+ title: '获取 Issue 评论',
97
+ args: {
98
+ repo: tool.schema.string().optional().describe('代码仓库名称, 如 my-user/my-repo'),
99
+ issueNumber: tool.schema.number().describe('Issue 编号'),
100
+ commentId: tool.schema.number().describe('评论 ID'),
101
+ },
102
+ summary: '获取 Issue 评论',
103
+ })
104
+ }
105
+ }).define(async (ctx) => {
106
+ const cnb = await cnbManager.getContext(ctx);
107
+ let repo = ctx.query?.repo || useKey('CNB_REPO_SLUG_LOWERCASE');
108
+ const issueNumber = ctx.query?.issueNumber;
109
+ const commentId = ctx.query?.commentId;
110
+
111
+ if (!repo) {
112
+ ctx.throw(400, '缺少参数 repo');
113
+ }
114
+ if (!issueNumber) {
115
+ ctx.throw(400, '缺少参数 issueNumber');
116
+ }
117
+ if (!commentId) {
118
+ ctx.throw(400, '缺少参数 commentId');
119
+ }
120
+
121
+ const res = await cnb.issue.getComment(repo, issueNumber, commentId);
122
+ ctx.forward(res);
123
+ }).addTo(app);
124
+
125
+ // 修改 Issue 评论
126
+ app.route({
127
+ path: 'cnb',
128
+ key: 'update-issue-comment',
129
+ description: '修改 Issue 评论, 参数 repo, issueNumber, commentId, body',
130
+ middleware: ['auth'],
131
+ metadata: {
132
+ tags: ['opencode'],
133
+ ...createSkill({
134
+ skill: 'update-issue-comment',
135
+ title: '修改 Issue 评论',
136
+ args: {
137
+ repo: tool.schema.string().optional().describe('代码仓库名称, 如 my-user/my-repo'),
138
+ issueNumber: tool.schema.number().describe('Issue 编号'),
139
+ commentId: tool.schema.number().describe('评论 ID'),
140
+ body: tool.schema.string().describe('评论内容'),
141
+ },
142
+ summary: '修改 Issue 评论',
143
+ })
144
+ }
145
+ }).define(async (ctx) => {
146
+ const cnb = await cnbManager.getContext(ctx);
147
+ let repo = ctx.query?.repo || useKey('CNB_REPO_SLUG_LOWERCASE');
148
+ const issueNumber = ctx.query?.issueNumber;
149
+ const commentId = ctx.query?.commentId;
150
+ const body = ctx.query?.body;
151
+
152
+ if (!repo) {
153
+ ctx.throw(400, '缺少参数 repo');
154
+ }
155
+ if (!issueNumber) {
156
+ ctx.throw(400, '缺少参数 issueNumber');
157
+ }
158
+ if (!commentId) {
159
+ ctx.throw(400, '缺少参数 commentId');
160
+ }
161
+ if (!body) {
162
+ ctx.throw(400, '缺少参数 body');
163
+ }
164
+
165
+ const res = await cnb.issue.updateComment(repo, issueNumber, commentId, body);
166
+ ctx.forward(res);
167
+ }).addTo(app);
@@ -1,2 +1,3 @@
1
1
  import './list.ts'
2
- import './issue.ts'
2
+ import './issue.ts'
3
+ import './comments.ts'
@@ -14,7 +14,7 @@ app.route({
14
14
  skill: 'list-issues',
15
15
  title: '查询 Issue 列表',
16
16
  args: {
17
- repo: tool.schema.string().describe('代码仓库名称, 如 my-user/my-repo'),
17
+ repo: tool.schema.string().optional().describe('代码仓库名称, 如 my-user/my-repo'),
18
18
  state: tool.schema.string().optional().describe('Issue 状态:open 或 closed'),
19
19
  keyword: tool.schema.string().optional().describe('问题搜索关键词'),
20
20
  labels: tool.schema.string().optional().describe('问题标签,多个用逗号分隔'),
@@ -27,7 +27,7 @@ app.route({
27
27
  }
28
28
  }).define(async (ctx) => {
29
29
  const cnb = await cnbManager.getContext(ctx);
30
- const repo = ctx.query?.repo || useKey('CNB_REPO_SLUG_LOWERCASE');
30
+ let repo = ctx.query?.repo || useKey('CNB_REPO_SLUG_LOWERCASE');
31
31
  const state = ctx.query?.state;
32
32
  const keyword = ctx.query?.keyword;
33
33
  const labels = ctx.query?.labels;
@@ -43,6 +43,27 @@ app.route({
43
43
  }
44
44
  }).addTo(app);
45
45
 
46
+ app.route({
47
+ path: 'cnb',
48
+ key: 'get-repo',
49
+ description: '获取代码仓库详情, 参数name',
50
+ middleware: ['auth'],
51
+ metadata: {
52
+ args: {
53
+ name: tool.schema.string().describe('代码仓库名称, 如 my-user/my-repo'),
54
+ }
55
+ }
56
+ }).define(async (ctx) => {
57
+ const cnb = await cnbManager.getContext(ctx);
58
+ const name = ctx.query?.name;
59
+
60
+ if (!name) {
61
+ ctx.throw(400, '缺少参数 name');
62
+ }
63
+ const res = await cnb.repo.getRepo(name);
64
+ ctx.forward(res);
65
+ }).addTo(app);
66
+
46
67
  app.route({
47
68
  path: 'cnb',
48
69
  key: 'create-repo-file',
@@ -106,7 +127,56 @@ app.route({
106
127
  if (!name) {
107
128
  ctx.throw(400, '缺少参数 name');
108
129
  }
130
+ try {
131
+ const resCookie = await cnb.user.checkCookieValid()
132
+ if (resCookie.code !== 200) {
133
+ ctx.throw(401, 'Cookie 无效或已过期');
134
+ }
135
+ const res = await cnb.repo.deleteRepoCookie(name);
136
+ ctx.forward(res);
137
+ } catch (error) {
138
+ ctx.code = 200
139
+ ctx.body = { content: '已经删除' }
140
+ }
141
+ }).addTo(app);
109
142
 
110
- const res = await cnb.repo.deleteRepoCookie(name);
143
+
144
+ app.route({
145
+ path: 'cnb',
146
+ key: 'update-repo-info',
147
+ description: '更新代码仓库信息, 参数name, description',
148
+ middleware: ['auth'],
149
+ metadata: {
150
+ tags: ['opencode'],
151
+ ...createSkill({
152
+ skill: 'update-repo-info',
153
+ title: '更新代码仓库信息',
154
+ args: {
155
+ name: tool.schema.string().describe('代码仓库名称'),
156
+ description: tool.schema.string().describe('代码仓库描述'),
157
+ license: tool.schema.string().describe('代码仓库许可证类型,如 MIT').optional(),
158
+ site: tool.schema.string().describe('代码仓库主页链接').optional(),
159
+ topics: tool.schema.array(tool.schema.string()).describe('代码仓库话题标签列表').optional(),
160
+ },
161
+ summary: '更新代码仓库的信息',
162
+ })
163
+ }
164
+ }).define(async (ctx) => {
165
+ const cnb = await cnbManager.getContext(ctx);
166
+ const name = ctx.query?.name;
167
+ const description = ctx.query?.description;
168
+ const license = ctx.query?.license;
169
+ const site = ctx.query?.site;
170
+ const topics = ctx.query?.topics;
171
+
172
+ if (!name) {
173
+ ctx.throw(400, '缺少参数 name');
174
+ }
175
+ if (!description) {
176
+ ctx.throw(400, '缺少参数 description');
177
+ }
178
+
179
+ const res = await cnb.repo.updateRepoInfo(name, { description, license, site, topics });
111
180
  ctx.forward(res);
112
- }).addTo(app);
181
+ }).addTo(app);
182
+
@@ -0,0 +1,43 @@
1
+ import { createSkill, tool } from '@kevisual/router';
2
+
3
+ import { app, cnbManager, notCNBCheck } from '../../app.ts';
4
+
5
+ // 启动工作空间
6
+ app.route({
7
+ path: 'cnb',
8
+ key: 'cloud-build',
9
+ description: '云端构建,参数 event, repo, branch, ref, config, env',
10
+ middleware: ['auth'],
11
+ metadata: {
12
+ tags: ['opencode'],
13
+ ...createSkill({
14
+ skill: 'cloud-build',
15
+ title: '云端构建',
16
+ summary: '在云端构建代码仓库,参数包括 event, repo, branch, ref, config, env',
17
+ args: {
18
+ env: tool.schema.any().optional().describe('构建环境变量,格式为 { "KEY": "VALUE" }'),
19
+ event: tool.schema.string().optional().describe('触发事件类型,例如 api_trigger_event'),
20
+ branch: tool.schema.string().optional().describe('分支名称,默认主分支'),
21
+ config: tool.schema.string().describe('构建config文件内容,例如 cloudbuild.yaml对应的yml的内容'),
22
+ repo: tool.schema.string().describe('代码仓库路径,例如 user/repo'),
23
+ },
24
+ })
25
+ }
26
+ }).define(async (ctx) => {
27
+ const cnb = await cnbManager.getContext(ctx);
28
+ const repo = ctx.query?.repo;
29
+ const branch = ctx.query?.branch || 'main';
30
+ const config = ctx.query?.config;
31
+ const event = ctx.query?.event || 'api_trigger_event';
32
+ const env = ctx.query?.env ?? {};
33
+ if (!repo) {
34
+ ctx.throw(400, '缺少参数 repo');
35
+ }
36
+ const res = await cnb.build.startBuild(repo, {
37
+ branch,
38
+ config,
39
+ event,
40
+ env,
41
+ });
42
+ ctx.forward(res);
43
+ }).addTo(app);
@@ -3,6 +3,7 @@ import { app, cnbManager, notCNBCheck } from '../../app.ts';
3
3
  import z from 'zod';
4
4
  import './skills.ts';
5
5
  import './keep.ts';
6
+ import './build.ts';
6
7
 
7
8
  // 启动工作空间
8
9
  app.route({
@@ -67,7 +68,7 @@ app.route({
67
68
  page: page ?? 1,
68
69
  pageSize: pageSize ?? 100,
69
70
  });
70
- ctx.forward({ code: 200, message: 'success', data: res });
71
+ ctx.forward(res);
71
72
  }).addTo(app);
72
73
 
73
74
  // 获取工作空间详情
@@ -99,7 +100,7 @@ app.route({
99
100
  ctx.throw(400, '缺少参数 sn');
100
101
  }
101
102
  const res = await cnb.workspace.getDetail(repo, sn);
102
- ctx.forward({ code: 200, message: 'success', data: res });
103
+ ctx.forward(res);
103
104
  }).addTo(app);
104
105
 
105
106
  // 删除工作空间
@@ -161,7 +162,6 @@ app.route({
161
162
  })
162
163
  }
163
164
  }).define(async (ctx) => {
164
- if (notCNBCheck(ctx)) { return; }
165
165
  const cnb = await cnbManager.getContext(ctx);
166
166
  const pipelineId = ctx.query?.pipelineId;
167
167
  const sn = ctx.query?.sn;
@@ -169,6 +169,7 @@ app.route({
169
169
  ctx.throw(400, 'pipelineId 和 sn 必须提供其中一个');
170
170
  }
171
171
  const res = await cnb.workspace.stopWorkspace({ pipelineId, sn });
172
- ctx.forward({ code: 200, message: 'success', data: res });
172
+ ctx.forward(res);
173
173
  }).addTo(app);
174
174
 
175
+