import { createSkill, tool } from '@kevisual/router'; import { app, cnb } from '../../app.ts'; import z from 'zod'; import './skills.ts'; import './keep.ts'; // 启动工作空间 app.route({ path: 'cnb', key: 'start-workspace', description: '启动开发工作空间, 参数 repo', middleware: ['admin-auth'], metadata: { tags: ['opencode'], ...createSkill({ skill: 'start-workspace', title: '启动cnb工作空间', summary: '启动cnb工作空间', args: { repo: tool.schema.string().describe('代码仓库路径,例如 user/repo'), branch: tool.schema.string().optional().describe('分支名称,默认主分支'), ref: tool.schema.string().optional().describe('提交引用,例如 commit sha'), }, }) } }).define(async (ctx) => { const repo = ctx.query?.repo; const branch = ctx.query?.branch; const ref = ctx.query?.ref; if (!repo) { ctx.throw(400, '缺少参数 repo'); } const res = await cnb.workspace.startWorkspace(repo, { branch, ref }); ctx.forward(res); }).addTo(app); // 获取cnb工作空间列表 app.route({ path: 'cnb', key: 'list-workspace', description: '获取cnb开发工作空间列表,可选参数 status=running 获取运行中的环境', middleware: ['admin-auth'], metadata: { tags: ['opencode'], ...createSkill({ skill: 'list-workspace', title: '列出cnb工作空间', summary: '列出cnb工作空间列表,支持按状态过滤, status 可选值 running 或 closed', args: { status: tool.schema.string().optional().describe('开发环境状态,running: 运行中,closed: 已关闭和停止的'), page: tool.schema.number().optional().describe('分页页码,默认 1'), pageSize: tool.schema.number().optional().describe('分页大小,默认 20,最大 100'), slug: tool.schema.string().optional().describe('仓库路径,例如 groupname/reponame'), branch: tool.schema.string().optional().describe('分支名称'), }, }) } }).define(async (ctx) => { const { status = 'running', page, pageSize, slug, branch } = ctx.query || {}; const res = await cnb.workspace.list({ status: status as 'running' | 'closed' | undefined, page: page ?? 1, pageSize: pageSize ?? 100, }); ctx.forward({ code: 200, message: 'success', data: res }); }).addTo(app); // 获取工作空间详情 app.route({ path: 'cnb', key: 'get-workspace', description: '获取工作空间详情,通过 repo 和 sn 获取', middleware: ['admin-auth'], metadata: { tags: ['opencode'], ...createSkill({ skill: 'get-workspace', title: '获取工作空间详情', summary: '获取工作空间详细信息', args: { repo: tool.schema.string().describe('代码仓库路径,例如 user/repo'), sn: tool.schema.string().describe('工作空间流水线的 sn'), }, }) } }).define(async (ctx) => { const repo = ctx.query?.repo; const sn = ctx.query?.sn; if (!repo) { ctx.throw(400, '缺少参数 repo'); } if (!sn) { ctx.throw(400, '缺少参数 sn'); } const res = await cnb.workspace.getDetail(repo, sn); ctx.forward({ code: 200, message: 'success', data: res }); }).addTo(app); // 删除工作空间 app.route({ path: 'cnb', key: 'delete-workspace', description: '删除工作空间,通过 pipelineId 或 sn', middleware: ['admin-auth'], metadata: { tags: ['opencode'], ...createSkill({ skill: 'delete-workspace', title: '删除工作空间', summary: '删除工作空间,pipelineId 和 sn 二选一', args: { pipelineId: tool.schema.string().optional().describe('流水线 ID,优先使用'), sn: tool.schema.string().optional().describe('流水线构建号'), sns: tool.schema.array(z.string()).optional().describe('批量流水线构建号'), }, }) } }).define(async (ctx) => { const pipelineId = ctx.query?.pipelineId; const sn = ctx.query?.sn; const sns = ctx.query?.sns; if (!pipelineId && !sn && (!sns || sns.length === 0)) { ctx.throw(400, 'pipelineId 和 sn 必须提供其中一个'); } if (sns && sns.length > 0) { const results = []; for (const snItem of sns) { const res = await cnb.workspace.deleteWorkspace({ sn: snItem }); results.push(res); } ctx.forward({ code: 200, message: 'success', data: results }); return; } const res = await cnb.workspace.deleteWorkspace({ pipelineId, sn }); ctx.forward(res); }).addTo(app); // 停止工作空间 app.route({ path: 'cnb', key: 'stop-workspace', description: '停止工作空间,通过 pipelineId 或 sn', middleware: ['admin-auth'], metadata: { tags: ['opencode'], ...createSkill({ skill: 'stop-workspace', title: '停止工作空间', summary: '停止运行中的工作空间', args: { pipelineId: tool.schema.string().optional().describe('流水线 ID,优先使用'), sn: tool.schema.string().optional().describe('流水线构建号'), }, }) } }).define(async (ctx) => { const pipelineId = ctx.query?.pipelineId; const sn = ctx.query?.sn; if (!pipelineId && !sn) { ctx.throw(400, 'pipelineId 和 sn 必须提供其中一个'); } const res = await cnb.workspace.stopWorkspace({ pipelineId, sn }); ctx.forward({ code: 200, message: 'success', data: res }); }).addTo(app);