Compare commits

...

50 Commits

Author SHA1 Message Date
412c057756 update 2026-02-01 21:13:36 +08:00
7a01339ef2 feat: 添加工作空间存活管理功能,重置和清理超时任务,使用 dayjs 格式化时间 2026-01-31 00:40:29 +08:00
6e5a642ab2 feat: 将所有路由的中间件从 'auth' 更新为 'admin-auth',并更新版本至 0.0.13 2026-01-31 00:30:37 +08:00
0d17d56628 feat: 添加工作空间保持存活功能,更新相关依赖和配置 2026-01-30 23:32:40 +08:00
972d68b87e feat: 更新版本至 0.0.10,修复依赖项 ws 的位置 2026-01-30 21:25:21 +08:00
28484baab3 feat: 更新 WebSocket Keep-Alive 客户端,添加 ws 作为外部依赖,修正导入路径 2026-01-30 21:23:40 +08:00
d7a4bcf58f feat: 重构 WebSocket Keep-Alive 客户端,添加连接和消息处理功能,更新依赖版本,增加 keep.ts 文件 2026-01-30 21:16:06 +08:00
1d4a27d1b2 add:TODO 2026-01-29 13:59:18 +08:00
5db3b4a51b feat(cnb-env): 添加用户登录状态检查路由,更新版本至 0.0.7,移除旧的用户检查路由 2026-01-29 13:34:04 +08:00
86b24cc9ef feat(issue): 添加查询 Issue 列表功能,更新完成 Issue 的状态处理,版本升级至 0.0.6 2026-01-29 13:23:17 +08:00
e15cf4f7be add version 2026-01-29 13:06:25 +08:00
xiongxiao
20fcf2dca8 feat(issue): add body field to IssueItem type and implement issue creation and completion routes
- Added 'body' field to IssueItem type in index.ts
- Created new routes for issue creation and completion in issue.ts
- Implemented validation for required parameters in issue creation and completion
- Added a new list route for issues in list.ts
2026-01-29 02:50:28 +08:00
xiongxiao
8e1880a343 update 2026-01-29 00:42:16 +08:00
xiongxiao
7b31367c1d 编辑文件 template.yml 2026-01-28 10:39:04 +08:00
xiongxiao
b15b11fa02 feat: 重构代码结构,更新插件导出,添加构建配置和npm配置文件 2026-01-27 04:12:07 +08:00
xiongxiao
50332fe2f4 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.
2026-01-27 04:02:34 +08:00
xiongxiao
da7b06e519 添加仓库管理功能,包括创建、删除和列出代码仓库,更新依赖安装逻辑,修复环境变量配置 2026-01-19 04:10:20 +08:00
xiongxiao
c099c7b67f 编辑文件 template.yml 2026-01-17 16:36:43 +08:00
c0ca1c819e update 2026-01-17 00:04:10 +08:00
xiongxiao
7787337f13 update add openapi api 2026-01-16 13:03:57 +08:00
xiongxiao
a8f2cb14a7 update 2026-01-16 11:27:26 +08:00
xiongxiao
f10f588ea5 更新代码仓库相关功能,修改 API 参数,添加删除仓库功能,更新文档和测试用例 2026-01-16 03:46:14 +08:00
xiongxiao
d85f42d38b update 2026-01-16 02:30:21 +08:00
xiongxiao
fc7a75b154 add test env 2026-01-15 23:51:11 +08:00
xiongxiao
65da3f8c60 test env 2026-01-15 23:41:10 +08:00
33d1c2b5ec Recover lost commits 2026-01-14 13:09:07 +08:00
xiongxiao
285cdddb43 add process 2026-01-13 18:06:05 +08:00
xiongxiao
881fa3318a update 2026-01-13 17:29:52 +08:00
xiongxiao
d86f4dc48f fix: update 2026-01-13 17:26:38 +08:00
xiongxiao
cedf3236b0 update 2026-01-13 17:04:43 +08:00
xiongxiao
1d7a51f76c update 2026-01-13 16:20:13 +08:00
xiongxiao
e5504284a1 update 2026-01-13 16:12:50 +08:00
xiongxiao
a77e178431 update 2026-01-13 16:12:35 +08:00
xiongxiao
e5a1d0d9e8 update 2026-01-13 16:09:59 +08:00
f0af9a8621 update 2026-01-13 13:16:45 +08:00
xiongxiao
b4e5e5dce7 update 2026-01-12 14:34:58 +08:00
xiongxiao
eab631c60b udpate 2026-01-12 14:17:39 +08:00
xiongxiao
4d119bdf56 update 2026-01-12 14:16:22 +08:00
xiongxiao
64f24910cb update 2026-01-12 14:12:10 +08:00
xiongxiao
4285a4760f test 2026-01-12 14:10:41 +08:00
xiongxiao
5691053f01 fix 2026-01-12 14:09:22 +08:00
xiongxiao
c0f018e85b test 2026-01-12 14:06:25 +08:00
xiongxiao
60b964715e update 2026-01-12 14:03:35 +08:00
xiongxiao
3cfe945a91 update 2026-01-12 14:02:30 +08:00
xiongxiao
115870b563 test 2026-01-12 14:02:05 +08:00
xiongxiao
0751c8029a updatge 2026-01-12 14:01:11 +08:00
xiongxiao
d32fce99e2 fix 2026-01-12 13:56:17 +08:00
xiongxiao
b660fa501c update 2026-01-12 13:52:10 +08:00
xiongxiao
451c4c0076 update 2026-01-12 13:50:56 +08:00
xiongxiao
0a7f48fa4e update 2026-01-12 13:50:56 +08:00
90 changed files with 41622 additions and 702 deletions

View File

@@ -1,4 +1,14 @@
# .cnb.yml
include:
- https://cnb.cool/kevisual/cnb/-/blob/main/.cnb/template.yml
.common_env: &common_env
env:
TO_REPO: kevisual/cnb
TO_URL: git.xiongxiao.me
imports:
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
$:
vscode:
- docker:
@@ -6,57 +16,29 @@ $:
services:
- vscode
- docker
imports: https://cnb.cool/kevisual/env/-/blob/main/.env.development
imports: !reference [.common_env, imports]
# 开发环境启动后会执行的任务
# stages:
# - name: pnpm install
# script: pnpm install
stages: !reference [.dev_template, stages]
.common_sync_to_gitea: &common_sync_to_gitea
services:
- docker
imports:
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
env:
TO_REPO: kevisual/cnb
TO_URL: git.xiongxiao.me
stages:
- name: '显示 git remote'
script: git remote -v
- name: sync to gitea
image: tencentcom/git-sync
settings:
target_url: https://${TO_URL}/${TO_REPO}.git
auth_type: https
username: "oauth2"
password: ${GITEA_TOKEN}
git_user: "abearxiong"
git_email: "xiongxiao@xiongxiao.me"
sync_mode: rebase
branch: main
- <<: *common_env
services: !reference [.common_sync_to_gitea_template, services]
stages: !reference [.common_sync_to_gitea_template, stages]
.common_sync_from_gitea: &common_sync_from_gitea
services:
- docker
imports:
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
env:
TO_REPO: kevisual/cnb
TO_URL: git.xiongxiao.me
stages:
- name: '添加 gitea的origin'
script: |
git remote remove gitea 2>/dev/null || true
git remote add gitea https://oauth2:${GITEA_TOKEN}@${TO_URL}/${TO_REPO}.git
- name: '同步gitea代码到当前仓库'
script: git pull gitea main
- name: '提交到原本的origin'
script: git push origin main
- <<: *common_env
services: !reference [.common_sync_from_gitea_template, services]
stages: !reference [.common_sync_from_gitea_template, stages]
main:
web_trigger_sync_to_gitea:
- <<: *common_sync_to_gitea
web_trigger_sync_to_gitea:
- <<: *common_sync_to_gitea
web_trigger_sync_from_gitea:
- <<: *common_sync_from_gitea
- <<: *common_sync_from_gitea
api_trigger_sync_to_gitea:
- <<: *common_sync_to_gitea
- <<: *common_sync_to_gitea
api_trigger_sync_from_gitea:
- <<: *common_sync_from_gitea
- <<: *common_sync_from_gitea

View File

@@ -0,0 +1,5 @@
echo "${KUBECONFIG_DATA}" | base64 -d > ~/.kube/config
chmod 600 ~/.kube/config
# 把 kube 配置转为 base64 编码输出,方便后续使用
# cat ~/.kube/config | base64 -w 0

173
.cnb/template.yml Normal file
View File

@@ -0,0 +1,173 @@
# 同步代码到gitea模板
.common_sync_to_gitea_template: &common_sync_to_gitea_template
services:
- docker
imports:
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
# env:
# TO_REPO: kevisual/cnb
# TO_URL: git.xiongxiao.me
stages:
- name: '显示 git remote'
script: git remote -v
- name: sync to gitea
image: tencentcom/git-sync
settings:
target_url: https://${TO_URL}/${TO_REPO}.git
auth_type: https
username: "oauth2"
password: ${GITEA_TOKEN}
git_user: "abearxiong"
git_email: "xiongxiao@xiongxiao.me"
sync_mode: rebase
branch: main
# 同步gitea代码到当前仓库
.common_sync_from_gitea_template: &common_sync_from_gitea_template
services:
- docker
imports:
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
# env:
# TO_REPO: kevisual/cnb
# TO_URL: git.xiongxiao.me
stages:
- name: '添加 gitea的origin'
script: |
git remote remove gitea 2>/dev/null || true
git remote add gitea https://oauth2:${GITEA_TOKEN}@${TO_URL}/${TO_REPO}.git
- name: '同步gitea代码到当前仓库'
script: git pull gitea main
- name: '提交到原本的origin'
script: git push origin main
# main:
# web_trigger_sync_to_gitea:
# - <<: *common_sync_to_gitea_template
# web_trigger_sync_from_gitea:
# - <<: *common_sync_from_gitea_template
# api_trigger_sync_to_gitea:
# - <<: *common_sync_to_gitea_template
# api_trigger_sync_from_gitea:
# - <<: *common_sync_from_gitea_template
# kubectl 部署模版
.kubectl_deploy_template: &kubectl_deploy_template
services:
- docker
docker:
image: docker.cnb.cool/kevisual/dev-env:latest
imports:
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
stages:
- name: '部署k8s模块'
script: |
echo "${KUBECONFIG_DATA}" | base64 -d > ~/.kube/config
chmod 600 ~/.kube/config
# 如果设置了 KUBE_CONTEXT则切换上下文
if [ -n "${KUBE_CONTEXT}" ]; then
kubectl config use-context "${KUBE_CONTEXT}"
fi
export NAMESPACE=${KUBE_NAMESPACE:-default}
kubectl rollout restart deployment/${KUBE_DEPLOYMENT} -n ${NAMESPACE}
# 打包并发布镜像
.build_images_app_template: &build_images_app_template
services:
- docker
docker:
image: docker.cnb.cool/kevisual/dev-env:latest
stages:
- name: 检查环境是否需要打包
script:
- |
if [ -f "package.json" ]; then
echo "📦 开始前端构建流程"
pnpm install
pnpm run build || echo "⚠️ 构建失败或无 build script"
else
echo "🔍 非前端项目,跳过打包"
fi
- name: Docker build
script: docker build -t ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:latest .
- name: Docker push
script: docker push ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:latest
# 开发环境模版
.dev_template: &dev_template
services:
- vscode
docker:
image: docker.cnb.cool/kevisual/dev-env:latest
stages:
- name: 软链 .env.development 文件到工作目录(仓库根目录)
script: |
if [ -e "/root/.cnb/.env.development" ]; then
ln -sf /root/.cnb/.env.development ./.env
else
echo "文件不存在"
fi
printenv > ~/.env
if [ -f "package.json" ]; then
echo "📦 开始安装前端页面"
pnpm install
else
echo "🔍 非前端项目,跳过安装"
fi
init_stages:
- name: '安装依赖'
script: |
pnpm install
if [ -e "/root/.cnb/.env" ]; then
ln -sf /root/.cnb/.env ./.env
else
echo "文件不存在"
fi
# 结束状态模版
.end_state_template: &end_state_template
endStages:
- name: '结束'
script: echo "流水线执行完毕,当前状态:${CNB_PIPELINE_STATUS}"
# 预览模板
.preview_template: &preview_template
services:
- name: vscode
options:
# 启用预览模式
onlyPreview: true
# 启动业务端口的命令,端口必须启动在 8686
launch: node index.js
# 保活时间,单位毫秒,不设置默认 10 分钟没有心跳(检测不到开发环境内的 http 连接)即关闭开发环境
keepAliveTimeout: 3600000
# 是否守护进程模式,默认为 false非守护进程模式
# true以守护进程模式启动即 launch 启动预览业务服务时,会直接运行在后台
# false非守护进程模式启动执行 launch 启动预览业务服务时,可以看到日志,且需要等待 launch 启动完成并主动退出,才会继续执行后续流程
# 推荐设置为 false由用户自己实现业务启动成功后退出进程并后台运行这样可以看到业务启动日志方便定位问题
daemon: true
- docker
# 构建知识库模版
.knowledge_template: &knowledge_template
stages:
- name: 搭建知识库
image: cnbcool/knowledge-base
settings:
include:
- "docs/**.md"
- "blogs/**.md"
- "data/**.md"
.dev_vscode_template: &dev_vscode_template
vscode:
- runner:
cpus: 2
services:
- vscode
- docker
stages:
- name: ls
script: ls -al

View File

@@ -4,8 +4,8 @@ branch:
- reg: "^main"
buttons:
- name: 同步代码到gitea
desc: 同步代码到gitea
description: 同步代码到gitea
event: web_trigger_sync_to_gitea
- name: 同步gitea代码到当前仓库
desc: 同步gitea代码到当前仓库
description: 同步gitea代码到当前仓库
event: web_trigger_sync_from_gitea

7
.gitignore vendored
View File

@@ -1,3 +1,8 @@
.env
.env.local
node_modules
.pnpm-store
.pnpm-store
dist
storage

3
.npmrc Normal file
View File

@@ -0,0 +1,3 @@
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
//npm.cnb.cool/kevisual/registry/-/packages/:_authToken=${CNB_API_KEY}
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

View File

@@ -1 +1 @@
export * from "../../agent/opencode-plugin";
export * from "../../agent/opencode";

View File

@@ -0,0 +1,72 @@
---
name: create-new-repo
description: 创建一个基本的新的代码仓库,并自动添加必要的配置文件。
---
# 创建新的代码仓库
该技能用于创建一个新的代码仓库,并自动添加必要的配置文件,如 `.cnb.yml`
## 应用的步骤
1. 执行`create-repo`工具,参数是`/group/repo_name`,例如 `kevisual/my-new-repo`
2. 调用`create-repo-file`工具, 添加`.cnb.yml`配置文件,其中仓库名称是第一步的仓库参数。
## 要求
1. 不要做任何多余的事情,只做相关的任务,不要额外的解释和说明。
2. 如果执行错误,只返回错误信息。
2. 确保`.cnb.yml`文件内容正确无误。
## 注意事项
### 1. `.cnb.yml配置文件内容示例`
TO_REPO 为 kevisual/repo_name
```yaml
# .cnb.yml
include:
- https://cnb.cool/kevisual/cnb/-/blob/main/.cnb/template.yml
.common_env: &common_env
env:
TO_REPO: kevisual/cnb
TO_URL: git.xiongxiao.me
imports:
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
$:
vscode:
- docker:
image: docker.cnb.cool/kevisual/dev-env:latest
services:
- vscode
- docker
imports: !reference [.common_env, imports]
# 开发环境启动后会执行的任务
# stages:
# - name: pnpm install
# script: pnpm install
stages: !reference [.dev_tempalte, stages]
.common_sync_to_gitea: &common_sync_to_gitea
- <<: *common_env
services: !reference [.common_sync_to_gitea_template, services]
stages: !reference [.common_sync_to_gitea_template, stages]
.common_sync_from_gitea: &common_sync_from_gitea
- <<: *common_env
services: !reference [.common_sync_from_gitea_template, services]
stages: !reference [.common_sync_from_gitea_template, stages]
main:
web_trigger_sync_to_gitea:
- <<: *common_sync_to_gitea
web_trigger_sync_from_gitea:
- <<: *common_sync_from_gitea
api_trigger_sync_to_gitea:
- <<: *common_sync_to_gitea
api_trigger_sync_from_gitea:
- <<: *common_sync_from_gitea
```

View File

@@ -1,14 +1,17 @@
import { QueryRouterServer as App } from '@kevisual/router'
import { useContextKey } from '@kevisual/context'
import { useConfig } from '@kevisual/use-config'
import { useConfig, useKey } from '@kevisual/use-config'
import { CNB } from '../src/index.ts';
import { nanoid } from 'nanoid';
export const config = useConfig()
export const cnb = useContextKey<CNB>('cnb', () => {
return new CNB({ token: config.CNB_TOKEN, cookie: config.CNB_COOKIE, group: config.CNB_GROUP });
// 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 });
})
export const appId = nanoid();
export const app = useContextKey<App>('app', () => {
return new App()
return new App({})
})

View File

@@ -1,35 +0,0 @@
import { tool } from "@opencode-ai/plugin/tool"
import { type Plugin } from "@opencode-ai/plugin"
import { app, cnb, appId } from './index.ts';
// opencode run "请使用 cnb-login-verify 工具验证登录信信息,检查cookie"
export const CnbPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
return {
'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 '无法获取登录状态,请检查配置。';
},
},
},
'tool.execute.before': async (opts) => {
// console.log('CnbPlugin: tool.execute.before', opts.tool);
}
}
}

6
agent/opencode.ts Normal file
View File

@@ -0,0 +1,6 @@
import { app } from './index.ts';
import { createRouterAgentPluginFn } from '@kevisual/router/opencode'
export const CnbPlugin = createRouterAgentPluginFn({
router: app,
})

View File

