From 50332fe2f4000288675e1714efcd29a9644b453d Mon Sep 17 00:00:00 2001 From: xiongxiao Date: Tue, 27 Jan 2026 04:02:34 +0800 Subject: [PATCH] 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. --- .vscode/settings.json | 6 - agent/app.ts | 5 +- agent/opencode-plugin.ts | 88 +---------- agent/routes/call/tempCodeRunnerFile.ts | 1 - agent/routes/cnb-env/env.ts | 48 ++++++ agent/routes/cnb-env/index.ts | 4 +- agent/routes/cnb-env/vscode.ts | 94 +++++++++++ agent/routes/index.ts | 2 + agent/routes/knowledge/ai.ts | 142 +++++++++++++++++ agent/routes/knowledge/index.ts | 1 + agent/routes/repo/list.ts | 16 +- agent/routes/repo/repo.ts | 4 +- .../readme.md => agent/routes/repo/skills.ts | 0 agent/routes/workspace/index.ts | 146 +++++++++++++++++- agent/routes/workspace/skills.ts | 64 ++++++++ package.json | 19 +-- pnpm-lock.yaml | 110 ++++++++----- requirements/v1.0/readme.md | 16 -- src/common/cnb-env.ts | 76 +++++---- src/knowledge/index.ts | 15 +- src/workspace/index.ts | 2 +- test/knowledge.ts | 2 +- test/repo.ts | 5 +- 23 files changed, 665 insertions(+), 201 deletions(-) delete mode 100644 .vscode/settings.json delete mode 100644 agent/routes/call/tempCodeRunnerFile.ts create mode 100644 agent/routes/cnb-env/env.ts create mode 100644 agent/routes/cnb-env/vscode.ts create mode 100644 agent/routes/knowledge/ai.ts create mode 100644 agent/routes/knowledge/index.ts rename requirements/v0.1/readme.md => agent/routes/repo/skills.ts (100%) create mode 100644 agent/routes/workspace/skills.ts delete mode 100644 requirements/v1.0/readme.md diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 62080af..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "code-runner.executorMap": { - "ts": "opencode run $selectedText" - }, - "code-runner.runInTerminal": true -} \ No newline at end of file diff --git a/agent/app.ts b/agent/app.ts index 74001ad..6642937 100644 --- a/agent/app.ts +++ b/agent/app.ts @@ -6,7 +6,10 @@ import { nanoid } from 'nanoid'; export const config = useConfig() export const cnb = useContextKey('cnb', () => { - const token = useKey('CNB_API_KEY') as string + // CNB_TOKEN是降级兼容变量,推荐使用CNB_API_KEY + // CNB_TOKEN 是流水线自己就有的变量,但是权限比较小 + const token = useKey('CNB_API_KEY') as string || useKey('CNB_TOKEN') as string + // cookie 变量是可选的 const cookie = useKey('CNB_COOKIE') as string return new CNB({ token: token, cookie: cookie }); }) diff --git a/agent/opencode-plugin.ts b/agent/opencode-plugin.ts index 6d84cef..ee10465 100644 --- a/agent/opencode-plugin.ts +++ b/agent/opencode-plugin.ts @@ -1,86 +1,6 @@ -import { tool } from "@opencode-ai/plugin/tool" -import { type Plugin } from "@opencode-ai/plugin" -import { app, cnb, appId } from './index.ts'; -import { } from 'es-toolkit' -import { Skill } from "@kevisual/router"; +import { app} from './index.ts'; +import { createRouterAgentPluginFn } from '@kevisual/router/opencode' -const routes = app.routes.filter(r => { - const metadata = r.metadata as Skill - if (metadata && metadata.tags && metadata.tags.includes('opencode')) { - return !!metadata.skill - } - return false +export const CnbPlugin = createRouterAgentPluginFn({ + router: app, }) - -// opencode run "请使用 cnb-login-verify 工具验证登录信信息,检查cookie" -export const CnbPlugin: Plugin = async ({ project, client, $, directory, worktree }) => { - return { - 'tool': { - ...routes.reduce((acc, route) => { - const metadata = route.metadata as Skill - acc[metadata.skill!] = { - name: metadata.title || metadata.skill, - description: metadata.summary || '', - args: metadata.args || {}, - async execute(args: Record) { - console.log(`Executing skill ${metadata.skill} with args:`, args); - await client.app.log({ - body: { - service: 'cnb', - level: 'info', - message: `Executing skill ${metadata.skill} with args: ${JSON.stringify(args)}` - } - }); - const res = await app.run({ - path: route.path, - key: route.key, - payload: args - }, { appId }); - if (res.code === 200) { - if (res.data?.content) { - return res.data.content; - } - const str = JSON.stringify(res.data || res, null, 2); - if (str.length > 5000) { - return str.slice(0, 5000) + '... (truncated)'; - } - return str; - } - return `Error: ${res?.message || '无法获取结果'}`; - } - } - return acc; - }, {} as Record) - }, - 'tool.execute.before': async (opts) => { - // console.log('CnbPlugin: tool.execute.before', opts.tool); - // delete toolSkills['cnb-login-verify'] - } - } -} - -const demo = { - 'tool': { - "cnb-login-verify": { - name: "CNB 登录验证信息", - description: "验证 CNB 登录信息是否有效", - args: { - checkToken: tool.schema.boolean().describe("是否检查 Token 的有效性").default(true), - checkCookie: tool.schema.boolean().describe("是否检查 Cookie 的有效性").default(false), - }, - async execute(args) { - const res = await app.run({ - path: 'cnb', - key: 'user-check', - payload: { - ...args - } - }, { appId }); - if (res.code === 200) { - return res.data?.output; - } - return '无法获取登录状态,请检查配置。'; - }, - }, - } -} \ No newline at end of file diff --git a/agent/routes/call/tempCodeRunnerFile.ts b/agent/routes/call/tempCodeRunnerFile.ts deleted file mode 100644 index 9f9212d..0000000 --- a/agent/routes/call/tempCodeRunnerFile.ts +++ /dev/null @@ -1 +0,0 @@ -调用 path: cnb key: get-repo-list payload: { page: 1, per_page: 10 } \ No newline at end of file diff --git a/agent/routes/cnb-env/env.ts b/agent/routes/cnb-env/env.ts new file mode 100644 index 0000000..962a5bb --- /dev/null +++ b/agent/routes/cnb-env/env.ts @@ -0,0 +1,48 @@ +import { createSkill, tool } from '@kevisual/router'; +import { app, cnb } from '../../app.ts'; + +// 设置 CNB_COOKIE环境变量和获取环境变量,用于界面操作定制模块功能 +app.route({ + path: 'cnb', + key: 'set-cnb-cookie', + description: '设置当前cnb工作空间的cookie环境变量', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'set-cnb-cookie', + title: '设置当前cnb工作空间的cookie环境变量', + summary: '设置当前cnb工作空间的cookie环境变量,用于界面操作定制模块功能,例子:CNBSESSION=xxxx;csrfkey=2222xxxx;', + args: { + cookie: tool.schema.string().describe('cnb的cookie值'), + } + }) + } +}).define(async (ctx) => { + const cookie = ctx.query?.cookie; + if (!cookie) { + ctx.body = { content: '请提供有效的cookie值' }; + return; + } + cnb.cookie = cookie; + ctx.body = { content: '已成功设置cnb的cookie环境变量' }; +}).addTo(app); + +// 获取 CNB_COOKIE环境变量 +app.route({ + path: 'cnb', + key: 'get-cnb-cookie', + description: '获取当前cnb工作空间的cookie环境变量', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'get-cnb-cookie', + title: '获取当前cnb工作空间的cookie环境变量', + summary: '获取当前cnb工作空间的cookie环境变量,用于界面操作定制模块功能', + }) + } +}).define(async (ctx) => { + const cookie = cnb.cookie || '未设置cookie环境变量'; + ctx.body = { content: `当前cnb工作空间的cookie环境变量为:${cookie}` }; +}).addTo(app); \ No newline at end of file diff --git a/agent/routes/cnb-env/index.ts b/agent/routes/cnb-env/index.ts index 860a753..043f8ba 100644 --- a/agent/routes/cnb-env/index.ts +++ b/agent/routes/cnb-env/index.ts @@ -1 +1,3 @@ -// 根据环境变量获取当前的 cnb 启动环境 \ No newline at end of file +// 根据环境变量获取当前的 cnb 启动环境 +import './vscode.ts'; +import './env.ts'; \ No newline at end of file diff --git a/agent/routes/cnb-env/vscode.ts b/agent/routes/cnb-env/vscode.ts new file mode 100644 index 0000000..96c9333 --- /dev/null +++ b/agent/routes/cnb-env/vscode.ts @@ -0,0 +1,94 @@ +import { createSkill, tool } from '@kevisual/router'; +import { app, cnb } from '../../app.ts'; + +import { CNB_ENV } from "@/common/cnb-env.ts"; + +// 执行技能 get-cnb-port-uri,端口为4096 +// 执行技能 get-cnb-port-uri,端口为51515 +app.route({ + path: 'cnb', + key: 'get-cnb-port-uri', + description: '获取当前cnb工作空间的port代理uri', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'get-cnb-port-uri', + title: '获取当前cnb工作空间的port代理uri', + summary: '获取当前cnb工作空间的port代理uri,用于端口转发', + args: { + port: tool.schema.number().optional().describe('端口号,默认为4096'), + } + }) + } +}).define(async (ctx) => { + const port = ctx.query?.port || 4096; + const uri = CNB_ENV?.CNB_VSCODE_PROXY_URI as string || ''; + const finalUri = uri.replace('{{port}}', port.toString()); + let content = ` + cnb工作空间的访问uri为:${finalUri} + ` + ctx.body = { content }; +}).addTo(app); + +// 获取当前cnb工作空间的vscode代理uri,执行技能 get-cnb-vscode-uri +// 包括 web 访问uri,vscode 访问uri,codebuddy 访问uri,cursor 访问uri,ssh 连接字符串 + +app.route({ + path: 'cnb', + key: 'get-cnb-vscode-uri', + description: '获取当前cnb工作空间的vscode代理uri, 包括多种访问方式, 如web、vscode、codebuddy、cursor、ssh', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'get-cnb-vscode-uri', + title: '获取当前cnb工作空间的编辑器访问地址', + summary: '获取当前cnb工作空间的vscode代理uri,用于在浏览器中访问vscode,包含多种访问方式,如web、vscode、codebuddy、cursor、ssh', + args: { + web: tool.schema.boolean().optional().describe('是否获取vscode web的访问uri,默认为false'), + vscode: tool.schema.boolean().optional().describe('是否获取vscode的代理uri,默认为true'), + codebuddy: tool.schema.boolean().optional().describe('是否获取codebuddy的代理uri,默认为false'), + cursor: tool.schema.boolean().optional().describe('是否获取cursor的代理uri,默认为false'), + // trae: tool.schema.boolean().optional().describe('是否获取trae的代理uri,默认为false'), + ssh: tool.schema.boolean().optional().describe('是否获取vscode remote ssh的连接字符串,默认为false'), + } + }) + } +}).define(async (ctx) => { + const web = ctx.query?.web ?? false; + const vscode = ctx.query?.vscode ?? true; // 默认true + const codebuddy = ctx.query?.codebuddy ?? false; + const cursor = ctx.query?.cursor ?? false; + // const trae = ctx.query?.trae ?? false; + const ssh = ctx.query?.ssh ?? false; + + const webUri = CNB_ENV?.CNB_VSCODE_WEB_URL as string || ''; + const vscodeSchma = CNB_ENV?.CNB_VSCODE_REMOTE_SSH_SCHEMA as string || ''; + let content = ` + 当前的cnb 仓库为:${CNB_ENV?.CNB_REPO_SLUG} + + ` + if (web) { + content += `VS Code Web 访问 URI:${webUri}\n\n`; + } + if (vscode) { + content += `VS Code 访问 URI:${vscodeSchma}\n\n`; + } + if (codebuddy) { + const codebuddyUri = vscodeSchma.replace('vscode://vscode-remote/ssh-remote+', 'codebuddycn://vscode-remote/codebuddy-remote'); + content += `CodeBuddy 访问 URI:${codebuddyUri}\n\n`; + } + if (cursor) { + const cursorUri = vscodeSchma.replace('vscode://', 'cursor://'); + content += `Cursor 访问 URI:${cursorUri}\n\n`; + } + // if (trae) { + // const traeUri = vscodeSchma.replace('vscode://vscode-remote/ssh-remote+', 'traecn://ssh-remote+'); + // content += `Trae 访问 URI:${traeUri}\n\n`; + // } + if (ssh) { + content += `VS Code Remote SSH 连接字符串:ssh ${CNB_ENV.CNB_PIPELINE_ID}.${CNB_ENV.CNB_VSCODE_SSH_TOKEN}@cnb.space`; + } + ctx.body = { content }; +}).addTo(app); \ No newline at end of file diff --git a/agent/routes/index.ts b/agent/routes/index.ts index 68dab1c..1c09df1 100644 --- a/agent/routes/index.ts +++ b/agent/routes/index.ts @@ -3,6 +3,8 @@ import './user/check.ts' import './repo/index.ts' import './workspace/index.ts' import './call/index.ts' +import './cnb-env/index.ts' +import './knowledge/index.ts' import { isEqual } from 'es-toolkit' /** diff --git a/agent/routes/knowledge/ai.ts b/agent/routes/knowledge/ai.ts new file mode 100644 index 0000000..90d001a --- /dev/null +++ b/agent/routes/knowledge/ai.ts @@ -0,0 +1,142 @@ +import { createSkill, tool } from '@kevisual/router'; +import { app, cnb } from '../../app.ts'; +import { CNBChat } from '@kevisual/ai/browser' + +/** + +调用cnb-ai-chat技能, repo为kevisual/starred-auto. +问题是:用户提供的问题是OpenListTeam/OpenList是什么,有多少star + + */ +app.route({ + path: 'cnb', + key: 'cnb-ai-chat', + description: '调用cnb的知识库ai对话功能进行聊天', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'cnb-ai-chat', + title: '调用cnb的知识库ai对话功能进行聊天', + summary: '调用cnb的知识库ai对话功能进行聊天,基于cnb提供的ai能力', + args: { + question: tool.schema.string().describe('用户输入的消息内容'), + repo: tool.schema.string().optional().describe('知识库仓库ID,默认为空表示使用默认知识库'), + } + }) + } +}).define(async (ctx) => { + const question = ctx.query?.question; + if (!question) { + ctx.body = { content: '请提供有效的消息内容' }; + return; + } + let repo = ctx.query?.repo; + if (!repo) { + // 如果未指定知识库仓库ID,则使用默认知识库 + const res = await cnb.repo.getRepoList({ flags: 'KnowledgeBase' }); + if (res.code === 200 && res.data.length > 0) { + repo = res.data[0].path; + } + } + console.log("Using knowledge base repo:", repo); + const ragRes = await cnb.knowledgeBase.queryKnowledgeBase(repo || '', { + query: question, + score_threshold: 0.62, + top_k: 10, + }); + if (ragRes.code !== 200) { + ctx.body = { content: `查询知识库失败,错误信息:${ragRes.message}` }; + return; + } + const list = ragRes.data || []; + // 构建RAG上下文,包含文件来源和相似度信息 + const ragContext = list.map((item, index) => { + const source = item.metadata?.path || item.metadata?.name || '未知来源'; + const type = item.metadata?.type === 'code' ? '〔代码〕' : '〔文档〕'; + const scorePercent = Math.round((item.score || 0) * 100); + const url = item.metadata?.url || '无'; + return `〔来源${index + 1}〕${type} ${source} (相似度: ${scorePercent}%)\n${item.chunk}\n访问地址: ${url}`; + }).join('\n\n---\n\n'); + // hunyuan-a13b + // enable_thinking + const chat = new CNBChat({ + repo, + token: cnb.token, + model: 'hunyuan-a13b' + }); + const messages = [ + { + role: 'system', + content: `[角色定义]='''\n你是一个专业的技术助手,擅长基于提供的知识库内容进行准确、有条理的分析和回答。你的特点是:\n1. 严格基于RAG检索到的上下文内容进行回答,不添加未经验证的信息\n2. 回答时清晰标注信息来源,便于用户追溯查证\n3. 面对不确定的信息,明确标注「根据提供的内容无法确定」\n4. 代码相关问题注重可执行性和最佳实践\n'''[要求]='''\n1. 严格遵循用户的提问要求,优先解决用户的核心问题\n2. 避免侵犯版权的内容,不复制原文超过100字(技术术语和函数名除外)\n3. 使用中文进行响应,语言专业且易于理解\n4. 如果上下文存在多个来源,优先使用相似度更高的内容\n5. 对于代码片段,确保完整且可直接使用\n6. 当上下文中没有相关信息时,直接说明「知识库中未找到相关内容」\n7. 在思考过程中分析:用户的真实意图是什么?提供的上下文是否足够回答?\n'''[回答格式]='''\n- 先简要说明回答的核心结论\n- 如有必要,分点阐述详细分析过程\n- 标注关键信息来源(标注【来源X】即可)\n- 提供可操作的建议或代码示例\n'''` + }, + { + role: 'user', + content: `[上下文]='''\n${ragContext}\n'''\n\n[用户问题]='''\n${question}\n'''\n\n请基于以上上下文知识库内容回答用户问题。` + } + ] as Array<{ role: 'system' | 'user' | 'assistant', content: string }>; + const response = await chat.chat({ + messages + }); + const txt = chat.responseText; + ctx.body = { content: txt, response }; +}).addTo(app); + + +// RAG知识库查询技能: 查询openlist有多少star +app.route({ + path: 'cnb', + key: 'cnb-rag-query', + description: '调用cnb的知识库RAG查询功能进行问答', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'cnb-rag-query', + title: '调用cnb的知识库RAG查询功能进行问答', + summary: '调用cnb的知识库RAG查询功能进行问答,基于cnb提供的知识库能力', + args: { + question: tool.schema.string().describe('用户输入的消息内容'), + repo: tool.schema.string().optional().describe('知识库仓库ID,默认为空表示使用默认知识库'), + } + }) + } +}).define(async (ctx) => { + const question = ctx.query?.question; + if (!question) { + ctx.body = { content: '请提供有效的消息内容' }; + return; + } + let repo = ctx.query?.repo; + if (!repo) { + // 如果未指定知识库仓库ID,则使用默认知识库 + const res = await cnb.repo.getRepoList({ flags: 'KnowledgeBase' }); + if (res.code === 200 && res.data.length > 0) { + repo = res.data[0].path; + } + } + console.log("Using knowledge base repo:", repo); + const ragRes = await cnb.knowledgeBase.queryKnowledgeBase(repo || '', { + query: question, + score_threshold: 0.62, + top_k: 10, + }); + if (ragRes.code !== 200) { + ctx.body = { content: `查询知识库失败,错误信息:${ragRes.message}` }; + return; + } + const list = ragRes.data || []; + let answer = `基于知识库「${repo}」的查询结果:\n\n`; + if (list.length === 0) { + answer += '知识库中未找到相关内容。'; + } else { + list.forEach((item, index) => { + const source = item.metadata?.path || item.metadata?.name || '未知来源'; + const type = item.metadata?.type === 'code' ? '〔代码〕' : '〔文档〕'; + const scorePercent = Math.round((item.score || 0) * 100); + const url = item.metadata?.url || '无'; + answer += `【来源${index + 1}】${type} ${source} (相似度: ${scorePercent}%)\n${item.chunk}\n访问地址: ${url}\n\n`; + }); + } + ctx.body = { content: answer }; +}).addTo(app); \ No newline at end of file diff --git a/agent/routes/knowledge/index.ts b/agent/routes/knowledge/index.ts new file mode 100644 index 0000000..5c460d2 --- /dev/null +++ b/agent/routes/knowledge/index.ts @@ -0,0 +1 @@ +import './ai.ts' \ No newline at end of file diff --git a/agent/routes/repo/list.ts b/agent/routes/repo/list.ts index 37e4f03..ce6d329 100644 --- a/agent/routes/repo/list.ts +++ b/agent/routes/repo/list.ts @@ -2,6 +2,8 @@ import { createSkill } from '@kevisual/router'; import { app, cnb } from '../../app.ts'; import { tool } from "@opencode-ai/plugin/tool" +// "列出我的代码仓库,search blog" +// 列出我的知识库的代码仓库 app.route({ path: 'cnb', key: 'list-repos', @@ -11,18 +13,24 @@ app.route({ tags: ['opencode'], ...createSkill({ skill: 'list-repos', - title: '列出代码仓库', - summary: '列出代码仓库', + title: '列出cnb代码仓库', + summary: '列出cnb代码仓库, 可选flags参数,如 KnowledgeBase', args: { search: tool.schema.string().optional().describe('搜索关键词'), pageSize: tool.schema.number().optional().describe('每页数量,默认999'), + flags: tool.schema.string().optional().describe('仓库标记,如果是知识库则填写 KnowledgeBase'), }, }) } }).define(async (ctx) => { const search = ctx.query?.search; const pageSize = ctx.query?.pageSize || 9999; - const res = await cnb.repo.getRepoList({ search, page_size: pageSize, role: 'developer' }); + const flags = ctx.query?.flags; + const params: any = {}; + if (flags) { + params.flags = flags; + } + const res = await cnb.repo.getRepoList({ search, page_size: pageSize, role: 'developer', ...params }); if (res.code === 200) { const repos = res.data.map((item) => ({ name: item.name, @@ -30,7 +38,7 @@ app.route({ description: item.description, web_url: item.web_url, })); - ctx.body = { content: JSON.stringify(repos) }; + ctx.body = { content: JSON.stringify(repos), list: res.data }; } else { ctx.throw(500, '获取仓库列表失败'); } diff --git a/agent/routes/repo/repo.ts b/agent/routes/repo/repo.ts index d797022..1ea62ee 100644 --- a/agent/routes/repo/repo.ts +++ b/agent/routes/repo/repo.ts @@ -1,6 +1,8 @@ import { app, cnb } from '../../app.ts'; import { createSkill, Skill } from '@kevisual/router' import { tool } from "@opencode-ai/plugin/tool" + +// 创建一个仓库 kevisual/test-repo app.route({ path: 'cnb', key: 'create-repo', @@ -79,7 +81,7 @@ app.route({ ctx.forward(res); }).addTo(app); - +// 删除一个仓库 kevisual/test-repo app.route({ path: 'cnb', key: 'delete-repo', diff --git a/requirements/v0.1/readme.md b/agent/routes/repo/skills.ts similarity index 100% rename from requirements/v0.1/readme.md rename to agent/routes/repo/skills.ts diff --git a/agent/routes/workspace/index.ts b/agent/routes/workspace/index.ts index a4cbcca..1df17a3 100644 --- a/agent/routes/workspace/index.ts +++ b/agent/routes/workspace/index.ts @@ -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); + diff --git a/agent/routes/workspace/skills.ts b/agent/routes/workspace/skills.ts new file mode 100644 index 0000000..026d1de --- /dev/null +++ b/agent/routes/workspace/skills.ts @@ -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); \ No newline at end of file diff --git a/package.json b/package.json index 03fa436..485bd4a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "bun run bun.config.ts" }, "keywords": [], "files": [ @@ -14,25 +14,26 @@ ], "author": "abearxiong (https://www.xiongxiao.me)", "license": "MIT", - "packageManager": "pnpm@10.28.0", + "packageManager": "pnpm@10.28.2", "type": "module", "devDependencies": { + "@kevisual/ai": "^0.0.22", "@kevisual/context": "^0.0.4", - "@kevisual/types": "^0.0.11", - "@opencode-ai/plugin": "^1.1.23", + "@kevisual/types": "^0.0.12", + "@opencode-ai/plugin": "^1.1.36", "@types/bun": "^1.3.6", - "@types/node": "^25.0.9", + "@types/node": "^25.0.10", "dotenv": "^17.2.3" }, "publishConfig": { "access": "public" }, "dependencies": { - "@kevisual/query": "^0.0.35", - "@kevisual/router": "^0.0.55", + "@kevisual/query": "^0.0.38", + "@kevisual/router": "^0.0.62", "@kevisual/use-config": "^1.0.28", - "es-toolkit": "^1.43.0", + "es-toolkit": "^1.44.0", "nanoid": "^5.1.6", - "zod": "^4.3.5" + "zod": "^4.3.6" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d5daf9..0ebed5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,76 +9,88 @@ importers: .: dependencies: '@kevisual/query': - specifier: ^0.0.35 - version: 0.0.35 + specifier: ^0.0.38 + version: 0.0.38 '@kevisual/router': - specifier: ^0.0.55 - version: 0.0.55 + specifier: ^0.0.62 + version: 0.0.62 '@kevisual/use-config': specifier: ^1.0.28 version: 1.0.28(dotenv@17.2.3) es-toolkit: - specifier: ^1.43.0 - version: 1.43.0 + specifier: ^1.44.0 + version: 1.44.0 nanoid: specifier: ^5.1.6 version: 5.1.6 zod: - specifier: ^4.3.5 - version: 4.3.5 + specifier: ^4.3.6 + version: 4.3.6 devDependencies: + '@kevisual/ai': + specifier: ^0.0.22 + version: 0.0.22 '@kevisual/context': specifier: ^0.0.4 version: 0.0.4 '@kevisual/types': - specifier: ^0.0.11 - version: 0.0.11 + specifier: ^0.0.12 + version: 0.0.12 '@opencode-ai/plugin': - specifier: ^1.1.23 - version: 1.1.23 + specifier: ^1.1.36 + version: 1.1.36 '@types/bun': specifier: ^1.3.6 version: 1.3.6 '@types/node': - specifier: ^25.0.9 - version: 25.0.9 + specifier: ^25.0.10 + version: 25.0.10 dotenv: specifier: ^17.2.3 version: 17.2.3 packages: + '@kevisual/ai@0.0.22': + resolution: {integrity: sha512-9kth0pvPD4jT8c6rJ0DzSuvUY0s0pdw+CnO+YU8bwpMg04k1jFkd1RSZdtFrVbc60e2cpooQMUt/oimkfs082A==} + '@kevisual/context@0.0.4': resolution: {integrity: sha512-HJeLeZQLU+7tCluSfOyvkgKLs0HjCZrdJlZgEgKRSa8XTwZfMAUt6J7qZTbrZAHBlPtX68EPu/PI8JMCeu3WAQ==} '@kevisual/load@0.0.6': resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==} - '@kevisual/query@0.0.35': - resolution: {integrity: sha512-80dyy2LMCmEC72g+X4QWUKlZErhawQPgnGSBNR4yhrBcFgHIJQ14LR1Z+bS5S1I7db+1PDNpaxBTjIaoYoXunw==} + '@kevisual/logger@0.0.4': + resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==} - '@kevisual/router@0.0.55': - resolution: {integrity: sha512-DVhXbbUCfSWWXsp1id1HBrkGiMZ6nFUBD1/C5E7IpLE5B32w7sv2xjKUt98OriFl0uyuneMEIZuZsAQaKplQ5g==} + '@kevisual/permission@0.0.3': + resolution: {integrity: sha512-8JsA/5O5Ax/z+M+MYpFYdlioHE6jNmWMuFSokBWYs9CCAHNiSKMR01YLkoVDoPvncfH/Y8F5K/IEXRCbptuMNA==} - '@kevisual/types@0.0.11': - resolution: {integrity: sha512-idNLDTEKVdNXZHFQq8PTN62nflh94kvGtx+v8YDcMxt0Zo+HWVZTFElm+dMQxAs/vn4wo8F2r3VwzWNX/vcqwQ==} + '@kevisual/query@0.0.38': + resolution: {integrity: sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q==} + + '@kevisual/router@0.0.62': + resolution: {integrity: sha512-kKQFYkJ/qemGAygGSKkM/TujvreoU9HxL7/2vx2RYDMRyRuZOYUuvrTF92XIffFnZHugFbs8uEt+Z8I11SrFUg==} + + '@kevisual/types@0.0.12': + resolution: {integrity: sha512-zJXH2dosir3jVrQ6QG4i0+iLQeT9gJ3H+cKXs8ReWboxBSYzUZO78XssVeVrFPsJ33iaAqo4q3DWbSS1dWGn7Q==} '@kevisual/use-config@1.0.28': resolution: {integrity: sha512-ngF+LDbjxpXWrZNmnShIKF/jPpAa+ezV+DcgoZIIzHlRnIjE+rr9sLkN/B7WJbiH9C/j1tQXOILY8ujBqILrow==} peerDependencies: dotenv: ^17 - '@opencode-ai/plugin@1.1.23': - resolution: {integrity: sha512-O/iLSKOUuzD95UWhj9y/tEuycPEBv36de0suHXXqeYLWZLZ16DAUSKR+YG7rvRjJS0sbn4biVMw+k7XXk/oxiQ==} + '@opencode-ai/plugin@1.1.36': + resolution: {integrity: sha512-b2XWeFZN7UzgwkkzTIi6qSntkpEA9En2zvpqakQzZAGQm6QBdGAlv6r1u5hEnmF12Gzyj5umTMWr5GzVbP/oAA==} - '@opencode-ai/sdk@1.1.23': - resolution: {integrity: sha512-YjN9ogzkLol92s+/iARXRop9/5oFIezUkvWVay12u1IM6A/WJs50DeKl3oL0x4a68P1a5tI5gD98dLnk2+AlsA==} + '@opencode-ai/sdk@1.1.36': + resolution: {integrity: sha512-feNHWnbxhg03TI2QrWnw3Chc0eYrWSDSmHIy/ejpSVfcKlfXREw1Tpg0L4EjrpeSc4jB1eM673dh+WM/Ko2SFQ==} '@types/bun@1.3.6': resolution: {integrity: sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA==} - '@types/node@25.0.9': - resolution: {integrity: sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==} + '@types/node@25.0.10': + resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==} bun-types@1.3.6: resolution: {integrity: sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ==} @@ -87,12 +99,16 @@ packages: resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} - es-toolkit@1.43.0: - resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==} + es-toolkit@1.44.0: + resolution: {integrity: sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==} eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + hono@4.11.6: + resolution: {integrity: sha512-ofIiiHyl34SV6AuhE3YT2mhO5HRWokce+eUYE82TsP6z0/H3JeJcjVWEMSIAiw2QkjDOEpES/lYsg8eEbsLtdw==} + engines: {node: '>=16.9.0'} + nanoid@5.1.6: resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} engines: {node: ^18 || >=20} @@ -107,55 +123,69 @@ packages: zod@4.1.8: resolution: {integrity: sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==} - zod@4.3.5: - resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} snapshots: + '@kevisual/ai@0.0.22': + dependencies: + '@kevisual/logger': 0.0.4 + '@kevisual/permission': 0.0.3 + '@kevisual/query': 0.0.38 + '@kevisual/context@0.0.4': {} '@kevisual/load@0.0.6': dependencies: eventemitter3: 5.0.1 - '@kevisual/query@0.0.35': + '@kevisual/logger@0.0.4': {} + + '@kevisual/permission@0.0.3': {} + + '@kevisual/query@0.0.38': dependencies: tslib: 2.8.1 - '@kevisual/router@0.0.55': {} + '@kevisual/router@0.0.62': + dependencies: + hono: 4.11.6 - '@kevisual/types@0.0.11': {} + '@kevisual/types@0.0.12': {} '@kevisual/use-config@1.0.28(dotenv@17.2.3)': dependencies: '@kevisual/load': 0.0.6 dotenv: 17.2.3 - '@opencode-ai/plugin@1.1.23': + '@opencode-ai/plugin@1.1.36': dependencies: - '@opencode-ai/sdk': 1.1.23 + '@opencode-ai/sdk': 1.1.36 zod: 4.1.8 - '@opencode-ai/sdk@1.1.23': {} + '@opencode-ai/sdk@1.1.36': {} '@types/bun@1.3.6': dependencies: bun-types: 1.3.6 - '@types/node@25.0.9': + '@types/node@25.0.10': dependencies: undici-types: 7.16.0 bun-types@1.3.6: dependencies: - '@types/node': 25.0.9 + '@types/node': 25.0.10 dotenv@17.2.3: {} - es-toolkit@1.43.0: {} + es-toolkit@1.44.0: {} eventemitter3@5.0.1: {} + hono@4.11.6: {} + nanoid@5.1.6: {} tslib@2.8.1: {} @@ -164,4 +194,4 @@ snapshots: zod@4.1.8: {} - zod@4.3.5: {} + zod@4.3.6: {} diff --git a/requirements/v1.0/readme.md b/requirements/v1.0/readme.md deleted file mode 100644 index 5ea7a6f..0000000 --- a/requirements/v1.0/readme.md +++ /dev/null @@ -1,16 +0,0 @@ -对/agents 下的代码进行修改 - -需求1: - -创建仓库, 需要仓库名字 -1. 自动添加.cnb.yml, 对.cnb.yml的内容进行修改。修改TO_REPO对应的内容, TO_REPO,是“group+repo”需要传入仓库名字,默认group为kevisual -2. 自动添加opencode.json - -需求2: -获取当前启动的云开发环境。 -输出对应的请求的地址,token等信息 -输出打开云开发的vscode,buddy,cursor,trae,qcoder等工具的地址 - -需求3: - - diff --git a/src/common/cnb-env.ts b/src/common/cnb-env.ts index 193dab2..61f46b9 100644 --- a/src/common/cnb-env.ts +++ b/src/common/cnb-env.ts @@ -7,41 +7,51 @@ import { useKey } from "@kevisual/use-config" export const CNB_ENV = { - // 仓库相关配置 - /** 仓库的 HTTPS 地址,如 "https://cnb.cool/kevisual/cnb" */ - CNB_REPO_URL_HTTPS: useKey('CNB_REPO_URL_HTTPS'), + // 仓库相关配置 + /** 仓库的 HTTPS 地址,如 "https://cnb.cool/kevisual/cnb" */ + CNB_REPO_URL_HTTPS: useKey('CNB_REPO_URL_HTTPS'), - // 构建相关配置 - /** 流水线 ID,唯一标识一次构建流水线,如 "cnb-108-1jer5qekq-001" */ - CNB_PIPELINE_ID: useKey('CNB_PIPELINE_ID'), - /** 构建 ID,与流水线 ID 相关联,如 "cnb-108-1jer5qekq" */ - CNB_BUILD_ID: useKey('CNB_BUILD_ID'), - /** 构建开始时间,ISO 8601 格式,如 "2026-01-13T07:58:41.825Z" */ - CNB_BUILD_START_TIME: useKey('CNB_BUILD_START_TIME'), - /** 构建日志 Web 界面 URL,用于在浏览器中查看构建日志 */ - CNB_BUILD_WEB_URL: useKey('CNB_BUILD_WEB_URL'), - /** 触发构建的事件类型,如 "vscode" 表示由 VS Code 触发 */ - CNB_EVENT: useKey('CNB_EVENT'), - /** 当前构建对应的 Git 提交哈希值 */ - CNB_COMMIT: useKey('CNB_COMMIT'), + // 构建相关配置 + /** 流水线 ID,唯一标识一次构建流水线,如 "cnb-upo-1jfth1771-001" */ + CNB_PIPELINE_ID: useKey('CNB_PIPELINE_ID'), + /** 构建 ID,与流水线 ID 相关联,如 "cnb-upo-1jfth1771" */ + CNB_BUILD_ID: useKey('CNB_BUILD_ID'), + /** 构建开始时间,ISO 8601 格式,如 "2026-01-13T07:58:41.825Z" */ + CNB_BUILD_START_TIME: useKey('CNB_BUILD_START_TIME'), + /** 构建日志 Web 界面 URL,用于在浏览器中查看构建日志 */ + CNB_BUILD_WEB_URL: useKey('CNB_BUILD_WEB_URL'), + /** 触发构建的事件类型,如 "vscode" 表示由 VS Code 触发 */ + CNB_EVENT: useKey('CNB_EVENT'), + /** 当前构建对应的 Git 提交哈希值 */ + CNB_COMMIT: useKey('CNB_COMMIT'), - // VS Code 相关配置 - /** VS Code Web 界面的访问 URL,用于在浏览器中打开 VS Code */ - CNB_VSCODE_WEB_URL: useKey('CNB_VSCODE_WEB_URL'), - /** VS Code 代理 URI,用于端口转发,{{port}} 会被替换为实际端口号, 例如: "https://1wnts22gq3-{{port}}.cnb.run"*/ - CNB_VSCODE_PROXY_URI: useKey('CNB_VSCODE_PROXY_URI'), + // VS Code 相关配置 + /** VS Code Web 界面的访问 URL,用于在浏览器中打开 VS Code,例如:'https://cnb.cool/kevisual/cnb/-/workspace/vscode-web/cnb-upo-1jfth1771-001/'*/ + CNB_VSCODE_WEB_URL: useKey('CNB_VSCODE_WEB_URL'), + /** VS Code 代理 URI,用于端口转发,{{port}} 会被替换为实际端口号, 例如: "https://1wnts22gq3-{{port}}.cnb.run"*/ + CNB_VSCODE_PROXY_URI: useKey('CNB_VSCODE_PROXY_URI'), + /** + * VS Code Remote SSH 连接字符串,例如: vscode://vscode-remote/ssh-remote+cnb-upo-1jfth1771-001.8939f3d1-de13-486e-921f-f07943fcfa28-qng@cnb.space/workspace/" + * 是CNB_PIPELINE_ID和CNB_VSCODE_SSH_TOKEN的组合 + */ + CNB_VSCODE_REMOTE_SSH_SCHEMA: useKey('CNB_VSCODE_REMOTE_SSH_SCHEMA'), + /** + * VS Code Remote SSH 连接的认证 Token 8939f3d1-de13-486e-921f-f07943fcfa28-qng + * 组装为ssh的链接字符串是 ssh CNB_PIPELINE_ID + '.' + CNB_VSCODE_SSH_TOKEN@cnb.space + */ + CNB_VSCODE_SSH_TOKEN: useKey('CNB_VSCODE_SSH_TOKEN'), - // 仓库标识配置 - /** 仓库标识符,格式为 "组名/仓库名",如 "kevisual/cnb" */ - CNB_REPO_SLUG: useKey('CNB_REPO_SLUG'), - /** 组名/命名空间标识符,如 "kevisual" */ - CNB_GROUP_SLUG: useKey('CNB_GROUP_SLUG'), + // 仓库标识配置 + /** 仓库标识符,格式为 "组名/仓库名",如 "kevisual/cnb" */ + CNB_REPO_SLUG: useKey('CNB_REPO_SLUG'), + /** 组名/命名空间标识符,如 "kevisual" */ + CNB_GROUP_SLUG: useKey('CNB_GROUP_SLUG'), - // 运行器资源配置 - /** 运行器分配的 CPU 核心数,单位为核, 例如: "8"*/ - CNB_CPUS: useKey('CNB_CPUS'), - /** 运行器分配的内存大小,单位为 GB, 例如: "16"*/ - CNB_MEMORY: useKey('CNB_MEMORY'), - /** 运行器的 IP 地址,用于网络连接和调试 */ - CNB_RUNNER_IP: useKey('CNB_RUNNER_IP'), + // 运行器资源配置 + /** 运行器分配的 CPU 核心数,单位为核, 例如: "8"*/ + CNB_CPUS: useKey('CNB_CPUS'), + /** 运行器分配的内存大小,单位为 GB, 例如: "16"*/ + CNB_MEMORY: useKey('CNB_MEMORY'), + /** 运行器的 IP 地址,用于网络连接和调试 */ + CNB_RUNNER_IP: useKey('CNB_RUNNER_IP'), } \ No newline at end of file diff --git a/src/knowledge/index.ts b/src/knowledge/index.ts index 2197199..6b69c9b 100644 --- a/src/knowledge/index.ts +++ b/src/knowledge/index.ts @@ -10,7 +10,7 @@ export class KnowledgeBase extends CNBCore { score_threshold?: number, top_k?: number, metadata_filtering_conditions?: MetadataFilteringConditions - }): Promise { + }): Promise> { const url = `/${repo}/-/knowledge/base/query`; let postData = { query: data.query, @@ -43,4 +43,17 @@ type MetadataFilteringConditions = { value?: string }> logical_operator?: 'adn' | 'or' +} + +type QueryRag = { + chunk: string; + score: number; + metadata: { + hash: string; + name: string; + path: string; + position: string; + type: string; // code, text + url: string; + } } \ No newline at end of file diff --git a/src/workspace/index.ts b/src/workspace/index.ts index cc59f79..4a547ba 100644 --- a/src/workspace/index.ts +++ b/src/workspace/index.ts @@ -54,7 +54,7 @@ export class Workspace extends CNBCore { return this.post({ url: '/workspace/delete', data }); } /** 获取我的云原生开发环境列表 */ - async list(params?: ListParams): Promise { + async list(params?: ListParams): Promise> { return this.get({ url: '/workspace/list', params }); } diff --git a/test/knowledge.ts b/test/knowledge.ts index 6a79694..d3d881d 100644 --- a/test/knowledge.ts +++ b/test/knowledge.ts @@ -3,7 +3,7 @@ import { KnowledgeBase } from "../src/knowledge/index.ts"; import { token, showMore, cookie } from "./common.ts"; // group: "kevisual/test", const repo = new KnowledgeBase({ token: token, cookie: cookie }); -const repoName = "test-local-docs"; +const repoName = "kevisual/starred-auto"; // const queryRes = await repo.getEmbeddingModels(repoName); diff --git a/test/repo.ts b/test/repo.ts index 5bd5ead..678c28a 100644 --- a/test/repo.ts +++ b/test/repo.ts @@ -5,6 +5,9 @@ import { token, showMore, cookie } from "./common.ts"; const repo = new Repo({ token: token, cookie: cookie }); -const listRes = await repo.getRepoList({ page: 1, page_size: 999, role: 'developer' }); +const listRes = await repo.getRepoList({ + page: 1, page_size: 999, role: 'developer', + flags: 'KnowledgeBase' +}); console.log("listRes", showMore(listRes), listRes.data?.length); \ No newline at end of file