@kevisual/cnb 0.0.2 → 0.0.3

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.
@@ -1,12 +1,26 @@
1
- import { app, cnb } from '@/agent/app.ts';
1
+ import { createSkill, tool } from '@kevisual/router';
2
+ import { app, cnb } from '../../app.ts';
3
+ import z from 'zod';
4
+ import './skills.ts';
2
5
 
6
+ // 启动工作空间
3
7
  app.route({
4
8
  path: 'cnb',
5
9
  key: 'start-workspace',
6
10
  description: '启动开发工作空间, 参数 repo',
7
11
  middleware: ['auth'],
8
12
  metadata: {
9
- tags: ['opencode']
13
+ tags: ['opencode'],
14
+ ...createSkill({
15
+ skill: 'start-workspace',
16
+ title: '启动cnb工作空间',
17
+ summary: '启动cnb工作空间',
18
+ args: {
19
+ repo: tool.schema.string().describe('代码仓库路径,例如 user/repo'),
20
+ branch: tool.schema.string().optional().describe('分支名称,默认主分支'),
21
+ ref: tool.schema.string().optional().describe('提交引用,例如 commit sha'),
22
+ },
23
+ })
10
24
  }
11
25
  }).define(async (ctx) => {
12
26
  const repo = ctx.query?.repo;
@@ -21,3 +35,133 @@ app.route({
21
35
  });
22
36
  ctx.forward(res);
23
37
  }).addTo(app);