@@ -0,0 +1,34 @@
import { createSkill } from '@kevisual/router'
import { app } from '../../app.ts'
import { tool } from '@opencode-ai/plugin/tool'
if (!app.hasRoute('call')) {
// "调用 path: cnb key: list-repos"
app.route({
path: 'call',
key: '',
description: '调用',
middleware: ['admin-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)
}

View File

@@ -0,0 +1,44 @@
import { createSkill } from '@kevisual/router';
import { app, cnb } from '../../app.ts';
import { tool } from '@opencode-ai/plugin/tool';
app.route({
path: 'cnb',
key: 'user-check',
description: '检查用户登录状态参数checkToken,default true; checkCookie, default false',
middleware: ['admin-auth'],
metadata: {
tags: ['opencode'],
...createSkill({
skill: 'cnb-login-verify',
title: 'CNB 登录验证信息',
summary: '验证 CNB 登录信息是否有效',
args: {
checkToken: tool.schema.boolean().describe('是否检查 Token 的有效性').default(true),
checkCookie: tool.schema.boolean().describe('是否检查 Cookie 的有效性').default(false),
},
})
}
}).define(async (ctx) => {
const checkToken = ctx.query?.checkToken ?? true;
const checkCookie = ctx.query?.checkCookie ?? false;
let content = '';
if (checkToken) {
const res = await cnb.user.getUser();
if (res?.code !== 200) {
content += `Token 无效,请检查 CNB_TOKEN 配置。\n`;
} else {
content += `Token 有效Token用户昵称${res.data?.nickname}\n`;
}
}
if (checkCookie) {
const res = await cnb.user.getCurrentUser();
if (res?.code !== 200) {
content += `Cookie 无效,请检查 CNB_COOKIE 配置。\n`;
} else {
content += `Cookie 有效当前Cookie用户${res.data?.nickname}\n`;
}
}
ctx.body = { content };
}).addTo(app);

View File

@@ -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: ['admin-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: ['admin-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);

View File

@@ -1 +1,3 @@
// 根据环境变量获取当前的 cnb 启动环境
// 根据环境变量获取当前的 cnb 启动环境
import './vscode.ts';
import './env.ts';

View File

@@ -0,0 +1,96 @@
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
// TODO 获取仓库的
app.route({
path: 'cnb',
key: 'get-cnb-port-uri',
description: '获取当前cnb工作空间的port代理uri',
middleware: ['admin-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 访问urivscode 访问uricodebuddy 访问uricursor 访问urissh 连接字符串
app.route({
path: 'cnb',
key: 'get-cnb-vscode-uri',
description: '获取当前cnb工作空间的vscode代理uri, 包括多种访问方式, 如web、vscode、codebuddy、cursor、ssh',
middleware: ['admin-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);

View File

@@ -1,8 +1,21 @@
import { app, appId } from '@/agent/app.ts';
import './user/check.ts'
import { app } from '../app.ts';
import './cnb-env/check.ts'
import './repo/index.ts'
import './workspace/index.ts'
import './call/index.ts'
import './cnb-env/index.ts'
import './knowledge/index.ts'
import './issues/index.ts'
/**
* 验证上下文中的 App ID 是否与指定的 App ID 匹配
* @param {any} ctx - 上下文对象,可能包含 appId 属性
* @param {string} appId - 需要验证的目标 App ID
* @returns {boolean} 如果 ctx 中包含 appId 且匹配则返回 true否则返回 false
* @throws {Error} 如果 ctx 中包含 appId 但不匹配,则抛出 403 错误
*/
const checkAppId = (ctx: any, appId: string) => {
const _appId = ctx?.appId;
const _appId = ctx?.app?.appId;
if (_appId) {
if (_appId !== appId) {
ctx.throw(403, 'Invalid App ID');
@@ -18,7 +31,7 @@ if (!app.hasRoute('auth')) {
path: 'auth',
}).define(async (ctx) => {
// ctx.body = 'Auth Route';
if (checkAppId(ctx, appId)) {
if (checkAppId(ctx, app.appId)) {
return;
}
}).addTo(app);
@@ -29,7 +42,7 @@ if (!app.hasRoute('auth')) {
middleware: ['auth'],
}).define(async (ctx) => {
// ctx.body = 'Admin Auth Route';
if (checkAppId(ctx, appId)) {
if (checkAppId(ctx, app.appId)) {
return;
}
}).addTo(app);

View File

@@ -0,0 +1,2 @@
import './list.ts'
import './issue.ts'

View File

@@ -0,0 +1,82 @@
import { createSkill, tool } from '@kevisual/router';
import { app, cnb } from '../../app.ts';
import { IssueItem } from '@/index.ts';
// 创建cnb issue, 仓库为 kevisual/kevisual 标题为 "自动化测试创建issue", 内容为 "这是通过API创建的issue用于测试目的", body: "这是通过API创建的issue用于测试目的"
app.route({
path: 'cnb',
key: 'create-issue',
description: '创建 Issue, 参数 repo, title, body, assignees, labels, priority',
middleware: ['admin-auth'],
metadata: {
tags: ['opencode'],
...createSkill({
skill: 'create-issue',
title: '创建 Issue',
args: {
repo: tool.schema.string().describe('代码仓库名称, 如 my-user/my-repo'),
title: tool.schema.string().describe('Issue 标题'),
body: tool.schema.string().optional().describe('Issue 描述内容'),
assignees: tool.schema.array(tool.schema.string()).optional().describe('指派人列表'),
labels: tool.schema.array(tool.schema.string()).optional().describe('标签列表'),
priority: tool.schema.string().optional().describe('优先级'),
},
summary: '创建一个新的 Issue',
})
}
}).define(async (ctx) => {
const repo = ctx.query?.repo;
const title = ctx.query?.title;
const body = ctx.query?.body;
const assignees = ctx.query?.assignees;
const labels = ctx.query?.labels;
const priority = ctx.query?.priority;
if (!repo || !title) {
ctx.throw(400, '缺少参数 repo 或 title');
}
const res = await cnb.issue.createIssue(repo, {
title,
body,
assignees,
labels,
priority,
});
ctx.forward(res);
}).addTo(app);
// 完成 issue 8 仓库是 kevisual/kevisaul
app.route({
path: 'cnb',
key: 'complete-issue',
description: '完成 Issue, 参数 repo, issueNumber',
middleware: ['admin-auth'],
metadata: {
tags: ['opencode'],
...createSkill({
skill: 'complete-issue',
title: '完成 CNB的任务Issue',
args: {
repo: tool.schema.string().describe('代码仓库名称, 如 my-user/my-repo'),
issueNumber: tool.schema.union([tool.schema.string(), tool.schema.number()]).describe('Issue 编号'),
state: tool.schema.string().optional().describe('Issue 状态,默认为 closed'),
},
summary: '完成一个 Issue将 state 改为 closed',
})
}
}).define(async (ctx) => {
const repo = ctx.query?.repo;
const issueNumber = ctx.query?.issueNumber;
const state = ctx.query?.state ?? 'closed';
if (!repo || !issueNumber) {
ctx.throw(400, '缺少参数 repo 或 issueNumber');
}
const iss: Partial<IssueItem> = { state: state };
if (iss.state === 'closed') {
iss.state_reason = 'completed';
}
const res = await cnb.issue.updateIssue(repo, issueNumber, iss);
ctx.forward(res);
}).addTo(app);

View File

@@ -0,0 +1,50 @@
import { createSkill, tool } from '@kevisual/router';
import { app, cnb } from '../../app.ts';
// 查询 Issue 列表 repo是 kevisual/kevisual
app.route({
path: 'cnb',
key: 'list-issues',
description: '查询 Issue 列表, 参数 repo, state, keyword, labels, page, page_size 等',
middleware: ['admin-auth'],
metadata: {
tags: ['opencode'],
...createSkill({
skill: 'list-issues',
title: '查询 Issue 列表',
args: {
repo: tool.schema.string().describe('代码仓库名称, 如 my-user/my-repo'),
state: tool.schema.string().optional().describe('Issue 状态open 或 closed'),
keyword: tool.schema.string().optional().describe('问题搜索关键词'),
labels: tool.schema.string().optional().describe('问题标签,多个用逗号分隔'),
page: tool.schema.number().optional().describe('分页页码,默认: 1'),
page_size: tool.schema.number().optional().describe('分页每页大小,默认: 30'),
order_by: tool.schema.string().optional().describe('排序方式,如 created_at, -updated_at'),
},
summary: '查询 Issue 列表',
})
}
}).define(async (ctx) => {
const repo = ctx.query?.repo;
const state = ctx.query?.state;
const keyword = ctx.query?.keyword;
const labels = ctx.query?.labels;
const page = ctx.query?.page ? Number(ctx.query.page) : undefined;
const page_size = ctx.query?.page_size ? Number(ctx.query.page_size) : undefined;
const order_by = ctx.query?.order_by;
if (!repo) {
ctx.throw(400, '缺少参数 repo');
}
const params: Record<string, any> = {};
if (state) params.state = state;
if (keyword) params.keyword = keyword;
if (labels) params.labels = labels;
if (page) params.page = page;
if (page_size) params.page_size = page_size;
if (order_by) params.order_by = order_by;
const res = await cnb.issue.getList(repo, params);
ctx.forward(res);
}).addTo(app);

View File

@@ -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: ['admin-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: ['admin-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);

View File

@@ -0,0 +1 @@
import './ai.ts'

View File

@@ -1,53 +1,2 @@
import { app, cnb } from '@/agent/app.ts';
app.route({
path: 'cnb',
key: 'repo-create',
description: '创建代码仓库, 参数name, visibility, description',
middleware: ['auth'],
metadata: {
tags: ['opencode']
}
}).define(async (ctx) => {
const name = ctx.query?.name;
const visibility = ctx.query?.visibility ?? 'private';
const description = ctx.query?.description ?? '';
if (!name) {
ctx.throw(400, '缺少参数 name');
}
const res = await cnb.repo.createRepo(cnb.group, {
name,
visibility,
description,
});
ctx.forward(res);
}).addTo(app);
app.route({
path: 'cnb',
key: 'repo-create-file',
description: '在代码仓库中创建文件, 参数repoName, path, content, encoding',
middleware: ['auth'],
metadata: {
tags: ['opencode']
}
}).define(async (ctx) => {
const repoName = ctx.query?.repoName;
const path = ctx.query?.path;
const content = ctx.query?.content;
const encoding = ctx.query?.encoding ?? 'raw';
if (!repoName || !path || !content) {
ctx.throw(400, '缺少参数 repoName, path 或 content');
}
const res = await cnb.repo.createCommit(repoName, {
message: `添加文件 ${path} 通过 API `,
files: [
{ path, content, encoding },
],
});
ctx.forward(res);
}).addTo(app);
import './list.ts'
import './repo.ts'

45
agent/routes/repo/list.ts Normal file
View File

@@ -0,0 +1,45 @@
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',
description: '列出我的代码仓库',
middleware: ['admin-auth'],
metadata: {
tags: ['opencode'],
...createSkill({
skill: 'list-repos',
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 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,
path: item.path,
description: item.description,
web_url: item.web_url,
}));
ctx.body = { content: JSON.stringify(repos), list: res.data };
} else {
ctx.throw(500, '获取仓库列表失败');
}
}).addTo(app);

110
agent/routes/repo/repo.ts Normal file
View File

@@ -0,0 +1,110 @@
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',
description: '创建代码仓库, 参数name, visibility, description',
middleware: ['admin-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: ['admin-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);
// 删除一个仓库 kevisual/test-repo
app.route({
path: 'cnb',
key: 'delete-repo',
description: '删除代码仓库, 参数name',
middleware: ['admin-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);

View File

@@ -1,33 +0,0 @@
import { app, cnb } from '@/agent/app.ts';
app.route({
path: 'cnb',
key: 'user-check',
description: '检查用户登录状态参数checkToken,default true; checkCookie, default false',
middleware: ['auth'],
metadata: {
tags: ['opencode']
}
}).define(async (ctx) => {
const checkToken = ctx.query?.checkToken ?? true;
const checkCookie = ctx.query?.checkCookie ?? false;
let output = '';
if (checkToken) {
const res = await cnb.user.getUser();
if (res?.code !== 200) {
output += `Token 无效,请检查 CNB_TOKEN 配置。\n`;
} else {
output += `Token 有效Token用户昵称${res.data?.nickname}\n`;
}
}
if (checkCookie) {
const res = await cnb.user.getCurrentUser();
if (res?.code !== 200) {
output += `Cookie 无效,请检查 CNB_COOKIE 配置。\n`;
} else {
output += `Cookie 有效当前Cookie用户${res.data?.nickname}\n`;
}
}
ctx.body = { output };
}).addTo(app);

View File

@@ -1,12 +1,27 @@
import { app, cnb } from '@/agent/app.ts';
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: ['auth'],
middleware: ['admin-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 +36,133 @@ app.route({
});
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);

View File

@@ -0,0 +1,214 @@
import { createSkill, tool } from '@kevisual/router';
import { app, cnb } from '../../app.ts';
import { nanoid } from 'nanoid';
import dayjs from 'dayjs';
import { createKeepAlive } from '../../../src/keep.ts';
type AliveInfo = {
startTime: number;
updatedTime?: number;
KeepAlive: ReturnType<typeof createKeepAlive>;
id: string;// 6位唯一标识符
}
const keepAliveMap = new Map<string, AliveInfo>();
// 保持工作空间存活技能
app.route({
path: 'cnb',
key: 'keep-workspace-alive',
description: '保持工作空间存活技能,参数wsUrl:工作空间访问URLcookie:访问工作空间所需的cookie',
middleware: ['admin-auth'],
metadata: {
tags: [],
...({
args: {
wsUrl: tool.schema.string().describe('工作空间的访问URL'),
cookie: tool.schema.string().describe('访问工作空间所需的cookie')
}
})
}
}).define(async (ctx) => {
const wsUrl = ctx.query?.wsUrl as string;
const cookie = ctx.query?.cookie as string;
if (!wsUrl) {
ctx.throw(400, '缺少工作空间访问URL参数');
}
if (!cookie) {
ctx.throw(400, '缺少访问工作空间所需的cookie参数');
}
// 检测是否已在运行(通过 wsUrl 遍历检查)
const existing = Array.from(keepAliveMap.values()).find(info => (info as AliveInfo).id && (info as any).KeepAlive?.wsUrl === wsUrl);
if (existing) {
ctx.body = { message: `工作空间 ${wsUrl} 的保持存活任务已在运行中`, id: (existing as AliveInfo).id };
return;
}
console.log(`启动保持工作空间 ${wsUrl} 存活的任务`);
const keep = createKeepAlive({
wsUrl,
cookie,
onConnect: () => {
console.log(`工作空间 ${wsUrl} 保持存活任务已连接`);
},
onMessage: (data) => {
// 可选:处理收到的消息
// console.log(`工作空间 ${wsUrl} 收到消息: ${data}`);
// 通过 wsUrl 找到对应的 id 并更新时间
for (const info of keepAliveMap.values()) {
if ((info as any).KeepAlive?.wsUrl === wsUrl) {
info.updatedTime = Date.now();
break;
}
}
},
debug: true,
onExit: (code) => {
console.log(`工作空间 ${wsUrl} 保持存活任务已退出,退出码: ${code}`);
// 通过 wsUrl 找到对应的 id 并删除
for (const [id, info] of keepAliveMap.entries()) {
if ((info as any).KeepAlive?.wsUrl === wsUrl) {
keepAliveMap.delete(id);
break;
}
}
}
});
const id = nanoid(6).toLowerCase();
keepAliveMap.set(id, { startTime: Date.now(), updatedTime: Date.now(), KeepAlive: keep, id });
ctx.body = { content: `已启动保持工作空间 ${wsUrl} 存活的任务`, id };
}).addTo(app);
// 获取保持工作空间存活任务列表技能
app.route({
path: 'cnb',
key: 'list-keep-alive-tasks',
description: '获取保持工作空间存活任务列表技能',
middleware: ['admin-auth'],
metadata: {
tags: [],
}
}).define(async (ctx) => {
const list = Array.from(keepAliveMap.entries()).map(([id, info]) => {
const now = Date.now();
const duration = Math.floor((now - info.startTime) / 60000); // 分钟
return {
id,
wsUrl: (info as any).KeepAlive?.wsUrl,
startTime: info.startTime,
startTimeStr: dayjs(info.startTime).format('YYYY-MM-DD HH:mm'),
updatedTime: info.updatedTime,
updatedTimeStr: dayjs(info.updatedTime).format('YYYY-MM-DD HH:mm'),
duration,
}
});
ctx.body = { list };
}).addTo(app);
// 停止保持工作空间存活技能
app.route({
path: 'cnb',
key: 'stop-keep-workspace-alive',
description: '停止保持工作空间存活技能, 参数wsUrl:工作空间访问URL或者id',
middleware: ['admin-auth'],
metadata: {
tags: [],
...({
args: {
wsUrl: tool.schema.string().optional().describe('工作空间的访问URL'),
id: tool.schema.string().optional().describe('保持存活任务的唯一标识符'),
}
})
}
}).define(async (ctx) => {
const wsUrl = ctx.query?.wsUrl as string;
const id = ctx.query?.id as string;
if (!wsUrl && !id) {
ctx.throw(400, '缺少工作空间访问URL参数或唯一标识符');
}
let targetId: string | undefined;
let wsUrlFound: string | undefined;
if (id) {
const info = keepAliveMap.get(id);
if (info) {
targetId = id;
wsUrlFound = (info as any).KeepAlive?.wsUrl;
}
} else if (wsUrl) {
for (const [key, info] of keepAliveMap.entries()) {
if ((info as any).KeepAlive?.wsUrl === wsUrl) {
targetId = key;
wsUrlFound = wsUrl;
break;
}
}
}
if (targetId) {
const keepAlive = keepAliveMap.get(targetId);
const endTime = Date.now();
const duration = endTime - keepAlive!.startTime;
keepAlive?.KeepAlive?.disconnect();
keepAliveMap.delete(targetId);
ctx.body = { content: `已停止保持工作空间 ${wsUrlFound} 存活的任务,持续时间: ${duration}ms`, id: targetId };
} else {
ctx.body = { content: `没有找到对应的工作空间保持存活任务` };
}
}).addTo(app);
app.route({
path: 'cnb',
key: 'reset-keep-workspace-alive',
description: '对存活的工作空间startTime进行重置',
middleware: ['admin-auth'],
metadata: {
tags: [],
}
}).define(async (ctx) => {
const now = Date.now();
for (const info of keepAliveMap.values()) {
info.startTime = now;
}
ctx.body = { content: `已重置所有存活工作空间的开始时间` };
}).addTo(app);
app.route({
path: 'cnb',
key: 'clear-keep-workspace-alive',
description: '对存活的工作空间超过5小时的进行清理',
middleware: ['admin-auth'],
metadata: {
tags: [],
}
}).define(async (ctx) => {
const res = clearKeepAlive();
ctx.body = {
content: `已清理所有存活工作空间中超过5小时的任务` + (res.length ? `,清理项:${res.map(i => i.wsUrl).join(', ')}` : ''),
list: res
};
}).addTo(app);
const clearKeepAlive = () => {
const now = Date.now();
let clearedArr: { id: string; wsUrl: string }[] = [];
for (const [id, info] of keepAliveMap.entries()) {
if (now - info.startTime > FIVE_HOURS) {
console.log(`工作空间 ${(info as any).KeepAlive?.wsUrl} 超过5小时自动停止`);
info.KeepAlive?.disconnect?.();
keepAliveMap.delete(id);
clearedArr.push({ id, wsUrl: (info as any).KeepAlive?.wsUrl });
}
}
return clearedArr;
}
// 每5小时自动清理超时的keepAlive任务
const FIVE_HOURS = 5 * 60 * 60 * 1000;
setInterval(() => {
clearKeepAlive();
}, FIVE_HOURS);

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: ['admin-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);

2
bun-test/.gitignore vendored
View File

@@ -1,2 +0,0 @@
node_modules
dist

View File

@@ -1,22 +0,0 @@
{
"name": "bun-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.25.0",
"devDependencies": {
"@types/bun": "^1.3.4"
},
"dependencies": {
"@rollup/plugin-node-resolve": "^16.0.3",
"esbuild": "^0.27.2",
"lodash-es": "^4.17.22",
"rollup": "^4.54.0"
}
}

View File

@@ -1,31 +0,0 @@
// rollup.config.ts
import type { Plugin, RollupOptions } from 'rollup';
import { nodeResolve } from '@rollup/plugin-node-resolve';
const cdnRewritePlugin: Plugin = {
name: 'rewrite-lodash-to-cdn',
resolveId(id) {
if (id === 'lodash-es') {
return 'https://esm.sh/lodash-es@5';
}
if (id.startsWith('lodash-es/')) {
const [, sub] = id.split('/', 2);
return `https://esm.sh/lodash-es@5/${sub}`;
}
return null;
},
};
const config: RollupOptions = {
input: 'src/index.ts',
output: {
dir: 'dist',
format: 'esm',
},
plugins: [cdnRewritePlugin, nodeResolve({ browser: true })],
external(id) {
return id.startsWith('https://');
},
};
export default config;

View File

@@ -1,3 +0,0 @@
import { debounce } from 'lodash-es';
export const debounceFn = debounce;

24
bun.config.ts Normal file
View File

@@ -0,0 +1,24 @@
import { resolvePath } from '@kevisual/use-config';
import { execSync } from 'node:child_process';
const buildFn = async (opts: { entry?: string, naming?: string }) => {
const entry = opts.entry || 'agent/opencode.ts';
const naming = opts.naming || 'opencode';
const external: string[] = ["bun", "ws"];
await Bun.build({
target: 'node',
format: 'esm',
entrypoints: [resolvePath(entry, { meta: import.meta })],
outdir: resolvePath('./dist', { meta: import.meta }),
naming: {
entry: `${naming}.js`,
},
external,
});
const cmd = `dts -i ${entry} -o ${naming}.d.ts`;
execSync(cmd);
};
await buildFn({ naming: 'opencode', entry: 'agent/opencode.ts' });
await buildFn({ naming: 'keep', entry: 'src/keep.ts' });
await buildFn({ naming: 'routes', entry: 'agent/index.ts' });

View File

@@ -0,0 +1,387 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/users/{username}/activities": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Activities"
],
"summary": "获取个人动态活跃详情汇总。Get user activities by date.",
"operationId": "GetUserActivitiesByDate",
"parameters": [
{
"type": "string",
"description": "UserName",
"name": "username",
"in": "path",
"required": true
},
{
"type": "string",
"description": "查询日期,格式 yyyyMM或者 yyyyMMdd",
"name": "date",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.ActivityDate"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-engage:r"
}
},
"/users/{username}/repo-activities/{activityType}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Activities"
],
"summary": "个人仓库动态详情列表。List of personal repository activity details.",
"operationId": "GetUserRepoActivityDetails",
"parameters": [
{
"type": "string",
"description": "UserName",
"name": "username",
"in": "path",
"required": true
},
{
"enum": [
"issue",
"pull_request",
"code_review"
],
"type": "string",
"description": "activity type",
"name": "activityType",
"in": "path",
"required": true
},
{
"type": "string",
"description": "仓库路径",
"name": "slug",
"in": "query",
"required": true
},
{
"type": "string",
"description": "查询日期,格式 yyyyMM或者 yyyyMMdd",
"name": "date",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "返回 []dto.ActivityPullRequestDetail|[]dto.ActivityIssueDetail",
"schema": {
"type": "array",
"items": {}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-engage:r"
}
},
"/{repo}/-/top-activity-users": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Activities"
],
"summary": "获取仓库 top 活跃用户。List the top active users",
"operationId": "TopContributors",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
},
{
"maximum": 10,
"minimum": 1,
"type": "integer",
"default": 5,
"description": "返回的用户个数",
"name": "top",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.UsersResult"
}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-base-info:r"
}
}
},
"definitions": {
"dto.ActivityDate": {
"type": "object",
"properties": {
"code_review_count": {
"type": "integer"
},
"code_reviews": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.ActivityRepoDetail"
}
},
"commit_count": {
"type": "integer"
},
"commits": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.ActivityRepoDetail"
}
},
"group_count": {
"type": "integer"
},
"groups": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.ActivityJoinGroupDetail"
}
},
"issues": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.ActivityRepoDetail"
}
},
"issues_count": {
"type": "integer"
},
"private_score": {
"type": "integer"
},
"pull_request_count": {
"type": "integer"
},
"pull_requests": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.ActivityRepoDetail"
}
},
"repo_count": {
"type": "integer"
},
"repos": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.ActivityCreateRepoDetail"
}
}
}
},
"dto.UsersResult": {
"type": "object",
"properties": {
"address": {
"type": "string"
},
"appreciate_status": {
"description": "用户赞赏码状态0-无赞赏码1-有",
"type": "integer"
},
"avatar": {
"type": "string"
},
"bio": {
"type": "string"
},
"company": {
"type": "string"
},
"created_at": {
"type": "string"
},
"email": {
"type": "string"
},
"follow_count": {
"type": "integer"
},
"follow_mission_count": {
"type": "integer"
},
"follow_repo_count": {
"type": "integer"
},
"follower_count": {
"type": "integer"
},
"freeze": {
"type": "boolean"
},
"gender": {
"type": "integer"
},
"group_count": {
"type": "integer"
},
"id": {
"type": "string"
},
"is_following": {
"description": "查询人是否follow了此用户",
"type": "boolean"
},
"location": {
"type": "string"
},
"locked": {
"type": "boolean"
},
"mission_count": {
"type": "integer"
},
"nickname": {
"type": "string"
},
"public_mission_count": {
"type": "integer"
},
"public_registry_count": {
"type": "integer"
},
"public_repo_count": {
"type": "integer"
},
"readme_repo_path": {
"type": "string",
"readOnly": true
},
"registry_count": {
"type": "integer"
},
"repo_count": {
"type": "integer"
},
"reward_amount": {
"type": "integer"
},
"reward_count": {
"type": "integer"
},
"site": {
"type": "string"
},
"stars_count": {
"type": "integer"
},
"type": {
"$ref": "#/definitions/constant.UserType"
},
"username": {
"type": "string"
},
"verified": {
"description": "认证类型",
"type": "integer"
},
"verified_expire_in": {
"description": "认证过期时间",
"type": "string"
},
"wechat_mp": {
"type": "string"
},
"wechat_mp_qrcode": {
"type": "string"
}
}
},
"constant.UserType": {
"type": "integer",
"format": "int32",
"enum": [
0,
1,
2,
3,
4
],
"x-enum-comments": {
"IoaUser": "IoaUser ioa 用户",
"OauthUser": "OauthUser oauth 授权用户",
"RobotUser": "RobotUser 助手用户",
"TestUser": "TestUser 测试用户",
"WeChatUser": "WeChatUser 微信用户"
},
"x-enum-descriptions": [
"WeChatUser 微信用户",
"OauthUser oauth 授权用户",
"TestUser 测试用户",
"RobotUser 助手用户",
"IoaUser ioa 用户"
],
"x-enum-varnames": [
"WeChatUser",
"OauthUser",
"TestUser",
"RobotUser",
"IoaUser"
]
}
}
}

