feat: update package dependencies and add new routes for CNB environment management

- Updated package.json and pnpm-lock.yaml with new dependencies and versions.
- Removed outdated readme files from requirements.
- Enhanced CNB environment configuration in cnb-env.ts with new VS Code remote SSH settings.
- Modified KnowledgeBase class to return structured results.
- Updated Workspace class to return structured results.
- Implemented new routes for managing CNB cookies and VS Code proxy URIs.
- Added AI chat functionality for querying knowledge base.
- Created skills for cleaning up closed workspaces.
This commit is contained in:
xiongxiao
2026-01-27 04:02:34 +08:00
parent da7b06e519
commit 50332fe2f4
23 changed files with 665 additions and 201 deletions

View File

@@ -1,12 +1,26 @@
import { createSkill, tool } from '@kevisual/router';
import { app, cnb } from '../../app.ts';
import z from 'zod';
import './skills.ts';
// 启动工作空间
app.route({
path: 'cnb',
key: 'start-workspace',
description: '启动开发工作空间, 参数 repo',
middleware: ['auth'],
metadata: {
tags: ['opencode']
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;
@@ -21,3 +35,133 @@ app.route({
});
ctx.forward(res);
}).addTo(app);
// 获取cnb工作空间列表
app.route({
path: 'cnb',
key: 'list-workspace',
description: '获取cnb开发工作空间列表可选参数 status=running 获取运行中的环境',
middleware: ['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: ['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: ['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: ['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);

View File

@@ -0,0 +1,64 @@
import { createSkill, tool } from '@kevisual/router';
import { app, cnb } from '../../app.ts';
// 批量删除已停止的cnb工作空间
// app.route({
// path: 'cnb',
// key: 'clean-closed-workspace-skill',
// description: '批量删除已停止的cnb工作空间',
// middleware: ['auth'],
// metadata: {
// tags: ['opencode'],
// ...createSkill({
// skill: 'clean-closed-workspace-skill',
// title: '清理已关闭的cnb工作空间',
// summary: '批量删除已停止的cnb工作空间释放资源',
// args: {
// question: tool.schema.string().optional().describe('具体的要求的信息'),
// }
// })
// }
// }).define(async (ctx) => {
// const question = ctx.query?.question || '';
// let content = `这是一个技能任务, 批量删除已停止的cnb工作空间释放资源
// 执行步骤:
// 1. 执行list-workspace获取状态为 closed 的工作空间列表提取sn
// 2. 执行delete-workspace技能传入sns列表的数组批量删除工作空间`
// if (question) {
// content += `\n注意用户的具体要求是${question}`;
// }
// ctx.body = { content }
// }).addTo(app);
// 批量删除已停止的cnb工作空间
app.route({
path: 'cnb',
key: 'clean-closed-workspace',
description: '批量删除已停止的cnb工作空间',
middleware: ['auth'],
metadata: {
tags: ['opencode'],
...createSkill({
skill: 'clean-closed-workspace',
title: '清理已关闭的cnb工作空间',
summary: '批量删除已停止的cnb工作空间释放资源',
})
}
}).define(async (ctx) => {
const closedWorkspaces = await cnb.workspace.list({ status: 'closed' });
if (closedWorkspaces.code !== 200) {
ctx.throw(500, '获取已关闭工作空间列表失败');
}
const list = closedWorkspaces.data?.list || [];
if (list.length === 0) {
ctx.forward({ code: 200, message: '没有已关闭的工作空间需要删除', data: [] });
return;
}
const sns = list.map(ws => ws.sn);
const results = [];
for (const sn of sns) {
const res = await cnb.workspace.deleteWorkspace({ sn });
results.push(res);
}
ctx.forward({ code: 200, message: '已关闭的工作空间删除完成', data: results });
}).addTo(app);