38
+
39
+ // 获取cnb工作空间列表
40
+ app.route({
41
+ path: 'cnb',
42
+ key: 'list-workspace',
43
+ description: '获取cnb开发工作空间列表,可选参数 status=running 获取运行中的环境',
44
+ middleware: ['auth'],
45
+ metadata: {
46
+ tags: ['opencode'],
47
+ ...createSkill({
48
+ skill: 'list-workspace',
49
+ title: '列出cnb工作空间',
50
+ summary: '列出cnb工作空间列表,支持按状态过滤, status 可选值 running 或 closed',
51
+ args: {
52
+ status: tool.schema.string().optional().describe('开发环境状态,running: 运行中,closed: 已关闭和停止的'),
53
+ page: tool.schema.number().optional().describe('分页页码,默认 1'),
54
+ pageSize: tool.schema.number().optional().describe('分页大小,默认 20,最大 100'),
55
+ slug: tool.schema.string().optional().describe('仓库路径,例如 groupname/reponame'),
56
+ branch: tool.schema.string().optional().describe('分支名称'),
57
+ },
58
+ })
59
+ }
60
+ }).define(async (ctx) => {
61
+ const { status = 'running', page, pageSize, slug, branch } = ctx.query || {};
62
+ const res = await cnb.workspace.list({
63
+ status: status as 'running' | 'closed' | undefined,
64
+ page: page ?? 1,
65
+ pageSize: pageSize ?? 100,
66
+ });
67
+ ctx.forward({ code: 200, message: 'success', data: res });
68
+ }).addTo(app);
69
+
70
+ // 获取工作空间详情
71
+ app.route({
72
+ path: 'cnb',
73
+ key: 'get-workspace',
74
+ description: '获取工作空间详情,通过 repo 和 sn 获取',
75
+ middleware: ['auth'],
76
+ metadata: {
77
+ tags: ['opencode'],
78
+ ...createSkill({
79
+ skill: 'get-workspace',
80
+ title: '获取工作空间详情',
81
+ summary: '获取工作空间详细信息',
82
+ args: {
83
+ repo: tool.schema.string().describe('代码仓库路径,例如 user/repo'),
84
+ sn: tool.schema.string().describe('工作空间流水线的 sn'),
85
+ },
86
+ })
87
+ }
88
+ }).define(async (ctx) => {
89
+ const repo = ctx.query?.repo;
90
+ const sn = ctx.query?.sn;
91
+ if (!repo) {
92
+ ctx.throw(400, '缺少参数 repo');
93
+ }
94
+ if (!sn) {
95
+ ctx.throw(400, '缺少参数 sn');
96
+ }
97
+ const res = await cnb.workspace.getDetail(repo, sn);
98
+ ctx.forward({ code: 200, message: 'success', data: res });
99
+ }).addTo(app);
100
+
101
+ // 删除工作空间
102
+ app.route({
103
+ path: 'cnb',
104
+ key: 'delete-workspace',
105
+ description: '删除工作空间,通过 pipelineId 或 sn',
106
+ middleware: ['auth'],
107
+ metadata: {
108
+ tags: ['opencode'],
109
+ ...createSkill({
110
+ skill: 'delete-workspace',
111
+ title: '删除工作空间',
112
+ summary: '删除工作空间,pipelineId 和 sn 二选一',
113
+ args: {
114
+ pipelineId: tool.schema.string().optional().describe('流水线 ID,优先使用'),
115
+ sn: tool.schema.string().optional().describe('流水线构建号'),
116
+ sns: tool.schema.array(z.string()).optional().describe('流水线构建号'),
117
+ },
118
+ })
119
+ }
120
+ }).define(async (ctx) => {
121
+ const pipelineId = ctx.query?.pipelineId;
122
+ const sn = ctx.query?.sn;
123
+ const sns = ctx.query?.sns;
124
+ if (!pipelineId && !sn && (!sns || sns.length === 0)) {
125
+ ctx.throw(400, 'pipelineId 和 sn 必须提供其中一个');
126
+ }
127
+ if (sns && sns.length > 0) {
128
+ const results = [];
129
+ for (const snItem of sns) {
130
+ const res = await cnb.workspace.deleteWorkspace({ sn: snItem });
131
+ results.push(res);
132
+ }
133
+ ctx.forward({ code: 200, message: 'success', data: results });
134
+ return;
135
+ }
136
+ const res = await cnb.workspace.deleteWorkspace({ pipelineId, sn });
137
+ ctx.forward(res);
138
+ }).addTo(app);
139
+
140
+ // 停止工作空间
141
+ app.route({
142
+ path: 'cnb',
143
+ key: 'stop-workspace',
144
+ description: '停止工作空间,通过 pipelineId 或 sn',
145
+ middleware: ['auth'],
146
+ metadata: {
147
+ tags: ['opencode'],
148
+ ...createSkill({
149
+ skill: 'stop-workspace',
150
+ title: '停止工作空间',
151
+ summary: '停止运行中的工作空间',
152
+ args: {
153
+ pipelineId: tool.schema.string().optional().describe('流水线 ID,优先使用'),
154
+ sn: tool.schema.string().optional().describe('流水线构建号'),
155
+ },
156
+ })
157
+ }
158
+ }).define(async (ctx) => {
159
+ const pipelineId = ctx.query?.pipelineId;
160
+ const sn = ctx.query?.sn;
161
+ if (!pipelineId && !sn) {
162
+ ctx.throw(400, 'pipelineId 和 sn 必须提供其中一个');
163
+ }
164
+ const res = await cnb.workspace.stopWorkspace({ pipelineId, sn });
165
+ ctx.forward({ code: 200, message: 'success', data: res });
166
+ }).addTo(app);
167
+
@@ -0,0 +1,64 @@
1
+ import { createSkill, tool } from '@kevisual/router';
2
+ import { app, cnb } from '../../app.ts';
3
+
4
+ // 批量删除已停止的cnb工作空间
5
+ // app.route({
6
+ // path: 'cnb',
7
+ // key: 'clean-closed-workspace-skill',
8
+ // description: '批量删除已停止的cnb工作空间',
9
+ // middleware: ['auth'],
10
+ // metadata: {
11
+ // tags: ['opencode'],
12
+ // ...createSkill({
13
+ // skill: 'clean-closed-workspace-skill',
14
+ // title: '清理已关闭的cnb工作空间',
15
+ // summary: '批量删除已停止的cnb工作空间,释放资源',
16
+ // args: {
17
+ // question: tool.schema.string().optional().describe('具体的要求的信息'),
18
+ // }
19
+ // })
20
+ // }
21
+ // }).define(async (ctx) => {
22
+ // const question = ctx.query?.question || '';
23
+ // let content = `这是一个技能任务, 批量删除已停止的cnb工作空间,释放资源
24
+ // 执行步骤:
25
+ // 1. 执行list-workspace,获取状态为 closed 的工作空间列表,提取sn
26
+ // 2. 执行delete-workspace技能,传入sns列表的数组,批量删除工作空间`
27
+ // if (question) {
28
+ // content += `\n注意用户的具体要求是:${question}`;
29
+ // }
30
+ // ctx.body = { content }
31
+ // }).addTo(app);
32
+
33
+ // 批量删除已停止的cnb工作空间
34
+ app.route({
35
+ path: 'cnb',
36
+ key: 'clean-closed-workspace',
37
+ description: '批量删除已停止的cnb工作空间',
38
+ middleware: ['auth'],
39
+ metadata: {
40
+ tags: ['opencode'],
41
+ ...createSkill({
42
+ skill: 'clean-closed-workspace',
43
+ title: '清理已关闭的cnb工作空间',
44
+ summary: '批量删除已停止的cnb工作空间,释放资源',
45
+ })
46
+ }
47
+ }).define(async (ctx) => {
48
+ const closedWorkspaces = await cnb.workspace.list({ status: 'closed' });
49
+ if (closedWorkspaces.code !== 200) {
50
+ ctx.throw(500, '获取已关闭工作空间列表失败');
51
+ }
52
+ const list = closedWorkspaces.data?.list || [];
53
+ if (list.length === 0) {
54
+ ctx.forward({ code: 200, message: '没有已关闭的工作空间需要删除', data: [] });
55
+ return;
56
+ }
57
+ const sns = list.map(ws => ws.sn);
58
+ const results = [];
59
+ for (const sn of sns) {
60
+ const res = await cnb.workspace.deleteWorkspace({ sn });
61
+ results.push(res);
62
+ }
63
+ ctx.forward({ code: 200, message: '已关闭的工作空间删除完成', data: results });
64
+ }).addTo(app);
@@ -0,0 +1,5 @@
1
+ import * as _opencode_ai_plugin from '@opencode-ai/plugin';
2
+
3
+ declare const CnbPlugin: _opencode_ai_plugin.Plugin;
4
+
5
+ export { CnbPlugin };