200
docs/api-groups/ai/api.json Normal file
View File

@@ -0,0 +1,200 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{repo}/-/ai/chat/completions": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"AI"
],
"summary": "AI 对话。调用者需有代码写权限CI 中使用 CNB_TOKEN 不检查写权限。AI chat completions. Requires caller to have repo write permission (except when using CNB_TOKEN in CI).",
"operationId": "AiChatCompletions",
"parameters": [
{
"type": "string",
"description": "仓库完整路径",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "AI chat completions params. The params may differ by model.",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.AiChatCompletionsReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.AiChatCompletionsResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:r"
}
},
"/{repo}/-/build/ai/auto-pr": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"AI"
],
"summary": "根据传入的需求内容和需求标题借助 AI 自动编码并提 PR。Automatically code and create a PR with AI based on the input requirement content and title.",
"operationId": "AiAutoPr",
"parameters": [
{
"type": "string",
"description": "仓库完整路径",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "AI auto PR params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.AiAutoPrReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.AiAutoPrResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:rw"
}
}
},
"definitions": {
"dto.AiChatCompletionsReq": {
"type": "object",
"properties": {
"messages": {
"description": "对话内容",
"type": "array",
"items": {
"$ref": "#/definitions/dto.Message"
}
},
"model": {
"description": "模型名称",
"type": "string"
},
"stream": {
"description": "是否流式返回结果,部分模型可能不支持非流式",
"type": "boolean"
}
}
},
"dto.AiChatCompletionsResult": {
"type": "object",
"properties": {
"choices": {
"description": "选择",
"type": "array",
"items": {
"$ref": "#/definitions/dto.AiChatCompletionsChoice"
}
},
"created": {
"description": "创建时间",
"type": "integer"
},
"id": {
"description": "ID",
"type": "string"
},
"model": {
"description": "模型",
"type": "string"
},
"object": {
"description": "对象",
"type": "string"
}
}
},
"dto.AiAutoPrReq": {
"type": "object",
"properties": {
"body": {
"description": "需求内容",
"type": "string"
},
"branch": {
"description": "基于该分支编码并提交代码到随机分支,然后将随机分支提 PR 到该分支",
"type": "string"
},
"source": {
"description": "需求来源,默认为 issue其他来源的需求可写上会出现在代码提交日志的描述信息里",
"type": "string"
},
"title": {
"description": "需求标题",
"type": "string"
},
"url": {
"description": "需求来源 URL 地址",
"type": "string"
}
}
},
"dto.AiAutoPrResult": {
"type": "object",
"properties": {
"buildLogUrl": {
"description": "构建链接",
"type": "string"
},
"message": {
"description": "message",
"type": "string"
},
"sn": {
"description": "构建号",
"type": "string"
}
}
}
}
}

View File

@@ -0,0 +1,158 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{repo}/-/assets/{assetID}": {
"delete": {
"security": [
{
"BearerAuth": []
}
],
"description": "通过 asset 记录 id 删除一个 assetrelease和commit附件不能通过该接口删除\n访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:rw",
"tags": [
"Assets"
],
"summary": "通过 asset 记录 id 删除一个 asset",
"operationId": "DeleteAsset",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "integer",
"format": "int64",
"description": "asset id",
"name": "assetID",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"422": {
"description": "release和commit附件不能通过该接口删除",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
}
}
},
"/{slug}/-/list-assets": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Assets"
],
"summary": "仓库的 asset 记录列表",
"operationId": "ListAssets",
"parameters": [
{
"type": "string",
"description": "slug",
"name": "slug",
"in": "path",
"required": true
},
{
"type": "integer",
"default": 1,
"description": "第几页从1开始",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "每页多少条数据",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.AssetRecords"
}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:r"
}
}
},
"definitions": {
"die.WebError": {
"type": "object",
"properties": {
"errcode": {
"type": "integer"
},
"errmsg": {
"type": "string"
},
"errparam": {
"type": "object",
"additionalProperties": {}
}
}
},
"dto.AssetRecords": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"origin_path": {
"description": "来源地址,例如 release 附件的来源地址是对应的 release 页面。issue和pr文件没有。",
"type": "string"
},
"path": {
"type": "string"
},
"record_type": {
"description": "资源类型slug_img和slug_file可调用DeleteAsset接口直接删除该资源repo_release和repo_commit则不行",
"allOf": [
{
"$ref": "#/definitions/dto.AssetRecordType"
}
]
},
"referer": {
"type": "string"
},
"size_in_byte": {
"type": "integer"
}
}
}
}
}

View File

@@ -0,0 +1,265 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{repo}/-/badge/git/{sha}/{badge}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Badge"
],
"summary": "获取徽章 svg 或 JSON 数据。Get badge svg or JSON data.",
"operationId": "GetBadge",
"parameters": [
{
"type": "string",
"description": "仓库完整路径",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "latest 或 commit 8 位短 hash例如 89d48c07",
"name": "sha",
"in": "path",
"required": true
},
{
"type": "string",
"description": "徽章名,例如 pr 事件徽章名为ci/status/pull_request, 如需获取 JSON 数据,可加上 .json 后缀ci/status/pull_request.json",
"name": "badge",
"in": "path",
"required": true
},
{
"description": "GetBadge params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.GetBadgeReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.GetBadgeResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-commit-status:r"
}
},
"/{repo}/-/badge/list": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Badge"
],
"summary": "获取徽章列表数据。List badge data",
"operationId": "ListBadge",
"parameters": [
{
"type": "string",
"description": "仓库完整路径",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "ListBadge params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.ListBadgeReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.ListBadgeResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-commit-status:r"
}
},
"/{repo}/-/badge/upload": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Badge"
],
"summary": "上传徽章数据。Upload badge data",
"operationId": "UploadBadge",
"parameters": [
{
"type": "string",
"description": "仓库完整路径",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "UploadBadge params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.UploadBadgeReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.UploadBadgeResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-commit-status:rw"
}
}
},
"definitions": {
"dto.GetBadgeReq": {
"type": "object",
"properties": {
"branch": {
"description": "分支名例如main。不传则为默认分支获取默认分支最新徽章。传了分支名则获取该分支最新提交记录对应的徽章。",
"type": "string"
}
}
},
"dto.GetBadgeResult": {
"type": "object",
"properties": {
"color": {
"description": "徽章颜色",
"type": "string"
},
"label": {
"description": "徽章左侧显示内容",
"type": "string"
},
"link": {
"description": "徽章链接",
"type": "string"
},
"links": {
"description": "徽章链接列表",
"type": "array",
"items": {
"type": "string"
}
},
"message": {
"description": "徽章右侧显示内容",
"type": "string"
}
}
},
"dto.ListBadgeReq": {
"type": "object"
},
"dto.ListBadgeResult": {
"type": "object",
"properties": {
"badges": {
"description": "徽章列表",
"type": "array",
"items": {
"$ref": "#/definitions/dto.Badge"
}
}
}
},
"dto.UploadBadgeReq": {
"type": "object",
"properties": {
"key": {
"description": "徽章 key。目前允许上传的 key 包括security/tca",
"type": "string"
},
"latest": {
"description": "是否上传 latest 徽章。默认为 false不上传 latest仅上传 commitid 对应的徽章true上传 latest 和 commitid 对应的徽章",
"type": "boolean"
},
"link": {
"description": "点击徽章右侧的跳转链接",
"type": "string"
},
"message": {
"description": "徽章右侧显示内容",
"type": "string"
},
"sha": {
"description": "commit id",
"type": "string"
},
"value": {
"description": "徽章数值,不传默认用 message 代替",
"type": "integer"
}
}
},
"dto.UploadBadgeResult": {
"type": "object",
"properties": {
"latest_url": {
"description": "latest 对应的徽章 url 地址。如果没有传 latest: true则该字段为空字符串",
"type": "string"
},
"url": {
"description": "commitid 对应的徽章 url 地址",
"type": "string"
}
}
}
}
}

View File

@@ -0,0 +1,630 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{repo}/-/build/crontab/sync/{branch}": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Build"
],
"summary": "同步仓库分支下的定时任务。 Synchronize the content under the repository branch.",
"operationId": "BuildCrontabSync",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Branch",
"name": "branch",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.BuildCommonResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-cnb-trigger:rw"
}
},
"/{repo}/-/build/logs": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Build"
],
"summary": "查询流水线构建列表。List pipeline builds.",
"operationId": "GetBuildLogs",
"parameters": [
{
"type": "string",
"description": "Repo path",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Start date in \"YYYY-MM-DD\" format, e.g. \"2024-12-01\"",
"name": "createTime",
"in": "query"
},
{
"type": "string",
"description": "End date in \"YYYY-MM-DD\" format, e.g. \"2024-12-01\"",
"name": "endTime",
"in": "query"
},
{
"type": "string",
"description": "Event name, e.g. \"push\"",
"name": "event",
"in": "query"
},
{
"type": "integer",
"description": "Pagination page number, default(1)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "Pagination page size, default(30), max(100)",
"name": "pagesize",
"in": "query"
},
{
"type": "string",
"description": "Commit ID, e.g. \"2221d4535ec0c921bcd0858627c5025a871dd2b5\"",
"name": "sha",
"in": "query"
},
{
"type": "string",
"description": "Build SN, e.g. \"cnb-1qa-1i3f5ecau",
"name": "sn",
"in": "query"
},
{
"type": "string",
"description": "Source branch name, e.g. \"dev\"",
"name": "sourceRef",
"in": "query"
},
{
"type": "string",
"description": "Build status: \"pending\", \"success\", \"error\", \"cancel\"",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "Target branch name, e.g. \"main\"",
"name": "targetRef",
"in": "query"
},
{
"type": "string",
"description": "User ID",
"name": "userId",
"in": "query"
},
{
"type": "string",
"description": "Username",
"name": "userName",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.BuildLogsResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-cnb-trigger:r"
}
},
"/{repo}/-/build/logs/stage/{sn}/{pipelineId}/{stageId}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Build"
],
"summary": "查询流水线Stage详情。Get pipeline build stage detail.",
"operationId": "GetBuildStage",
"parameters": [
{
"type": "string",
"description": "Repo path",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "SN",
"name": "sn",
"in": "path",
"required": true
},
{
"type": "string",
"description": "PipelineId",
"name": "pipelineId",
"in": "path",
"required": true
},
{
"type": "string",
"description": "stageId",
"name": "stageId",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.BuildStageResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-cnb-trigger:r"
}
},
"/{repo}/-/build/logs/{sn}": {
"delete": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Build"
],
"summary": "删除流水线日志内容。Delete pipeline logs content.",
"operationId": "BuildLogsDelete",
"parameters": [
{
"type": "string",
"description": "Repo path",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Sn",
"name": "sn",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.BuildCommonResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-cnb-trigger:rw"
}
},
"/{repo}/-/build/runner/download/log/{pipelineId}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"tags": [
"Build"
],
"summary": "流水线runner日志下载。Pipeline runner log download.",
"operationId": "BuildRunnerDownloadLog",
"parameters": [
{
"type": "string",
"description": "Repo path",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "PipelineId",
"name": "pipelineId",
"in": "path",
"required": true
}
],
"responses": {
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-cnb-trigger:r"
}
},
"/{repo}/-/build/start": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Build"
],
"summary": "开始一个构建。Start a build.",
"operationId": "StartBuild",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "Build params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.StartBuildReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.BuildResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-cnb-trigger:rw"
}
},
"/{repo}/-/build/status/{sn}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Build"
],
"summary": "查询流水线构建状态。Get pipeline build status.",
"operationId": "GetBuildStatus",
"parameters": [
{
"type": "string",
"description": "Repo path",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "SN",
"name": "sn",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.BuildStatusResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-cnb-trigger:r"
}
},
"/{repo}/-/build/stop/{sn}": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Build"
],
"summary": "停止一个构建。 Stop a build.",
"operationId": "StopBuild",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "SN",
"name": "sn",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.BuildResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-cnb-trigger:rw"
}
}
},
"definitions": {
"dto.BuildCommonResult": {
"type": "object",
"properties": {
"code": {
"description": "返回码0 表示成功1 表示失败",
"type": "integer"
},
"message": {
"description": "描述",
"type": "string"
}
}
},
"dto.BuildLogsResult": {
"type": "object",
"properties": {
"data": {
"description": "构建数据列表",
"type": "array",
"items": {
"$ref": "#/definitions/dto.LogInfo"
}
},
"init": {
"description": "当前仓库是否已经有构建记录1 表示有构建记录0 表示没有构建记录",
"type": "boolean"
},
"timestamp": {
"description": "当前时间戳",
"type": "integer"
},
"total": {
"description": "总数",
"type": "integer"
}
}
},
"dto.BuildStageResult": {
"type": "object",
"properties": {
"content": {
"description": "stage 日志内容,数组格式,一个元素表示一行日志",
"type": "array",
"items": {
"type": "string"
}
},
"duration": {
"description": "stage 耗时单位ms",
"type": "integer"
},
"endTime": {
"description": "stage 结束时间",
"type": "integer"
},
"error": {
"description": "stage 错误信息",
"type": "string"
},
"id": {
"description": "stage id",
"type": "string"
},
"name": {
"description": "stage 名称",
"type": "string"
},
"startTime": {
"description": "stage 开始时间",
"type": "integer"
},
"status": {
"description": "stage 状态: \"pending\", \"start\", \"success\", \"error\", \"cancel\", \"skipped\"",
"type": "string"
}
}
},
"die.WebError": {
"type": "object",
"properties": {
"errcode": {
"type": "integer"
},
"errmsg": {
"type": "string"
},
"errparam": {
"type": "object",
"additionalProperties": {}
}
}
},
"dto.StartBuildReq": {
"type": "object",
"properties": {
"branch": {
"description": "触发分支,默认为主分支",
"type": "string"
},
"config": {
"description": "指定配置文件内容yaml 格式",
"type": "string"
},
"env": {
"description": "环境变量,对象格式",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"event": {
"description": "事件名,必须是 api_trigger 或以 api_trigger_ 开头,默认为 `api_trigger`",
"type": "string"
},
"sha": {
"description": "commit id ,优先级比 tag 高,默认为分支最新提交记录",
"type": "string"
},
"sync": {
"description": "是否等待构建正式触发为false时会立刻返回 sn 和 buildLogUrl",
"type": "string"
},
"tag": {
"description": "触发 tag优先级比 branch 高",
"type": "string"
}
}
},
"dto.BuildResult": {
"type": "object",
"properties": {
"buildLogUrl": {
"description": "构建链接",
"type": "string"
},
"message": {
"description": "构建信息",
"type": "string"
},
"sn": {
"description": "构建号",
"type": "string"
},
"success": {
"description": "构建是否触发成功,不代表构建结果",
"type": "boolean"
}
}
},
"dto.BuildStatusResult": {
"type": "object",
"properties": {
"jsonConfig": {
"description": "解析后的流水线JSON格式配置内容",
"type": "string"
},
"pipelinesStatus": {
"description": "流水线的状态",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/dto.PipelineStatus"
}
},
"rawConfig": {
"description": "流水线原始配置内容",
"type": "string"
},
"status": {
"description": "构建状态",
"type": "string"
}
}
}
}
}

