From da7b06e5190122b717f7ca178bcf5bf2377968c3 Mon Sep 17 00:00:00 2001 From: xiongxiao Date: Mon, 19 Jan 2026 04:10:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BB=93=E5=BA=93=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85=E6=8B=AC=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E3=80=81=E5=88=A0=E9=99=A4=E5=92=8C=E5=88=97=E5=87=BA?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BB=93=E5=BA=93=EF=BC=8C=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E5=AE=89=E8=A3=85=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cnb/template.yml | 7 ++ .vscode/settings.json | 6 ++ agent/routes/call/index.ts | 32 +++++++ agent/routes/call/tempCodeRunnerFile.ts | 1 + agent/routes/index.ts | 2 + agent/routes/repo/index.ts | 110 +----------------------- agent/routes/repo/list.ts | 37 ++++++++ agent/routes/repo/repo.ts | 108 +++++++++++++++++++++++ bun.config.ts | 0 create-repo-test.ts | 14 --- src/repo/index.ts | 5 ++ test/common.ts | 2 +- test/repo.ts | 4 +- 13 files changed, 203 insertions(+), 125 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 agent/routes/call/index.ts create mode 100644 agent/routes/call/tempCodeRunnerFile.ts create mode 100644 agent/routes/repo/list.ts create mode 100644 agent/routes/repo/repo.ts create mode 100644 bun.config.ts delete mode 100644 create-repo-test.ts diff --git a/.cnb/template.yml b/.cnb/template.yml index 9e7fd60..0883915 100644 --- a/.cnb/template.yml +++ b/.cnb/template.yml @@ -109,6 +109,12 @@ echo "文件不存在" fi printenv > ~/.env + if [ -f "package.json" ]; then + echo "📦 开始安装前端页面" + pnpm install + else + echo "🔍 非前端项目,跳过安装" + fi init_stages: - name: '安装依赖' script: | @@ -153,6 +159,7 @@ include: - "docs/**.md" - "blogs/**.md" + - "data/**.md" .dev_vscode_template: &dev_vscode_template vscode: diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..62080af --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "code-runner.executorMap": { + "ts": "opencode run $selectedText" + }, + "code-runner.runInTerminal": true +} \ No newline at end of file diff --git a/agent/routes/call/index.ts b/agent/routes/call/index.ts new file mode 100644 index 0000000..e0e837d --- /dev/null +++ b/agent/routes/call/index.ts @@ -0,0 +1,32 @@ +import { createSkill } from '@kevisual/router' +import { app } from '../../app.ts' +import { tool } from '@opencode-ai/plugin/tool' + +// "调用 path: cnb key: list-repos" +app.route({ + path: 'call', + key: '', + description: '调用', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'call-app', + title: '调用app应用', + summary: '调用router的应用, 参数path, key, payload', + args: { + path: tool.schema.string().describe('应用路径,例如 cnb'), + key: tool.schema.string().optional().describe('应用key,例如 list-repos'), + payload: tool.schema.object({}).optional().describe('调用参数'), + } + }) + }, +}).define(async (ctx) => { + const { path, key } = ctx.query; + console.log('call app', ctx.query); + if (!path) { + ctx.throw('路径path不能为空'); + } + const res = await ctx.run({ path, key, payload: ctx.query.payload || {} }); + ctx.forward(res); +}).addTo(app) \ No newline at end of file diff --git a/agent/routes/call/tempCodeRunnerFile.ts b/agent/routes/call/tempCodeRunnerFile.ts new file mode 100644 index 0000000..9f9212d --- /dev/null +++ b/agent/routes/call/tempCodeRunnerFile.ts @@ -0,0 +1 @@ +调用 path: cnb key: get-repo-list payload: { page: 1, per_page: 10 } \ No newline at end of file diff --git a/agent/routes/index.ts b/agent/routes/index.ts index e48ed8e..68dab1c 100644 --- a/agent/routes/index.ts +++ b/agent/routes/index.ts @@ -2,6 +2,8 @@ import { app, appId } from '../app.ts'; import './user/check.ts' import './repo/index.ts' import './workspace/index.ts' +import './call/index.ts' + import { isEqual } from 'es-toolkit' /** * 验证上下文中的 App ID 是否与指定的 App ID 匹配 diff --git a/agent/routes/repo/index.ts b/agent/routes/repo/index.ts index d797022..892020d 100644 --- a/agent/routes/repo/index.ts +++ b/agent/routes/repo/index.ts @@ -1,108 +1,2 @@ -import { app, cnb } from '../../app.ts'; -import { createSkill, Skill } from '@kevisual/router' -import { tool } from "@opencode-ai/plugin/tool" -app.route({ - path: 'cnb', - key: 'create-repo', - description: '创建代码仓库, 参数name, visibility, description', - middleware: ['auth'], - metadata: { - tags: ['opencode'], - ...createSkill({ - skill: 'create-repo', - title: '创建代码仓库', - args: { - name: tool.schema.string().describe('代码仓库名称, 如 my-user/my-repo'), - visibility: tool.schema.string().describe('代码仓库可见性, public 或 private').default('public'), - description: tool.schema.string().describe('代码仓库描述'), - }, - summary: '创建一个新的代码仓库', - }) - } -}).define(async (ctx) => { - const name = ctx.query?.name; - const visibility = ctx.query?.visibility ?? 'public'; - const description = ctx.query?.description ?? ''; - - if (!name) { - ctx.throw(400, '缺少参数 name'); - } - try { - - const res = await cnb.repo.createRepo({ - name, - visibility, - description, - }); - ctx.forward(res); - } catch (error) { - ctx.code = 200 - ctx.body = { content: 'JS仓库可能已存在' } - } -}).addTo(app); - -app.route({ - path: 'cnb', - key: 'create-repo-file', - description: '在代码仓库中创建文件, repoName, filePath, content, encoding', - middleware: ['auth'], - metadata: { - tags: ['opencode'], - ...createSkill({ - skill: 'create-repo-file', - title: '在代码仓库中创建文件', - summary: `在代码仓库中创建文件, encoding 可选,默认 raw`, - args: { - repoName: tool.schema.string().describe('代码仓库名称, 如 my-user/my-repo'), - filePath: tool.schema.string().describe('文件路径, 如 src/index.ts'), - content: tool.schema.string().describe('文本的字符串的内容'), - encoding: tool.schema.string().describe('编码方式,如 raw').optional(), - }, - }) - } -}).define(async (ctx) => { - const repoName = ctx.query?.repoName; - const filePath = ctx.query?.filePath; - const content = ctx.query?.content; - const encoding = ctx.query?.encoding ?? 'raw'; - - if (!repoName || !filePath || !content) { - ctx.throw(400, '缺少参数 repoName, filePath 或 content'); - } - - const res = await cnb.repo.createCommit(repoName, { - message: `添加文件 ${filePath} 通过 API `, - files: [ - { path: filePath, content, encoding }, - ], - }); - ctx.forward(res); -}).addTo(app); - - -app.route({ - path: 'cnb', - key: 'delete-repo', - description: '删除代码仓库, 参数name', - middleware: ['auth'], - metadata: { - tags: ['opencode'], - ...createSkill({ - skill: 'delete-repo', - title: '删除代码仓库', - args: { - name: tool.schema.string().describe('代码仓库名称'), - }, - summary: '删除一个代码仓库', - }) - } -}).define(async (ctx) => { - const name = ctx.query?.name; - - if (!name) { - ctx.throw(400, '缺少参数 name'); - } - - const res = await cnb.repo.deleteRepo(name); - ctx.forward(res); -}).addTo(app); \ No newline at end of file +import './list.ts' +import './repo.ts' \ No newline at end of file diff --git a/agent/routes/repo/list.ts b/agent/routes/repo/list.ts new file mode 100644 index 0000000..37e4f03 --- /dev/null +++ b/agent/routes/repo/list.ts @@ -0,0 +1,37 @@ +import { createSkill } from '@kevisual/router'; +import { app, cnb } from '../../app.ts'; +import { tool } from "@opencode-ai/plugin/tool" + +app.route({ + path: 'cnb', + key: 'list-repos', + description: '列出我的代码仓库', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'list-repos', + title: '列出代码仓库', + summary: '列出代码仓库', + args: { + search: tool.schema.string().optional().describe('搜索关键词'), + pageSize: tool.schema.number().optional().describe('每页数量,默认999'), + }, + }) + } +}).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' }); + if (res.code === 200) { + const repos = res.data.map((item) => ({ + name: item.name, + path: item.path, + description: item.description, + web_url: item.web_url, + })); + ctx.body = { content: JSON.stringify(repos) }; + } else { + ctx.throw(500, '获取仓库列表失败'); + } +}).addTo(app); \ No newline at end of file diff --git a/agent/routes/repo/repo.ts b/agent/routes/repo/repo.ts new file mode 100644 index 0000000..d797022 --- /dev/null +++ b/agent/routes/repo/repo.ts @@ -0,0 +1,108 @@ +import { app, cnb } from '../../app.ts'; +import { createSkill, Skill } from '@kevisual/router' +import { tool } from "@opencode-ai/plugin/tool" +app.route({ + path: 'cnb', + key: 'create-repo', + description: '创建代码仓库, 参数name, visibility, description', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'create-repo', + title: '创建代码仓库', + args: { + name: tool.schema.string().describe('代码仓库名称, 如 my-user/my-repo'), + visibility: tool.schema.string().describe('代码仓库可见性, public 或 private').default('public'), + description: tool.schema.string().describe('代码仓库描述'), + }, + summary: '创建一个新的代码仓库', + }) + } +}).define(async (ctx) => { + const name = ctx.query?.name; + const visibility = ctx.query?.visibility ?? 'public'; + const description = ctx.query?.description ?? ''; + + if (!name) { + ctx.throw(400, '缺少参数 name'); + } + try { + + const res = await cnb.repo.createRepo({ + name, + visibility, + description, + }); + ctx.forward(res); + } catch (error) { + ctx.code = 200 + ctx.body = { content: 'JS仓库可能已存在' } + } +}).addTo(app); + +app.route({ + path: 'cnb', + key: 'create-repo-file', + description: '在代码仓库中创建文件, repoName, filePath, content, encoding', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'create-repo-file', + title: '在代码仓库中创建文件', + summary: `在代码仓库中创建文件, encoding 可选,默认 raw`, + args: { + repoName: tool.schema.string().describe('代码仓库名称, 如 my-user/my-repo'), + filePath: tool.schema.string().describe('文件路径, 如 src/index.ts'), + content: tool.schema.string().describe('文本的字符串的内容'), + encoding: tool.schema.string().describe('编码方式,如 raw').optional(), + }, + }) + } +}).define(async (ctx) => { + const repoName = ctx.query?.repoName; + const filePath = ctx.query?.filePath; + const content = ctx.query?.content; + const encoding = ctx.query?.encoding ?? 'raw'; + + if (!repoName || !filePath || !content) { + ctx.throw(400, '缺少参数 repoName, filePath 或 content'); + } + + const res = await cnb.repo.createCommit(repoName, { + message: `添加文件 ${filePath} 通过 API `, + files: [ + { path: filePath, content, encoding }, + ], + }); + ctx.forward(res); +}).addTo(app); + + +app.route({ + path: 'cnb', + key: 'delete-repo', + description: '删除代码仓库, 参数name', + middleware: ['auth'], + metadata: { + tags: ['opencode'], + ...createSkill({ + skill: 'delete-repo', + title: '删除代码仓库', + args: { + name: tool.schema.string().describe('代码仓库名称'), + }, + summary: '删除一个代码仓库', + }) + } +}).define(async (ctx) => { + const name = ctx.query?.name; + + if (!name) { + ctx.throw(400, '缺少参数 name'); + } + + const res = await cnb.repo.deleteRepo(name); + ctx.forward(res); +}).addTo(app); \ No newline at end of file diff --git a/bun.config.ts b/bun.config.ts new file mode 100644 index 0000000..e69de29 diff --git a/create-repo-test.ts b/create-repo-test.ts deleted file mode 100644 index 7733268..0000000 --- a/create-repo-test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { CNB } from './src/index.ts'; - -const cnb = new CNB({ - token: 'cIDfLOOIr1Trt15cdnwfndupEZG', - cookie: '' -}); - -const res = await cnb.repo.createRepo('kevisual', { - name: 'exam-kevisual', - description: 'exam repository for kevisual', - visibility: 'public' -}); - -console.log('Result:', JSON.stringify(res, null, 2)); diff --git a/src/repo/index.ts b/src/repo/index.ts index 21d16b1..1ad90fc 100644 --- a/src/repo/index.ts +++ b/src/repo/index.ts @@ -89,10 +89,15 @@ export class Repo extends CNBCore { }): Promise> { const url = '/user/repos'; let _params = { + role: 'developer', + status: 'active', ...params, page: params.page || 1, page_size: params.page_size || 999, } + if(!_params.search) { + delete _params.search; + } return this.get({ url, params: _params }); } } diff --git a/test/common.ts b/test/common.ts index 4674ddb..821efce 100644 --- a/test/common.ts +++ b/test/common.ts @@ -3,7 +3,7 @@ import dotenv from "dotenv"; import util from 'node:util'; import { useConfig, useKey } from "@kevisual/use-config"; const config = useConfig() -export const token = useKey("CNB_TOKEN") as string || ''; +export const token = useKey("CNB_API_KEY") as string || ''; export const cookie = useKey("CNB_COOKIE") as string || ''; console.log('token', token) import { app } from '../agent/index.ts' diff --git a/test/repo.ts b/test/repo.ts index 991670f..5bd5ead 100644 --- a/test/repo.ts +++ b/test/repo.ts @@ -5,6 +5,6 @@ import { token, showMore, cookie } from "./common.ts"; const repo = new Repo({ token: token, cookie: cookie }); -const listRes = await repo.getRepoList({ page: 1, page_size: 10 }); +const listRes = await repo.getRepoList({ page: 1, page_size: 999, role: 'developer' }); -console.log("listRes", showMore(listRes)); \ No newline at end of file +console.log("listRes", showMore(listRes), listRes.data?.length); \ No newline at end of file