feat: 重构CNB管理模块,添加清理记录功能,更新中间件为统一认证方式,优化工作空间相关路由
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,5 +4,6 @@ node_modules
|
|||||||
.pnpm-store
|
.pnpm-store
|
||||||
|
|
||||||
dist
|
dist
|
||||||
|
pack-dist
|
||||||
|
|
||||||
storage
|
storage
|
||||||
40
agent/app.ts
40
agent/app.ts
@@ -1,17 +1,35 @@
|
|||||||
import { QueryRouterServer as App } from '@kevisual/router'
|
import { QueryRouterServer as App } from '@kevisual/router'
|
||||||
import { useContextKey } from '@kevisual/context'
|
import { useContextKey } from '@kevisual/context'
|
||||||
import { useConfig, useKey } from '@kevisual/use-config'
|
import { useKey } from '@kevisual/use-config'
|
||||||
import { CNB } from '../src/index.ts';
|
import { CNB } from '../src/index.ts';
|
||||||
|
import { CNBManager } from './modules/cnb-manager.ts'
|
||||||
|
export const cnbManager = new CNBManager()
|
||||||
|
|
||||||
export const config = useConfig()
|
// CNB_TOKEN是降级兼容变量,推荐使用CNB_API_KEY
|
||||||
export const cnb = useContextKey<CNB>('cnb', () => {
|
// CNB_TOKEN 是流水线自己就有的变量,但是权限比较小
|
||||||
// CNB_TOKEN是降级兼容变量,推荐使用CNB_API_KEY
|
const token = useKey('CNB_API_KEY') as string || useKey('CNB_TOKEN') as string
|
||||||
// CNB_TOKEN 是流水线自己就有的变量,但是权限比较小
|
// cookie 变量是可选的
|
||||||
const token = useKey('CNB_API_KEY') as string || useKey('CNB_TOKEN') as string
|
const cookie = useKey('CNB_COOKIE') as string
|
||||||
// cookie 变量是可选的
|
try {
|
||||||
const cookie = useKey('CNB_COOKIE') as string
|
cnbManager.addCNB({
|
||||||
return new CNB({ token: token, cookie: cookie });
|
username: 'default',
|
||||||
})
|
token: token,
|
||||||
export const app = useContextKey<App>('app', () => {
|
cookie: cookie,
|
||||||
|
cnb: new CNB({ token: token, cookie: cookie })
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
export const cnb = (await cnbManager.getCNB({ username: 'default' })).cnb
|
||||||
|
export const app = await useContextKey<App>('app', () => {
|
||||||
return new App({})
|
return new App({})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const notCNBCheck = (ctx: any) => {
|
||||||
|
const isCNB = useKey('CNB');
|
||||||
|
if (!isCNB) {
|
||||||
|
ctx.throw(400, '当前环境非 cnb-board 环境,无法获取 live 内容');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
18
agent/main.ts
Normal file
18
agent/main.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// import { RemoteApp } from '@kevisual/remote-app';
|
||||||
|
import { app } from './index.ts'
|
||||||
|
// import { QueryLoginNode } from '@kevisual/api/login-node';
|
||||||
|
// const queryLoginNode = new QueryLoginNode({});
|
||||||
|
// await queryLoginNode.init()
|
||||||
|
// const token = await queryLoginNode.getToken();
|
||||||
|
// app.createRouteList()
|
||||||
|
// const remoteApp = new RemoteApp({
|
||||||
|
// id: 'cnb-agent',
|
||||||
|
// token: token,
|
||||||
|
// url: 'https://kevisual.cn/ws/proxy',
|
||||||
|
// app: app as any,
|
||||||
|
// })
|
||||||
|
// const isConnected = await remoteApp.isConnect();
|
||||||
|
// if (isConnected) {
|
||||||
|
// console.log('Remote app connected successfully');
|
||||||
|
// remoteApp.listenProxy();
|
||||||
|
// }
|
||||||
118
agent/modules/cnb-manager.ts
Normal file
118
agent/modules/cnb-manager.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { Result } from '@kevisual/query';
|
||||||
|
import { CNB } from '../../src/index.ts';
|
||||||
|
export const getConfig = async (opts: { token?: string }) => {
|
||||||
|
const res = await fetch('https://kevisual.cn/api/router', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
path: 'config',
|
||||||
|
key: 'get',
|
||||||
|
data: {
|
||||||
|
key: "cnb_center_config.json"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${opts.token!}`
|
||||||
|
},
|
||||||
|
}).then(res => res.json());
|
||||||
|
return res as Result<{
|
||||||
|
id: string, key: 'cnb_center_config.json', data: {
|
||||||
|
CNB_API_KEY: string,
|
||||||
|
CNB_COOKIE: string
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
type CNBItem = {
|
||||||
|
username: string,
|
||||||
|
token: string,
|
||||||
|
cookie?: string
|
||||||
|
runAt?: number
|
||||||
|
owner?: boolean
|
||||||
|
cnb: CNB
|
||||||
|
}
|
||||||
|
export class CNBManager {
|
||||||
|
cnbMap: Map<string, CNBItem> = new Map()
|
||||||
|
constructor() {
|
||||||
|
setInterval(() => {
|
||||||
|
this.clearExpiredCNB()
|
||||||
|
}, 1000 * 60 * 30) // 每30分钟清理一次过期的 CNB 实例
|
||||||
|
}
|
||||||
|
getDefaultCNB() {
|
||||||
|
const cnbItem = this.cnbMap.get('default')
|
||||||
|
if (!cnbItem) {
|
||||||
|
throw new Error('Default CNB not found')
|
||||||
|
}
|
||||||
|
return cnbItem
|
||||||
|
}
|
||||||
|
async getCNB(opts?: { username?: string, kevisualToken?: string }): Promise<CNBItem | null> {
|
||||||
|
if (opts?.username) {
|
||||||
|
return this.getDefaultCNB()
|
||||||
|
}
|
||||||
|
const username = opts?.kevisualToken
|
||||||
|
const cnbItem = this.cnbMap.get(username)
|
||||||
|
if (cnbItem) {
|
||||||
|
cnbItem.runAt = Date.now()
|
||||||
|
return cnbItem
|
||||||
|
}
|
||||||
|
const res = await getConfig({ token: opts?.kevisualToken })
|
||||||
|
if (res.code === 200) {
|
||||||
|
const cookie = res.data?.data?.CNB_COOKIE
|
||||||
|
const token = res.data?.data?.CNB_API_KEY
|
||||||
|
if (token) {
|
||||||
|
return this.addCNB({ username, token, cookie })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 通过上下文获取 CNB 实例(直接返回 cnb 对象)
|
||||||
|
* @param ctx
|
||||||
|
* @returns CNB 实例
|
||||||
|
*/
|
||||||
|
async getContext(ctx: any) {
|
||||||
|
const tokenUser = ctx?.state?.tokenUser
|
||||||
|
const username = tokenUser?.username
|
||||||
|
if (!username) {
|
||||||
|
ctx.throw(403, 'Unauthorized')
|
||||||
|
}
|
||||||
|
if (username === 'default') {
|
||||||
|
return this.getDefaultCNB().cnb
|
||||||
|
}
|
||||||
|
const kevisualToken = ctx.query?.token;
|
||||||
|
const item = await this.getCNB({ username, kevisualToken });
|
||||||
|
if (!item) {
|
||||||
|
ctx.throw(400, '不存在的 CNB 配置项,请检查 登录 Token 是否正确,或添加 CNB 配置')
|
||||||
|
}
|
||||||
|
return item.cnb
|
||||||
|
}
|
||||||
|
addCNB(opts: Partial<CNBItem>) {
|
||||||
|
if (!opts.username || !opts.token) {
|
||||||
|
throw new Error('username and token are required')
|
||||||
|
}
|
||||||
|
const exist = this.cnbMap.get(opts.username)
|
||||||
|
if (exist) {
|
||||||
|
exist.runAt = Date.now()
|
||||||
|
return exist
|
||||||
|
}
|
||||||
|
const cnb = opts?.cnb || new CNB({ token: opts.token, cookie: opts.cookie });
|
||||||
|
opts.cnb = cnb;
|
||||||
|
opts.runAt = Date.now()
|
||||||
|
this.cnbMap.set(opts.username, opts as CNBItem)
|
||||||
|
return opts as CNBItem
|
||||||
|
}
|
||||||
|
// 定期清理过期的 CNB 实例,默认过期时间为 1 小时
|
||||||
|
clearExpiredCNB(expireTime = 1000 * 60 * 60) {
|
||||||
|
const now = Date.now()
|
||||||
|
for (const [username, item] of this.cnbMap.entries()) {
|
||||||
|
if (username === 'default') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (item.runAt && now - item.runAt > expireTime) {
|
||||||
|
this.cnbMap.delete(username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearUsername(username: string) {
|
||||||
|
this.cnbMap.delete(username)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,5 +2,5 @@ import { app } from './index.ts';
|
|||||||
import { createRouterAgentPluginFn } from '@kevisual/router/opencode'
|
import { createRouterAgentPluginFn } from '@kevisual/router/opencode'
|
||||||
|
|
||||||
export const CnbPlugin = createRouterAgentPluginFn({
|
export const CnbPlugin = createRouterAgentPluginFn({
|
||||||
router: app,
|
router: app as any,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,23 +1,13 @@
|
|||||||
import { app } from '../../app.ts';
|
import { app, notCNBCheck } from '../../app.ts';
|
||||||
import { useKey } from '@kevisual/context'
|
import { useKey } from '@kevisual/context'
|
||||||
import { getLiveMdContent } from './live/live-content.ts';
|
import { getLiveMdContent } from './live/live-content.ts';
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
const notCNBCheck = (ctx: any) => {
|
|
||||||
const isCNB = useKey('CNB');
|
|
||||||
if (!isCNB) {
|
|
||||||
ctx.body = {
|
|
||||||
title: '非 cnb-board 环境',
|
|
||||||
list: []
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
app.route({
|
app.route({
|
||||||
path: 'cnb_board',
|
path: 'cnb_board',
|
||||||
key: 'live',
|
key: 'live',
|
||||||
description: '获取cnb-board live的mdContent内容',
|
description: '获取cnb-board live的mdContent内容',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
args: {
|
args: {
|
||||||
more: z.boolean().optional().describe('是否获取更多系统信息,默认false'),
|
more: z.boolean().optional().describe('是否获取更多系统信息,默认false'),
|
||||||
@@ -37,7 +27,7 @@ app.route({
|
|||||||
path: 'cnb_board',
|
path: 'cnb_board',
|
||||||
key: 'live_repo_info',
|
key: 'live_repo_info',
|
||||||
description: '获取cnb-board live的repo信息',
|
description: '获取cnb-board live的repo信息',
|
||||||
middleware: ['auth-admin']
|
middleware: ['auth']
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
const repoSlug = useKey('CNB_REPO_SLUG') || '';
|
const repoSlug = useKey('CNB_REPO_SLUG') || '';
|
||||||
const repoName = useKey('CNB_REPO_NAME') || '';
|
const repoName = useKey('CNB_REPO_NAME') || '';
|
||||||
@@ -90,7 +80,7 @@ app.route({
|
|||||||
path: 'cnb_board',
|
path: 'cnb_board',
|
||||||
key: 'live_build_info',
|
key: 'live_build_info',
|
||||||
description: '获取cnb-board live的构建信息',
|
description: '获取cnb-board live的构建信息',
|
||||||
middleware: ['auth-admin']
|
middleware: ['auth']
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
if (notCNBCheck(ctx)) return;
|
if (notCNBCheck(ctx)) return;
|
||||||
const labels = [
|
const labels = [
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { app } from '../../app.ts';
|
import { app, notCNBCheck } from '../../app.ts';
|
||||||
import './cnb-dev-env.ts';
|
import './cnb-dev-env.ts';
|
||||||
import { useKey } from '@kevisual/context';
|
import { useKey } from '@kevisual/context';
|
||||||
import { spawnSync } from 'node:child_process';
|
import { spawnSync } from 'node:child_process';
|
||||||
@@ -31,8 +31,9 @@ app.route({
|
|||||||
path: 'cnb_board',
|
path: 'cnb_board',
|
||||||
key: 'exit',
|
key: 'exit',
|
||||||
description: 'cnb的工作环境退出程序',
|
description: 'cnb的工作环境退出程序',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
if (notCNBCheck(ctx)) return;
|
||||||
const cmd = 'kill 1';
|
const cmd = 'kill 1';
|
||||||
execCommand(cmd);
|
execCommand(cmd);
|
||||||
}).addTo(app);
|
}).addTo(app);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSkill } from '@kevisual/router';
|
import { createSkill } from '@kevisual/router';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app, cnbManager } from '../../app.ts';
|
||||||
import { tool } from '@opencode-ai/plugin/tool';
|
import { tool } from '@opencode-ai/plugin/tool';
|
||||||
|
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'user-check',
|
key: 'user-check',
|
||||||
description: '检查用户登录状态,参数checkToken,default true; checkCookie, default false',
|
description: '检查用户登录状态,参数checkToken,default true; checkCookie, default false',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -24,6 +24,7 @@ app.route({
|
|||||||
const checkToken = ctx.query?.checkToken ?? true;
|
const checkToken = ctx.query?.checkToken ?? true;
|
||||||
const checkCookie = ctx.query?.checkCookie ?? false;
|
const checkCookie = ctx.query?.checkCookie ?? false;
|
||||||
let content = '';
|
let content = '';
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
if (checkToken) {
|
if (checkToken) {
|
||||||
const res = await cnb.user.getUser();
|
const res = await cnb.user.getUser();
|
||||||
if (res?.code !== 200) {
|
if (res?.code !== 200) {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { createSkill, tool } from '@kevisual/router';
|
import { createSkill, tool } from '@kevisual/router';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app, cnbManager } from '../../app.ts';
|
||||||
|
|
||||||
// 设置 CNB_COOKIE环境变量和获取环境变量,用于界面操作定制模块功能
|
// 设置 CNB_COOKIE环境变量和获取环境变量,用于界面操作定制模块功能
|
||||||
app.route({
|
app.route({
|
||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'set-cnb-cookie',
|
key: 'set-cnb-cookie',
|
||||||
description: '设置当前cnb工作空间的cookie环境变量',
|
description: '设置当前cnb工作空间的cookie环境变量',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -19,6 +19,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const cookie = ctx.query?.cookie;
|
const cookie = ctx.query?.cookie;
|
||||||
if (!cookie) {
|
if (!cookie) {
|
||||||
ctx.body = { content: '请提供有效的cookie值' };
|
ctx.body = { content: '请提供有效的cookie值' };
|
||||||
@@ -33,7 +34,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'get-cnb-cookie',
|
key: 'get-cnb-cookie',
|
||||||
description: '获取当前cnb工作空间的cookie环境变量',
|
description: '获取当前cnb工作空间的cookie环境变量',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -43,6 +44,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const cookie = cnb.cookie || '未设置cookie环境变量';
|
const cookie = cnb.cookie || '未设置cookie环境变量';
|
||||||
ctx.body = { content: `当前cnb工作空间的cookie环境变量为:${cookie}` };
|
ctx.body = { content: `当前cnb工作空间的cookie环境变量为:${cookie}` };
|
||||||
}).addTo(app);
|
}).addTo(app);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSkill, tool } from '@kevisual/router';
|
import { createSkill, tool } from '@kevisual/router';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app, notCNBCheck } from '../../app.ts';
|
||||||
|
|
||||||
import { CNB_ENV } from "@/common/cnb-env.ts";
|
import { CNB_ENV } from "@/common/cnb-env.ts";
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'get-cnb-port-uri',
|
key: 'get-cnb-port-uri',
|
||||||
description: '获取当前cnb工作空间的port代理uri',
|
description: '获取当前cnb工作空间的port代理uri',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -24,6 +24,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
if (notCNBCheck(ctx)) return;
|
||||||
const port = ctx.query?.port || 51515;
|
const port = ctx.query?.port || 51515;
|
||||||
const uri = CNB_ENV?.CNB_VSCODE_PROXY_URI as string || '';
|
const uri = CNB_ENV?.CNB_VSCODE_PROXY_URI as string || '';
|
||||||
const finalUri = uri.replace('{{port}}', port.toString());
|
const finalUri = uri.replace('{{port}}', port.toString());
|
||||||
@@ -40,7 +41,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'get-cnb-vscode-uri',
|
key: 'get-cnb-vscode-uri',
|
||||||
description: '获取当前cnb工作空间的vscode代理uri, 包括多种访问方式, 如web、vscode、codebuddy、cursor、ssh',
|
description: '获取当前cnb工作空间的vscode代理uri, 包括多种访问方式, 如web、vscode、codebuddy、cursor、ssh',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -58,6 +59,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
if (notCNBCheck(ctx)) return;
|
||||||
const web = ctx.query?.web ?? false;
|
const web = ctx.query?.web ?? false;
|
||||||
const vscode = ctx.query?.vscode ?? true; // 默认true
|
const vscode = ctx.query?.vscode ?? true; // 默认true
|
||||||
const codebuddy = ctx.query?.codebuddy ?? false;
|
const codebuddy = ctx.query?.codebuddy ?? false;
|
||||||
|
|||||||
24
agent/routes/cnb-manager/index.ts
Normal file
24
agent/routes/cnb-manager/index.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { app, cnbManager } from '../../app.ts';
|
||||||
|
|
||||||
|
// "列出我的代码仓库,search blog"
|
||||||
|
// 列出我的知识库的代码仓库
|
||||||
|
app.route({
|
||||||
|
path: 'cnb',
|
||||||
|
key: 'clear-me-manager',
|
||||||
|
description: '清理我的cnb-manager记录',
|
||||||
|
middleware: ['auth'],
|
||||||
|
|
||||||
|
}).define(async (ctx) => {
|
||||||
|
const tokenUser = ctx.tokenUser;
|
||||||
|
if (!tokenUser) {
|
||||||
|
ctx.throw(401, '未授权');
|
||||||
|
}
|
||||||
|
const username = tokenUser.username;
|
||||||
|
if (!username) {
|
||||||
|
ctx.throw(400, '无效的用户信息');
|
||||||
|
}
|
||||||
|
if (username !== 'default') {
|
||||||
|
cnbManager.clearUsername(username);
|
||||||
|
}
|
||||||
|
ctx.body = { content: '已清理cnb-manager记录' };
|
||||||
|
}).addTo(app);
|
||||||
@@ -8,6 +8,8 @@ import './knowledge/index.ts'
|
|||||||
import './issues/index.ts'
|
import './issues/index.ts'
|
||||||
import './cnb-board/index.ts';
|
import './cnb-board/index.ts';
|
||||||
import './share/index.ts';
|
import './share/index.ts';
|
||||||
|
import './cnb-manager/index.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证上下文中的 App ID 是否与指定的 App ID 匹配
|
* 验证上下文中的 App ID 是否与指定的 App ID 匹配
|
||||||
* @param {any} ctx - 上下文对象,可能包含 appId 属性
|
* @param {any} ctx - 上下文对象,可能包含 appId 属性
|
||||||
@@ -32,6 +34,9 @@ app.route({
|
|||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
// ctx.body = 'Auth Route';
|
// ctx.body = 'Auth Route';
|
||||||
if (checkAppId(ctx, app.appId)) {
|
if (checkAppId(ctx, app.appId)) {
|
||||||
|
ctx.state.tokenUser = {
|
||||||
|
username: 'default',
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}).addTo(app, { overwrite: false });
|
}).addTo(app, { overwrite: false });
|
||||||
@@ -43,6 +48,9 @@ app.route({
|
|||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
// ctx.body = 'Admin Auth Route';
|
// ctx.body = 'Admin Auth Route';
|
||||||
if (checkAppId(ctx, app.appId)) {
|
if (checkAppId(ctx, app.appId)) {
|
||||||
|
ctx.state.tokenUser = {
|
||||||
|
username: 'default',
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}).addTo(app, { overwrite: false });
|
}).addTo(app, { overwrite: false });
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSkill, tool } from '@kevisual/router';
|
import { createSkill, tool } from '@kevisual/router';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app, cnbManager } from '../../app.ts';
|
||||||
import { IssueItem } from '@/index.ts';
|
import { IssueItem } from '@/index.ts';
|
||||||
|
|
||||||
// 创建cnb issue, 仓库为 kevisual/kevisual 标题为 "自动化测试创建issue", 内容为 "这是通过API创建的issue,用于测试目的", body: "这是通过API创建的issue,用于测试目的"
|
// 创建cnb issue, 仓库为 kevisual/kevisual 标题为 "自动化测试创建issue", 内容为 "这是通过API创建的issue,用于测试目的", body: "这是通过API创建的issue,用于测试目的"
|
||||||
@@ -7,7 +7,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'create-issue',
|
key: 'create-issue',
|
||||||
description: '创建 Issue, 参数 repo, title, body, assignees, labels, priority',
|
description: '创建 Issue, 参数 repo, title, body, assignees, labels, priority',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -25,6 +25,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const repo = ctx.query?.repo;
|
const repo = ctx.query?.repo;
|
||||||
const title = ctx.query?.title;
|
const title = ctx.query?.title;
|
||||||
const body = ctx.query?.body;
|
const body = ctx.query?.body;
|
||||||
@@ -51,7 +52,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'complete-issue',
|
key: 'complete-issue',
|
||||||
description: '完成 Issue, 参数 repo, issueNumber',
|
description: '完成 Issue, 参数 repo, issueNumber',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -66,6 +67,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const repo = ctx.query?.repo;
|
const repo = ctx.query?.repo;
|
||||||
const issueNumber = ctx.query?.issueNumber;
|
const issueNumber = ctx.query?.issueNumber;
|
||||||
const state = ctx.query?.state ?? 'closed';
|
const state = ctx.query?.state ?? 'closed';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSkill, tool } from '@kevisual/router';
|
import { createSkill, tool } from '@kevisual/router';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app, cnbManager } from '../../app.ts';
|
||||||
import { useKey } from '@kevisual/context';
|
import { useKey } from '@kevisual/context';
|
||||||
|
|
||||||
// 查询 Issue 列表 repo是 kevisual/kevisual
|
// 查询 Issue 列表 repo是 kevisual/kevisual
|
||||||
@@ -7,7 +7,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'list-issues',
|
key: 'list-issues',
|
||||||
description: '查询 Issue 列表, 参数 repo, state, keyword, labels, page, page_size 等',
|
description: '查询 Issue 列表, 参数 repo, state, keyword, labels, page, page_size 等',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -26,6 +26,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const repo = ctx.query?.repo || useKey('CNB_REPO_SLUG_LOWERCASE');
|
const repo = ctx.query?.repo || useKey('CNB_REPO_SLUG_LOWERCASE');
|
||||||
const state = ctx.query?.state;
|
const state = ctx.query?.state;
|
||||||
const keyword = ctx.query?.keyword;
|
const keyword = ctx.query?.keyword;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSkill, tool } from '@kevisual/router';
|
import { createSkill, tool } from '@kevisual/router';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app, cnbManager } from '../../app.ts';
|
||||||
import { CNBChat } from '@kevisual/ai/browser'
|
import { CNBChat } from '@kevisual/ai/browser'
|
||||||
import { useKey } from '@kevisual/context';
|
import { useKey } from '@kevisual/context';
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'cnb-ai-chat',
|
key: 'cnb-ai-chat',
|
||||||
description: '调用cnb的知识库ai对话功能进行聊天',
|
description: '调用cnb的知识库ai对话功能进行聊天',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -27,6 +27,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const question = ctx.query?.question;
|
const question = ctx.query?.question;
|
||||||
if (!question) {
|
if (!question) {
|
||||||
ctx.body = { content: '请提供有效的消息内容' };
|
ctx.body = { content: '请提供有效的消息内容' };
|
||||||
@@ -89,7 +90,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'cnb-rag-query',
|
key: 'cnb-rag-query',
|
||||||
description: '调用cnb的知识库RAG查询功能进行问答',
|
description: '调用cnb的知识库RAG查询功能进行问答',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -103,6 +104,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const question = ctx.query?.question;
|
const question = ctx.query?.question;
|
||||||
if (!question) {
|
if (!question) {
|
||||||
ctx.body = { content: '请提供有效的消息内容' };
|
ctx.body = { content: '请提供有效的消息内容' };
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSkill, tool } from '@kevisual/router';
|
import { createSkill, tool } from '@kevisual/router';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app, cnbManager } from '../../app.ts';
|
||||||
|
|
||||||
// "列出我的代码仓库,search blog"
|
// "列出我的代码仓库,search blog"
|
||||||
// 列出我的知识库的代码仓库
|
// 列出我的知识库的代码仓库
|
||||||
@@ -7,7 +7,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'list-repos',
|
key: 'list-repos',
|
||||||
description: '列出我的代码仓库',
|
description: '列出我的代码仓库',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -22,6 +22,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const search = ctx.query?.search;
|
const search = ctx.query?.search;
|
||||||
const pageSize = ctx.query?.pageSize || 9999;
|
const pageSize = ctx.query?.pageSize || 9999;
|
||||||
const flags = ctx.query?.flags;
|
const flags = ctx.query?.flags;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { app, cnb } from '../../app.ts';
|
import { app, cnbManager } from '../../app.ts';
|
||||||
import { createSkill, Skill, tool } from '@kevisual/router'
|
import { createSkill, Skill, tool } from '@kevisual/router'
|
||||||
|
|
||||||
// 创建一个仓库 kevisual/test-repo
|
// 创建一个仓库 kevisual/test-repo
|
||||||
@@ -6,7 +6,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'create-repo',
|
key: 'create-repo',
|
||||||
description: '创建代码仓库, 参数name, visibility, description',
|
description: '创建代码仓库, 参数name, visibility, description',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -21,6 +21,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const name = ctx.query?.name;
|
const name = ctx.query?.name;
|
||||||
const visibility = ctx.query?.visibility ?? 'public';
|
const visibility = ctx.query?.visibility ?? 'public';
|
||||||
const description = ctx.query?.description ?? '';
|
const description = ctx.query?.description ?? '';
|
||||||
@@ -46,7 +47,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'create-repo-file',
|
key: 'create-repo-file',
|
||||||
description: '在代码仓库中创建文件, repoName, filePath, content, encoding。使用CNB_COOKIE进行鉴权',
|
description: '在代码仓库中创建文件, repoName, filePath, content, encoding。使用CNB_COOKIE进行鉴权',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -62,6 +63,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const repoName = ctx.query?.repoName;
|
const repoName = ctx.query?.repoName;
|
||||||
const filePath = ctx.query?.filePath;
|
const filePath = ctx.query?.filePath;
|
||||||
const content = ctx.query?.content;
|
const content = ctx.query?.content;
|
||||||
@@ -85,7 +87,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'delete-repo',
|
key: 'delete-repo',
|
||||||
description: '删除代码仓库, 参数name',
|
description: '删除代码仓库, 参数name',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -98,12 +100,13 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const name = ctx.query?.name;
|
const name = ctx.query?.name;
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
ctx.throw(400, '缺少参数 name');
|
ctx.throw(400, '缺少参数 name');
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await cnb.repo.deleteRepo(name);
|
const res = await cnb.repo.deleteRepoCookie(name);
|
||||||
ctx.forward(res);
|
ctx.forward(res);
|
||||||
}).addTo(app);
|
}).addTo(app);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useKey } from '@kevisual/context';
|
import { useKey } from '@kevisual/context';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app } from '../../app.ts';
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
|
|
||||||
app.route({
|
app.route({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSkill, tool } from '@kevisual/router';
|
import { createSkill, tool } from '@kevisual/router';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app, cnbManager, notCNBCheck } from '../../app.ts';
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
import './skills.ts';
|
import './skills.ts';
|
||||||
import './keep.ts';
|
import './keep.ts';
|
||||||
@@ -9,7 +9,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'start-workspace',
|
key: 'start-workspace',
|
||||||
description: '启动开发工作空间, 参数 repo',
|
description: '启动开发工作空间, 参数 repo',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -24,6 +24,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const repo = ctx.query?.repo;
|
const repo = ctx.query?.repo;
|
||||||
const branch = ctx.query?.branch;
|
const branch = ctx.query?.branch;
|
||||||
const ref = ctx.query?.ref;
|
const ref = ctx.query?.ref;
|
||||||
@@ -42,7 +43,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'list-workspace',
|
key: 'list-workspace',
|
||||||
description: '获取cnb开发工作空间列表,可选参数 status=running 获取运行中的环境',
|
description: '获取cnb开发工作空间列表,可选参数 status=running 获取运行中的环境',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -59,6 +60,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const { status = 'running', page, pageSize, slug, branch } = ctx.query || {};
|
const { status = 'running', page, pageSize, slug, branch } = ctx.query || {};
|
||||||
const res = await cnb.workspace.list({
|
const res = await cnb.workspace.list({
|
||||||
status: status as 'running' | 'closed' | undefined,
|
status: status as 'running' | 'closed' | undefined,
|
||||||
@@ -73,7 +75,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'get-workspace',
|
key: 'get-workspace',
|
||||||
description: '获取工作空间详情,通过 repo 和 sn 获取',
|
description: '获取工作空间详情,通过 repo 和 sn 获取',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -87,6 +89,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const repo = ctx.query?.repo;
|
const repo = ctx.query?.repo;
|
||||||
const sn = ctx.query?.sn;
|
const sn = ctx.query?.sn;
|
||||||
if (!repo) {
|
if (!repo) {
|
||||||
@@ -104,7 +107,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'delete-workspace',
|
key: 'delete-workspace',
|
||||||
description: '删除工作空间,通过 pipelineId 或 sn',
|
description: '删除工作空间,通过 pipelineId 或 sn',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -119,6 +122,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const pipelineId = ctx.query?.pipelineId;
|
const pipelineId = ctx.query?.pipelineId;
|
||||||
const sn = ctx.query?.sn;
|
const sn = ctx.query?.sn;
|
||||||
const sns = ctx.query?.sns;
|
const sns = ctx.query?.sns;
|
||||||
@@ -143,7 +147,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'stop-workspace',
|
key: 'stop-workspace',
|
||||||
description: '停止工作空间,通过 pipelineId 或 sn',
|
description: '停止工作空间,通过 pipelineId 或 sn',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -157,6 +161,8 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
if (notCNBCheck(ctx)) { return; }
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const pipelineId = ctx.query?.pipelineId;
|
const pipelineId = ctx.query?.pipelineId;
|
||||||
const sn = ctx.query?.sn;
|
const sn = ctx.query?.sn;
|
||||||
if (!pipelineId && !sn) {
|
if (!pipelineId && !sn) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { tool } from '@kevisual/router';
|
import { tool } from '@kevisual/router';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app, cnbManager, notCNBCheck } from '../../app.ts';
|
||||||
import { addKeepAliveData, KeepAliveData, removeKeepAliveData, createLiveData } from '../../../src/workspace/keep-file-live.ts';
|
import { addKeepAliveData, KeepAliveData, removeKeepAliveData, createLiveData } from '../../../src/workspace/keep-file-live.ts';
|
||||||
import { useKey } from '@kevisual/context';
|
import { useKey } from '@kevisual/context';
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'keep-workspace-alive',
|
key: 'keep-workspace-alive',
|
||||||
description: '保持工作空间存活技能,参数repo:代码仓库路径,例如 user/repo,pipelineId:流水线ID,例如 cnb-708-1ji9sog7o-001',
|
description: '保持工作空间存活技能,参数repo:代码仓库路径,例如 user/repo,pipelineId:流水线ID,例如 cnb-708-1ji9sog7o-001',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: [],
|
tags: [],
|
||||||
...({
|
...({
|
||||||
@@ -19,9 +19,11 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const repo = ctx.query?.repo as string;
|
const repo = ctx.query?.repo as string;
|
||||||
const pipelineId = ctx.query?.pipelineId as string;
|
const pipelineId = ctx.query?.pipelineId as string;
|
||||||
|
if (notCNBCheck(ctx)) return;
|
||||||
if (!repo || !pipelineId) {
|
if (!repo || !pipelineId) {
|
||||||
ctx.throw(400, '缺少参数 repo 或 pipelineId');
|
ctx.throw(400, '缺少参数 repo 或 pipelineId');
|
||||||
}
|
}
|
||||||
@@ -51,7 +53,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'stop-keep-workspace-alive',
|
key: 'stop-keep-workspace-alive',
|
||||||
description: '停止保持工作空间存活技能, 参数repo:代码仓库路径,例如 user/repo,pipelineId:流水线ID,例如 cnb-708-1ji9sog7o-001',
|
description: '停止保持工作空间存活技能, 参数repo:代码仓库路径,例如 user/repo,pipelineId:流水线ID,例如 cnb-708-1ji9sog7o-001',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: [],
|
tags: [],
|
||||||
...({
|
...({
|
||||||
@@ -62,6 +64,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
if (notCNBCheck(ctx)) return;
|
||||||
const repo = ctx.query?.repo as string;
|
const repo = ctx.query?.repo as string;
|
||||||
const pipelineId = ctx.query?.pipelineId as string;
|
const pipelineId = ctx.query?.pipelineId as string;
|
||||||
|
|
||||||
@@ -79,7 +82,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'keep-alive-current-workspace',
|
key: 'keep-alive-current-workspace',
|
||||||
description: '保持当前工作空间存活技能',
|
description: '保持当前工作空间存活技能',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
skill: 'keep-alive-current-workspace',
|
skill: 'keep-alive-current-workspace',
|
||||||
@@ -87,6 +90,7 @@ app.route({
|
|||||||
summary: '保持当前工作空间存活,防止被关闭或释放资源',
|
summary: '保持当前工作空间存活,防止被关闭或释放资源',
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
if (notCNBCheck(ctx)) return;
|
||||||
const pipelineId = useKey('CNB_PIPELINE_ID');
|
const pipelineId = useKey('CNB_PIPELINE_ID');
|
||||||
const repo = useKey('CNB_REPO_SLUG_LOWERCASE');
|
const repo = useKey('CNB_REPO_SLUG_LOWERCASE');
|
||||||
if (!pipelineId || !repo) {
|
if (!pipelineId || !repo) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSkill, tool } from '@kevisual/router';
|
import { createSkill, tool } from '@kevisual/router';
|
||||||
import { app, cnb } from '../../app.ts';
|
import { app, cnbManager } from '../../app.ts';
|
||||||
|
|
||||||
// 批量删除已停止的cnb工作空间
|
// 批量删除已停止的cnb工作空间
|
||||||
// app.route({
|
// app.route({
|
||||||
@@ -35,7 +35,7 @@ app.route({
|
|||||||
path: 'cnb',
|
path: 'cnb',
|
||||||
key: 'clean-closed-workspace',
|
key: 'clean-closed-workspace',
|
||||||
description: '批量删除已停止的cnb工作空间',
|
description: '批量删除已停止的cnb工作空间',
|
||||||
middleware: ['auth-admin'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['opencode'],
|
tags: ['opencode'],
|
||||||
...createSkill({
|
...createSkill({
|
||||||
@@ -45,6 +45,7 @@ app.route({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
|
const cnb = await cnbManager.getContext(ctx);
|
||||||
const closedWorkspaces = await cnb.workspace.list({ status: 'closed', pageSize: 100 });
|
const closedWorkspaces = await cnb.workspace.list({ status: 'closed', pageSize: 100 });
|
||||||
if (closedWorkspaces.code !== 200) {
|
if (closedWorkspaces.code !== 200) {
|
||||||
ctx.throw(500, '获取已关闭工作空间列表失败');
|
ctx.throw(500, '获取已关闭工作空间列表失败');
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ await buildWithBun({ naming: 'opencode', entry: 'agent/opencode.ts', dts: true }
|
|||||||
await buildWithBun({ naming: 'keep', entry: 'src/keep.ts', dts: true, target: 'node' });
|
await buildWithBun({ naming: 'keep', entry: 'src/keep.ts', dts: true, target: 'node' });
|
||||||
await buildWithBun({ naming: 'routes', entry: 'agent/index.ts', dts: true });
|
await buildWithBun({ naming: 'routes', entry: 'agent/index.ts', dts: true });
|
||||||
|
|
||||||
await buildWithBun({ naming: 'cli', entry: 'agent/command.ts', dts: true, target: 'node' });
|
await buildWithBun({ naming: 'cli', entry: 'agent/commander.ts', dts: true, target: 'node' });
|
||||||
18
package.json
18
package.json
@@ -3,9 +3,16 @@
|
|||||||
"version": "0.0.37",
|
"version": "0.0.37",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
"basename": "/root/cnb",
|
||||||
|
"app": {
|
||||||
|
"type": "system-app",
|
||||||
|
"entry": "./dist/routes.js",
|
||||||
|
"engine": "bun"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "bun bun.config.ts",
|
"build": "bun bun.config.ts",
|
||||||
"flow": "ev npm patch && pnpm build && ev npm publish npm -p"
|
"flow": "ev npm patch && pnpm build && ev npm publish npm -p",
|
||||||
|
"pub": "ev pack -u -m false -c -p"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -14,20 +21,21 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
"src",
|
"src",
|
||||||
"mod.ts",
|
|
||||||
"agent"
|
"agent"
|
||||||
],
|
],
|
||||||
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"packageManager": "pnpm@10.30.3",
|
"packageManager": "pnpm@10.31.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kevisual/ai": "^0.0.26",
|
"@kevisual/ai": "^0.0.26",
|
||||||
|
"@kevisual/api": "^0.0.62",
|
||||||
"@kevisual/code-builder": "^0.0.6",
|
"@kevisual/code-builder": "^0.0.6",
|
||||||
"@kevisual/context": "^0.0.8",
|
"@kevisual/context": "^0.0.8",
|
||||||
"@kevisual/dts": "^0.0.4",
|
"@kevisual/dts": "^0.0.4",
|
||||||
|
"@kevisual/remote-app": "^0.0.6",
|
||||||
"@kevisual/types": "^0.0.12",
|
"@kevisual/types": "^0.0.12",
|
||||||
"@opencode-ai/plugin": "^1.2.20",
|
"@opencode-ai/plugin": "^1.2.22",
|
||||||
"@types/bun": "^1.3.10",
|
"@types/bun": "^1.3.10",
|
||||||
"@types/node": "^25.3.5",
|
"@types/node": "^25.3.5",
|
||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
@@ -43,7 +51,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kevisual/query": "^0.0.53",
|
"@kevisual/query": "^0.0.53",
|
||||||
"@kevisual/router": "^0.0.88",
|
"@kevisual/router": "^0.0.90",
|
||||||
"@kevisual/use-config": "^1.0.30",
|
"@kevisual/use-config": "^1.0.30",
|
||||||
"es-toolkit": "^1.45.1",
|
"es-toolkit": "^1.45.1",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
|
|||||||
12
test/a-config.ts
Normal file
12
test/a-config.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { getConfig } from '../agent/modules/cnb-manager';
|
||||||
|
import { QueryLoginNode } from '@kevisual/api/login-node';
|
||||||
|
const queryLoginNode = new QueryLoginNode({});
|
||||||
|
await queryLoginNode.init()
|
||||||
|
const testConfig = async () => {
|
||||||
|
const token = await queryLoginNode.getToken();
|
||||||
|
console.log('Token:', token);
|
||||||
|
const res = await getConfig({ token });
|
||||||
|
console.log('Config:', res);
|
||||||
|
}
|
||||||
|
|
||||||
|
testConfig();
|
||||||
Reference in New Issue
Block a user