View File

@@ -0,0 +1,119 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{slug}/-/charge/special-amount": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "查看根组织的特权额度,需要根组织的 master 以上权限才可以查看\n访问令牌调用此接口需包含以下权限。Required permissions for access token. \ngroup-resource:r",
"tags": [
"Charge"
],
"summary": "查看特权额度",
"operationId": "GetSpecialAmount",
"parameters": [
{
"type": "string",
"description": "group slug",
"name": "slug",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.SpecialAmount"
}
}
}
}
}
},
"definitions": {
"dto.SpecialAmount": {
"type": "object",
"properties": {
"compute_build_corehour": {
"description": "云原生构建cpu核时",
"type": "integer"
},
"compute_build_desc": {
"type": "string"
},
"compute_build_expire": {
"description": "过期时间时为 null 时永久有效",
"type": "string"
},
"compute_build_gpu_corehour": {
"description": "云原生构建gpu核时",
"type": "integer"
},
"compute_build_gpu_desc": {
"type": "string"
},
"compute_build_gpu_expire": {
"description": "过期时间时为 null 时永久有效",
"type": "string"
},
"compute_develop_corehour": {
"description": "云原生开发cpu核时",
"type": "integer"
},
"compute_develop_desc": {
"type": "string"
},
"compute_develop_expire": {
"description": "过期时间时为 null 时永久有效",
"type": "string"
},
"compute_develop_gpu_corehour": {
"description": "云原生开发gpu核时",
"type": "integer"
},
"compute_develop_gpu_desc": {
"type": "string"
},
"compute_develop_gpu_expire": {
"description": "过期时间时为 null 时永久有效",
"type": "string"
},
"storage_git_desc": {
"type": "string"
},
"storage_git_expire": {
"description": "过期时间时为 null 时永久有效",
"type": "string"
},
"storage_git_gib": {
"description": "git存储空间",
"type": "integer"
},
"storage_object_desc": {
"type": "string"
},
"storage_object_expire": {
"description": "过期时间时为 null 时永久有效",
"type": "string"
},
"storage_object_gib": {
"description": "对象存储空间",
"type": "integer"
}
}
}
}
}

View File

@@ -0,0 +1,300 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{slug}/-/outside-collaborators": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Members",
"Collaborators"
],
"summary": "获取指定仓库内的外部贡献者。List external contributors in specified repository.",
"operationId": "ListOutsideCollaborators",
"parameters": [
{
"type": "string",
"description": "slug",
"name": "slug",
"in": "path",
"required": true
},
{
"type": "integer",
"default": 1,
"description": "Pagination page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "Pagination page size",
"name": "page_size",
"in": "query"
},
{
"enum": [
"Guest",
"Reporter",
"Developer",
"Master"
],
"type": "string",
"description": "Role",
"name": "role",
"in": "query"
},
{
"type": "string",
"default": "",
"description": "过滤成员。Filter by member.",
"name": "search",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.OutsideCollaboratorInRepo"
}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:r"
}
},
"/{slug}/-/outside-collaborators/{username}": {
"put": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Members",
"Collaborators"
],
"summary": "更新指定仓库的外部贡献者权限信息。 Update permission information for external contributors in specified repository.",
"operationId": "UpdateOutsideCollaborators",
"parameters": [
{
"type": "string",
"description": "slug",
"name": "slug",
"in": "path",
"required": true
},
{
"type": "string",
"description": "username",
"name": "username",
"in": "path",
"required": true
},
{
"enum": [
"Guest",
"Reporter",
"Developer"
],
"type": "string",
"description": "Role",
"name": "role",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:rw"
},
"delete": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Members",
"Collaborators"
],
"summary": "删除指定仓库的外部贡献者。Removes external contributors from specified repository.",
"operationId": "DeleteOutsideCollaborators",
"parameters": [
{
"type": "string",
"description": "slug",
"name": "slug",
"in": "path",
"required": true
},
{
"type": "string",
"description": "username",
"name": "username",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:rw"
}
}
},
"definitions": {
"dto.OutsideCollaboratorInRepo": {
"type": "object",
"properties": {
"access_level": {
"$ref": "#/definitions/constant.AccessRole"
},
"avatar": {
"type": "string"
},
"created_at": {
"type": "string"
},
"email": {
"type": "string"
},
"freeze": {
"type": "boolean"
},
"id": {
"type": "string"
},
"join_time": {
"type": "string"
},
"locked": {
"type": "boolean"
},
"nickname": {
"type": "string"
},
"type": {
"$ref": "#/definitions/constant.UserType"
},
"username": {
"type": "string"
},
"verified": {
"description": "认证类型",
"type": "integer"
},
"verified_expire_in": {
"description": "认证过期时间",
"type": "string"
}
}
},
"constant.AccessRole": {
"type": "string",
"enum": [
"Unknown",
"Guest",
"Reporter",
"Developer",
"Master",
"Owner"
],
"x-enum-comments": {
"Anonymous": "Anonymous 匿名",
"DEVELOPER": "DEVELOPER 开发",
"Guest": "Guest 访客",
"MASTER": "MASTER 管理",
"OWNER": "OWNER 负责人",
"REPORTER": "REPORTER 助手"
},
"x-enum-descriptions": [
"Anonymous 匿名",
"Guest 访客",
"REPORTER 助手",
"DEVELOPER 开发",
"MASTER 管理",
"OWNER 负责人"
],
"x-enum-varnames": [
"Anonymous",
"Guest",
"REPORTER",
"DEVELOPER",
"MASTER",
"OWNER"
]
},
"constant.UserType": {
"type": "integer",
"format": "int32",
"enum": [
0,
1,
2,
3,
4
],
"x-enum-comments": {
"IoaUser": "IoaUser ioa 用户",
"OauthUser": "OauthUser oauth 授权用户",
"RobotUser": "RobotUser 助手用户",
"TestUser": "TestUser 测试用户",
"WeChatUser": "WeChatUser 微信用户"
},
"x-enum-descriptions": [
"WeChatUser 微信用户",
"OauthUser oauth 授权用户",
"TestUser 测试用户",
"RobotUser 助手用户",
"IoaUser ioa 用户"
],
"x-enum-varnames": [
"WeChatUser",
"OauthUser",
"TestUser",
"RobotUser",
"IoaUser"
]
}
}
}

View File

@@ -0,0 +1,56 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/events/{repo}/-/{date}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Event"
],
"summary": "获取仓库动态预签名地址并返回内容。Get events pre-signed URL and return content.",
"operationId": "GetEvents",
"parameters": [
{
"type": "string",
"description": "repo path",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "动态日期,支持按天或小时为维度获取,格式为yy-mm-dd-h or yy-mm-dd, eg:2025-09-11-5",
"name": "date",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
}
},
"definitions": {}
}

View File

@@ -0,0 +1,147 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/users/{username}/followers": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Followers"
],
"summary": "获取指定用户的粉丝列表。Get the followers list of specified user.",
"operationId": "GetFollowersByUserID",
"parameters": [
{
"type": "string",
"description": "Username",
"name": "username",
"in": "path",
"required": true
},
{
"type": "integer",
"default": 1,
"description": "Pagination page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "Pagination page size",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.UserFollowResult"
}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-engage:r"
}
},
"/users/{username}/following": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Followers"
],
"summary": "获取指定用户的关注人列表。Get the list of users that the specified user is following.",
"operationId": "GetFollowingByUserID",
"parameters": [
{
"type": "string",
"description": "Username",
"name": "username",
"in": "path",
"required": true
},
{
"type": "integer",
"default": 1,
"description": "Pagination page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "Pagination page size",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.UserFollowResult"
}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-engage:r"
}
}
},
"definitions": {
"dto.UserFollowResult": {
"type": "object",
"properties": {
"freeze": {
"type": "boolean"
},
"is_following": {
"description": "查询人是否follow了此用户",
"type": "boolean"
},
"locked": {
"type": "boolean"
},
"nickname": {
"type": "string"
},
"username": {
"type": "string"
}
}
}
}
}

2402
docs/api-groups/git/api.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,744 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{repo}/-/settings/branch-protections": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"GitSettings"
],
"summary": "查询仓库保护分支规则列表。List branch protection rules.",
"operationId": "ListBranchProtections",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/api.BranchProtection"
}
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:r"
},
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"GitSettings"
],
"summary": "新增仓库保护分支规则。Create branch protection rule.",
"operationId": "PostBranchProtection",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "Branch Protection Form",
"name": "branch_protection_form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.BranchProtection"
}
}
],
"responses": {
"201": {
"description": "Created"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:rw"
}
},
"/{repo}/-/settings/branch-protections/{id}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"GitSettings"
],
"summary": "查询仓库保护分支规则。Get branch protection rule.",
"operationId": "GetBranchProtection",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "保护分支规则唯一标识符。",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.BranchProtection"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:r"
},
"delete": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"GitSettings"
],
"summary": "删除仓库保护分支规则。 Delete branch protection rule.",
"operationId": "DeleteBranchProtection",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "保护分支规则唯一标识符。",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:rw"
},
"patch": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"GitSettings"
],
"summary": "更新仓库保护分支规则。Update branch protection rule.",
"operationId": "PatchBranchProtection",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "保护分支规则唯一标识符。",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Branch Protection Form",
"name": "branch_protection_form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.BranchProtection"
}
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:rw"
}
},
"/{repo}/-/settings/cloud-native-build": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [],
"tags": [
"GitSettings"
],
"summary": "查询仓库云原生构建设置。List pipeline settings.",
"operationId": "GetPipelineSettings",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.PipelineSettings"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:r"
},
"put": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"GitSettings"
],
"summary": "更新仓库云原生构建设置。Update pipeline settings.",
"operationId": "PutPipelineSettings",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "Cloud Native Build Form",
"name": "pipeline_form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.PipelineSettings"
}
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:rw"
}
},
"/{repo}/-/settings/pull-request": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"GitSettings"
],
"summary": "查询仓库合并请求设置。List pull request settings.",
"operationId": "GetPullRequestSettings",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.PullRequestSettings"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:r"
},
"put": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"GitSettings"
],
"summary": "更新仓库合并请求设置。Set pull request settings.",
"operationId": "PutPullRequestSettings",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "Pull Request Form",
"name": "pull_request_form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.PullRequestSettings"
}
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:rw"
}
},
"/{repo}/-/settings/push-limit": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"GitSettings"
],
"summary": "查询仓库推送设置。List push limit settings.",
"operationId": "GetPushLimitSettings",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.PushLimitSettings"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:r"
},
"put": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"GitSettings"
],
"summary": "设置仓库推送设置。Set push limit settings.",
"operationId": "PutPushLimitSettings",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "Push Limit Form",
"name": "push_limit_form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.PushLimitSettings"
}
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-manage:rw"
}
}
},
"definitions": {
"api.BranchProtection": {
"type": "object",
"properties": {
"allow_creation": {
"description": "是否允许所有人创建保护分支。",
"type": "boolean"
},
"allow_deletions": {
"description": "是否允许所有人删除保护分支。",
"type": "boolean"
},
"allow_force_pushes": {
"description": "是否允许所有人强制推送。",
"type": "boolean"
},
"allow_master_creation": {
"description": "是否仅允许仓库管理员及负责人创建保护分支。",
"type": "boolean"
},
"allow_master_deletions": {
"description": "是否仅允许仓库管理员及负责人删除保护分支。",
"type": "boolean"
},
"allow_master_force_pushes": {
"description": "是否仅允许仓库管理员及负责人强制推送。",
"type": "boolean"
},
"allow_master_manual_merge": {
"description": "是否允许仓库管理员及负责人手动合并到目标分支。",
"type": "boolean"
},
"allow_master_pushes": {
"description": "是否仅允许仓库管理员及负责人推送代码到保护分支中。",
"type": "boolean"
},
"allow_pushes": {
"description": "是否允许所有人推送代码到保护分支中。",
"type": "boolean"
},
"id": {
"description": "保护分支规则唯一标识符。",
"type": "string"
},
"required_approved_review_count": {
"description": "需要的代码评审者数量。格式:`评审者数量 ∈ [1,5]`",
"type": "integer"
},
"required_approved_review_ratio": {
"description": "需要的评审通过率。格式:`通过率 ∈ [1, 100]`",
"type": "integer"
},
"required_linear_history": {
"description": "是否仅允许线性提交。",
"type": "boolean"
},
"required_master_approve": {
"description": "是否需至少一个仓库管理员批准。",
"type": "boolean"
},
"required_must_auto_merge": {
"description": "是否仅允许自动合并。",
"type": "boolean"
},
"required_must_push_via_pull_request": {
"description": "是否必须通过合并请求推送代码到此规则匹配分支中。",
"type": "boolean"
},
"required_pull_request_reviews": {
"description": "保护分支的合并请求是否需要代码评审。",
"type": "boolean"
},
"required_status_checks": {
"description": "是否需要通过状态检查。",
"type": "boolean"
},
"rule": {
"description": "保护分支规则名称,支持通配符。",
"type": "string"
}
}
},
"die.WebError": {
"type": "object",
"properties": {
"errcode": {
"type": "integer"
},
"errmsg": {
"type": "string"
},
"errparam": {
"type": "object",
"additionalProperties": {}
}
}
},
"api.PipelineSettings": {
"type": "object",
"properties": {
"auto_trigger": {
"description": "是否允许仓库按照.cnb.yml配置自动触发云原生构建。",
"type": "boolean"
},
"forked_repo_auto_trigger": {
"description": "是否允许本仓Fork出来的仓库按照.cnb.yml配置自动触发云原生构建。",
"type": "boolean"
}
}
},
"api.PullRequestSettings": {
"type": "object",
"properties": {
"allow_merge_commit_merge": {
"description": "是否允许直接提交合并。",
"type": "boolean"
},
"allow_rebase_merge": {
"description": "是否允许变基合并。",
"type": "boolean"
},
"allow_squash_merge": {
"description": "是否允许压缩合并。",
"type": "boolean"
},
"master_auto_as_reviewer": {
"description": "是否允许自动添加仓库管理员为评审者。",
"type": "boolean"
},
"merge_commit_message_style": {
"description": "直接提交合并操作时默认生成的提交信息内容。可选值:`default`,`pull_request_title`,`pull_request_title_with_body`",
"type": "string"
},
"squash_commit_message_style": {
"description": "压缩合并操作时默认生成的提交信息内容。可选值:`default`,`pull_request_title`,`pull_request_title_with_body`",
"type": "string"
}
}
},
"api.PushLimitSettings": {
"type": "object",
"properties": {
"allow_single_push_number": {
"description": "允许单次推送最多允许更新分支和标签的个数数量。",
"type": "integer"
},
"check_single_push_number": {
"description": "是否开启单次更新分支和标签的个数限制。",
"type": "boolean"
},
"only_master_can_push_tag": {
"description": "是否仅允许负责人和管理员推送或删除标签、创建或删除版本。",
"type": "boolean"
},
"push_commit_must_be": {
"description": "推送提交到仓库,对提交作者和提交人进行检查。可选值:`any`,`registered`,`pusher`",
"type": "string"
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,348 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{repo}/-/knowledge/base": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"KnowledgeBase"
],
"summary": "获取知识库信息",
"operationId": "GetKnowledgeBaseInfo",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.KnowledgeBaseInfoRes"
}
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:r"
},
"delete": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"KnowledgeBase"
],
"summary": "删除知识库",
"operationId": "DeleteKnowledgeBase",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:rw"
}
},
"/{repo}/-/knowledge/base/query": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"KnowledgeBase"
],
"summary": "查询知识库使用文档https://docs.cnb.cool/zh/ai/knowledge-base.html",
"operationId": "QueryKnowledgeBase",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "查询内容",
"name": "query",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.QueryKnowledgeBaseReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.QueryKnowledgeBaseRes"
}
}
},
"400": {
"description": "Bad Request"
},
"500": {
"description": "Internal Server Error"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:r"
}
},
"/{repo}/-/knowledge/embedding/models": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"KnowledgeBase"
],
"summary": "获取当前支持的 Embedding 模型列表",
"operationId": "GetModels",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.EmbeddingModel"
}
}
},
"400": {
"description": "Bad Request"
},
"500": {
"description": "Internal Server Error"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:r"
}
}
},
"definitions": {
"dto.KnowledgeBaseInfoRes": {
"type": "object",
"properties": {
"embedding_model": {
"$ref": "#/definitions/dto.EmbeddingModels"
},
"exclude": {
"type": "string"
},
"id": {
"type": "string"
},
"include": {
"type": "string"
},
"issue_last_sync_time": {
"type": "string"
},
"issue_sync_enabled": {
"type": "boolean"
},
"last_commit_sha": {
"type": "string"
},
"metadata": {
"$ref": "#/definitions/dto.KnowledgeBaseMetadata"
},
"statistics": {
"$ref": "#/definitions/dto.DocumentStatistics"
}
}
},
"dto.QueryKnowledgeBaseReq": {
"type": "object",
"properties": {
"metadata_filtering_conditions": {
"description": "元数据过滤条件",
"allOf": [
{
"$ref": "#/definitions/dto.MetadataFilteringConditions"
}
]
},
"query": {
"description": "查询语句",
"type": "string"
},
"score_threshold": {
"description": "分数阈值",
"type": "number"
},
"top_k": {
"description": "返回结果的数量",
"type": "integer"
}
}
},
"dto.QueryKnowledgeBaseRes": {
"type": "object",
"properties": {
"chunk": {
"type": "string"
},
"metadata": {
"type": "object",
"additionalProperties": true
},
"score": {
"type": "number"
}
}
},
"dto.EmbeddingModel": {
"type": "object",
"properties": {
"dimension": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.EmbeddingModels": {
"type": "object",
"properties": {
"dimension": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.KnowledgeBaseMetadata": {
"type": "object",
"properties": {
"issue": {
"$ref": "#/definitions/dto.IssueConfig"
},
"processing": {
"$ref": "#/definitions/dto.ProcessingConfig"
},
"version": {
"type": "string"
}
}
},
"dto.DocumentStatistics": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"size": {
"type": "integer"
}
}
},
"dto.IssueConfig": {
"type": "object",
"properties": {
"labels": {
"description": "逗号分隔的标签字符串,如 \"bug,feature\"",
"type": "string"
},
"state": {
"description": "\"open\", \"closed\"",
"type": "string"
}
}
},
"dto.ProcessingConfig": {
"type": "object",
"properties": {
"chunk_overlap": {
"type": "integer"
},
"chunk_size": {
"type": "integer"
},
"text_separator": {
"type": "string"
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,632 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{mission}": {
"delete": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Missions"
],
"summary": "删除指定任务集。Delete the specified mission.",
"operationId": "DeleteMission",
"parameters": [
{
"type": "string",
"description": "mission path",
"name": "mission",
"in": "path",
"required": true
},
{
"type": "string",
"description": "微信身份验证票据首次请求不传会返回新票据。WeChat auth ticket, will return new ticket if not provided in first request.",
"name": "x-cnb-identity-ticket",
"in": "header"
}
],
"responses": {
"200": {
"description": "OK"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nmission-delete:rw"
}
},
"/{mission}/-/mission/view": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Missions"
],
"summary": "查询任务集视图配置信息。Get mission view config.",
"operationId": "GetMissionViewConfig",
"parameters": [
{
"type": "string",
"description": "Mission slug",
"name": "mission",
"in": "path",
"required": true
},
{
"type": "string",
"description": "View ID",
"name": "id",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.MissionViewConfig"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nmission-manage:r"
},
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Missions"
],
"summary": "设置任务集视图配置信息。Set mission view config.",
"operationId": "PostMissionViewConfig",
"parameters": [
{
"type": "string",
"description": "Mission slug",
"name": "mission",
"in": "path",
"required": true
},
{
"description": "Params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.MissionViewConfig"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nmission-manage:rw"
}
},
"/{mission}/-/mission/view-list": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Missions"
],
"summary": "获取任务集视图列表。Get view list of a mission.",
"operationId": "GetMissionViewList",
"parameters": [
{
"type": "string",
"description": "mission",
"name": "mission",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.MissionView"
}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nmission-manage:r"
},
"put": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Missions"
],
"summary": "添加、修改任务集视图。Update a mission view or add a new one.",
"operationId": "PutMissionViewList",
"parameters": [
{
"type": "string",
"description": "Mission slug",
"name": "mission",
"in": "path",
"required": true
},
{
"description": "Params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.MissionView"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nmission-manage:rw"
},
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Missions"
],
"summary": "排序任务集视图。Sort mission view list.",
"operationId": "PostMissionViewList",
"parameters": [
{
"type": "string",
"description": "Mission slug",
"name": "mission",
"in": "path",
"required": true
},
{
"description": "Params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.MissionPostViewReq"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nmission-manage:rw"
}
},
"/{mission}/-/settings/set_visibility": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Missions"
],
"summary": "改变任务集可见性。Update the visibility of a mission.",
"operationId": "SetMissionVisibility",
"parameters": [
{
"type": "string",
"description": "mission path",
"name": "mission",
"in": "path",
"required": true
},
{
"enum": [
"Private",
"Public"
],
"type": "string",
"description": "任务集可见性",
"name": "visibility",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nmission-manage:rw"
}
},
"/{slug}/-/missions": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Missions"
],
"summary": "查询组织下面用户有权限查看到的任务集。Query all missions that the user has permission to see under the specific organization.",
"operationId": "GetGroupSubMissions",
"parameters": [
{
"type": "string",
"description": "组织 slug",
"name": "slug",
"in": "path",
"required": true
},
{
"type": "integer",
"format": "int64",
"default": 1,
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"format": "int64",
"default": 10,
"description": "每页数量",
"name": "page_size",
"in": "query"
},
{
"enum": [
"private",
"public"
],
"type": "string",
"description": "任务集类型",
"name": "filter_type",
"in": "query"
},
{
"enum": [
"created_at",
"name"
],
"type": "string",
"description": "排序类型默认created_at",
"name": "order_by",
"in": "query"
},
{
"type": "boolean",
"default": false,
"description": "排序顺序",
"name": "desc",
"in": "query"
},
{
"enum": [
"all",
"sub",
"grand"
],
"type": "string",
"description": "查全部/查询直接属于当前组织的仓库/查询子组织的仓库",
"name": "descendant",
"in": "query"
},
{
"type": "string",
"description": "搜索关键字",
"name": "search",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.Missions4User"
}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \ngroup-resource:r"
},
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Missions"
],
"summary": "创建任务集。Create a mission.",
"operationId": "CreateMission",
"parameters": [
{
"type": "string",
"description": "Group slug",
"name": "slug",
"in": "path",
"required": true
},
{
"description": "mission information",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.CreateMissionReq"
}
}
],
"responses": {
"201": {
"description": "Created"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \ngroup-resource:rw"
}
}
},
"definitions": {
"dto.MissionViewConfig": {
"type": "object",
"properties": {
"fields": {
"description": "字段配置",
"type": "array",
"items": {
"$ref": "#/definitions/dto.MissionViewFieldConfig"
}
},
"group": {
"description": "分组信息",
"allOf": [
{
"$ref": "#/definitions/dto.MissionViewGroup"
}
]
},
"id": {
"description": "视图唯一标识",
"type": "string"
},
"selectors": {
"description": "筛选条件",
"type": "array",
"items": {
"$ref": "#/definitions/dto.MissionViewSelector"
}
},
"sorts": {
"description": "排序条件",
"type": "array",
"items": {
"$ref": "#/definitions/dto.MissionViewSort"
}
},
"type": {
"description": "视图类型",
"allOf": [
{
"$ref": "#/definitions/dto.MissionViewType"
}
]
}
}
},
"dto.MissionView": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"$ref": "#/definitions/dto.MissionViewType"
}
}
},
"dto.MissionPostViewReq": {
"type": "object",
"properties": {
"ids": {
"description": "视图唯一标识列表,按此顺序排序",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"dto.Missions4User": {
"type": "object",
"properties": {
"access": {
"allOf": [
{
"$ref": "#/definitions/constant.AccessRole"
}
],
"readOnly": true
},
"created_at": {
"type": "string"
},
"description": {
"type": "string"
},
"freeze": {
"type": "boolean",
"readOnly": true
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"pinned": {
"type": "boolean"
},
"pinned_time": {
"type": "string"
},
"star_time": {
"type": "string"
},
"stared": {
"type": "boolean"
},
"updated_at": {
"type": "string"
},
"visibility_level": {
"$ref": "#/definitions/constant.Visibility"
}
}
},
"dto.CreateMissionReq": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"name": {
"type": "string"
},
"repos": {
"type": "array",
"items": {
"type": "string"
}
},
"visibility": {
"type": "string",
"default": "public",
"enum": [
"public",
"private"
]
}
}
},
"dto.MissionViewType": {
"type": "string",
"enum": [
"table",
"board",
"gantt"
],
"x-enum-varnames": [
"MissionViewTypeTable",
"MissionViewTypeBoard",
"MissionViewTypeGantt"
]
},
"constant.Visibility": {
"type": "string",
"enum": [
"Private",
"Public",
"Secret"
],
"x-enum-comments": {
"VisibilityPrivate": "VisibilityPrivate 私有仓库 - 仓库的访问必须显式授予每个用户",
"VisibilityPublic": "VisibilityPublic 公共仓库 - 可以不经任何身份验证克隆该项目",
"VisibilitySecret": "VisibilitySecret 加密仓库 - 仓库特定角色身份才能读取解密"
},
"x-enum-descriptions": [
"VisibilityPrivate 私有仓库 - 仓库的访问必须显式授予每个用户",
"VisibilityPublic 公共仓库 - 可以不经任何身份验证克隆该项目",
"VisibilitySecret 加密仓库 - 仓库特定角色身份才能读取解密"
],
"x-enum-varnames": [
"VisibilityPrivate",
"VisibilityPublic",
"VisibilitySecret"
]
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,932 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{repo}/-/releases": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Releases"
],
"summary": "查询 release 列表。List releases.",
"operationId": "ListReleases",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "integer",
"default": 1,
"description": "分页页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 30,
"description": "分页页大小",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/api.Release"
}
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:r"
},
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Releases"
],
"summary": "新增一个 release。Create a release.",
"operationId": "PostRelease",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "Post Release Form, attachment is optional",
"name": "create_release_form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/openapi.PostReleaseForm"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/api.Release"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:rw"
}
},
"/{repo}/-/releases/download/{tag}/{filename}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"tags": [
"Releases"
],
"summary": "发起一个获取 release 附件的请求, 302到有一定效期的下载地址。Get a request to fetch a release assets and returns 302 redirect to the assets URL with specific valid time.",
"operationId": "GetReleasesAsset",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "标签名称。示例:`v1.0.0`",
"name": "tag",
"in": "path",
"required": true
},
{
"type": "string",
"description": "文件名称。示例:`test.png`",
"name": "filename",
"in": "path",
"required": true
},
{
"type": "boolean",
"default": false,
"description": "是否可以下载true表示302的下载地址有效期12小时最多下载10次。",
"name": "share",
"in": "query"
}
],
"responses": {
"302": {
"description": "Found"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-contents:r"
}
},
"/{repo}/-/releases/latest": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Releases"
],
"summary": "查询最新的 release。Query the latest release.",
"operationId": "GetLatestRelease",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.Release"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:r"
}
},
"/{repo}/-/releases/tags/{tag}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Releases"
],
"summary": "通过 tag 查询指定 release,包含附件信息。Get a release by tag, include assets information.",
"operationId": "GetReleaseByTag",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "标签名称。",
"name": "tag",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.Release"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:r"
}
},
"/{repo}/-/releases/{release_id}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Releases"
],
"summary": "根据 id\t查询指定 release, 包含附件信息。Get a release by id, include assets information.",
"operationId": "GetReleaseByID",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "版本唯一标识符。",
"name": "release_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.Release"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:r"
},
"delete": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Releases"
],
"summary": "删除指定的 release。Delete a release.",
"operationId": "DeleteRelease",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "版本唯一标识符。",
"name": "release_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:rw"
},
"patch": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Releases"
],
"summary": "更新 release。Update a release.",
"operationId": "PatchRelease",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "版本唯一标识符。",
"name": "release_id",
"in": "path",
"required": true
},
{
"description": "patch release form",
"name": "patch_release_form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/openapi.PatchReleaseForm"
}
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:rw"
}
},
"/{repo}/-/releases/{release_id}/asset-upload-confirmation/{upload_token}/{asset_path}": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Releases"
],
"summary": "确认 release 附件上传完成。Confirm release asset upload.",
"operationId": "PostReleaseAssetUploadConfirmation",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "版本唯一标识符。",
"name": "release_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "PostReleaseAssetUploadURL接口返回值verify_url字段提取的upload_token。",
"name": "upload_token",
"in": "path",
"required": true
},
{
"type": "string",
"description": "PostReleaseAssetUploadURL接口返回值verify_url字段提取的asset_path。",
"name": "asset_path",
"in": "path",
"required": true
},
{
"type": "integer",
"format": "int64",
"description": "附件保持的天数。0 表示永久,最大不能超过 180 天",
"name": "ttl",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:rw"
}
},
"/{repo}/-/releases/{release_id}/asset-upload-url": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Releases"
],
"summary": "新增一个 release 附件。Create a release asset.",
"operationId": "PostReleaseAssetUploadURL",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "版本唯一标识符。",
"name": "release_id",
"in": "path",
"required": true
},
{
"description": "Post Release Asset Upload URL Form",
"name": "create_release_asset_upload_url_form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/openapi.PostReleaseAssetUploadURLForm"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/openapi.ReleaseAssetUploadURL"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:rw"
}
},
"/{repo}/-/releases/{release_id}/assets/{asset_id}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"tags": [
"Releases"
],
"summary": "查询指定的 release 附件 the specified release asset.",
"operationId": "GetReleaseAsset",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "版本唯一标识符。",
"name": "release_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "附件唯一标识符。",
"name": "asset_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.ReleaseAsset"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:r"
},
"delete": {
"security": [
{
"BearerAuth": []
}
],
"tags": [
"Releases"
],
"summary": "删除指定的 release 附件 the specified release asset.",
"operationId": "DeleteReleaseAsset",
"parameters": [
{
"type": "string",
"description": "不带.git后缀的仓库名称。格式`组织名称/仓库名称`",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "版本唯一标识符。",
"name": "release_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "附件唯一标识符。",
"name": "asset_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:rw"
}
}
},
"definitions": {
"api.Release": {
"type": "object",
"properties": {
"assets": {
"description": "附件列表。",
"type": "array",
"items": {
"$ref": "#/definitions/api.ReleaseAsset"
}
},
"author": {
"description": "作者信息。",
"allOf": [
{
"$ref": "#/definitions/git_woa_com_cnb_monorepo_git_internal_app_git_service_bff_api.UserInfo"
}
]
},
"body": {
"description": "版本描述。",
"type": "string"
},
"created_at": {
"description": "创建时间。",
"type": "string"
},
"draft": {
"description": "是否为草稿版本。",
"type": "boolean"
},
"id": {
"description": "版本唯一标识符。",
"type": "string"
},
"is_latest": {
"description": "是否为最新版本。",
"type": "boolean"
},
"name": {
"description": "版本标题。",
"type": "string"
},
"prerelease": {
"description": "是否为预发布版本。",
"type": "boolean"
},
"published_at": {
"description": "版本发布时间。",
"type": "string"
},
"tag_commitish": {
"description": "标签与提交标识符。",
"type": "string"
},
"tag_name": {
"description": "标签名称。",
"type": "string"
},
"updated_at": {
"description": "更新时间。",
"type": "string"
}
}
},
"die.WebError": {
"type": "object",
"properties": {
"errcode": {
"type": "integer"
},
"errmsg": {
"type": "string"
},
"errparam": {
"type": "object",
"additionalProperties": {}
}
}
},
"openapi.PostReleaseForm": {
"type": "object",
"properties": {
"body": {
"description": "版本描述。",
"type": "string"
},
"draft": {
"description": "是否为草稿版本。",
"type": "boolean"
},
"make_latest": {
"description": "是否设置为最新版本。可选值:`true`,`false`,`legacy`",
"type": "string"
},
"name": {
"description": "版本标题。",
"type": "string"
},
"prerelease": {
"description": "是否为预发布版本。",
"type": "boolean"
},
"tag_name": {
"description": "标签名称。",
"type": "string"
},
"target_commitish": {
"description": "目标提交哈希或分支名称。",
"type": "string"
}
}
},
"openapi.PatchReleaseForm": {
"type": "object",
"properties": {
"body": {
"description": "版本描述。",
"type": "string"
},
"draft": {
"description": "是否为草稿版本。",
"type": "boolean"
},
"make_latest": {
"description": "是否设置为最新版本。可选值:`true`,`false`,`legacy`",
"type": "string"
},
"name": {
"description": "版本标题。",
"type": "string"
},
"prerelease": {
"description": "是否为预发布版本。",
"type": "boolean"
}
}
},
"openapi.PostReleaseAssetUploadURLForm": {
"type": "object",
"properties": {
"asset_name": {
"description": "附件名称。",
"type": "string"
},
"overwrite": {
"description": "是否覆盖同名附件。",
"type": "boolean"
},
"size": {
"description": "附件大小,单位为字节。",
"type": "integer"
},
"ttl": {
"description": "附件存在时间,单位为天",
"type": "integer"
}
}
},
"openapi.ReleaseAssetUploadURL": {
"type": "object",
"properties": {
"expires_in_sec": {
"description": "URL过期时间单位为秒。",
"type": "integer"
},
"upload_url": {
"description": "附件上传URL。",
"type": "string"
},
"verify_url": {
"description": "附件上传确认验证URL。",
"type": "string"
}
}
},
"api.ReleaseAsset": {
"type": "object",
"properties": {
"brower_download_url": {
"description": "浏览器下载URL通过主域名用于用户直接访问。",
"type": "string"
},
"content_type": {
"description": "附件内容类型。",
"type": "string"
},
"created_at": {
"description": "创建时间。",
"type": "string"
},
"download_count": {
"description": "下载次数。",
"type": "integer"
},
"id": {
"description": "附件唯一标识符。",
"type": "string"
},
"name": {
"description": "附件名称。",
"type": "string"
},
"path": {
"description": "附件路径。",
"type": "string"
},
"size": {
"description": "附件大小(字节)。",
"type": "integer"
},
"updated_at": {
"description": "更新时间。",
"type": "string"
},
"uploader": {
"description": "附件上传者信息。",
"allOf": [
{
"$ref": "#/definitions/git_woa_com_cnb_monorepo_git_internal_app_git_service_bff_api.UserInfo"
}
]
},
"url": {
"description": "API下载URL通过API域名用于程序化下载。",
"type": "string"
}
}
}
}
}

View File

@@ -0,0 +1,134 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{slug}/-/contributor/trend": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [],
"tags": [
"RepoContributor"
],
"summary": "查询仓库贡献者前 100 名的详细趋势数据。Query detailed trend data for top 100 contributors of the repository.",
"operationId": "GetRepoContributorTrend",
"parameters": [
{
"type": "string",
"description": "slug",
"name": "slug",
"in": "path",
"required": true
},
{
"type": "integer",
"default": 14,
"description": "limit, 0~100",
"name": "limit",
"in": "query"
},
{
"type": "boolean",
"default": false,
"description": "exclude_external_users, true|false",
"name": "exclude_external_users",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/web.RepoContribTrend"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-code:r"
}
}
},
"definitions": {
"web.RepoContribTrend": {
"type": "object",
"properties": {
"meta": {
"$ref": "#/definitions/web.Meta"
},
"repo_data": {
"type": "array",
"items": {
"$ref": "#/definitions/web.Week"
}
},
"user_total": {
"type": "integer"
},
"users_data": {
"type": "array",
"items": {
"$ref": "#/definitions/web.ContributorTrend"
}
},
"week_total": {
"type": "integer"
},
"with_line_counts": {
"description": "是否统计增删的行数, 默认总提交超过 10000 的仓库不统计",
"type": "boolean"
}
}
},
"die.WebError": {
"type": "object",
"properties": {
"errcode": {
"type": "integer"
},
"errmsg": {
"type": "string"
},
"errparam": {
"type": "object",
"additionalProperties": {}
}
}
},
"web.Meta": {
"type": "object",
"properties": {
"gen_branch": {
"type": "string"
},
"gen_hash": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
}
}
}

View File

@@ -0,0 +1,314 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{repo}/-/labels": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"RepoLabels"
],
"summary": "查询仓库的标签列表。List repository labels.",
"operationId": "ListLabels",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "integer",
"default": 1,
"description": "pagination page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 30,
"description": "pagination page size",
"name": "page_size",
"in": "query"
},
{
"type": "string",
"description": "label search key",
"name": "keyword",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/api.Label"
}
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-notes:r"
},
"post": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"RepoLabels"
],
"summary": "创建一个 标签。Create a label.",
"operationId": "PostLabel",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "Post Label Form",
"name": "post_label_form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.PostLabelForm"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/api.Label"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-notes:rw"
}
},
"/{repo}/-/labels/{name}": {
"delete": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"RepoLabels"
],
"summary": "删除指定的仓库标签。Delete the specified repository label.",
"operationId": "DeleteLabel",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "label name",
"name": "name",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-notes:rw"
},
"patch": {
"security": [
{
"BearerAuth": []
}
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"RepoLabels"
],
"summary": "更新标签信息。Update label information.",
"operationId": "PatchLabel",
"parameters": [
{
"type": "string",
"description": "repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "label name",
"name": "name",
"in": "path",
"required": true
},
{
"description": "Patch Label Form",
"name": "patch_label_form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.PatchLabelForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.Label"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-notes:rw"
}
}
},
"definitions": {
"api.Label": {
"type": "object",
"properties": {
"color": {
"type": "string"
},
"description": {
"type": "string"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"die.WebError": {
"type": "object",
"properties": {
"errcode": {
"type": "integer"
},
"errmsg": {
"type": "string"
},
"errparam": {
"type": "object",
"additionalProperties": {}
}
}
},
"api.PostLabelForm": {
"type": "object",
"properties": {
"color": {
"description": "The hexadecimal color code for the label, without the leading `#`.",
"type": "string"
},
"description": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"api.PatchLabelForm": {
"type": "object",
"properties": {
"color": {
"description": "The hexadecimal color code for the label, without the leading `#`.",
"type": "string"
},
"description": {
"type": "string"
},
"new_name": {
"type": "string"
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,286 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/{repo}/-/security/overview": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Security"
],
"summary": "查询仓库安全模块概览数据。Query the security overview data of a repository",
"operationId": "GetRepoSecurityOverview",
"parameters": [
{
"type": "string",
"description": "仓库名称",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "类型多个类型用逗号分隔code_sensitive,code_vulnerability,code_issue为空默认查询所有类型",
"name": "types",
"in": "query"
},
{
"type": "string",
"description": "查询类型下开启或忽略的各风险类型概览数量,可选值open,ignore,all默认all",
"name": "tab",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.RepoSecurityOverview"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-security:r"
}
}
},
"definitions": {
"dto.RepoSecurityOverview": {
"type": "object",
"properties": {
"code_issue": {
"$ref": "#/definitions/dto.CodeIssueSummary"
},
"code_sensitive": {
"$ref": "#/definitions/dto.CodeSensitiveSummary"
},
"code_vulnerability": {
"$ref": "#/definitions/dto.CodeVulOverview"
},
"risk_cnt": {
"$ref": "#/definitions/dto.RepoSecurityRiskCnt"
}
}
},
"die.WebError": {
"type": "object",
"properties": {
"errcode": {
"type": "integer"
},
"errmsg": {
"type": "string"
},
"errparam": {
"type": "object",
"additionalProperties": {}
}
}
},
"dto.CodeIssueSummary": {
"type": "object",
"properties": {
"critical_count": {
"description": "严重风险问题数量",
"type": "integer"
},
"critical_ignore_count": {
"description": "严重风险问题忽略数量",
"type": "integer"
},
"enable": {
"description": "是否开启源码信息扫描",
"type": "boolean"
},
"high_count": {
"description": "高风险问题数量",
"type": "integer"
},
"high_ignore_count": {
"description": "高风险问题忽略数量",
"type": "integer"
},
"ignored": {
"description": "忽略的问题数量",
"type": "integer"
},
"low_count": {
"description": "低风险问题数量",
"type": "integer"
},
"low_ignore_count": {
"description": "低风险问题忽略数量",
"type": "integer"
},
"medium_count": {
"description": "中风险问题数量",
"type": "integer"
},
"medium_ignore_count": {
"description": "中风险问题忽略数量",
"type": "integer"
},
"open": {
"description": "开启中问题数量",
"type": "integer"
}
}
},
"dto.CodeSensitiveSummary": {
"type": "object",
"properties": {
"enable": {
"description": "是否开启代码敏感信息扫描",
"type": "boolean"
},
"high_count": {
"description": "高风险问题数量",
"type": "integer"
},
"high_ignore_count": {
"description": "高风险问题忽略数量",
"type": "integer"
},
"ignored": {
"description": "忽略问题数量",
"type": "integer"
},
"low_count": {
"description": "低风险问题数量",
"type": "integer"
},
"low_ignore_count": {
"description": "低风险问题忽略数量",
"type": "integer"
},
"medium_count": {
"description": "中风险问题数量",
"type": "integer"
},
"medium_ignore_count": {
"description": "中风险问题忽略数量",
"type": "integer"
},
"open": {
"description": "开启中问题数量",
"type": "integer"
}
}
},
"dto.CodeVulOverview": {
"type": "object",
"properties": {
"critical_vul_ignore_cnt": {
"description": "忽略的严重风险漏洞的数量",
"type": "integer"
},
"critical_vul_open_cnt": {
"description": "打开的严重风险级别漏洞的数量",
"type": "integer"
},
"enable": {
"description": "是否开启代码漏洞扫描",
"type": "boolean"
},
"high_vul_ignore_cnt": {
"description": "忽略的高风险级别漏洞的数量",
"type": "integer"
},
"high_vul_open_cnt": {
"description": "打开的高风险级别漏洞的数量",
"type": "integer"
},
"ignored": {
"description": "忽略问题数量",
"type": "integer"
},
"low_vul_ignore_cnt": {
"description": "忽略的低风险级别漏洞的数量",
"type": "integer"
},
"low_vul_open_cnt": {
"description": "打开的低风险级别漏洞的数量",
"type": "integer"
},
"medium_vul_ignore_cnt": {
"description": "忽略的中风险级别漏洞的数量",
"type": "integer"
},
"medium_vul_open_cnt": {
"description": "打开的中风险级别漏洞的数量",
"type": "integer"
},
"open": {
"description": "开启中问题数量",
"type": "integer"
}
}
},
"dto.RepoSecurityRiskCnt": {
"type": "object",
"properties": {
"code_issue_enable": {
"description": "是否开启源码扫描",
"type": "boolean"
},
"code_issue_risk_cnt": {
"description": "源码扫描风险数量 (严重+高风险)",
"type": "integer"
},
"code_sensitive_enable": {
"description": "是否开启代码敏感信息扫描",
"type": "boolean"
},
"code_sensitive_risk_cnt": {
"description": "敏感信息风险数量(高风险)",
"type": "integer"
},
"code_vulnerability_enable": {
"description": "是否开启代码漏洞扫描",
"type": "boolean"
},
"code_vulnerability_risk_cnt": {
"description": "代码漏洞风险数量(严重+高风险)",
"type": "integer"
},
"enable": {
"description": "是否开启安全模块",
"type": "boolean"
},
"total": {
"description": "总计数",
"type": "integer"
}
}
}
}
}

View File

@@ -0,0 +1,463 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/user/stared-repos": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Starring"
],
"summary": "获取当前用户 star 的仓库列表。List all stared repositories.",
"operationId": "GetUserAllStaredRepos",
"parameters": [
{
"type": "integer",
"default": 1,
"description": "Pagination page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "Pagination page size",
"name": "page_size",
"in": "query"
},
{
"type": "string",
"default": "",
"description": "Filter by repositories",
"name": "search",
"in": "query"
},
{
"type": "boolean",
"default": false,
"description": "排序顺序。Ordering.",
"name": "desc",
"in": "query"
},
{
"enum": [
"created_at",
"last_updated_at",
"stared_time",
"stars",
"forks"
],
"type": "string",
"default": "last_updated_at",
"description": "Order field",
"name": "order_by",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.Repos4User"
}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-engage:r"
}
},
"/users/{username}/stared-repos": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Starring"
],
"summary": "获取指定用户的 star 仓库列表。Get the list of repositories starred by the specified user.",
"operationId": "GetUserStaredRepos",
"parameters": [
{
"type": "string",
"description": "UserName",
"name": "username",
"in": "path",
"required": true
},
{
"type": "string",
"default": "",
"description": "过滤仓库。Filter by repositories",
"name": "search",
"in": "query"
},
{
"type": "integer",
"default": 1,
"description": "Pagination page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "Pagination page size",
"name": "page_size",
"in": "query"
},
{
"type": "boolean",
"default": false,
"description": "Ordering",
"name": "desc",
"in": "query"
},
{
"enum": [
"created_at",
"last_updated_at",
"stars",
"forks"
],
"type": "string",
"default": "last_updated_at",
"description": "Order field",
"name": "order_by",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.Repos4User"
}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-engage:r"
}
},
"/{slug}/-/stars": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Starring"
],
"summary": "获取指定仓库的star用户列表。Get the list of users who starred the specified repository.",
"operationId": "ListStarUsers",
"parameters": [
{
"type": "string",
"description": "slug",
"name": "slug",
"in": "path",
"required": true
},
{
"enum": [
"all",
"followed"
],
"type": "string",
"description": "Filter type",
"name": "filter_type",
"in": "query",
"required": true
},
{
"type": "integer",
"default": 1,
"description": "page",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "page",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.RepoStarUsers"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-basic-info:r"
}
}
},
"definitions": {
"dto.Repos4User": {
"type": "object",
"properties": {
"access": {
"allOf": [
{
"$ref": "#/definitions/constant.AccessRole"
}
],
"readOnly": true
},
"created_at": {
"type": "string"
},
"description": {
"type": "string"
},
"display_module": {
"$ref": "#/definitions/constant.RepoDisplayModule"
},
"flags": {
"$ref": "#/definitions/flags.Repo"
},
"fork_count": {
"type": "integer"
},
"forked_from_repo": {
"description": "预留",
"allOf": [
{
"$ref": "#/definitions/dto.Slugs"
}
]
},
"freeze": {
"type": "boolean",
"readOnly": true
},
"id": {
"type": "string"
},
"language": {
"description": "仓库程序语言,预留",
"type": "string"
},
"languages": {
"description": "仓库语言",
"allOf": [
{
"$ref": "#/definitions/dto.RepoLanguage"
}
]
},
"last_update_nickname": {
"description": "最新代码更新人姓名",
"type": "string"
},
"last_update_username": {
"description": "最新代码更新人账户名",
"type": "string"
},
"last_updated_at": {
"description": "最新代码更新时间",
"allOf": [
{
"$ref": "#/definitions/convert.NullTime"
}
]
},
"license": {
"type": "string"
},
"mark_count": {
"type": "integer"
},
"name": {
"type": "string"
},
"open_issue_count": {
"description": "开启的issue数",
"type": "integer"
},
"open_pull_request_count": {
"description": "开启的pull request数",
"type": "integer"
},
"path": {
"description": "完整仓库路径",
"type": "string"
},
"pinned": {
"type": "boolean"
},
"pinned_time": {
"type": "string"
},
"second_languages": {
"description": "第二语言",
"allOf": [
{
"$ref": "#/definitions/dto.RepoLanguage"
}
]
},
"site": {
"type": "string"
},
"star_count": {
"type": "integer"
},
"star_time": {
"type": "string"
},
"stared": {
"type": "boolean"
},
"status": {
"allOf": [
{
"$ref": "#/definitions/constant.RepoStatus"
}
],
"readOnly": true
},
"tags": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
},
"topics": {
"type": "string"
},
"updated_at": {
"type": "string"
},
"visibility_level": {
"$ref": "#/definitions/constant.Visibility"
},
"web_url": {
"type": "string"
}
}
},
"dto.RepoStarUsers": {
"type": "object",
"properties": {
"my_follow_count": {
"type": "integer"
},
"total": {
"type": "integer"
},
"users": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.StarUser"
}
}
}
},
"constant.RepoDisplayModule": {
"type": "object",
"properties": {
"activity": {
"description": "仓库动态",
"type": "boolean"
},
"contributors": {
"description": "仓库贡献者",
"type": "boolean"
},
"release": {
"description": "仓库版本",
"type": "boolean"
}
}
},
"flags.Repo": {
"description": "仓库特性标记,内容为枚举的组合,英文逗号分割",
"type": "string",
"enum": [
"Unknown",
"KnowledgeBase"
],
"x-enum-comments": {
"KnowledgeBase": "KnowledgeBase 知识库",
"Unknown": "Unknown 未知"
},
"x-enum-descriptions": [
"Unknown 未知",
"KnowledgeBase 知识库"
],
"x-enum-varnames": [
"Unknown",
"KnowledgeBase"
]
},
"constant.Visibility": {
"type": "string",
"enum": [
"Private",
"Public",
"Secret"
],
"x-enum-comments": {
"VisibilityPrivate": "VisibilityPrivate 私有仓库 - 仓库的访问必须显式授予每个用户",
"VisibilityPublic": "VisibilityPublic 公共仓库 - 可以不经任何身份验证克隆该项目",
"VisibilitySecret": "VisibilitySecret 加密仓库 - 仓库特定角色身份才能读取解密"
},
"x-enum-descriptions": [
"VisibilityPrivate 私有仓库 - 仓库的访问必须显式授予每个用户",
"VisibilityPublic 公共仓库 - 可以不经任何身份验证克隆该项目",
"VisibilitySecret 加密仓库 - 仓库特定角色身份才能读取解密"
],
"x-enum-varnames": [
"VisibilityPrivate",
"VisibilityPublic",
"VisibilitySecret"
]
}
}
}

View File

@@ -0,0 +1,691 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/user": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Users"
],
"summary": "获取指定用户的详情信息。Get detailed information for a specified user.",
"operationId": "GetUserInfo",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.UsersResultForSelf"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-profile:r"
},
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Users"
],
"summary": "更新指定用户的详情信息。Updates the specified user's profile information.",
"operationId": "UpdateUserInfo",
"parameters": [
{
"description": "user info",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.UpdateUserInfoPayload"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-profile:rw"
}
},
"/user/autocomplete_source": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Users"
],
"summary": "查询当前用户用户拥有指定权限的所有资源列表。List resources that the current user has specified permissions for.",
"operationId": "AutoCompleteSource",
"parameters": [
{
"enum": [
"Group",
"Repo"
],
"type": "string",
"default": "Group",
"description": "Source type",
"name": "source_type",
"in": "query"
},
{
"type": "integer",
"default": 1,
"description": "Pagination page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "Pagination page size",
"name": "page_size",
"in": "query"
},
{
"type": "string",
"default": "",
"description": "Filter by resources.",
"name": "search",
"in": "query"
},
{
"enum": [
"Reporter",
"Developer",
"Master",
"Owner"
],
"type": "string",
"default": "Owner",
"description": "最小仓库权限默认owner。Minima repository permissions",
"name": "access",
"in": "query"
},
{
"enum": [
"created_at",
"slug_path"
],
"type": "string",
"default": "created_at",
"description": "Order field",
"name": "order_by",
"in": "query"
},
{
"type": "boolean",
"default": false,
"description": "排序顺序。Ordering.",
"name": "desc",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-engage:r"
}
},
"/user/gpg-keys": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Users"
],
"summary": "获取用户 GPG keys 列表。List GPG Keys.",
"operationId": "ListGPGKeys",
"parameters": [
{
"type": "integer",
"default": 1,
"description": "pagination page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "pagination page size",
"name": "page_size",
"in": "query"
},
{
"type": "string",
"description": "gpg search key",
"name": "keyword",
"in": "query"
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/api.GPGPublicKey"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/die.WebError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/die.WebError"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-profile:r"
}
},
"/users/{username}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Users"
],
"summary": "获取指定用户的详情信息。Get detailed information for a specified user.",
"operationId": "GetUserInfoByName",
"parameters": [
{
"type": "string",
"description": "User Name",
"name": "username",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.UsersResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-profile:r"
}
}
},
"definitions": {
"dto.UsersResultForSelf": {
"type": "object",
"properties": {
"address": {
"type": "string"
},
"appearance": {
"type": "string"
},
"appreciate_status": {
"description": "用户赞赏码状态0-无赞赏码1-有",
"type": "integer"
},
"avatar": {
"type": "string"
},
"bio": {
"type": "string"
},
"company": {
"type": "string"
},
"created_at": {
"type": "string"
},
"editable": {
"$ref": "#/definitions/constant.UserEditable"
},
"email": {
"type": "string"
},
"follow_count": {
"type": "integer"
},
"follow_mission_count": {
"type": "integer"
},
"follow_repo_count": {
"type": "integer"
},
"follower_count": {
"type": "integer"
},
"freeze": {
"type": "boolean"
},
"gender": {
"type": "integer"
},
"group_count": {
"type": "integer"
},
"id": {
"type": "string"
},
"language": {
"type": "string"
},
"last_login_at": {
"type": "string"
},
"last_login_ip": {
"type": "string"
},
"location": {
"type": "string"
},
"locked": {
"type": "boolean"
},
"mission_count": {
"type": "integer"
},
"next_updated_name_at": {
"type": "string"
},
"nickname": {
"type": "string"
},
"public_mission_count": {
"type": "integer"
},
"public_registry_count": {
"type": "integer"
},
"public_repo_count": {
"type": "integer"
},
"readme_repo_path": {
"type": "string",
"readOnly": true
},
"registry_count": {
"type": "integer"
},
"repo_count": {
"type": "integer"
},
"reward_amount": {
"type": "integer"
},
"reward_count": {
"type": "integer"
},
"site": {
"type": "string"
},
"stars_count": {
"type": "integer"
},
"type": {
"$ref": "#/definitions/constant.UserType"
},
"updated_name_at": {
"type": "string"
},
"updated_nick_at": {
"type": "string"
},
"username": {
"type": "string"
},
"verified": {
"description": "认证类型",
"type": "integer"
},
"verified_expire_in": {
"description": "认证过期时间",
"type": "string"
},
"wechat_mp": {
"type": "string"
},
"wechat_mp_qrcode": {
"type": "string"
}
}
},
"dto.UpdateUserInfoPayload": {
"type": "object",
"properties": {
"address": {
"type": "string"
},
"bio": {
"type": "string"
},
"company": {
"type": "string"
},
"location": {
"type": "string"
},
"name": {
"type": "string"
},
"nickname": {
"type": "string"
},
"readme_repo_id": {
"type": "integer"
},
"readme_repo_path": {
"type": "string"
},
"site": {
"type": "string"
},
"wechat_mp": {
"type": "string"
},
"wechat_mp_qrcode": {
"type": "string"
}
}
},
"api.GPGPublicKey": {
"type": "object",
"properties": {
"created_at": {
"description": "主密钥添加时间",
"type": "string"
},
"emails": {
"description": "邮箱",
"type": "array",
"items": {
"$ref": "#/definitions/api.GPGEmail"
}
},
"expired_at": {
"description": "主密钥过期时间",
"type": "string"
},
"id": {
"description": "主密钥ID",
"type": "string"
},
"key_id": {
"description": "公钥 KeyID",
"type": "string"
},
"name": {
"description": "标题",
"type": "string"
},
"raw_key": {
"description": "PGP公钥文本",
"type": "string"
},
"subkeys": {
"description": "子密钥",
"type": "array",
"items": {
"$ref": "#/definitions/api.GPGSubkey"
}
}
}
},
"die.WebError": {
"type": "object",
"properties": {
"errcode": {
"type": "integer"
},
"errmsg": {
"type": "string"
},
"errparam": {
"type": "object",
"additionalProperties": {}
}
}
},
"dto.UsersResult": {
"type": "object",
"properties": {
"address": {
"type": "string"
},
"appreciate_status": {
"description": "用户赞赏码状态0-无赞赏码1-有",
"type": "integer"
},
"avatar": {
"type": "string"
},
"bio": {
"type": "string"
},
"company": {
"type": "string"
},
"created_at": {
"type": "string"
},
"email": {
"type": "string"
},
"follow_count": {
"type": "integer"
},
"follow_mission_count": {
"type": "integer"
},
"follow_repo_count": {
"type": "integer"
},
"follower_count": {
"type": "integer"
},
"freeze": {
"type": "boolean"
},
"gender": {
"type": "integer"
},
"group_count": {
"type": "integer"
},
"id": {
"type": "string"
},
"is_following": {
"description": "查询人是否follow了此用户",
"type": "boolean"
},
"location": {
"type": "string"
},
"locked": {
"type": "boolean"
},
"mission_count": {
"type": "integer"
},
"nickname": {
"type": "string"
},
"public_mission_count": {
"type": "integer"
},
"public_registry_count": {
"type": "integer"
},
"public_repo_count": {
"type": "integer"
},
"readme_repo_path": {
"type": "string",
"readOnly": true
},
"registry_count": {
"type": "integer"
},
"repo_count": {
"type": "integer"
},
"reward_amount": {
"type": "integer"
},
"reward_count": {
"type": "integer"
},
"site": {
"type": "string"
},
"stars_count": {
"type": "integer"
},
"type": {
"$ref": "#/definitions/constant.UserType"
},
"username": {
"type": "string"
},
"verified": {
"description": "认证类型",
"type": "integer"
},
"verified_expire_in": {
"description": "认证过期时间",
"type": "string"
},
"wechat_mp": {
"type": "string"
},
"wechat_mp_qrcode": {
"type": "string"
}
}
},
"constant.UserEditable": {
"type": "object",
"properties": {
"avatar": {
"description": "可修改账户头像",
"type": "boolean"
},
"email": {
"description": "可修改主邮箱",
"type": "boolean"
},
"logoff": {
"description": "是否允许注销账户",
"type": "boolean"
},
"nickname": {
"description": "可修改昵称",
"type": "boolean"
},
"sync-data": {
"description": "是否允许同步昵称和头像",
"type": "boolean"
},
"username": {
"description": "可修改账户名",
"type": "boolean"
}
}
},
"constant.UserType": {
"type": "integer",
"format": "int32",
"enum": [
0,
1,
2,
3,
4
],
"x-enum-comments": {
"IoaUser": "IoaUser ioa 用户",
"OauthUser": "OauthUser oauth 授权用户",
"RobotUser": "RobotUser 助手用户",
"TestUser": "TestUser 测试用户",
"WeChatUser": "WeChatUser 微信用户"
},
"x-enum-descriptions": [
"WeChatUser 微信用户",
"OauthUser oauth 授权用户",
"TestUser 测试用户",
"RobotUser 助手用户",
"IoaUser ioa 用户"
],
"x-enum-varnames": [
"WeChatUser",
"OauthUser",
"TestUser",
"RobotUser",
"IoaUser"
]
}
}
}

View File

@@ -0,0 +1,427 @@
{
"swagger": "2.0",
"info": {
"title": "CNB OPENAPI",
"contact": {
"name": "Open API Support",
"url": "https://docs.cnb.cool/",
"email": "cnb@tencent.com"
},
"version": "1.0"
},
"paths": {
"/workspace/delete": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Workspace"
],
"summary": "删除我的云原生开发环境。Delete my workspace.",
"operationId": "DeleteWorkspace",
"parameters": [
{
"description": "params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.WorkspaceDeleteReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.WorkspaceDeleteResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-engage:rw"
}
},
"/workspace/list": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Workspace"
],
"summary": "获取我的云原生开发环境列表。List my workspaces.",
"operationId": "ListWorkspaces",
"parameters": [
{
"type": "string",
"description": "Git branch name, e.g. \"main\"",
"name": "branch",
"in": "query"
},
{
"type": "string",
"description": "查询结束时间。Query end time. format YYYY-MM-DD HH:mm:ssZZ, e.g. 2024-12-01 00:00:00+0800",
"name": "end",
"in": "query"
},
{
"type": "integer",
"description": "Pagination page number, default(1)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "Pagination page size, default(20), max(100)",
"name": "pageSize",
"in": "query"
},
{
"type": "string",
"description": "Repository path, e.g. \"groupname/reponame\"",
"name": "slug",
"in": "query"
},
{
"type": "string",
"description": "查询开始时间。Query start time. format YYYY-MM-DD HH:mm:ssZZ, e.g. 2024-12-01 00:00:00+0800",
"name": "start",
"in": "query"
},
{
"type": "string",
"description": "开发环境状态running: 开发环境已启动closed开发环境已关闭。Workspace status: \"running\" for started, \"closed\" for stopped.",
"name": "status",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.WorkspaceListResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-engage:r"
}
},
"/workspace/stop": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Workspace"
],
"summary": "停止/关闭我的云原生开发环境。Stop/close my workspace.",
"operationId": "WorkspaceStop",
"parameters": [
{
"description": "params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.WorkspaceStopReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.WorkspaceStopResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \naccount-engage:rw"
}
},
"/{repo}/-/workspace/detail/{sn}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Workspace"
],
"summary": "根据流水线sn查询云原生开发访问地址。Query cloud-native development access address by pipeline SN.",
"operationId": "GetWorkspaceDetail",
"parameters": [
{
"type": "string",
"description": "Repo path",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "SN",
"name": "sn",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.WorkspaceDetailResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-cnb-detail:r"
}
},
"/{repo}/-/workspace/start": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/vnd.cnb.api+json"
],
"tags": [
"Workspace"
],
"summary": "启动云原生开发环境已存在环境则直接打开否则重新创建开发环境。Start cloud-native dev. Opens existing env or creates a new one.",
"operationId": "StartWorkspace",
"parameters": [
{
"type": "string",
"description": "仓库完整路径",
"name": "repo",
"in": "path",
"required": true
},
{
"description": "StartWorkspace params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.StartWorkspaceReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.StartWorkspaceResult"
}
}
},
"description": "访问令牌调用此接口需包含以下权限。Required permissions for access token. \nrepo-cnb-trigger:rw"
}
}
},
"definitions": {
"dto.WorkspaceDeleteReq": {
"type": "object",
"properties": {
"pipelineId": {
"description": "表示要删除的开发环境流水线 idsn 和 pipelineId 二选一,优先使用 pipelineId",
"type": "string"
},
"sn": {
"description": "表示要删除的开发环境流水线构建号sn 和 pipelineId 二选一,优先使用 pipelineId",
"type": "string"
}
}
},
"dto.WorkspaceDeleteResult": {
"type": "object",
"properties": {
"code": {
"description": "返回码0 表示成功1 表示失败",
"type": "integer"
},
"message": {
"description": "描述",
"type": "string"
}
}
},
"dto.WorkspaceListResult": {
"type": "object",
"properties": {
"hasMore": {
"description": "是否有更多数据",
"type": "boolean"
},
"list": {
"description": "云原生开发环境列表",
"type": "array",
"items": {
"$ref": "#/definitions/dto.WorkspaceInfo"
}
},
"pageInfo": {
"description": "分页信息",
"allOf": [
{
"$ref": "#/definitions/dto.WorkspacePageInfo"
}
]
},
"total": {
"description": "总数",
"type": "integer"
}
}
},
"dto.WorkspaceStopReq": {
"type": "object",
"properties": {
"pipelineId": {
"description": "表示要停止的开发环境的流水线 idsn 和 pipelineId 二选一,优先使用 pipelineId",
"type": "string"
},
"sn": {
"description": "表示要停止的开发环境流水线构建号sn 和 pipelineId 二选一,优先使用 pipelineId",
"type": "string"
}
}
},
"dto.WorkspaceStopResult": {
"type": "object",
"properties": {
"buildLogUrl": {
"description": "表示停止的开发环境流水线日志地址",
"type": "string"
},
"message": {
"description": "表示操作结果提示信息",
"type": "string"
},
"sn": {
"description": "表示停止的开发环境流水线构建号",
"type": "string"
}
}
},
"dto.WorkspaceDetailResult": {
"type": "object",
"properties": {
"codebuddy": {
"description": "CodeBuddy 国际版客户端 remote-ssh 访问 schema 地址",
"type": "string"
},
"codebuddycn": {
"description": "CodeBuddy 国内版客户端 remote-ssh 访问 schema 地址",
"type": "string"
},
"cursor": {
"description": "Cursor 客户端 remote-ssh 访问 schema 地址",
"type": "string"
},
"jetbrains": {
"description": "jetbrains 系列 ide 的 jetbrains gateway 访问 schema 地址,环境内有安装 JetBrains 系列 ide 才会有",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"jumpUrl": {
"description": "选择入口页面 url",
"type": "string"
},
"remoteSsh": {
"description": "remote-ssh 连接地址",
"type": "string"
},
"ssh": {
"description": "ssh 登录命令",
"type": "string"
},
"vscode": {
"description": "VSCode 客户端 remote-ssh 访问 schema 地址",
"type": "string"
},
"vscode-insiders": {
"description": "Vscode 预览版客户端 remote-ssh 访问 schema 地址",
"type": "string"
},
"webide": {
"description": "WebIDE 访问 url",
"type": "string"
}
}
},
"dto.StartWorkspaceReq": {
"type": "object",
"properties": {
"branch": {
"description": "分支名或 tag 名例如main 或 v1.0.0。",
"type": "string"
},
"ref": {
"description": "Git ref例如refs/heads/main 或 refs/tags/v1.0.0。不传 ref 时默认基于分支启动",
"type": "string"
}
}
},
"dto.StartWorkspaceResult": {
"type": "object",
"properties": {
"buildLogUrl": {
"description": "仅新创建开发环境时返回,表示创建开发环境的流水线日志地址",
"type": "string"
},
"message": {
"description": "仅新创建开发环境时返回,表示创建开发环境的提示信息",
"type": "string"
},
"sn": {
"description": "仅新创建开发环境时返回,表示创建开发环境的流水线 sn",
"type": "string"
},
"url": {
"description": "如果存在开发环境,则返回 WebIDE 访问 url如果不存在开发环境则返回启动云原生开发的 loading 页面 url 地址",
"type": "string"
}
}
}
}
}

19212
docs/openapi/swagger-2.0.json Normal file

File diff suppressed because it is too large Load Diff

10
docs/skill/split/SKILL.md Normal file
View File

@@ -0,0 +1,10 @@
---
name: split-cnb-openapi
description: 拆分 OpenAPI 文档
---
使用 bun 执行 docs/split.ts 脚本,拆分 OpenAPI 文档为多个小文件。
```bash
bun docs/split.ts
```

105
docs/split.ts Normal file
View File

@@ -0,0 +1,105 @@
import * as fs from 'fs';
import * as path from 'path';
const swaggerPath = 'docs/openapi/swagger-2.0.json';
const outputDir = 'docs/api-groups';
// 读取 swagger JSON
const swagger = JSON.parse(fs.readFileSync(swaggerPath, 'utf8'));
// 收集所有 tag 及其对应的 API
const tagApis: Record<string, any> = {};
// 遍历所有路径和方法
for (const [apiPath, methods] of Object.entries(swagger.paths)) {
for (const [method, operation] of Object.entries(methods as Record<string, any>)) {
const tags = operation.tags || ['default'];
for (const tag of tags) {
if (!tagApis[tag]) {
tagApis[tag] = {};
}
if (!tagApis[tag][apiPath]) {
tagApis[tag][apiPath] = {};
}
tagApis[tag][apiPath][method] = operation;
}
}
}
// 收集每个 tag 需要的 definitions
function getUsedDefinitions(apis: Record<string, any>): Set<string> {
const usedDefs = new Set<string>();
const jsonStr = JSON.stringify(apis);
const refMatches = jsonStr.match(/#\/definitions\/[^"]+/g) || [];
for (const ref of refMatches) {
const defName = ref.replace('#/definitions/', '');
usedDefs.add(defName);
}
return usedDefs;
}
// 递归收集依赖的 definitions
function getAllRequiredDefinitions(startDefs: Set<string>, allDefs: Record<string, any>): Set<string> {
const result = new Set<string>(startDefs);
let added = true;
while (added) {
added = false;
for (const defName of Array.from(result)) {
const def = allDefs[defName];
if (def && def.properties) {
for (const prop of Object.values(def.properties) as any[]) {
if (prop.$ref) {
const refName = prop.$ref.replace('#/definitions/', '');
if (!result.has(refName)) {
result.add(refName);
added = true;
}
}
}
}
// 检查数组类型的 items
if (def && def.items && def.items.$ref) {
const refName = def.items.$ref.replace('#/definitions/', '');
if (!result.has(refName)) {
result.add(refName);
added = true;
}
}
}
}
return result;
}
// 为每个 tag 创建输出目录和文件
for (const [tag, apis] of Object.entries(tagApis)) {
const tagDir = path.join(outputDir, tag.toLowerCase());
if (!fs.existsSync(tagDir)) {
fs.mkdirSync(tagDir, { recursive: true });
}
// 收集使用的 definitions
const usedDefs = getUsedDefinitions(apis);
const allRequiredDefs = getAllRequiredDefinitions(usedDefs, swagger.definitions || {});
// 构建输出的 swagger 片段
const outputSwagger: any = {
swagger: swagger.swagger,
info: swagger.info,
paths: apis,
definitions: {}
};
// 只包含需要的 definitions
for (const defName of allRequiredDefs) {
if (swagger.definitions && swagger.definitions[defName]) {
outputSwagger.definitions[defName] = swagger.definitions[defName];
}
}
// 写入文件
const outputPath = path.join(tagDir, 'api.json');
fs.writeFileSync(outputPath, JSON.stringify(outputSwagger, null, 2));
console.log(`Created: ${outputPath}`);
}
console.log(`\nTotal tags processed: ${Object.keys(tagApis).length}`);

View File

@@ -1,47 +0,0 @@
{
"permissions": {
"allow": [
"Read(*)",
"Write(*)",
"Edit(*)",
"Glob(*)",
"Grep(*)",
"Bash(node:*)",
"Bash(npm:*)",
"Bash(npx:*)",
"Bash(pnpm:*)",
"Bash(deno:*)",
"Bash(bun:*)",
"Bash(kubectl:*)",
"Bash(git:*)",
"Bash(git:*:*)",
"Bash(python:*)",
"Bash(pip:*)",
"Bash(mkdir:*)",
"Bash(rm:*)",
"Bash(cp:*)",
"Bash(mv:*)",
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(rm:*)",
"Bash(du:*)",
"Bash(df:*)",
"Bash(pwd:*)",
"Bash(whoami:*)",
"Bash(test:*)",
"Bash(echo:*)",
"Bash(timeout:*:*)",
"Bash(touch:*)",
"Bash(file:*)",
"Bash(type:*)",
"Bash(ev:*)",
"Bash(opencode:*)"
],
"deny": [],
"ask": [
"Bash(rm:-rf)",
"Bash(rm:-R)",
"Bash(rm:-r)"
]
}
}

View File

@@ -38,6 +38,19 @@
"baseURL": "https://api.minimaxi.com/anthropic/v1",
"apiKey": "{env:MINIMAX_API_KEY}"
}
}
},
"custom-doubao": {
"npm": "@ai-sdk/openai-compatible",
"name": "国内火山AI",
"models": {
"ark-code-latest": {
"name": "ark-code-latest"
}
},
"options": {
"baseURL": "https://ark.cn-beijing.volces.com/api/coding/v3",
"apiKey": "{env:DOUBAO_API_KEY}"
}
},
}
}

View File

@@ -1,37 +1,55 @@
{
"name": "@kevisual/cnb",
"version": "0.0.1",
"version": "0.0.13",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"build": "bun run bun.config.ts"
},
"keywords": [],
"files": [
"dist",
"src",
"mod.ts",
"agent"
],
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
"license": "MIT",
"packageManager": "pnpm@10.28.0",
"packageManager": "pnpm@10.28.2",
"type": "module",
"devDependencies": {
"@kevisual/ai": "^0.0.24",
"@kevisual/context": "^0.0.4",
"@kevisual/types": "^0.0.10",
"@opencode-ai/plugin": "^1.1.13",
"@types/bun": "^1.3.5",
"@types/node": "^25.0.6",
"@kevisual/types": "^0.0.12",
"@opencode-ai/plugin": "^1.1.44",
"@types/bun": "^1.3.8",
"@types/node": "^25.1.0",
"@types/ws": "^8.18.1",
"dayjs": "^1.11.19",
"dotenv": "^17.2.3"
},
"publishConfig": {
"access": "public"
},
"resolutions": {
"zod": "^4.3.6"
},
"dependencies": {
"@kevisual/query": "^0.0.35",
"@kevisual/router": "^0.0.52",
"@kevisual/use-config": "^1.0.24",
"es-toolkit": "^1.43.0",
"nanoid": "^5.1.6"
"@kevisual/query": "^0.0.38",
"@kevisual/router": "^0.0.64",
"@kevisual/use-config": "^1.0.28",
"es-toolkit": "^1.44.0",
"nanoid": "^5.1.6",
"unstorage": "^1.17.4",
"ws": "npm:@kevisual/ws",
"zod": "^4.3.6"
},
"exports": {
".": "./mod.ts",
"./opencode": "./dist/opencode.js",
"./keep": "./dist/keep.js",
"./routes": "./dist/routes.js",
"./src/*": "./src/*",
"./agent/*": "./agent/*"
}
}

1113
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
对/agents 下的代码进行修改
需求1
创建仓库, 需要仓库名字
1. 自动添加.cnb.yml, 对.cnb.yml的内容进行修改。修改TO_REPO对应的内容 TO_REPO是“group+repo”需要传入仓库名字默认group为kevisual
2. 自动添加opencode.json
需求2
获取当前启动的云开发环境。
输出对应的请求的地址token等信息
输出打开云开发的vscodebuddycursortraeqcoder等工具的地址
需求3

View File

@@ -1,14 +0,0 @@
---
name: create-new-repo
description: 创建一个新的代码仓库,并自动添加必要的配置文件。
---
# 创建新的代码仓库
该技能用于创建一个新的代码仓库,并自动添加必要的配置文件,如 `.cnb.yml``opencode.json`
## 调用工具链
1. 执行`create-repo`工具
2. 判断是否需要立刻需要云开发打开
3. 如果需要,执行`open-cloud-editor`工具
4. 返回创建的仓库信息和云开发环境信息(如果适用)

View File

@@ -69,12 +69,12 @@ export class CNBCore {
headers: _headers,
body: _body,
});
const res = (data: any, message?: string) => {
const res = (data: any, message?: string, code?: number) => {
if (useOrigin) {
return data;
}
return {
code: 200,
code: code ?? 200,
message: message || 'success',
data,
};
@@ -83,7 +83,7 @@ export class CNBCore {
const errorText = await response.text();
if (useOrigin)
throw new Error(`Request failed with status ${response.status}: ${errorText}`);
return res(null, `Request failed with status ${response.status}: ${errorText}`);
return res(null, `Request failed with status ${response.status}: ${errorText}`, response.status);
}
const contentType = response.headers.get('Content-Type');

57
src/common/cnb-env.ts Normal file
View File

@@ -0,0 +1,57 @@
/**
* CNB 环境变量配置
* 该模块定义了 CNB (Cloud Native Build) 平台提供的所有环境变量
* 用于获取当前构建环境、仓库信息、运行器配置等元数据
*/
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'),
// 构建相关配置
/** 流水线 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例如'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'),
// 运行器资源配置
/** 运行器分配的 CPU 核心数,单位为核, 例如: "8"*/
CNB_CPUS: useKey('CNB_CPUS'),
/** 运行器分配的内存大小,单位为 GB 例如: "16"*/
CNB_MEMORY: useKey('CNB_MEMORY'),
/** 运行器的 IP 地址,用于网络连接和调试 */
CNB_RUNNER_IP: useKey('CNB_RUNNER_IP'),
}

View File

@@ -1,5 +1,5 @@
import { CNBCore, CNBCoreOptions } from "./cnb-core.ts";
import { Workspace } from "./workspace.ts";
import { Workspace } from "./workspace/index.ts";
import { KnowledgeBase } from "./knowledge/index.ts";
import { Repo } from "./repo/index.ts";
import { User } from "./user/index.ts";
@@ -9,7 +9,6 @@ import { Mission } from "./mission/index.ts";
import { AiBase } from "./ai/index.ts";
type CNBOptions = CNBCoreOptions<{
group?: string;
}>;
export class CNB extends CNBCore {
@@ -21,7 +20,6 @@ export class CNB extends CNBCore {
issue!: Issue;
mission!: Mission;
ai!: AiBase;
group!: string;
constructor(options: CNBOptions) {
super({ token: options.token, cookie: options.cookie, cnb: options.cnb });
this.init(options);
@@ -31,7 +29,6 @@ export class CNB extends CNBCore {
const cookie = this.cookie;
const options = { token, cookie };
this.workspace = new Workspace(options.token);
const group = cnbOptions?.group || '';
this.knowledgeBase = new KnowledgeBase({ token: options.token, cookie: options.cookie });
this.repo = new Repo({ token: options.token, cookie: options.cookie });
this.user = new User({ token: options.token, cookie: options.cookie });
@@ -39,10 +36,6 @@ export class CNB extends CNBCore {
this.issue = new Issue({ token: options.token, cookie: options.cookie });
this.mission = new Mission({ token: options.token, cookie: options.cookie });
this.ai = new AiBase({ token: options.token, cookie: options.cookie });
this.group = group;
}
setGroup(group: string) {
this.group = group;
}
setToken(token: string) {
this.token = token;
@@ -64,7 +57,7 @@ export class CNB extends CNBCore {
}
}
export * from './workspace.ts'
export * from './workspace/index.ts'
export * from './cnb-core.ts'
export * from './knowledge/index.ts'
export * from './repo/index.ts'

View File

@@ -26,6 +26,7 @@ export type IssueItem = {
author: IssueAuthor;
labels: IssueLabel[];
body: string;
last_acted_at: string;
number: string;
priority: string;

1
src/keep.ts Normal file
View File

@@ -0,0 +1 @@
export * from './workspace/keep-live.ts';

View File

@@ -10,7 +10,7 @@ export class KnowledgeBase extends CNBCore {
score_threshold?: number,
top_k?: number,
metadata_filtering_conditions?: MetadataFilteringConditions
}): Promise<any> {
}): Promise<Result<QueryRag[]>> {
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;
}
}

View File

@@ -6,27 +6,32 @@ export class Repo extends CNBCore {
}
/**
* 创建代码仓库
* @param group e.g. my-group
* @param data
* @returns
*/
createRepo(group: string, data: CreateRepoData): Promise<any> {
createRepo(data: CreateRepoData): Promise<any> {
const name = data.name;
const [group, repo] = name.includes('/') ? name.split('/') : ['', name];
const url = `/${group}/-/repos`;
let postData: CreateRepoData = {
...data,
description: data.description || '',
name: data.name,
name: repo,
license: data.license || 'Unlicense',
visibility: data.visibility || 'private',
};
return this.post({ url, data: postData });
}
deleteRepo(name: string): Promise<any> {
const url = `https://cnb.cool/${name}`;
return this.delete({ url, useCookie: true });
}
async createCommit(repo: string, data: CreateCommitData): Promise<any> {
const commitList = await this.getCommitList(repo, {
page: 1,
page_size: 1,
}, { useOrigin: true }).catch((err) => {
console.error("Error fetching commit list:", err);
// console.error("Error fetching commit list:", err);
return []
});
const preCommitSha = commitList.length > 0 ? commitList[0].sha : undefined;
@@ -46,7 +51,14 @@ export class Repo extends CNBCore {
delete postData.parent_commit_sha;
delete postData.base_branch;
}
return this.post({ url, data: postData, useCookie: true, });
return this.post({
url,
data: postData,
useCookie: true,
headers: {
'Accept': 'application/vnd.cnb.web+json',
}
});
}
createBlobs(repo: string, data: { content: string, encoding?: 'utf-8' | 'base64' }): Promise<any> {
const url = `/${repo}/-/git/blobs`;
@@ -77,10 +89,15 @@ export class Repo extends CNBCore {
}): Promise<Result<RepoItem[]>> {
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 });
}
}

View File

@@ -6,7 +6,7 @@
*/
import { Result } from "@kevisual/query/query";
import { CNBCore } from "./cnb-core.ts";
import { CNBCore } from "../cnb-core.ts";
/**
*
@@ -54,7 +54,7 @@ export class Workspace extends CNBCore {
return this.post({ url: '/workspace/delete', data });
}
/** 获取我的云原生开发环境列表 */
async list(params?: ListParams): Promise<WorkspaceResult> {
async list(params?: ListParams): Promise<Result<WorkspaceResult>> {
return this.get({ url: '/workspace/list', params });
}

191
src/workspace/keep-live.ts Normal file
View File

@@ -0,0 +1,191 @@
// WebSocket Keep-Alive Client Library
import WebSocket from "ws";
export interface KeepAliveConfig {
wsUrl: string;
cookie: string;
reconnectInterval?: number;
maxReconnectAttempts?: number;
pingInterval?: number;
onMessage?: (data: Buffer | string) => void;
onConnect?: () => void;
onDisconnect?: (code: number) => void;
onError?: (error: Error) => void;
onSign?: (data: { type: string; data: string; signedData: string }) => void;
onExit?: (code?: number) => void;
debug?: boolean;
}
export interface ParsedMessage {
type: string;
raw: Buffer;
payload?: any;
}
type MessageHandler = (msg: ParsedMessage) => void;
export class WSKeepAlive {
private ws: WebSocket | null = null;
private config: Required<KeepAliveConfig>;
private reconnectAttempts = 0;
private pingTimer: NodeJS.Timeout | null = null;
private messageHandlers: Set<MessageHandler> = new Set();
private url: URL;
constructor(config: KeepAliveConfig) {
this.config = {
wsUrl: config.wsUrl,
cookie: config.cookie,
reconnectInterval: config.reconnectInterval ?? 5000,
maxReconnectAttempts: config.maxReconnectAttempts ?? 3,
pingInterval: config.pingInterval ?? 30000,
onMessage: config.onMessage ?? (() => { }),
onConnect: config.onConnect ?? (() => { }),
onDisconnect: config.onDisconnect ?? (() => { }),
onError: config.onError ?? (() => { }),
onSign: config.onSign ?? (() => { }),
onExit: config.onExit ?? (() => { }),
debug: config.debug ?? false,
};
this.url = new URL(this.config.wsUrl);
}
private log(message: string) {
if (!this.config.debug) return;
const timestamp = new Date().toISOString();
const msg = `[${timestamp}] ${message}`;
console.log(msg);
}
private parseMessage(data: Buffer): ParsedMessage | null {
const result: ParsedMessage = { type: "unknown", raw: data };
if (data.length < 14) {
result.type = "raw";
return result;
}
const prefix = data.slice(0, 13);
const msgType = prefix[0];
const jsonStart = data.indexOf(0x71); // 0x71 = 'q'
if (jsonStart !== -1) {
try {
const jsonStr = data.slice(jsonStart + 1).toString();
const payload = JSON.parse(jsonStr);
result.type = `binary(0x${msgType.toString(16)})`;
result.payload = payload;
// 特殊处理 sign 类型
if (payload.type === "sign" && this.config.onSign) {
this.config.onSign(payload);
}
return result;
} catch {
result.type = "binary(json-parse-error)";
return result;
}
}
result.type = "raw";
return result;
}
connect() {
const { wsUrl, cookie, debug } = this.config;
this.log(`Connecting to ${wsUrl}...`);
this.ws = new WebSocket(wsUrl, {
headers: {
"Origin": this.url.origin,
"Cookie": cookie,
"Cache-Control": "no-cache",
"Accept-Language": "zh-CN,zh;q=0.9",
"Pragma": "no-cache",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Sec-WebSocket-Extensions": "permessage-deflate",
}
});
this.ws.on("open", () => {
debug && this.log("Connected!");
this.reconnectAttempts = 0;
this.config.onConnect();
this.startPing();
});
this.ws.on("message", (data: any) => {
if (Buffer.isBuffer(data)) {
const parsed = this.parseMessage(data);
this.config.onMessage(parsed?.raw ?? data);
this.messageHandlers.forEach(handler => {
if (parsed) handler(parsed);
});
} else {
this.config.onMessage(data);
}
});
this.ws.on("close", (code: number) => {
debug && this.log(`Disconnected (code: ${code})`);
this.stopPing();
this.config.onDisconnect(code);
this.handleReconnect();
});
this.ws.on("error", (err: Error) => {
debug && this.log(`Error: ${err.message}`);
this.config.onError(err);
});
}
private startPing() {
this.stopPing();
this.pingTimer = setInterval(() => {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.ping();
this.log("Sent ping");
}
}, this.config.pingInterval);
}
private stopPing() {
if (this.pingTimer) {
clearInterval(this.pingTimer);
this.pingTimer = null;
}
}
private handleReconnect() {
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
this.log(`Max reconnect attempts (${this.config.maxReconnectAttempts}) reached. Giving up.`);
this.config.onExit(1);
return;
}
this.reconnectAttempts++;
this.log(`Reconnecting in ${this.config.reconnectInterval}ms... (attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts})`);
setTimeout(() => this.connect(), this.config.reconnectInterval);
}
onMessage(handler: MessageHandler) {
this.messageHandlers.add(handler);
return () => this.messageHandlers.delete(handler);
}
disconnect() {
this.stopPing();
if (this.ws) {
this.ws.close();
this.config.onExit(0);
this.ws = null;
}
}
}
// 便捷函数:快速创建并启动
export function createKeepAlive(config: KeepAliveConfig): WSKeepAlive {
const client = new WSKeepAlive(config);
client.connect();
return client;
}

View File

@@ -0,0 +1,13 @@
import { createKeepAlive } from '@kevisual/cnb/keep';
const wsUrl = process.argv[2];
const cookie = process.argv[3];
createKeepAlive({
wsUrl,
cookie,
onConnect: () => console.log('已连接'),
debug: true
});
process.on('SIGINT', () => process.exit(0));

37
test/agent.ts Normal file
View File

@@ -0,0 +1,37 @@
import { app, showMore } from './common.ts';
// const res = await app.run({
// path: 'cnb',
// key: 'create-repo',
// payload: {
// name: 'kevisual/exam',
// description: 'kevisual 创建的代码仓库',
// visibility: 'public',
// }
// })
// console.log(showMore(res));
// const res2 = await app.run({
// path: 'cnb',
// key: 'create-repo-file',
// payload: {
// name: 'kevisual/exam',
// path: 'README.md',
// content: '# Example Skill\nThis is an example skill created via API.',
// encoding: 'raw',
// },
// })
// console.log(showMore(res2));
// const deleteRes = await app.run({
// path: 'cnb',
// key: 'delete-repo',
// payload: {
// name: 'kevisual/exam',
// },
// })
// console.log(showMore(deleteRes));

View File

@@ -15,4 +15,4 @@ const main = async () => {
console.log("build", build);
}
main()
// main()

View File

@@ -1,16 +1,21 @@
import { CNB } from "../src";
import dotenv from "dotenv";
import util from 'node:util';
dotenv.config();
export const token = process.env.CNB_TOKEN || "";
export const cookie = process.env.CNB_COOKIE || "";
import { useConfig, useKey } from "@kevisual/use-config";
const config = useConfig()
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'
export { app }
export const cnb = new CNB({ token, cookie });
export const showMore = (obj: any) => {
return util.inspect(obj, { showHidden: false, depth: null, colors: true });
}
// const worksaceList = await cnb.workspace.list({ status: 'running' });
// console.log("worksaceList", worksaceList);
// console.log("worksaceList", showMore(worksaceList));
// const sn = 'cnb-o18-1jbklfuoh'

View File

@@ -1,9 +1,11 @@
import { Repo } from "../src/repo";
import { app } from '../agent/index.ts'
import { token, showMore, cookie } from "./common.ts";
import util from 'node:util';
const repo = new Repo({ token: token, cookie: cookie });
export { app }
// const res = await repo.createRepo({
// name: "test-cnb-2",
// description: "This is my new repository",

13
test/keep-test.ts Normal file
View File

@@ -0,0 +1,13 @@
import { createKeepAlive } from "@kevisual/cnb/keep";
const config = {
"wss": "wss://cnb-dk4-1jgcjjqvc-001.cnb.space:443/stable-3c0b449c6e6e37b44a8a7938c0d8a3049926a64c?reconnectionToken=d70ab69b-5e92-471a-b3d2-31f554b468d4&reconnection=false&skipWebSocketFrames=false",
"cookie": "orange:workspace:cookie-session:cnb-dk4-1jgcjjqvc-001=01fea6db-d73f-4ce8-8929-36903ee7a266",
"url": "https://cnb-dk4-1jgcjjqvc-001.cnb.space/?folder=/workspace"
}
createKeepAlive({
wsUrl: config.wss,
cookie: config.cookie,
debug: true,
});

20
test/keep.ts Normal file
View File

@@ -0,0 +1,20 @@
// WebSocket Keep-Alive Client with node+ws
// import { createKeepAlive } from "../src/keep.ts";
import { createKeepAlive } from "../dist/keep.js";
const WS_URL = "wss://cnb-l6o-1jg7aoevl-001.cnb.space/stable-3c0b449c6e6e37b44a8a7938c0d8a3049926a64c?reconnectionToken=a6517530-9911-406b-a65f-0d9d4b3f0d6f&reconnection=false&skipWebSocketFrames=false";
const COOKIE = "orange:workspace:cookie-session:cnb-l6o-1jg7aoevl-001=1ba3d696-1805-4c6b-b109-222738be570f";
// 使用库创建客户端
const client = createKeepAlive({
wsUrl: WS_URL,
cookie: COOKIE,
debug: true,
});
// 监听解析后的消息
client.onMessage((msg) => {
console.log(`[Received] ${msg.raw.length} bytes`);
});
console.log("开始激活 WebSocket 连接...");

View File

@@ -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);

View File

@@ -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: 10 });
const listRes = await repo.getRepoList({
page: 1, page_size: 999, role: 'developer',
flags: 'KnowledgeBase'
});
console.log("listRes", showMore(listRes));
console.log("listRes", showMore(listRes), listRes.data?.length);

View File

@@ -3,6 +3,7 @@
"compilerOptions": {
"module": "NodeNext",
"target": "esnext",
"rootDir": ".",
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types",
@@ -19,6 +20,7 @@
},
"include": [
"src/**/*",
"agent/**/*",
"mod.ts",
"agent/**/*"
],
}