Compare commits
23 Commits
13401d9aa0
...
21ba07e55b
| Author | SHA1 | Date | |
|---|---|---|---|
| 21ba07e55b | |||
| 81a3aae8ec | |||
| dd5331bbaa | |||
| 2eecbe273e | |||
| a563f3e0d6 | |||
| 327db1e09a | |||
| 8465ba7182 | |||
| 841ed6ffa7 | |||
| 5043392939 | |||
| 8a6bb9bbe9 | |||
| 8ca6b77e4d | |||
| d3286e2766 | |||
| dd691f7a59 | |||
| e6042e025f | |||
|
|
43b61dc656 | ||
|
|
3ff9e4e374 | ||
|
|
24ee793db1 | ||
|
|
d16adc07fe | ||
|
|
0ebc94a7d0 | ||
| 47229c6db9 | |||
| d231f3748a | |||
| 9bb9f447ec | |||
| ea137eb70b |
28
.cnb.yml
28
.cnb.yml
@@ -4,8 +4,7 @@ include:
|
||||
|
||||
.common_env: &common_env
|
||||
env:
|
||||
TO_REPO: kevisual/cnb
|
||||
TO_URL: git.xiongxiao.me
|
||||
USERNAME: root
|
||||
imports:
|
||||
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
|
||||
|
||||
@@ -16,29 +15,6 @@ $:
|
||||
services:
|
||||
- vscode
|
||||
- docker
|
||||
env: !reference [.common_env, env]
|
||||
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
|
||||
- <<: *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
|
||||
1
.env.example
Normal file
1
.env.example
Normal file
@@ -0,0 +1 @@
|
||||
CNB_COOKIE=CNBSESSION=1771242023.1935321989751226368.8841cb77d609c050b1a19877644487b6543b587a80953cbdf3018a15b9948b48;csrfkey=309068260
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
.env
|
||||
.env.local
|
||||
.env*
|
||||
!.env*example
|
||||
node_modules
|
||||
.pnpm-store
|
||||
|
||||
|
||||
5
.opencode/package.json
Normal file
5
.opencode/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@opencode-ai/plugin": "1.2.9"
|
||||
}
|
||||
}
|
||||
4
agent/command.ts
Normal file
4
agent/command.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { app } from './index.ts';
|
||||
import { parse } from '@kevisual/router/src/commander.ts';
|
||||
|
||||
parse({ app: app as any, description: 'CNB控制台命令行工具', parse: true })
|
||||
@@ -1,34 +1,31 @@
|
||||
import { createSkill } from '@kevisual/router'
|
||||
import { createSkill, tool } 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)
|
||||
}
|
||||
// "调用 path: cnb key: list-repos"
|
||||
app.route({
|
||||
path: 'call',
|
||||
key: '',
|
||||
description: '调用',
|
||||
middleware: ['auth-admin'],
|
||||
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, { overwrite: false })
|
||||
424
agent/routes/cnb-board/cnb-dev-env.ts
Normal file
424
agent/routes/cnb-board/cnb-dev-env.ts
Normal file
@@ -0,0 +1,424 @@
|
||||
import { app } from '../../app.ts';
|
||||
import { useKey } from '@kevisual/context'
|
||||
import { getLiveMdContent } from './live/live-content.ts';
|
||||
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({
|
||||
path: 'cnb_board',
|
||||
key: 'live',
|
||||
description: '获取cnb-board live的mdContent内容',
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
args: {
|
||||
more: z.boolean().optional().describe('是否获取更多系统信息,默认false'),
|
||||
}
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const more = ctx.query?.more ?? false
|
||||
if (notCNBCheck(ctx)) return;
|
||||
const list = getLiveMdContent({ more: more });
|
||||
ctx.body = {
|
||||
title: '开发环境模式配置',
|
||||
list,
|
||||
};
|
||||
}).addTo(app);
|
||||
|
||||
app.route({
|
||||
path: 'cnb_board',
|
||||
key: 'live_repo_info',
|
||||
description: '获取cnb-board live的repo信息',
|
||||
middleware: ['auth-admin']
|
||||
}).define(async (ctx) => {
|
||||
const repoSlug = useKey('CNB_REPO_SLUG') || '';
|
||||
const repoName = useKey('CNB_REPO_NAME') || '';
|
||||
const repoId = useKey('CNB_REPO_ID') || '';
|
||||
const repoUrlHttps = useKey('CNB_REPO_UR if (notCNBCheck(ctx)) return;L_HTTPS') || '';
|
||||
if (notCNBCheck(ctx)) return;
|
||||
// 从 repoSlug 提取仓库名称
|
||||
const repoNameFromSlug = repoSlug.split('/').pop() || '';
|
||||
|
||||
const labels = [
|
||||
{
|
||||
title: 'CNB_REPO_SLUG',
|
||||
value: repoSlug,
|
||||
description: '目标仓库路径,格式为 group_slug / repo_name,group_slug / sub_gourp_slug /.../repo_name'
|
||||
},
|
||||
{
|
||||
title: 'CNB_REPO_SLUG_LOWERCASE',
|
||||
value: repoSlug.toLowerCase(),
|
||||
description: '目标仓库路径小写格式'
|
||||
},
|
||||
{
|
||||
title: 'CNB_REPO_NAME',
|
||||
value: repoName || repoNameFromSlug,
|
||||
description: '目标仓库名称'
|
||||
},
|
||||
{
|
||||
title: 'CNB_REPO_NAME_LOWERCASE',
|
||||
value: (repoName || repoNameFromSlug).toLowerCase(),
|
||||
description: '目标仓库名称小写格式'
|
||||
},
|
||||
{
|
||||
title: 'CNB_REPO_ID',
|
||||
value: repoId,
|
||||
description: '目标仓库的 id'
|
||||
},
|
||||
{
|
||||
title: 'CNB_REPO_URL_HTTPS',
|
||||
value: repoUrlHttps,
|
||||
description: '目标仓库 https 地址'
|
||||
}
|
||||
]
|
||||
ctx.body = {
|
||||
title: 'CNB_BOARD_LIVE_REPO_INFO',
|
||||
list: labels
|
||||
};
|
||||
}).addTo(app);
|
||||
|
||||
// 构建类变量
|
||||
app.route({
|
||||
path: 'cnb_board',
|
||||
key: 'live_build_info',
|
||||
description: '获取cnb-board live的构建信息',
|
||||
middleware: ['auth-admin']
|
||||
}).define(async (ctx) => {
|
||||
if (notCNBCheck(ctx)) return;
|
||||
const labels = [
|
||||
{
|
||||
title: 'CNB_BUILD_ID',
|
||||
value: useKey('CNB_BUILD_ID') || '',
|
||||
description: '当前构建的流水号,全局唯一'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_WEB_URL',
|
||||
value: useKey('CNB_BUILD_WEB_URL') || '',
|
||||
description: '当前构建的日志地址'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_START_TIME',
|
||||
value: useKey('CNB_BUILD_START_TIME') || '',
|
||||
description: '当前构建的开始时间,UTC 格式,示例 2025-08-21T09:13:45.803Z'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_USER',
|
||||
value: useKey('CNB_BUILD_USER') || '',
|
||||
description: '当前构建的触发者用户名'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_USER_NICKNAME',
|
||||
value: useKey('CNB_BUILD_USER_NICKNAME') || '',
|
||||
description: '当前构建的触发者昵称'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_USER_EMAIL',
|
||||
value: useKey('CNB_BUILD_USER_EMAIL') || '',
|
||||
description: '当前构建的触发者邮箱'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_USER_ID',
|
||||
value: useKey('CNB_BUILD_USER_ID') || '',
|
||||
description: '当前构建的触发者 id'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_USER_NPC_SLUG',
|
||||
value: useKey('CNB_BUILD_USER_NPC_SLUG') || '',
|
||||
description: '当前构建若为 NPC 触发,则为 NPC 所属仓库的路径'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_USER_NPC_NAME',
|
||||
value: useKey('CNB_BUILD_USER_NPC_NAME') || '',
|
||||
description: '当前构建若为 NPC 触发,则为 NPC 角色名'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_STAGE_NAME',
|
||||
value: useKey('CNB_BUILD_STAGE_NAME') || '',
|
||||
description: '当前构建的 stage 名称'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_JOB_NAME',
|
||||
value: useKey('CNB_BUILD_JOB_NAME') || '',
|
||||
description: '当前构建的 job 名称'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_JOB_KEY',
|
||||
value: useKey('CNB_BUILD_JOB_KEY') || '',
|
||||
description: '当前构建的 job key,同 stage 下唯一'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_WORKSPACE',
|
||||
value: useKey('CNB_BUILD_WORKSPACE') || '',
|
||||
description: '自定义 shell 脚本执行的工作空间根目录'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_FAILED_MSG',
|
||||
value: useKey('CNB_BUILD_FAILED_MSG') || '',
|
||||
description: '流水线构建失败的错误信息,可在 failStages 中使用'
|
||||
},
|
||||
{
|
||||
title: 'CNB_BUILD_FAILED_STAGE_NAME',
|
||||
value: useKey('CNB_BUILD_FAILED_STAGE_NAME') || '',
|
||||
description: '流水线构建失败的 stage 的名称,可在 failStages 中使用'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PIPELINE_NAME',
|
||||
value: useKey('CNB_PIPELINE_NAME') || '',
|
||||
description: '当前 pipeline 的 name,没声明时为空'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PIPELINE_KEY',
|
||||
value: useKey('CNB_PIPELINE_KEY') || '',
|
||||
description: '当前 pipeline 的索引 key,例如 pipeline-0'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PIPELINE_ID',
|
||||
value: useKey('CNB_PIPELINE_ID') || '',
|
||||
description: '当前 pipeline 的 id,全局唯一字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PIPELINE_DOCKER_IMAGE',
|
||||
value: useKey('CNB_PIPELINE_DOCKER_IMAGE') || '',
|
||||
description: '当前 pipeline 所使用的 docker image,如:alpine:latest'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PIPELINE_STATUS',
|
||||
value: useKey('CNB_PIPELINE_STATUS') || '',
|
||||
description: '当前流水线的构建状态,可在 endStages 中查看,其可能的值包括:success、error、cancel'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PIPELINE_MAX_RUN_TIME',
|
||||
value: useKey('CNB_PIPELINE_MAX_RUN_TIME') || '',
|
||||
description: '流水线最大运行时间,单位为毫秒'
|
||||
},
|
||||
{
|
||||
title: 'CNB_RUNNER_IP',
|
||||
value: useKey('CNB_RUNNER_IP') || '',
|
||||
description: '当前 pipeline 所在 Runner 的 ip'
|
||||
},
|
||||
{
|
||||
title: 'CNB_CPUS',
|
||||
value: useKey('CNB_CPUS') || '',
|
||||
description: '当前构建流水线可以使用的最大 CPU 核数'
|
||||
},
|
||||
{
|
||||
title: 'CNB_MEMORY',
|
||||
value: useKey('CNB_MEMORY') || '',
|
||||
description: '当前构建流水线可以使用的最大内存大小,单位为 GiB'
|
||||
},
|
||||
{
|
||||
title: 'CNB_IS_RETRY',
|
||||
value: useKey('CNB_IS_RETRY') || '',
|
||||
description: '当前构建是否由 rebuild 触发'
|
||||
},
|
||||
{
|
||||
title: 'HUSKY_SKIP_INSTALL',
|
||||
value: useKey('HUSKY_SKIP_INSTALL') || '',
|
||||
description: '兼容 ci 环境下 husky'
|
||||
}
|
||||
]
|
||||
ctx.body = {
|
||||
title: 'CNB_BOARD_LIVE_BUILD_INFO',
|
||||
list: labels
|
||||
};
|
||||
}).addTo(app);
|
||||
|
||||
// PR/合并类变量
|
||||
app.route({
|
||||
path: 'cnb_board',
|
||||
key: 'live_pull_info',
|
||||
description: '获取cnb-board live的PR信息',
|
||||
middleware: ['auth-admin']
|
||||
}).define(async (ctx) => {
|
||||
const labels = [
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST',
|
||||
value: useKey('CNB_PULL_REQUEST') || '',
|
||||
description: '对于由 pull_request、pull_request.update、pull_request.target 触发的构建,值为 true,否则为 false'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_LIKE',
|
||||
value: useKey('CNB_PULL_REQUEST_LIKE') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为 true,否则为 false'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_PROPOSER',
|
||||
value: useKey('CNB_PULL_REQUEST_PROPOSER') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为提出 PR 者名称,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_TITLE',
|
||||
value: useKey('CNB_PULL_REQUEST_TITLE') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为提 PR 时候填写的标题,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_BRANCH',
|
||||
value: useKey('CNB_PULL_REQUEST_BRANCH') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为发起 PR 的源分支名称,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_SHA',
|
||||
value: useKey('CNB_PULL_REQUEST_SHA') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为当前 PR 源分支最新的提交 sha,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_TARGET_SHA',
|
||||
value: useKey('CNB_PULL_REQUEST_TARGET_SHA') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为当前 PR 目标分支最新的提交 sha,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_MERGE_SHA',
|
||||
value: useKey('CNB_PULL_REQUEST_MERGE_SHA') || '',
|
||||
description: '对于由 pull_request.merged 触发的构建,值为合并后的 sha;对于 pull_request 等触发的构建,值为预合并后的 sha,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_SLUG',
|
||||
value: useKey('CNB_PULL_REQUEST_SLUG') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为源仓库的仓库 slug,如 group_slug/repo_name,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_ACTION',
|
||||
value: useKey('CNB_PULL_REQUEST_ACTION') || '',
|
||||
description: '对于由 合并类事件 触发的构建,可能的值有:created(新建PR)、code_update(源分支push)、status_update(评审通过或CI状态变更),否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_ID',
|
||||
value: useKey('CNB_PULL_REQUEST_ID') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为当前或者关联 PR 的全局唯一 id,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_IID',
|
||||
value: useKey('CNB_PULL_REQUEST_IID') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为当前或者关联 PR 在仓库中的编号 iid,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_REVIEWERS',
|
||||
value: useKey('CNB_PULL_REQUEST_REVIEWERS') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为评审人列表,多个以 , 分隔,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_REVIEW_STATE',
|
||||
value: useKey('CNB_PULL_REQUEST_REVIEW_STATE') || '',
|
||||
description: '对于由 合并类事件 触发的构建,有评审者且有人通过评审为 approve,有评审者但无人通过评审为 unapprove,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_REVIEW_REVIEWED_BY',
|
||||
value: useKey('CNB_REVIEW_REVIEWED_BY') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为同意评审的评审人列表,多个以 , 分隔,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_REVIEW_LAST_REVIEWED_BY',
|
||||
value: useKey('CNB_REVIEW_LAST_REVIEWED_BY') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为最后一个同意评审的评审人,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_PULL_REQUEST_IS_WIP',
|
||||
value: useKey('CNB_PULL_REQUEST_IS_WIP') || '',
|
||||
description: '对于由 合并类事件 触发的构建,值为 true、false,表示 PR 是否被设置为 [WIP],否则为空字符串'
|
||||
}
|
||||
]
|
||||
ctx.body = {
|
||||
title: 'CNB_BOARD_LIVE_PULL_INFO',
|
||||
list: labels
|
||||
};
|
||||
}).addTo(app);
|
||||
|
||||
// NPC 类变量
|
||||
app.route({
|
||||
path: 'cnb_board',
|
||||
key: 'live_npc_info',
|
||||
description: '获取cnb-board live的NPC信息',
|
||||
middleware: ['auth-admin']
|
||||
}).define(async (ctx) => {
|
||||
if (notCNBCheck(ctx)) return;
|
||||
const labels = [
|
||||
{
|
||||
title: 'CNB_NPC_SLUG',
|
||||
value: useKey('CNB_NPC_SLUG') || '',
|
||||
description: '对于 @ 知识库角色触发的 NPC 事件,值为 NPC 所属仓库路径,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_NPC_NAME',
|
||||
value: useKey('CNB_NPC_NAME') || '',
|
||||
description: '对于 NPC 事件触发的构建,值为 NPC 角色名,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_NPC_SHA',
|
||||
value: useKey('CNB_NPC_SHA') || '',
|
||||
description: '对于 @ 知识库角色触发的 NPC 事件,值为 NPC 所属仓库默认分支最新提交的 sha,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_NPC_PROMPT',
|
||||
value: useKey('CNB_NPC_PROMPT') || '',
|
||||
description: '对于 @ 知识库角色触发的 NPC 事件,值为 NPC 角色 Prompt,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_NPC_AVATAR',
|
||||
value: useKey('CNB_NPC_AVATAR') || '',
|
||||
description: '对于 @ 知识库角色触发的 NPC 事件,值为 NPC 角色头像,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_NPC_ENABLE_THINKING',
|
||||
value: useKey('CNB_NPC_ENABLE_THINKING') || '',
|
||||
description: '对于 @npc 事件触发的构建,值为 NPC 角色是否开启思考,否则为空字符串'
|
||||
}
|
||||
]
|
||||
ctx.body = {
|
||||
title: 'CNB_BOARD_LIVE_NPC_INFO',
|
||||
list: labels
|
||||
};
|
||||
}).addTo(app);
|
||||
|
||||
// 评论类变量
|
||||
app.route({
|
||||
path: 'cnb_board',
|
||||
key: 'live_comment_info',
|
||||
description: '获取cnb-board live的评论信息',
|
||||
middleware: ['auth-admin']
|
||||
}).define(async (ctx) => {
|
||||
if (notCNBCheck(ctx)) return;
|
||||
const labels = [
|
||||
{
|
||||
title: 'CNB_COMMENT_ID',
|
||||
value: useKey('CNB_COMMENT_ID') || '',
|
||||
description: '对于评论事件触发的构建,值为评论全局唯一 ID,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_COMMENT_BODY',
|
||||
value: useKey('CNB_COMMENT_BODY') || '',
|
||||
description: '对于评论事件触发的构建,值为评论内容,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_COMMENT_TYPE',
|
||||
value: useKey('CNB_COMMENT_TYPE') || '',
|
||||
description: '对于 PR 代码评审评论,值为 diff_note;对于 PR 非代码评审评论以及 Issue 评论,值为 note;否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_COMMENT_FILE_PATH',
|
||||
value: useKey('CNB_COMMENT_FILE_PATH') || '',
|
||||
description: '对于 PR 代码评审评论,值为评论所在文件,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_COMMENT_RANGE',
|
||||
value: useKey('CNB_COMMENT_RANGE') || '',
|
||||
description: '对于 PR 代码评审评论,值为评论所在代码行。如,单行为 L12,多行为 L13-L16,否则为空字符串'
|
||||
},
|
||||
{
|
||||
title: 'CNB_REVIEW_ID',
|
||||
value: useKey('CNB_REVIEW_ID') || '',
|
||||
description: '对于 PR 代码评审,值为评审 ID,否则为空字符串'
|
||||
}
|
||||
]
|
||||
ctx.body = {
|
||||
title: 'CNB_BOARD_LIVE_COMMENT_INFO',
|
||||
list: labels
|
||||
};
|
||||
}).addTo(app);
|
||||
0
agent/routes/cnb-board/common.ts
Normal file
0
agent/routes/cnb-board/common.ts
Normal file
38
agent/routes/cnb-board/index.ts
Normal file
38
agent/routes/cnb-board/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { app } from '../../app.ts';
|
||||
import './cnb-dev-env.ts';
|
||||
import { useKey } from '@kevisual/context';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
export const execCommand = (command: string, options: { cwd?: string } = {}) => {
|
||||
const { cwd } = options;
|
||||
return spawnSync(command, {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
cwd: cwd,
|
||||
env: process.env,
|
||||
});
|
||||
};
|
||||
app.route({
|
||||
path: 'cnb_board',
|
||||
key: 'is-cnb-board',
|
||||
description: '检查是否是 cnb-board 环境',
|
||||
middleware: ['auth-admin']
|
||||
}).define(async (ctx) => {
|
||||
const isCNB = useKey('CNB');
|
||||
ctx.body = {
|
||||
isCNB: !!isCNB,
|
||||
};
|
||||
}).addTo(app);
|
||||
|
||||
|
||||
|
||||
|
||||
app.route({
|
||||
path: 'cnb_board',
|
||||
key: 'exit',
|
||||
description: 'cnb的工作环境退出程序',
|
||||
middleware: ['auth-admin'],
|
||||
}).define(async (ctx) => {
|
||||
const cmd = 'kill 1';
|
||||
execCommand(cmd);
|
||||
}).addTo(app);
|
||||
341
agent/routes/cnb-board/live/live-content.ts
Normal file
341
agent/routes/cnb-board/live/live-content.ts
Normal file
@@ -0,0 +1,341 @@
|
||||
|
||||
import { useKey } from "@kevisual/context"
|
||||
import os from 'node:os';
|
||||
import { execSync } from 'node:child_process';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export const getLiveMdContent = (opts?: { more?: boolean }) => {
|
||||
const more = opts?.more ?? false
|
||||
const url = useKey('CNB_VSCODE_PROXY_URI') || ''
|
||||
const token = useKey('CNB_TOKEN') || ''
|
||||
const openclawPort = useKey('OPENCLAW_PORT') || '80'
|
||||
const openclawUrl = url.replace('{{port}}', openclawPort)
|
||||
const openclawUrlSecret = openclawUrl + '/openclaw?token=' + token
|
||||
|
||||
const opencodePort = useKey('OPENCODE_PORT') || '100'
|
||||
const opencodeUrl = url.replace('{{port}}', opencodePort)
|
||||
// btoa('root:password'); //
|
||||
const _opencodeURL = new URL(opencodeUrl)
|
||||
_opencodeURL.username = 'root'
|
||||
_opencodeURL.password = token
|
||||
const opencodeUrlSecret = _opencodeURL.toString()
|
||||
|
||||
// console.log('btoa opencode auth: ', Buffer.from(`root:${token}`).toString('base64'))
|
||||
const kevisualUrl = url.replace('{{port}}', '51515')
|
||||
|
||||
const openWebUrl = url.replace('{{port}}', '200')
|
||||
|
||||
const vscodeWebUrl = useKey('CNB_VSCODE_WEB_URL') || ''
|
||||
|
||||
const TEMPLATE = `# 开发环境模式配置
|
||||
|
||||
### 服务访问地址
|
||||
#### nginx 反向代理访问(推荐)
|
||||
- OpenClaw: ${openclawUrl + '/openclaw'}
|
||||
- OpenCode: ${opencodeUrl}
|
||||
- VSCode Web: ${vscodeWebUrl}
|
||||
- OpenWebUI: ${openWebUrl}
|
||||
- Kevisual: ${kevisualUrl}
|
||||
|
||||
### 密码访问
|
||||
- OpenClaw: ${openclawUrlSecret}
|
||||
- OpenCode: ${opencodeUrlSecret}
|
||||
|
||||
### 环境变量
|
||||
- CNB_TOKEN: ${token}
|
||||
|
||||
### 其他说明
|
||||
|
||||
1. 保活说明
|
||||
使用插件访问vscode web获取wss进行保活,避免长时间不操作导致的自动断开连接。
|
||||
|
||||
方法1: 使用插件访问vscode web获取wss进行保活,避免长时间不操作导致的自动断开连接。
|
||||
|
||||
1. 安装插件[CNB LIVE](https://chromewebstore.google.com/detail/cnb-live/iajpiophkcdghonpijkcgpjafbcjhkko?pli=1)
|
||||
2. 打开vscode web获取,点击插件,获取json数据,替换keep.json中的数据,保持在线状态。
|
||||
3. keep.json中的数据结构说明:
|
||||
- wss: vscode web的websocket地址
|
||||
- cookie: vscode web的cookie,保持和浏览器一致
|
||||
- url: vscode web的访问地址,可以直接访问vscode web
|
||||
4. 运行cli命令,ev cnb live -c /workspace/live/keep.json.(直接对话opencode或者openclaw调用cnb-live技能即可)
|
||||
|
||||
方法2:环境变量设置CNB_COOKIE,直接opencode或者openclaw的ui界面对话说,cnb-keep-live保活,他会自动调用保活,同时不需要点cnb-lie插件获取配置。
|
||||
|
||||
2. Opencode web访问说明
|
||||
Opencode打开web地址,需要在浏览器输入用户名和密码,用户名固定为root,密码为CNB_TOKEN的值. 纯连接打开包含账号密码,第一次点击后,需要把账号密码清理掉才能访问,opencode的bug导致的。
|
||||
`
|
||||
const labels = [
|
||||
{
|
||||
key: 'vscodeWebUrl',
|
||||
title: 'VSCode Web 地址',
|
||||
value: vscodeWebUrl,
|
||||
description: 'VSCode Web 的访问地址'
|
||||
},
|
||||
{
|
||||
key: 'kevisualUrl',
|
||||
title: 'Kevisual 地址',
|
||||
value: kevisualUrl,
|
||||
description: 'Kevisual 的访问地址,可以通过该地址访问 Kevisual 服务'
|
||||
},
|
||||
{
|
||||
key: 'cnbTempToken',
|
||||
title: 'CNB Token',
|
||||
value: token,
|
||||
description: 'CNB 临时 Token,保持和环境变量 CNB_TOKEN 一致'
|
||||
},
|
||||
{
|
||||
key: 'openWebUrl',
|
||||
title: 'OpenWebUI 地址',
|
||||
value: openWebUrl,
|
||||
description: 'OpenWebUI 的访问地址,可以通过该地址访问 OpenWebUI 服务'
|
||||
},
|
||||
{
|
||||
key: 'openclawUrl',
|
||||
title: 'OpenClaw 地址',
|
||||
value: openclawUrl + '/openclaw',
|
||||
description: 'OpenClaw 的访问地址,可以通过该地址访问 OpenClaw 服务'
|
||||
},
|
||||
{
|
||||
key: 'openclawUrlSecret',
|
||||
title: 'OpenClaw 访问地址(含 Token)',
|
||||
value: openclawUrlSecret,
|
||||
description: 'OpenClaw 的访问地址,包含 token 参数,可以直接访问 OpenClaw 服务'
|
||||
},
|
||||
{
|
||||
key: 'opencodeUrl',
|
||||
title: 'OpenCode 地址',
|
||||
value: opencodeUrl,
|
||||
description: 'OpenCode 的访问地址,可以通过该地址访问 OpenCode 服务'
|
||||
},
|
||||
{
|
||||
key: 'opencodeUrlSecret',
|
||||
title: 'OpenCode 访问地址(含 Token)',
|
||||
value: opencodeUrlSecret,
|
||||
description: 'OpenCode 的访问地址,包含 token 参数,可以直接访问 OpenCode 服务'
|
||||
},
|
||||
{
|
||||
key: 'docs',
|
||||
title: '配置说明文档',
|
||||
value: TEMPLATE,
|
||||
description: '开发环境模式配置说明文档'
|
||||
}
|
||||
]
|
||||
|
||||
const osInfoList = createOSInfo(more)
|
||||
labels.push(...osInfoList)
|
||||
return labels
|
||||
}
|
||||
|
||||
const createOSInfo = (more = false) => {
|
||||
const labels: Array<{ key: string; title: string; value: string | number; description: string }> = []
|
||||
const startTimer = useKey('CNB_BUILD_START_TIME') || ''
|
||||
|
||||
// CPU 使用率
|
||||
const cpus = os.cpus()
|
||||
let totalIdle = 0
|
||||
let totalTick = 0
|
||||
cpus.forEach((cpu) => {
|
||||
for (const type in cpu.times) {
|
||||
totalTick += cpu.times[type as keyof typeof cpu.times]
|
||||
}
|
||||
totalIdle += cpu.times.idle
|
||||
})
|
||||
const cpuUsage = ((1 - totalIdle / totalTick) * 100).toFixed(2)
|
||||
|
||||
// 内存使用情况 (使用 free 命令)
|
||||
let memUsed = 0
|
||||
let memTotal = 0
|
||||
let memFree = 0
|
||||
try {
|
||||
const freeOutput = execSync('free -b', { encoding: 'utf-8' })
|
||||
const lines = freeOutput.trim().split('\n')
|
||||
const memLine = lines.find(line => line.startsWith('Mem:'))
|
||||
if (memLine) {
|
||||
const parts = memLine.split(/\s+/)
|
||||
memTotal = parseInt(parts[1])
|
||||
memUsed = parseInt(parts[2])
|
||||
memFree = parseInt(parts[3])
|
||||
}
|
||||
} catch (e) {
|
||||
// 如果 free 命令失败,使用 os 模块
|
||||
memTotal = os.totalmem()
|
||||
memFree = os.freemem()
|
||||
memUsed = memTotal - memFree
|
||||
}
|
||||
const memUsage = memTotal > 0 ? ((memUsed / memTotal) * 100).toFixed(2) : '0.00'
|
||||
|
||||
// 格式化字节为人类可读格式
|
||||
const formatBytes = (bytes: number) => {
|
||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
if (bytes === 0) return '0 B'
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024))
|
||||
return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
// 运行时间格式化
|
||||
const formatUptime = (seconds: number) => {
|
||||
const days = Math.floor(seconds / 86400)
|
||||
const hours = Math.floor((seconds % 86400) / 3600)
|
||||
const minutes = Math.floor((seconds % 3600) / 60)
|
||||
const secs = Math.floor(seconds % 60)
|
||||
let uptimeStr = ''
|
||||
if (days > 0) uptimeStr += `${days}天 `
|
||||
if (hours > 0) uptimeStr += `${hours}小时 `
|
||||
if (minutes > 0) uptimeStr += `${minutes}分钟 `
|
||||
return `${uptimeStr}${secs}秒`
|
||||
}
|
||||
|
||||
// 磁盘使用情况 (使用 du 命令,获取当前目录)
|
||||
let diskUsage = ''
|
||||
try {
|
||||
const duOutput = execSync('du -sh .', { encoding: 'utf-8' })
|
||||
diskUsage = duOutput.trim().split('\t')[0]
|
||||
} catch (e) {
|
||||
diskUsage = '获取失败'
|
||||
}
|
||||
|
||||
labels.push(
|
||||
{
|
||||
key: 'cpuUsage',
|
||||
title: 'CPU 使用率',
|
||||
value: `${cpuUsage}%`,
|
||||
description: 'CPU 使用率'
|
||||
},
|
||||
{
|
||||
key: 'cpuCores',
|
||||
title: 'CPU 核心数',
|
||||
value: cpus.length,
|
||||
description: 'CPU 核心数'
|
||||
},
|
||||
{
|
||||
key: 'memoryUsed',
|
||||
title: '已使用内存',
|
||||
value: formatBytes(memUsed),
|
||||
description: '已使用内存'
|
||||
},
|
||||
{
|
||||
key: 'memoryTotal',
|
||||
title: '总内存',
|
||||
value: formatBytes(memTotal),
|
||||
description: '总内存'
|
||||
},
|
||||
{
|
||||
key: 'memoryFree',
|
||||
title: '空闲内存',
|
||||
value: formatBytes(memFree),
|
||||
description: '空闲内存'
|
||||
},
|
||||
{
|
||||
key: 'memoryUsage',
|
||||
title: '内存使用率',
|
||||
value: `${memUsage}%`,
|
||||
description: '内存使用率'
|
||||
},
|
||||
{
|
||||
key: 'diskUsage',
|
||||
title: '磁盘使用',
|
||||
value: diskUsage,
|
||||
description: '当前目录磁盘使用情况'
|
||||
},
|
||||
)
|
||||
|
||||
// 如果有 CNB_BUILD_START_TIME,添加构建启动时间
|
||||
if (startTimer) {
|
||||
// startTimer 是日期字符串格式
|
||||
const buildStartTime = dayjs(startTimer as string).format('YYYY-MM-DD HH:mm:ss')
|
||||
const buildStartTimestamp = dayjs(startTimer as string).valueOf()
|
||||
const buildUptime = Date.now() - buildStartTimestamp
|
||||
const buildUptimeStr = formatUptime(Math.floor(buildUptime / 1000))
|
||||
const maxRunTime = useKey('CNB_PIPELINE_MAX_RUN_TIME') || 0 // 毫秒
|
||||
|
||||
labels.push(
|
||||
{
|
||||
key: 'buildStartTime',
|
||||
title: '构建启动时间',
|
||||
value: buildStartTime,
|
||||
description: '构建启动时间'
|
||||
},
|
||||
{
|
||||
key: 'buildUptime',
|
||||
title: '构建已运行时间',
|
||||
value: buildUptime,
|
||||
description: `构建已运行时间: ${buildUptimeStr}`
|
||||
}
|
||||
)
|
||||
if (maxRunTime > 0) {
|
||||
// 计算到达4点的倒计时
|
||||
const now = dayjs()
|
||||
const today4am = now.hour(4).minute(0).second(0).millisecond(0)
|
||||
let timeTo4 = today4am.valueOf() - now.valueOf()
|
||||
if (timeTo4 < 0) {
|
||||
// 如果已经过了4点,计算到明天4点
|
||||
timeTo4 = today4am.add(1, 'day').valueOf() - now.valueOf()
|
||||
}
|
||||
const timeTo4Str = `[距离晚上4点重启时间: ${formatUptime(Math.floor(timeTo4 / 1000))}]`
|
||||
|
||||
labels.push({
|
||||
key: 'buildMaxRunTime',
|
||||
title: '最大运行时间',
|
||||
value: formatUptime(Math.floor(maxRunTime / 1000)),
|
||||
description: '构建最大运行时间(限制时间)'
|
||||
})
|
||||
labels.unshift({
|
||||
key: 'remainingTime',
|
||||
title: '剩余时间',
|
||||
value: maxRunTime - buildUptime,
|
||||
description: '构建剩余时间' + formatUptime(Math.floor((maxRunTime - buildUptime) / 1000)) + ' ' + timeTo4Str
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// more 为 true 时添加更多系统信息
|
||||
if (more) {
|
||||
const loadavg = os.loadavg()
|
||||
labels.push(
|
||||
{
|
||||
key: 'hostname',
|
||||
title: '主机名',
|
||||
value: os.hostname(),
|
||||
description: '主机名'
|
||||
},
|
||||
{
|
||||
key: 'platform',
|
||||
title: '运行平台',
|
||||
value: os.platform(),
|
||||
description: '运行平台'
|
||||
},
|
||||
{
|
||||
key: 'arch',
|
||||
title: '系统架构',
|
||||
value: os.arch(),
|
||||
description: '系统架构'
|
||||
},
|
||||
{
|
||||
key: 'osType',
|
||||
title: '操作系统类型',
|
||||
value: os.type(),
|
||||
description: '操作系统类型'
|
||||
},
|
||||
{
|
||||
key: 'loadavg1m',
|
||||
title: '系统负载 (1分钟)',
|
||||
value: loadavg[0].toFixed(2),
|
||||
description: '系统负载 (1分钟)'
|
||||
},
|
||||
{
|
||||
key: 'loadavg5m',
|
||||
title: '系统负载 (5分钟)',
|
||||
value: loadavg[1].toFixed(2),
|
||||
description: '系统负载 (5分钟)'
|
||||
},
|
||||
{
|
||||
key: 'loadavg15m',
|
||||
title: '系统负载 (15分钟)',
|
||||
value: loadavg[2].toFixed(2),
|
||||
description: '系统负载 (15分钟)'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return labels
|
||||
}
|
||||
1
agent/routes/cnb-board/modules/index.ts
Normal file
1
agent/routes/cnb-board/modules/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './is-cnb.ts';
|
||||
6
agent/routes/cnb-board/modules/is-cnb.ts
Normal file
6
agent/routes/cnb-board/modules/is-cnb.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { useKey } from "@kevisual/context";
|
||||
|
||||
export const isCnb = () => {
|
||||
const CNB = useKey('CNB');
|
||||
return !!CNB;
|
||||
}
|
||||
@@ -7,7 +7,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'user-check',
|
||||
description: '检查用户登录状态,参数checkToken,default true; checkCookie, default false',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
|
||||
@@ -6,7 +6,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'set-cnb-cookie',
|
||||
description: '设置当前cnb工作空间的cookie环境变量',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -33,7 +33,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'get-cnb-cookie',
|
||||
description: '获取当前cnb工作空间的cookie环境变量',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
|
||||
@@ -11,7 +11,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'get-cnb-port-uri',
|
||||
description: '获取当前cnb工作空间的port代理uri',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -19,12 +19,12 @@ app.route({
|
||||
title: '获取当前cnb工作空间的port代理uri',
|
||||
summary: '获取当前cnb工作空间的port代理uri,用于端口转发',
|
||||
args: {
|
||||
port: tool.schema.number().optional().describe('端口号,默认为4096'),
|
||||
port: tool.schema.number().optional().describe('端口号,默认为51515'),
|
||||
}
|
||||
})
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const port = ctx.query?.port || 4096;
|
||||
const port = ctx.query?.port || 51515;
|
||||
const uri = CNB_ENV?.CNB_VSCODE_PROXY_URI as string || '';
|
||||
const finalUri = uri.replace('{{port}}', port.toString());
|
||||
let content = `
|
||||
@@ -40,7 +40,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'get-cnb-vscode-uri',
|
||||
description: '获取当前cnb工作空间的vscode代理uri, 包括多种访问方式, 如web、vscode、codebuddy、cursor、ssh',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
|
||||
@@ -6,7 +6,8 @@ import './call/index.ts'
|
||||
import './cnb-env/index.ts'
|
||||
import './knowledge/index.ts'
|
||||
import './issues/index.ts'
|
||||
|
||||
import './cnb-board/index.ts';
|
||||
import './share/index.ts';
|
||||
/**
|
||||
* 验证上下文中的 App ID 是否与指定的 App ID 匹配
|
||||
* @param {any} ctx - 上下文对象,可能包含 appId 属性
|
||||
@@ -25,25 +26,23 @@ const checkAppId = (ctx: any, appId: string) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!app.hasRoute('auth')) {
|
||||
app.route({
|
||||
id: 'auth',
|
||||
path: 'auth',
|
||||
}).define(async (ctx) => {
|
||||
// ctx.body = 'Auth Route';
|
||||
if (checkAppId(ctx, app.appId)) {
|
||||
return;
|
||||
}
|
||||
}).addTo(app);
|
||||
app.route({
|
||||
id: 'auth',
|
||||
path: 'auth',
|
||||
}).define(async (ctx) => {
|
||||
// ctx.body = 'Auth Route';
|
||||
if (checkAppId(ctx, app.appId)) {
|
||||
return;
|
||||
}
|
||||
}).addTo(app, { overwrite: false });
|
||||
|
||||
app.route({
|
||||
id: 'admin-auth',
|
||||
path: 'admin-auth',
|
||||
middleware: ['auth'],
|
||||
}).define(async (ctx) => {
|
||||
// ctx.body = 'Admin Auth Route';
|
||||
if (checkAppId(ctx, app.appId)) {
|
||||
return;
|
||||
}
|
||||
}).addTo(app);
|
||||
}
|
||||
app.route({
|
||||
id: 'auth-admin',
|
||||
path: 'auth-admin',
|
||||
middleware: ['auth'],
|
||||
}).define(async (ctx) => {
|
||||
// ctx.body = 'Admin Auth Route';
|
||||
if (checkAppId(ctx, app.appId)) {
|
||||
return;
|
||||
}
|
||||
}).addTo(app, { overwrite: false });
|
||||
@@ -7,7 +7,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'create-issue',
|
||||
description: '创建 Issue, 参数 repo, title, body, assignees, labels, priority',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -51,7 +51,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'complete-issue',
|
||||
description: '完成 Issue, 参数 repo, issueNumber',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { createSkill, tool } from '@kevisual/router';
|
||||
import { app, cnb } from '../../app.ts';
|
||||
import { useKey } from '@kevisual/context';
|
||||
|
||||
// 查询 Issue 列表 repo是 kevisual/kevisual
|
||||
app.route({
|
||||
path: 'cnb',
|
||||
key: 'list-issues',
|
||||
description: '查询 Issue 列表, 参数 repo, state, keyword, labels, page, page_size 等',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -25,7 +26,7 @@ app.route({
|
||||
})
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const repo = ctx.query?.repo;
|
||||
const repo = ctx.query?.repo || useKey('CNB_REPO_SLUG_LOWERCASE');
|
||||
const state = ctx.query?.state;
|
||||
const keyword = ctx.query?.keyword;
|
||||
const labels = ctx.query?.labels;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createSkill, tool } from '@kevisual/router';
|
||||
import { app, cnb } from '../../app.ts';
|
||||
import { CNBChat } from '@kevisual/ai/browser'
|
||||
import { useKey } from '@kevisual/context';
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +13,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'cnb-ai-chat',
|
||||
description: '调用cnb的知识库ai对话功能进行聊天',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -88,7 +89,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'cnb-rag-query',
|
||||
description: '调用cnb的知识库RAG查询功能进行问答',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -107,7 +108,7 @@ app.route({
|
||||
ctx.body = { content: '请提供有效的消息内容' };
|
||||
return;
|
||||
}
|
||||
let repo = ctx.query?.repo;
|
||||
let repo = ctx.query?.repo || useKey('CNB_REPO_SLUG_LOWERCASE');
|
||||
if (!repo) {
|
||||
// 如果未指定知识库仓库ID,则使用默认知识库
|
||||
const res = await cnb.repo.getRepoList({ flags: 'KnowledgeBase' });
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createSkill } from '@kevisual/router';
|
||||
import { createSkill, tool } from '@kevisual/router';
|
||||
import { app, cnb } from '../../app.ts';
|
||||
import { tool } from "@opencode-ai/plugin/tool"
|
||||
|
||||
// "列出我的代码仓库,search blog"
|
||||
// 列出我的知识库的代码仓库
|
||||
@@ -8,7 +7,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'list-repos',
|
||||
description: '列出我的代码仓库',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { app, cnb } from '../../app.ts';
|
||||
import { createSkill, Skill } from '@kevisual/router'
|
||||
import { tool } from "@opencode-ai/plugin/tool"
|
||||
import { createSkill, Skill, tool } from '@kevisual/router'
|
||||
|
||||
// 创建一个仓库 kevisual/test-repo
|
||||
app.route({
|
||||
path: 'cnb',
|
||||
key: 'create-repo',
|
||||
description: '创建代码仓库, 参数name, visibility, description',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -46,8 +45,8 @@ app.route({
|
||||
app.route({
|
||||
path: 'cnb',
|
||||
key: 'create-repo-file',
|
||||
description: '在代码仓库中创建文件, repoName, filePath, content, encoding',
|
||||
middleware: ['admin-auth'],
|
||||
description: '在代码仓库中创建文件, repoName, filePath, content, encoding。使用CNB_COOKIE进行鉴权',
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -86,7 +85,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'delete-repo',
|
||||
description: '删除代码仓库, 参数name',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
|
||||
48
agent/routes/share/index.ts
Normal file
48
agent/routes/share/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useKey } from '@kevisual/context';
|
||||
import { app, cnb } from '../../app.ts';
|
||||
import z from 'zod';
|
||||
|
||||
app.route({
|
||||
path: 'cnb',
|
||||
key: 'get-assistant-url',
|
||||
description: '获取cnb工作空间中部署的各个助手的访问地址',
|
||||
middleware: ['auth'],
|
||||
metadata: {
|
||||
args: {
|
||||
more: z.boolean().describe('需要更多信息')
|
||||
}
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const uri = useKey('CNB_VSCODE_PROXY_URI') as string || '';
|
||||
const base = {
|
||||
base: uri,
|
||||
link: uri.replace('{{port}}', '51515'),
|
||||
kevisual: uri.replace('{{port}}', '51515'),
|
||||
openclaw: uri.replace('{{port}}', '80'),
|
||||
opencode: uri.replace('{{port}}', '100'),
|
||||
openwebui: uri.replace('{{port}}', '200'),
|
||||
note: uri.replace('{{port}}', '3000'),
|
||||
uptime: uri.replace('{{port}}', '3001'),
|
||||
immich: uri.replace('{{port}}', '2283'),
|
||||
nocodb: uri.replace('{{port}}', '4000'),
|
||||
openlist: uri.replace('{{port}}', '5244'),
|
||||
xiaoyao: uri.replace('{{port}}', '5678'),
|
||||
meilisearch: uri.replace('{{port}}', '7700'),
|
||||
bark: uri.replace('{{port}}', '9111'),
|
||||
vaultwarden: uri.replace('{{port}}', '8180'),
|
||||
music: uri.replace('{{port}}', '8096'),
|
||||
jellyfin: uri.replace('{{port}}', '8096'),
|
||||
homeassistant: uri.replace('{{port}}', '8123'),
|
||||
cloudreve: uri.replace('{{port}}', '5212'),
|
||||
filebrowser: uri.replace('{{port}}', '8081'),
|
||||
// newapi: uri.replace('{{port}}', '8080'),
|
||||
vscode: useKey('CNB_VSCODE_WEB_URL') as string || '',
|
||||
codeServer: uri.replace('{{port}}', '10000'),
|
||||
gitea: uri.replace('{{port}}', '3000'),
|
||||
calibre: uri.replace('{{port}}', '8083'),
|
||||
searXNG: uri.replace('{{port}}', '8888'),
|
||||
}
|
||||
ctx.body = {
|
||||
...base,
|
||||
}
|
||||
}).addTo(app);
|
||||
@@ -9,7 +9,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'start-workspace',
|
||||
description: '启动开发工作空间, 参数 repo',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -42,7 +42,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'list-workspace',
|
||||
description: '获取cnb开发工作空间列表,可选参数 status=running 获取运行中的环境',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -73,7 +73,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'get-workspace',
|
||||
description: '获取工作空间详情,通过 repo 和 sn 获取',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -104,7 +104,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'delete-workspace',
|
||||
description: '删除工作空间,通过 pipelineId 或 sn',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -143,7 +143,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'stop-workspace',
|
||||
description: '停止工作空间,通过 pipelineId 或 sn',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
|
||||
@@ -1,214 +1,97 @@
|
||||
import { createSkill, tool } from '@kevisual/router';
|
||||
import { 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>();
|
||||
import { addKeepAliveData, KeepAliveData, removeKeepAliveData, createLiveData } from '../../../src/workspace/keep-file-live.ts';
|
||||
import { useKey } from '@kevisual/context';
|
||||
|
||||
// 保持工作空间存活技能
|
||||
app.route({
|
||||
path: 'cnb',
|
||||
key: 'keep-workspace-alive',
|
||||
description: '保持工作空间存活技能,参数wsUrl:工作空间访问URL,cookie:访问工作空间所需的cookie',
|
||||
middleware: ['admin-auth'],
|
||||
description: '保持工作空间存活技能,参数repo:代码仓库路径,例如 user/repo,pipelineId:流水线ID,例如 cnb-708-1ji9sog7o-001',
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: [],
|
||||
...({
|
||||
args: {
|
||||
wsUrl: tool.schema.string().describe('工作空间的访问URL'),
|
||||
cookie: tool.schema.string().describe('访问工作空间所需的cookie')
|
||||
repo: tool.schema.string().describe('代码仓库路径,例如 user/repo'),
|
||||
pipelineId: tool.schema.string().describe('流水线ID,例如 cnb-708-1ji9sog7o-001'),
|
||||
}
|
||||
})
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const wsUrl = ctx.query?.wsUrl as string;
|
||||
const cookie = ctx.query?.cookie as string;
|
||||
if (!wsUrl) {
|
||||
ctx.throw(400, '缺少工作空间访问URL参数');
|
||||
const repo = ctx.query?.repo as string;
|
||||
const pipelineId = ctx.query?.pipelineId as string;
|
||||
|
||||
if (!repo || !pipelineId) {
|
||||
ctx.throw(400, '缺少参数 repo 或 pipelineId');
|
||||
}
|
||||
if (!cookie) {
|
||||
ctx.throw(400, '缺少访问工作空间所需的cookie参数');
|
||||
const validCookie = await cnb.user.checkCookieValid()
|
||||
if (validCookie.code !== 200) {
|
||||
ctx.throw(401, 'CNB_COOKIE 环境变量无效或已过期,请重新登录获取新的cookie');
|
||||
}
|
||||
const res = await cnb.workspace.getWorkspaceCookie(repo, pipelineId);
|
||||
if (res.code !== 200 || !res.data?.cookie) {
|
||||
ctx.throw(500, `获取工作空间 Cookie 失败: ${res.message}`);
|
||||
}
|
||||
|
||||
// 检测是否已在运行(通过 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 liveData = createLiveData({
|
||||
repo,
|
||||
pipelineId,
|
||||
cookie: res.data.cookie
|
||||
});
|
||||
addKeepAliveData(liveData);
|
||||
console.log('已添加 keep-alive 数据');
|
||||
|
||||
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 };
|
||||
ctx.body = { content: `已启动保持工作空间 ${repo}/${pipelineId} 存活的任务`, data: liveData };
|
||||
}).addTo(app);
|
||||
|
||||
// 停止保持工作空间存活技能
|
||||
app.route({
|
||||
path: 'cnb',
|
||||
key: 'stop-keep-workspace-alive',
|
||||
description: '停止保持工作空间存活技能, 参数wsUrl:工作空间访问URL或者id',
|
||||
middleware: ['admin-auth'],
|
||||
description: '停止保持工作空间存活技能, 参数repo:代码仓库路径,例如 user/repo,pipelineId:流水线ID,例如 cnb-708-1ji9sog7o-001',
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: [],
|
||||
...({
|
||||
args: {
|
||||
wsUrl: tool.schema.string().optional().describe('工作空间的访问URL'),
|
||||
id: tool.schema.string().optional().describe('保持存活任务的唯一标识符'),
|
||||
repo: tool.schema.string().describe('代码仓库路径,例如 user/repo'),
|
||||
pipelineId: tool.schema.string().describe('流水线ID,例如 cnb-708-1ji9sog7o-001'),
|
||||
}
|
||||
})
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const wsUrl = ctx.query?.wsUrl as string;
|
||||
const id = ctx.query?.id as string;
|
||||
if (!wsUrl && !id) {
|
||||
ctx.throw(400, '缺少工作空间访问URL参数或唯一标识符');
|
||||
}
|
||||
const repo = ctx.query?.repo as string;
|
||||
const pipelineId = ctx.query?.pipelineId as string;
|
||||
|
||||
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: `没有找到对应的工作空间保持存活任务` };
|
||||
if (!repo || !pipelineId) {
|
||||
ctx.throw(400, '缺少参数 repo 或 pipelineId');
|
||||
}
|
||||
removeKeepAliveData(repo, pipelineId);
|
||||
ctx.body = { content: `已停止保持工作空间 ${repo}/${pipelineId} 存活的任务` };
|
||||
}).addTo(app);
|
||||
|
||||
|
||||
|
||||
|
||||
app.route({
|
||||
path: 'cnb',
|
||||
key: 'reset-keep-workspace-alive',
|
||||
description: '对存活的工作空间,startTime进行重置',
|
||||
middleware: ['admin-auth'],
|
||||
key: 'keep-alive-current-workspace',
|
||||
description: '保持当前工作空间存活技能',
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: [],
|
||||
tags: ['opencode'],
|
||||
skill: 'keep-alive-current-workspace',
|
||||
title: '保持当前工作空间存活',
|
||||
summary: '保持当前工作空间存活,防止被关闭或释放资源',
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const now = Date.now();
|
||||
for (const info of keepAliveMap.values()) {
|
||||
info.startTime = now;
|
||||
const pipelineId = useKey('CNB_PIPELINE_ID');
|
||||
const repo = useKey('CNB_REPO_SLUG_LOWERCASE');
|
||||
if (!pipelineId || !repo) {
|
||||
ctx.throw(400, '当前环境缺少 CNB_PIPELINE_ID 或 CNB_REPO_SLUG_LOWERCASE 环境变量,无法保持工作空间存活');
|
||||
}
|
||||
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);
|
||||
const res = await app.run({ path: 'cnb', key: 'keep-workspace-alive', payload: { repo, pipelineId } }, ctx);
|
||||
ctx.forward(res);
|
||||
}).addTo(app);
|
||||
@@ -35,7 +35,7 @@ app.route({
|
||||
path: 'cnb',
|
||||
key: 'clean-closed-workspace',
|
||||
description: '批量删除已停止的cnb工作空间',
|
||||
middleware: ['admin-auth'],
|
||||
middleware: ['auth-admin'],
|
||||
metadata: {
|
||||
tags: ['opencode'],
|
||||
...createSkill({
|
||||
@@ -45,7 +45,7 @@ app.route({
|
||||
})
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const closedWorkspaces = await cnb.workspace.list({ status: 'closed' });
|
||||
const closedWorkspaces = await cnb.workspace.list({ status: 'closed', pageSize: 100 });
|
||||
if (closedWorkspaces.code !== 200) {
|
||||
ctx.throw(500, '获取已关闭工作空间列表失败');
|
||||
}
|
||||
|
||||
2
bin/index.js
Executable file
2
bin/index.js
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bun
|
||||
import '../dist/cli.js';
|
||||
@@ -1,4 +1,6 @@
|
||||
import { buildWithBun } from '@kevisual/code-builder'
|
||||
await buildWithBun({ naming: 'opencode', entry: 'agent/opencode.ts', dts: true });
|
||||
await buildWithBun({ naming: 'keep', entry: 'src/keep.ts', dts: true });
|
||||
await buildWithBun({ naming: 'routes', entry: 'agent/index.ts', dts: true });
|
||||
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: 'cli', entry: 'agent/command.ts', dts: true, target: 'node' });
|
||||
236
bun.lock
Normal file
236
bun.lock
Normal file
@@ -0,0 +1,236 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "@kevisual/cnb",
|
||||
"dependencies": {
|
||||
"@kevisual/query": "^0.0.49",
|
||||
"@kevisual/router": "^0.0.80",
|
||||
"@kevisual/use-config": "^1.0.30",
|
||||
"es-toolkit": "^1.44.0",
|
||||
"nanoid": "^5.1.6",
|
||||
"unstorage": "^1.17.4",
|
||||
"ws": "npm:@kevisual/ws",
|
||||
"zod": "^4.3.6",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kevisual/ai": "^0.0.24",
|
||||
"@kevisual/code-builder": "^0.0.6",
|
||||
"@kevisual/context": "^0.0.8",
|
||||
"@kevisual/dts": "^0.0.4",
|
||||
"@kevisual/types": "^0.0.12",
|
||||
"@opencode-ai/plugin": "^1.2.10",
|
||||
"@types/bun": "^1.3.9",
|
||||
"@types/node": "^25.3.0",
|
||||
"@types/ws": "^8.18.1",
|
||||
"dayjs": "^1.11.19",
|
||||
"dotenv": "^17.3.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
"overrides": {
|
||||
"zod": "^4.3.6",
|
||||
},
|
||||
"packages": {
|
||||
"@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@kevisual/ai": ["@kevisual/ai@0.0.24", "", { "dependencies": { "@kevisual/logger": "^0.0.4", "@kevisual/permission": "^0.0.3", "@kevisual/query": "^0.0.38" } }, "sha512-7jvZk1/L//VIClK7usuNgN4ZA9Etgbooka1Sj5quE/0UywR+NNnwqXVZ89Y1fBhI1TkhauDsdJBAtcQ7r/vbVw=="],
|
||||
|
||||
"@kevisual/code-builder": ["@kevisual/code-builder@0.0.6", "", { "bin": { "code-builder": "bin/code.js", "builder": "bin/code.js" } }, "sha512-0aqATB31/yw4k4s5/xKnfr4DKbUnx8e3Z3BmKbiXTrc+CqWiWTdlGe9bKI9dZ2Df+xNp6g11W4xM2NICNyyCCw=="],
|
||||
|
||||
"@kevisual/context": ["@kevisual/context@0.0.8", "", {}, "sha512-DTJpyHI34NE76B7g6f+QlIqiCCyqI2qkBMQE736dzeRDGxOjnbe2iQY9W+Rt2PE6kmymM3qyOmSfNovyWyWrkA=="],
|
||||
|
||||
"@kevisual/dts": ["@kevisual/dts@0.0.4", "", { "dependencies": { "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-typescript": "^12.3.0", "rollup": "^4.57.1", "rollup-plugin-dts": "^6.3.0", "tslib": "^2.8.1" }, "bin": { "dts": "bin/dts.mjs" } }, "sha512-FVUaH/0nyhbHWpEVjFTGP54PLMm4Hf06aqWLdHOYHNPIgr1aK1C26kOH7iumklGFGk9w93IGxj8Zxe5fap5N2A=="],
|
||||
|
||||
"@kevisual/load": ["@kevisual/load@0.0.6", "", { "dependencies": { "eventemitter3": "^5.0.1" } }, "sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA=="],
|
||||
|
||||
"@kevisual/logger": ["@kevisual/logger@0.0.4", "", {}, "sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw=="],
|
||||
|
||||
"@kevisual/permission": ["@kevisual/permission@0.0.3", "", {}, "sha512-8JsA/5O5Ax/z+M+MYpFYdlioHE6jNmWMuFSokBWYs9CCAHNiSKMR01YLkoVDoPvncfH/Y8F5K/IEXRCbptuMNA=="],
|
||||
|
||||
"@kevisual/query": ["@kevisual/query@0.0.49", "", {}, "sha512-GrWW+QlBO5lkiqvb7PjOstNtpTQVSR74EHHWjm7YoL9UdT1wuPQXGUApZHmMBSh3NIWCf0AL2G1hPWZMC7YeOQ=="],
|
||||
|
||||
"@kevisual/router": ["@kevisual/router@0.0.80", "", { "dependencies": { "es-toolkit": "^1.44.0" } }, "sha512-rVwi6Yf411bnNm2x94lMm+s4Csw0Yb7u/aj+VJJ59iouAYhjLuL7Rs1EcARhnQf47cegBJi6zozfGHgLsLHN2w=="],
|
||||
|
||||
"@kevisual/types": ["@kevisual/types@0.0.12", "", {}, "sha512-zJXH2dosir3jVrQ6QG4i0+iLQeT9gJ3H+cKXs8ReWboxBSYzUZO78XssVeVrFPsJ33iaAqo4q3DWbSS1dWGn7Q=="],
|
||||
|
||||
"@kevisual/use-config": ["@kevisual/use-config@1.0.30", "", { "dependencies": { "@kevisual/load": "^0.0.6" }, "peerDependencies": { "dotenv": "^17" } }, "sha512-kPdna0FW/X7D600aMdiZ5UTjbCo6d8d4jjauSc8RMmBwUU6WliFDSPUNKVpzm2BsDX5Nth1IXFPYMqH+wxqAmw=="],
|
||||
|
||||
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.2.10", "", { "dependencies": { "@opencode-ai/sdk": "1.2.10", "zod": "4.1.8" } }, "sha512-Z1BMqNHnD8AGAEb+kUz0b2SOuiODwdQLdCA4aVGTXqkGzhiD44OVxr85MeoJ5AMTnnea9SnJ3jp9GAQ5riXA5g=="],
|
||||
|
||||
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.2.10", "", {}, "sha512-SyXcVqry2hitPVvQtvXOhqsWyFhSycG/+LTLYXrcq8AFmd9FR7dyBSDB3f5Ol6IPkYOegk8P2Eg2kKPNSNiKGw=="],
|
||||
|
||||
"@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@29.0.0", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-U2YHaxR2cU/yAiwKJtJRhnyLk7cifnQw0zUpISsocBDoHDJn+HTV74ABqnwr5bEgWUwFZC9oFL6wLe21lHu5eQ=="],
|
||||
|
||||
"@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg=="],
|
||||
|
||||
"@rollup/plugin-typescript": ["@rollup/plugin-typescript@12.3.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.14.0||^3.0.0||^4.0.0", "tslib": "*", "typescript": ">=3.7.0" }, "optionalPeers": ["rollup", "tslib"] }, "sha512-7DP0/p7y3t67+NabT9f8oTBFE6gGkto4SA6Np2oudYmZE/m1dt8RB0SjL1msMxFpLo631qjRCcBlAbq1ml/Big=="],
|
||||
|
||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw=="],
|
||||
|
||||
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw=="],
|
||||
|
||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/node": ["@types/node@25.3.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="],
|
||||
|
||||
"@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
||||
|
||||
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||
|
||||
"commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="],
|
||||
|
||||
"cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="],
|
||||
|
||||
"crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="],
|
||||
|
||||
"dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="],
|
||||
|
||||
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
||||
|
||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
|
||||
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
|
||||
|
||||
"dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="],
|
||||
|
||||
"es-toolkit": ["es-toolkit@1.44.0", "", {}, "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg=="],
|
||||
|
||||
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"h3": ["h3@1.15.5", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="],
|
||||
|
||||
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
|
||||
|
||||
"is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="],
|
||||
|
||||
"is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="],
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
|
||||
|
||||
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
|
||||
|
||||
"node-mock-http": ["node-mock-http@1.0.4", "", {}, "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ=="],
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
|
||||
|
||||
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="],
|
||||
|
||||
"readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||
|
||||
"resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
|
||||
|
||||
"rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="],
|
||||
|
||||
"rollup-plugin-dts": ["rollup-plugin-dts@6.3.0", "", { "dependencies": { "magic-string": "^0.30.21" }, "optionalDependencies": { "@babel/code-frame": "^7.27.1" }, "peerDependencies": { "rollup": "^3.29.4 || ^4", "typescript": "^4.5 || ^5.0" } }, "sha512-d0UrqxYd8KyZ6i3M2Nx7WOMy708qsV/7fTHMHxCMCBOAe3V/U7OMPu5GkX8hC+cmkHhzGnfeYongl1IgiooddA=="],
|
||||
|
||||
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
|
||||
|
||||
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
||||
|
||||
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
"unstorage": ["unstorage@1.17.4", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.5", "lru-cache": "^11.2.0", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw=="],
|
||||
|
||||
"ws": ["@kevisual/ws@8.19.0", "", {}, "sha512-jLsL80wBBKkrJZrfk3SQpJ9JA/zREdlUROj7eCkmzqduAWKSI0wVcXuCKf+mLFCHB0Q0Tkh2rgzjSlurt3JQgw=="],
|
||||
|
||||
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||
|
||||
"@kevisual/ai/@kevisual/query": ["@kevisual/query@0.0.38", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q=="],
|
||||
|
||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
}
|
||||
}
|
||||
13
keep.ts
Normal file
13
keep.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createKeepAlive } from "@kevisual/cnb/keep";
|
||||
|
||||
const config = {
|
||||
"wss": "wss://cnb-tmm-1jhgl3i0m-001.cnb.space:443/stable-3c0b449c6e6e37b44a8a7938c0d8a3049926a64c?reconnectionToken=26ba6a08-1c57-41cc-8099-1f6e64863bf6&reconnection=false&skipWebSocketFrames=false",
|
||||
"cookie": "orange:workspace:cookie-session:cnb-tmm-1jhgl3i0m-001=93d7bc9b-9ca0-4867-963d-1928ad3038c7",
|
||||
"url": "https://cnb-tmm-1jhgl3i0m-001.cnb.space/?folder=/workspace"
|
||||
}
|
||||
|
||||
createKeepAlive({
|
||||
wsUrl: config.wss,
|
||||
cookie: config.cookie,
|
||||
debug: true,
|
||||
});
|
||||
32
package.json
32
package.json
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "@kevisual/cnb",
|
||||
"version": "0.0.22",
|
||||
"version": "0.0.37",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"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"
|
||||
},
|
||||
"keywords": [],
|
||||
"bin": {
|
||||
"cnb": "./bin/index.js"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src",
|
||||
@@ -16,20 +19,21 @@
|
||||
],
|
||||
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||
"license": "MIT",
|
||||
"packageManager": "pnpm@10.29.1",
|
||||
"packageManager": "pnpm@10.30.3",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@kevisual/ai": "^0.0.24",
|
||||
"@kevisual/ai": "^0.0.26",
|
||||
"@kevisual/code-builder": "^0.0.6",
|
||||
"@kevisual/dts": "^0.0.3",
|
||||
"@kevisual/context": "^0.0.4",
|
||||
"@kevisual/context": "^0.0.8",
|
||||
"@kevisual/dts": "^0.0.4",
|
||||
"@kevisual/types": "^0.0.12",
|
||||
"@opencode-ai/plugin": "^1.1.53",
|
||||
"@types/bun": "^1.3.8",
|
||||
"@types/node": "^25.2.2",
|
||||
"@opencode-ai/plugin": "^1.2.20",
|
||||
"@types/bun": "^1.3.10",
|
||||
"@types/node": "^25.3.5",
|
||||
"@types/ws": "^8.18.1",
|
||||
"commander": "^14.0.3",
|
||||
"dayjs": "^1.11.19",
|
||||
"dotenv": "^17.2.4"
|
||||
"dotenv": "^17.3.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -38,10 +42,10 @@
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kevisual/query": "^0.0.40",
|
||||
"@kevisual/router": "^0.0.70",
|
||||
"@kevisual/query": "^0.0.53",
|
||||
"@kevisual/router": "^0.0.88",
|
||||
"@kevisual/use-config": "^1.0.30",
|
||||
"es-toolkit": "^1.44.0",
|
||||
"es-toolkit": "^1.45.1",
|
||||
"nanoid": "^5.1.6",
|
||||
"unstorage": "^1.17.4",
|
||||
"ws": "npm:@kevisual/ws",
|
||||
@@ -51,6 +55,8 @@
|
||||
".": "./mod.ts",
|
||||
"./opencode": "./dist/opencode.js",
|
||||
"./keep": "./dist/keep.js",
|
||||
"./keep.ts": "./src/keep.ts",
|
||||
"./keep-file-live.ts": "./src/workspace/keep-file-live.ts",
|
||||
"./routes": "./dist/routes.js",
|
||||
"./src/*": "./src/*",
|
||||
"./agent/*": "./agent/*"
|
||||
|
||||
173
pnpm-lock.yaml
generated
173
pnpm-lock.yaml
generated
@@ -12,17 +12,17 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
'@kevisual/query':
|
||||
specifier: ^0.0.40
|
||||
version: 0.0.40
|
||||
specifier: ^0.0.53
|
||||
version: 0.0.53
|
||||
'@kevisual/router':
|
||||
specifier: ^0.0.70
|
||||
version: 0.0.70
|
||||
specifier: ^0.0.88
|
||||
version: 0.0.88
|
||||
'@kevisual/use-config':
|
||||
specifier: ^1.0.30
|
||||
version: 1.0.30(dotenv@17.2.4)
|
||||
version: 1.0.30(dotenv@17.3.1)
|
||||
es-toolkit:
|
||||
specifier: ^1.44.0
|
||||
version: 1.44.0
|
||||
specifier: ^1.45.1
|
||||
version: 1.45.1
|
||||
nanoid:
|
||||
specifier: ^5.1.6
|
||||
version: 5.1.6
|
||||
@@ -37,38 +37,41 @@ importers:
|
||||
version: 4.3.6
|
||||
devDependencies:
|
||||
'@kevisual/ai':
|
||||
specifier: ^0.0.24
|
||||
version: 0.0.24
|
||||
specifier: ^0.0.26
|
||||
version: 0.0.26
|
||||
'@kevisual/code-builder':
|
||||
specifier: ^0.0.6
|
||||
version: 0.0.6
|
||||
'@kevisual/context':
|
||||
specifier: ^0.0.4
|
||||
version: 0.0.4
|
||||
specifier: ^0.0.8
|
||||
version: 0.0.8
|
||||
'@kevisual/dts':
|
||||
specifier: ^0.0.3
|
||||
version: 0.0.3(typescript@5.9.3)
|
||||
specifier: ^0.0.4
|
||||
version: 0.0.4(typescript@5.9.3)
|
||||
'@kevisual/types':
|
||||
specifier: ^0.0.12
|
||||
version: 0.0.12
|
||||
'@opencode-ai/plugin':
|
||||
specifier: ^1.1.53
|
||||
version: 1.1.53
|
||||
specifier: ^1.2.20
|
||||
version: 1.2.20
|
||||
'@types/bun':
|
||||
specifier: ^1.3.8
|
||||
version: 1.3.8
|
||||
specifier: ^1.3.10
|
||||
version: 1.3.10
|
||||
'@types/node':
|
||||
specifier: ^25.2.2
|
||||
version: 25.2.2
|
||||
specifier: ^25.3.5
|
||||
version: 25.3.5
|
||||
'@types/ws':
|
||||
specifier: ^8.18.1
|
||||
version: 8.18.1
|
||||
commander:
|
||||
specifier: ^14.0.3
|
||||
version: 14.0.3
|
||||
dayjs:
|
||||
specifier: ^1.11.19
|
||||
version: 1.11.19
|
||||
dotenv:
|
||||
specifier: ^17.2.4
|
||||
version: 17.2.4
|
||||
specifier: ^17.3.1
|
||||
version: 17.3.1
|
||||
|
||||
packages:
|
||||
|
||||
@@ -83,18 +86,18 @@ packages:
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
'@kevisual/ai@0.0.24':
|
||||
resolution: {integrity: sha512-7jvZk1/L//VIClK7usuNgN4ZA9Etgbooka1Sj5quE/0UywR+NNnwqXVZ89Y1fBhI1TkhauDsdJBAtcQ7r/vbVw==}
|
||||
'@kevisual/ai@0.0.26':
|
||||
resolution: {integrity: sha512-lhaMpxi+vgqPdyBKiuNbSil4hy13tNLbDiqCtG0qUXKtvoowK6xMx269pSSYkYBivczM8g8I0XEouuJceUpJPg==}
|
||||
|
||||
'@kevisual/code-builder@0.0.6':
|
||||
resolution: {integrity: sha512-0aqATB31/yw4k4s5/xKnfr4DKbUnx8e3Z3BmKbiXTrc+CqWiWTdlGe9bKI9dZ2Df+xNp6g11W4xM2NICNyyCCw==}
|
||||
hasBin: true
|
||||
|
||||
'@kevisual/context@0.0.4':
|
||||
resolution: {integrity: sha512-HJeLeZQLU+7tCluSfOyvkgKLs0HjCZrdJlZgEgKRSa8XTwZfMAUt6J7qZTbrZAHBlPtX68EPu/PI8JMCeu3WAQ==}
|
||||
'@kevisual/context@0.0.8':
|
||||
resolution: {integrity: sha512-DTJpyHI34NE76B7g6f+QlIqiCCyqI2qkBMQE736dzeRDGxOjnbe2iQY9W+Rt2PE6kmymM3qyOmSfNovyWyWrkA==}
|
||||
|
||||
'@kevisual/dts@0.0.3':
|
||||
resolution: {integrity: sha512-4T/m2LqhtwWEW+lWmg7jLxKFW7VtIAftsWFDDZvh10bZunqFf8iXxChHcVSQWikghJb4cq1IkWzPkvc2l+Asdw==}
|
||||
'@kevisual/dts@0.0.4':
|
||||
resolution: {integrity: sha512-FVUaH/0nyhbHWpEVjFTGP54PLMm4Hf06aqWLdHOYHNPIgr1aK1C26kOH7iumklGFGk9w93IGxj8Zxe5fap5N2A==}
|
||||
hasBin: true
|
||||
|
||||
'@kevisual/load@0.0.6':
|
||||
@@ -103,17 +106,17 @@ packages:
|
||||
'@kevisual/logger@0.0.4':
|
||||
resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==}
|
||||
|
||||
'@kevisual/permission@0.0.3':
|
||||
resolution: {integrity: sha512-8JsA/5O5Ax/z+M+MYpFYdlioHE6jNmWMuFSokBWYs9CCAHNiSKMR01YLkoVDoPvncfH/Y8F5K/IEXRCbptuMNA==}
|
||||
'@kevisual/permission@0.0.4':
|
||||
resolution: {integrity: sha512-zwBYPnT/z21W4q2wkklJrxvoYBYWG/+a3iXFDKqXQAnDOcxm/SU1f1N6FQb9KxGKl36/fclVlhxlxqszvKCenQ==}
|
||||
|
||||
'@kevisual/query@0.0.38':
|
||||
resolution: {integrity: sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q==}
|
||||
'@kevisual/query@0.0.52':
|
||||
resolution: {integrity: sha512-m1UbyDTIxtfAQXM+EqhXA4ytE2V8rV8mXTZVBwzfW9O6+gtvAcRY7K1YYxfewTSXLVh9nwvfHe0KQ8MDL5ukyw==}
|
||||
|
||||
'@kevisual/query@0.0.40':
|
||||
resolution: {integrity: sha512-7m5BgDzd01m51hCHUId6ugQHdwgrLTb6fI7DSuMY17VjWb0+zGnkYmvRBqkTXzoIjjYbP5iwtRnrooEoToQfhg==}
|
||||
'@kevisual/query@0.0.53':
|
||||
resolution: {integrity: sha512-PAhpCLBr0emz0lGNlTVHMbJiC5wrtGLbInPddRzgKE35fiyNt+SWSsUWABiD0DeNrLN/OxWyAFobt880Z/e5MQ==}
|
||||
|
||||
'@kevisual/router@0.0.70':
|
||||
resolution: {integrity: sha512-vXlIj9jRufhcIfeuPWemjSI+dxdzSmIBq5eRxQzqEfAJ7k+mBPhoI4KxH8vHnwyL30bqm8EdODL/p6Wg8uBw3g==}
|
||||
'@kevisual/router@0.0.88':
|
||||
resolution: {integrity: sha512-T8kEbxyTGxZpbxAKDplDjZMIY+HCnXOeEdjwQ11AQetrLuqLFDZS5PnaWdVAHnONUDLhYoftkNj7bGWLtyQDlg==}
|
||||
|
||||
'@kevisual/types@0.0.12':
|
||||
resolution: {integrity: sha512-zJXH2dosir3jVrQ6QG4i0+iLQeT9gJ3H+cKXs8ReWboxBSYzUZO78XssVeVrFPsJ33iaAqo4q3DWbSS1dWGn7Q==}
|
||||
@@ -127,14 +130,14 @@ packages:
|
||||
resolution: {integrity: sha512-jLsL80wBBKkrJZrfk3SQpJ9JA/zREdlUROj7eCkmzqduAWKSI0wVcXuCKf+mLFCHB0Q0Tkh2rgzjSlurt3JQgw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
'@opencode-ai/plugin@1.1.53':
|
||||
resolution: {integrity: sha512-9ye7Wz2kESgt02AUDaMea4hXxj6XhWwKAG8NwFhrw09Ux54bGaMJFt1eIS8QQGIMaD+Lp11X4QdyEg96etEBJw==}
|
||||
'@opencode-ai/plugin@1.2.20':
|
||||
resolution: {integrity: sha512-BE6TOXVxgF24g5QgtlogSY5B+/AmZJ3cYaVjHZhUVuAli9JEg4RblrbrK2rfgbyZBoZDpjBLGTYtIRTVmOccEA==}
|
||||
|
||||
'@opencode-ai/sdk@1.1.53':
|
||||
resolution: {integrity: sha512-RUIVnPOP1CyyU32FrOOYuE7Ge51lOBuhaFp2NSX98ncApT7ffoNetmwzqrhOiJQgZB1KrbCHLYOCK6AZfacxag==}
|
||||
'@opencode-ai/sdk@1.2.20':
|
||||
resolution: {integrity: sha512-U5ROpG21D8jg9rkc1IgKAk1g5dn6X/rkOBfveupd0peSDO9n6VM9aikYccVLaMObxVqdjtG08IeQOFTPVS8ySQ==}
|
||||
|
||||
'@rollup/plugin-commonjs@28.0.9':
|
||||
resolution: {integrity: sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==}
|
||||
'@rollup/plugin-commonjs@29.0.0':
|
||||
resolution: {integrity: sha512-U2YHaxR2cU/yAiwKJtJRhnyLk7cifnQw0zUpISsocBDoHDJn+HTV74ABqnwr5bEgWUwFZC9oFL6wLe21lHu5eQ==}
|
||||
engines: {node: '>=16.0.0 || 14 >= 14.17'}
|
||||
peerDependencies:
|
||||
rollup: ^2.68.0||^3.0.0||^4.0.0
|
||||
@@ -311,14 +314,14 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@types/bun@1.3.8':
|
||||
resolution: {integrity: sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA==}
|
||||
'@types/bun@1.3.10':
|
||||
resolution: {integrity: sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ==}
|
||||
|
||||
'@types/estree@1.0.8':
|
||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||
|
||||
'@types/node@25.2.2':
|
||||
resolution: {integrity: sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ==}
|
||||
'@types/node@25.3.5':
|
||||
resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==}
|
||||
|
||||
'@types/resolve@1.20.2':
|
||||
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
||||
@@ -330,13 +333,17 @@ packages:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
bun-types@1.3.8:
|
||||
resolution: {integrity: sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q==}
|
||||
bun-types@1.3.10:
|
||||
resolution: {integrity: sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg==}
|
||||
|
||||
chokidar@5.0.0:
|
||||
resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
commander@14.0.3:
|
||||
resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
commondir@1.0.1:
|
||||
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
|
||||
|
||||
@@ -359,12 +366,12 @@ packages:
|
||||
destr@2.0.5:
|
||||
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
|
||||
|
||||
dotenv@17.2.4:
|
||||
resolution: {integrity: sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw==}
|
||||
dotenv@17.3.1:
|
||||
resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
es-toolkit@1.44.0:
|
||||
resolution: {integrity: sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==}
|
||||
es-toolkit@1.45.1:
|
||||
resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==}
|
||||
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
@@ -493,8 +500,8 @@ packages:
|
||||
uncrypto@0.1.3:
|
||||
resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
|
||||
|
||||
undici-types@7.16.0:
|
||||
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
||||
undici-types@7.18.2:
|
||||
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
|
||||
|
||||
unstorage@1.17.4:
|
||||
resolution: {integrity: sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==}
|
||||
@@ -575,19 +582,19 @@ snapshots:
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@kevisual/ai@0.0.24':
|
||||
'@kevisual/ai@0.0.26':
|
||||
dependencies:
|
||||
'@kevisual/logger': 0.0.4
|
||||
'@kevisual/permission': 0.0.3
|
||||
'@kevisual/query': 0.0.38
|
||||
'@kevisual/permission': 0.0.4
|
||||
'@kevisual/query': 0.0.52
|
||||
|
||||
'@kevisual/code-builder@0.0.6': {}
|
||||
|
||||
'@kevisual/context@0.0.4': {}
|
||||
'@kevisual/context@0.0.8': {}
|
||||
|
||||
'@kevisual/dts@0.0.3(typescript@5.9.3)':
|
||||
'@kevisual/dts@0.0.4(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@rollup/plugin-commonjs': 28.0.9(rollup@4.57.1)
|
||||
'@rollup/plugin-commonjs': 29.0.0(rollup@4.57.1)
|
||||
'@rollup/plugin-node-resolve': 16.0.3(rollup@4.57.1)
|
||||
'@rollup/plugin-typescript': 12.3.0(rollup@4.57.1)(tslib@2.8.1)(typescript@5.9.3)
|
||||
rollup: 4.57.1
|
||||
@@ -602,37 +609,33 @@ snapshots:
|
||||
|
||||
'@kevisual/logger@0.0.4': {}
|
||||
|
||||
'@kevisual/permission@0.0.3': {}
|
||||
'@kevisual/permission@0.0.4': {}
|
||||
|
||||
'@kevisual/query@0.0.38':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
'@kevisual/query@0.0.52': {}
|
||||
|
||||
'@kevisual/query@0.0.40':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
'@kevisual/query@0.0.53': {}
|
||||
|
||||
'@kevisual/router@0.0.70':
|
||||
'@kevisual/router@0.0.88':
|
||||
dependencies:
|
||||
es-toolkit: 1.44.0
|
||||
es-toolkit: 1.45.1
|
||||
|
||||
'@kevisual/types@0.0.12': {}
|
||||
|
||||
'@kevisual/use-config@1.0.30(dotenv@17.2.4)':
|
||||
'@kevisual/use-config@1.0.30(dotenv@17.3.1)':
|
||||
dependencies:
|
||||
'@kevisual/load': 0.0.6
|
||||
dotenv: 17.2.4
|
||||
dotenv: 17.3.1
|
||||
|
||||
'@kevisual/ws@8.19.0': {}
|
||||
|
||||
'@opencode-ai/plugin@1.1.53':
|
||||
'@opencode-ai/plugin@1.2.20':
|
||||
dependencies:
|
||||
'@opencode-ai/sdk': 1.1.53
|
||||
'@opencode-ai/sdk': 1.2.20
|
||||
zod: 4.3.6
|
||||
|
||||
'@opencode-ai/sdk@1.1.53': {}
|
||||
'@opencode-ai/sdk@1.2.20': {}
|
||||
|
||||
'@rollup/plugin-commonjs@28.0.9(rollup@4.57.1)':
|
||||
'@rollup/plugin-commonjs@29.0.0(rollup@4.57.1)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.57.1)
|
||||
commondir: 1.0.1
|
||||
@@ -746,35 +749,37 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.57.1':
|
||||
optional: true
|
||||
|
||||
'@types/bun@1.3.8':
|
||||
'@types/bun@1.3.10':
|
||||
dependencies:
|
||||
bun-types: 1.3.8
|
||||
bun-types: 1.3.10
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@types/node@25.2.2':
|
||||
'@types/node@25.3.5':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
undici-types: 7.18.2
|
||||
|
||||
'@types/resolve@1.20.2': {}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 25.2.2
|
||||
'@types/node': 25.3.5
|
||||
|
||||
anymatch@3.1.3:
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
bun-types@1.3.8:
|
||||
bun-types@1.3.10:
|
||||
dependencies:
|
||||
'@types/node': 25.2.2
|
||||
'@types/node': 25.3.5
|
||||
|
||||
chokidar@5.0.0:
|
||||
dependencies:
|
||||
readdirp: 5.0.0
|
||||
|
||||
commander@14.0.3: {}
|
||||
|
||||
commondir@1.0.1: {}
|
||||
|
||||
cookie-es@1.2.2: {}
|
||||
@@ -791,9 +796,9 @@ snapshots:
|
||||
|
||||
destr@2.0.5: {}
|
||||
|
||||
dotenv@17.2.4: {}
|
||||
dotenv@17.3.1: {}
|
||||
|
||||
es-toolkit@1.44.0: {}
|
||||
es-toolkit@1.45.1: {}
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
@@ -927,7 +932,7 @@ snapshots:
|
||||
|
||||
uncrypto@0.1.3: {}
|
||||
|
||||
undici-types@7.16.0: {}
|
||||
undici-types@7.18.2: {}
|
||||
|
||||
unstorage@1.17.4:
|
||||
dependencies:
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
packages:
|
||||
- web
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CNBCore, CNBCoreOptions, RequestOptions, Result } from "../cnb-core.ts";
|
||||
import { extractAliveInfo } from "./issue-alive.ts";
|
||||
|
||||
export type IssueAssignee = {
|
||||
nickname: string;
|
||||
@@ -88,6 +89,51 @@ export class Issue extends CNBCore {
|
||||
};
|
||||
return this.post({ url, data: postData });
|
||||
}
|
||||
/**
|
||||
* 获取alive issue的元数据
|
||||
* @param repo
|
||||
* @param issueNumber
|
||||
* @returns
|
||||
*/
|
||||
async getAliveMetadata(repo: string, issueNumber: string | number): Promise<Result<AliveMetadata>> {
|
||||
const url = this.hackURL + `/${repo}/-/issues/${issueNumber}`;
|
||||
const resHtml = await this.get({
|
||||
url,
|
||||
useCookie: true,
|
||||
headers: {
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
Accept: "text/html; charset=utf-8",
|
||||
}
|
||||
});
|
||||
if (resHtml.code !== 200) {
|
||||
return resHtml;
|
||||
}
|
||||
const html = resHtml.data as string;
|
||||
const { aliveSessionID, aliveChannelID } = extractAliveInfo(html);
|
||||
if (!aliveSessionID || !aliveChannelID) {
|
||||
return {
|
||||
code: 500,
|
||||
message: 'Failed to extract alive metadata',
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
const off = Date.now();
|
||||
return {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: {
|
||||
aliveSessionID,
|
||||
aliveChannelID,
|
||||
domain: 'alive.cnb.cool',
|
||||
subscriptions: [
|
||||
{ event: 'subscribe', data: { id: `${aliveChannelID}--issue:info-update`, off } },
|
||||
{ event: 'subscribe', data: { id: `${aliveChannelID}--issue:add-comment`, off } },
|
||||
{ event: 'subscribe', data: { id: `${aliveChannelID}--issue:comment-update`, off } },
|
||||
{ event: 'subscribe', data: { id: `${aliveChannelID}--issue:invisible`, off } },
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
type GetListParams = {
|
||||
@@ -119,4 +165,17 @@ type GetListParams = {
|
||||
updated_time_begin?: string;
|
||||
/** 问题更新时间过滤-结束,例如: 2022-01-31 */
|
||||
updated_time_end?: string;
|
||||
}
|
||||
|
||||
export type AliveMetadata = {
|
||||
aliveSessionID: string;
|
||||
aliveChannelID: string;
|
||||
domain: string;
|
||||
subscriptions: {
|
||||
event: 'subscribe',
|
||||
data: {
|
||||
id: string;
|
||||
off: number;
|
||||
}
|
||||
}[]
|
||||
}
|
||||
15
src/issue/issue-alive.ts
Normal file
15
src/issue/issue-alive.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export function extractAliveInfo(html: string): { aliveSessionID: string | null; aliveChannelID: string | null } {
|
||||
const match = html.match(/<script[^>]*id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/);
|
||||
if (!match || !match[1]) {
|
||||
return { aliveSessionID: null, aliveChannelID: null };
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(match[1]);
|
||||
const aliveSessionID = data?.props?.pageProps?.aliveSessionID ?? null;
|
||||
const aliveChannelID = data?.props?.pageProps?.aliveChannelID ?? null;
|
||||
return { aliveSessionID, aliveChannelID };
|
||||
} catch {
|
||||
return { aliveSessionID: null, aliveChannelID: null };
|
||||
}
|
||||
}
|
||||
24
src/issue/issue-alive/issue-alive.md
Normal file
24
src/issue/issue-alive/issue-alive.md
Normal file
@@ -0,0 +1,24 @@
|
||||
```js
|
||||
fetch("wss://alive.cnb.cool/?id=26354bd0-3e00-4869-93c9-b687f19d96c1", {
|
||||
"headers": {
|
||||
"accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||
"cache-control": "no-cache",
|
||||
"pragma": "no-cache",
|
||||
"sec-websocket-extensions": "permessage-deflate; client_max_window_bits",
|
||||
"sec-websocket-key": "vXnvsfo4splpfeDSnJLxKA==",
|
||||
"sec-websocket-protocol": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiY29va2llIiwic2NvcGUiOiIiLCJtZXRhIjp7IkxpY2Vuc2UiOnsiY29tbW9uTmFtZSI6IiouY25iLmNvb2wiLCJub3RCZWZvcmUiOiIyMDI1LTA2LTE4VDA5OjE3OjE5WiIsIm5vdEFmdGVyIjoiMjAzNS0wNi0xNlQwOToxNzoxOVoiLCJpc3N1ZXIiOiJUZW5jZW50LCBJbmMiLCJvcmdhbml6YXRpb24iOiLohb7orq_kupEiLCJtZW1iZXJzIjoxMDAwMDAwLCJjaGFubmVsIjoiU2FhUyJ9LCJyb290X29yZ2FuaXphdGlvbl9zZXR0aW5nIjp7ImhpZGVfbWVtYmVycyI6MCwiaGlkZV9zdWJfZ3JvdXBzIjowLCJzaG93X3ByaXZhdGVfcmVwb193YXRlcm1hcmsiOjAsImdyb3VwX3Byb3RlY3Rpb24iOjEsImVtYWlsX3ZlcmlmaWNhdGlvbiI6IiIsInZhbHVlcyI6IiJ9fSwicGxhdGZvcm0iOiIiLCJ1c2VyX2lkIjoiIiwidXNlcl9lbWFpbCI6IiIsIm5pY2tuYW1lIjoiIiwidXNlcm5hbWUiOiIiLCJ2ZXJpZmllZCI6ZmFsc2UsImZyZWV6ZSI6ZmFsc2UsImJhbiI6ZmFsc2UsImxvY2tlZCI6ZmFsc2UsInVzZXJfZGV2aWNlX3R5cGUiOjAsInNsdWciOiJrZXZpc3VhbC9rZXZpc3VhbCIsInNsdWdfaWQiOiIxOTE1NDE1NDE0NTE4NjU3MDI0Iiwic2x1Z190eXBlIjoxLCJzbHVnX3N0YXR1cyI6MCwic2x1Z19mcmVlemUiOmZhbHNlLCJzbHVnX3Zpc2liaWxpdHkiOiJQdWJsaWMiLCJzbHVnX3Jvb3RfaWQiOiIxOTE1MzUzNzE5MDE4MzM2MjU2Iiwic2x1Z19yb2xlIjoiVW5rbm93biIsImxhbmd1YWdlIjoiZW4tVVMiLCJjb250ZXh0Ijoie30iLCJpc3MiOiJhY2Nlc3Mtcm91dGVyLTVmNzg5ZGM3N2ItN3Y1bnMiLCJpYXQiOjE3NzE1Mjk1MTQsImp0aSI6Ijk2MjYzIn0.PrlpGLNw7-1ucm3CiTQsPH7nIYFgvhZqQGhlws3R4ME",
|
||||
"sec-websocket-version": "13"
|
||||
},
|
||||
"body": null,
|
||||
"method": "GET",
|
||||
"mode": "cors",
|
||||
"credentials": "omit"
|
||||
});
|
||||
```
|
||||
|
||||
```json
|
||||
{"event":"subscribe","data":{"id":"495b92b2fbcd908e6ca1c809a3873bb373a7a6c6b814dfce5ff60b5f87dd0944--issue:info-update","off":1771529514922}}
|
||||
{"event":"subscribe","data":{"id":"495b92b2fbcd908e6ca1c809a3873bb373a7a6c6b814dfce5ff60b5f87dd0944--issue:add-comment","off":1771529514922}}
|
||||
{"event":"subscribe","data":{"id":"495b92b2fbcd908e6ca1c809a3873bb373a7a6c6b814dfce5ff60b5f87dd0944--issue:comment-update","off":1771529514922}}
|
||||
{"event":"subscribe","data":{"id":"495b92b2fbcd908e6ca1c809a3873bb373a7a6c6b814dfce5ff60b5f87dd0944--issue:invisible","off":1771529514922}}
|
||||
```
|
||||
2
src/issue/issue-alive/issues-alive.html
Normal file
2
src/issue/issue-alive/issues-alive.html
Normal file
File diff suppressed because one or more lines are too long
@@ -108,6 +108,10 @@ export class Repo extends CNBCore {
|
||||
const url = `/${repo}`;
|
||||
return this.patch({ url, data: params });
|
||||
}
|
||||
getRepo(repo: string): Promise<Result<RepoItem>> {
|
||||
const url = `/${repo}`;
|
||||
return this.get({ url });
|
||||
}
|
||||
}
|
||||
type UpdateRepoInfo = {
|
||||
description?: string;
|
||||
|
||||
@@ -16,6 +16,18 @@ export class User extends CNBCore {
|
||||
useCookie: true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 判断当前 Cookie 是否有效
|
||||
* @returns
|
||||
*/
|
||||
async checkCookieValid(): Promise<Result> {
|
||||
const user = await this.getCurrentUser();
|
||||
if (user.code === 200) {
|
||||
return { code: 200, message: 'cookie valid' };
|
||||
} else {
|
||||
return { code: 401, message: 'cookie invalid' };
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 使用 Token 获取用户信息
|
||||
* @returns
|
||||
|
||||
@@ -62,7 +62,7 @@ export class Workspace extends CNBCore {
|
||||
* 停止我的云原生开发环境
|
||||
* @param params 停止参数,pipelineId 和 sn 二选一,优先使用 pipelineId
|
||||
*/
|
||||
async stopWorkspace(params: { pipelineId?: string; sn?: string }): Promise<{ buildLogUrl: string; message: string; sn: string }> {
|
||||
async stopWorkspace(params: { pipelineId?: string; sn?: string }): Promise<Result<{ buildLogUrl: string; message: string; sn: string }>> {
|
||||
const data: { pipelineId?: string; sn?: string } = {};
|
||||
|
||||
if (params.pipelineId) {
|
||||
@@ -113,7 +113,62 @@ export class Workspace extends CNBCore {
|
||||
|
||||
return this.post({ url: `/${repo}/-/workspace/start`, data });
|
||||
}
|
||||
/**
|
||||
* 添加使用cookie获取工作空间访问权限的功能,适用于需要保持工作空间连接状态的场景,
|
||||
* 例如使用 WebSocket 连接工作空间时需要携带 cookie 进行身份验证。
|
||||
* https://cnb.cool/kevisual/dev-env/-/workspace/vscode-web/cnb-708-1ji9sog7o-001
|
||||
* @param repo
|
||||
* @param pipelineId
|
||||
* @returns
|
||||
*/
|
||||
async getWorkspaceCookie(repo: string, pipelineId: string): Promise<Result<{ value: string, cookie: string; cookieName: string }>> {
|
||||
const url = `${this.hackURL}/${repo}/-/workspace/vscode-web/${pipelineId}`;
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
redirect: 'manual',
|
||||
headers: {
|
||||
'Cookie': this.cookie || '',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
// 第一次 302 重定向
|
||||
if (response.status === 302 || response.status === 301) {
|
||||
// 包含token的重定向 URL 通常在 Location 头中返回
|
||||
// 类似 https://cnb-708-1ji9sog7o-001.cnb.space/login?t=orange:workspace:login-token:963691a2-35ce-4fef-a7ba-72723cefd226
|
||||
const loginURL = response.headers.get('Location');
|
||||
// 从 URL 参数中获取 cookieName,例如: orange:workspace:cookie-session:cnb-708-1ji9sog7o-001
|
||||
const cookieName = `orange:workspace:cookie-session:${pipelineId}`;
|
||||
// 第二次请求,也设置为 manual 防止自动重定向
|
||||
const response2 = await fetch(loginURL || '', {
|
||||
method: 'GET',
|
||||
redirect: 'manual',
|
||||
headers: {
|
||||
'Cookie': this.cookie || '',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
// 第二次 302 重定向,获取最终的 cookie 值
|
||||
if (response2.status === 302 || response2.status === 301) {
|
||||
// 从 Set-Cookie 头中获取 cookie 值
|
||||
const setCookie = response2.headers.get('Set-Cookie');
|
||||
// 解析 cookie 值
|
||||
const cookieValue = setCookie?.split(';')[0]?.split('=')[1] || '';
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: { value: cookieValue, cookieName, cookie: `${cookieName}=${cookieValue}` }
|
||||
};
|
||||
}
|
||||
|
||||
// 如果不是重定向,尝试获取 JSON 数据
|
||||
return { code: 500 };
|
||||
}
|
||||
|
||||
return { code: 500, };
|
||||
}
|
||||
}
|
||||
export interface WorkspaceLinkDetail {
|
||||
codebuddy: string;
|
||||
|
||||
137
src/workspace/keep-file-live.ts
Normal file
137
src/workspace/keep-file-live.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
export type KeepAliveData = {
|
||||
wsUrl: string;
|
||||
cookie: string;
|
||||
repo: string;
|
||||
pipelineId: string;
|
||||
createdTime: number;
|
||||
filePath: string;
|
||||
pm2Name: string;
|
||||
}
|
||||
|
||||
type KeepAliveCache = {
|
||||
data: KeepAliveData[];
|
||||
}
|
||||
const baseDir = path.join(os.homedir(), '.cnb', 'live');
|
||||
const keepAliveFilePath = path.join(baseDir, 'keepAliveCache.json');
|
||||
|
||||
export const runLive = (filePath: string, pm2Name: string) => {
|
||||
// 使用 npx 运行命令
|
||||
const cmdArgs = `cnb live -c ${filePath}`;
|
||||
|
||||
// 先停止已存在的同名 pm2 进程
|
||||
const stopCmd = `pm2 delete ${pm2Name} 2>/dev/null || true`;
|
||||
console.log('停止已存在的进程:', stopCmd);
|
||||
try {
|
||||
execSync(stopCmd, { stdio: 'inherit' });
|
||||
} catch (error) {
|
||||
console.log('停止进程失败或进程不存在:', error);
|
||||
}
|
||||
|
||||
// 使用pm2启动
|
||||
const pm2Cmd = `pm2 start ev --name ${pm2Name} --no-autorestart -- ${cmdArgs}`;
|
||||
console.log('执行命令:', pm2Cmd);
|
||||
try {
|
||||
const result = execSync(pm2Cmd, { stdio: 'pipe', encoding: 'utf8' });
|
||||
console.log(result);
|
||||
} catch (error) {
|
||||
console.error("状态码:", error.status);
|
||||
console.error("错误详情:", error.stderr.toString()); // 这里会显示 ev 命令报的具体错误
|
||||
}
|
||||
}
|
||||
|
||||
export const stopLive = (pm2Name: string): boolean => {
|
||||
const stopCmd = `pm2 delete ${pm2Name} 2>/dev/null || true`;
|
||||
console.log('停止进程:', stopCmd);
|
||||
try {
|
||||
execSync(stopCmd, { stdio: 'inherit' });
|
||||
console.log(`已停止 ${pm2Name} 的保持存活任务`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('停止进程失败:', error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getKeepAliveCache(): KeepAliveCache {
|
||||
try {
|
||||
if (fs.existsSync(keepAliveFilePath)) {
|
||||
const data = fs.readFileSync(keepAliveFilePath, 'utf-8');
|
||||
const cache = JSON.parse(data) as KeepAliveCache;
|
||||
return cache;
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('读取保持存活缓存文件失败:', error);
|
||||
return { data: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export function addKeepAliveData(data: KeepAliveData): KeepAliveCache {
|
||||
const cache = getKeepAliveCache();
|
||||
cache.data.push(data);
|
||||
runLive(data.filePath, data.pm2Name);
|
||||
try {
|
||||
if (!fs.existsSync(path.dirname(keepAliveFilePath))) {
|
||||
fs.mkdirSync(path.dirname(keepAliveFilePath), { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(keepAliveFilePath, JSON.stringify(cache, null, 2), 'utf-8');
|
||||
return cache;
|
||||
} catch (error) {
|
||||
console.error('写入保持存活缓存文件失败:', error);
|
||||
return { data: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export function removeKeepAliveData(repo: string, pipelineId: string): KeepAliveCache {
|
||||
const cache = getKeepAliveCache();
|
||||
const item = cache.data.find(item => item.repo === repo && item.pipelineId === pipelineId);
|
||||
if (item) {
|
||||
stopLive(item.pm2Name);
|
||||
}
|
||||
cache.data = cache.data.filter(item => item.repo !== repo || item.pipelineId !== pipelineId);
|
||||
try {
|
||||
fs.writeFileSync(keepAliveFilePath, JSON.stringify(cache, null, 2), 'utf-8');
|
||||
return cache;
|
||||
} catch (error) {
|
||||
console.error('写入保持存活缓存文件失败:', error);
|
||||
return { data: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export const createLiveData = (data: { cookie: string, repo: string, pipelineId: string }): KeepAliveData => {
|
||||
const { cookie, repo, pipelineId } = data;
|
||||
const createdTime = Date.now();
|
||||
const wsUrl = `wss://${pipelineId}.cnb.space:443?skipWebSocketFrames=false`;
|
||||
const pm2Name = `keep_${repo}__${pipelineId}`.replace(/\//g, '__');
|
||||
const filePath = path.join(baseDir, `${pm2Name}.json`);
|
||||
const _newData = { wss: wsUrl, wsUrl, cookie, repo, pipelineId, createdTime, filePath, pm2Name };
|
||||
if (!fs.existsSync(path.dirname(filePath))) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(filePath, JSON.stringify(_newData, null, 2), 'utf-8');
|
||||
return _newData;
|
||||
}
|
||||
|
||||
export class KeepAliveManager {
|
||||
static getCache() {
|
||||
return getKeepAliveCache();
|
||||
}
|
||||
|
||||
static add(data: KeepAliveData) {
|
||||
return addKeepAliveData(data);
|
||||
}
|
||||
|
||||
static createLiveData(data: { cookie: string, repo: string, pipelineId: string }): KeepAliveData {
|
||||
return createLiveData(data);
|
||||
}
|
||||
|
||||
static remove(repo: string, pipelineId: string) {
|
||||
return removeKeepAliveData(repo, pipelineId);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,16 @@
|
||||
// WebSocket Keep-Alive Client Library
|
||||
import WebSocket from "ws";
|
||||
// 运行时检测:Bun 使用原生 WebSocket,Node.js 使用 ws 库
|
||||
let WebSocketModule: any;
|
||||
|
||||
if (typeof Bun !== 'undefined') {
|
||||
// Bun 环境:使用原生 WebSocket
|
||||
WebSocketModule = { WebSocket: globalThis.WebSocket };
|
||||
} else {
|
||||
// Node.js 环境:使用 ws 库
|
||||
WebSocketModule = await import('ws');
|
||||
}
|
||||
|
||||
const WebSocket = WebSocketModule.WebSocket;
|
||||
|
||||
export interface KeepAliveConfig {
|
||||
wsUrl: string;
|
||||
@@ -31,6 +42,7 @@ export class WSKeepAlive {
|
||||
private pingTimer: NodeJS.Timeout | null = null;
|
||||
private messageHandlers: Set<MessageHandler> = new Set();
|
||||
private url: URL;
|
||||
private readonly isBun: boolean;
|
||||
|
||||
constructor(config: KeepAliveConfig) {
|
||||
this.config = {
|
||||
@@ -48,6 +60,7 @@ export class WSKeepAlive {
|
||||
debug: config.debug ?? false,
|
||||
};
|
||||
this.url = new URL(this.config.wsUrl);
|
||||
this.isBun = typeof Bun !== 'undefined';
|
||||
}
|
||||
|
||||
private log(message: string) {
|
||||
@@ -107,44 +120,91 @@ export class WSKeepAlive {
|
||||
}
|
||||
});
|
||||
|
||||
this.ws.on("open", () => {
|
||||
debug && this.log("Connected!");
|
||||
this.reconnectAttempts = 0;
|
||||
this.config.onConnect();
|
||||
this.startPing();
|
||||
});
|
||||
if (this.isBun) {
|
||||
// Bun 环境:使用标准 Web API
|
||||
const ws = this.ws as any;
|
||||
ws.onopen = () => {
|
||||
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);
|
||||
ws.onmessage = async (event: MessageEvent) => {
|
||||
let data: Buffer | string;
|
||||
|
||||
this.messageHandlers.forEach(handler => {
|
||||
if (parsed) handler(parsed);
|
||||
});
|
||||
} else {
|
||||
this.config.onMessage(data);
|
||||
}
|
||||
});
|
||||
if (event.data instanceof Blob) {
|
||||
data = Buffer.from(await event.data.arrayBuffer());
|
||||
} else if (event.data instanceof ArrayBuffer) {
|
||||
data = Buffer.from(event.data);
|
||||
} else if (typeof event.data === 'string') {
|
||||
data = event.data;
|
||||
} else {
|
||||
data = Buffer.from(event.data);
|
||||
}
|
||||
|
||||
this.ws.on("close", (code: number) => {
|
||||
debug && this.log(`Disconnected (code: ${code})`);
|
||||
this.stopPing();
|
||||
this.config.onDisconnect(code);
|
||||
this.handleReconnect();
|
||||
});
|
||||
this.handleMessage(data);
|
||||
};
|
||||
|
||||
this.ws.on("error", (err: Error) => {
|
||||
debug && this.log(`Error: ${err.message}`);
|
||||
this.config.onError(err);
|
||||
});
|
||||
ws.onclose = (event: CloseEvent) => {
|
||||
debug && this.log(`Disconnected (code: ${event.code})`);
|
||||
this.stopPing();
|
||||
this.config.onDisconnect(event.code);
|
||||
this.handleReconnect();
|
||||
};
|
||||
|
||||
ws.onerror = (event: Event) => {
|
||||
debug && this.log(`Error: ${event}`);
|
||||
this.config.onError(new Error("WebSocket error"));
|
||||
};
|
||||
} else {
|
||||
// Node.js (ws 库):使用 EventEmitter 模式
|
||||
const ws = this.ws as any;
|
||||
ws.on("open", () => {
|
||||
debug && this.log("Connected!");
|
||||
this.reconnectAttempts = 0;
|
||||
this.config.onConnect();
|
||||
this.startPing();
|
||||
});
|
||||
|
||||
ws.on("message", (data: any) => {
|
||||
this.handleMessage(data);
|
||||
});
|
||||
|
||||
ws.on("close", (code: number) => {
|
||||
debug && this.log(`Disconnected (code: ${code})`);
|
||||
this.stopPing();
|
||||
this.config.onDisconnect(code);
|
||||
this.handleReconnect();
|
||||
});
|
||||
|
||||
ws.on("error", (err: Error) => {
|
||||
debug && this.log(`Error: ${err.message}`);
|
||||
this.config.onError(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 统一的消息处理方法
|
||||
private handleMessage(data: Buffer | string) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private startPing() {
|
||||
this.stopPing();
|
||||
this.pingTimer = setInterval(() => {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.ws.ping();
|
||||
// 使用 JSON 格式的 ping 消息,兼容 Bun 和 Node.js
|
||||
this.ws.send(JSON.stringify({ type: "ping", timestamp: Date.now() }));
|
||||
this.log("Sent ping");
|
||||
}
|
||||
}, this.config.pingInterval);
|
||||
|
||||
1
test/build-log.ts
Normal file
1
test/build-log.ts
Normal file
@@ -0,0 +1 @@
|
||||
// 不用查看
|
||||
@@ -60,4 +60,34 @@ main:
|
||||
api_trigger_sync_from_gitea:
|
||||
- <<: *common_sync_from_gitea
|
||||
`
|
||||
buildByConfig()
|
||||
// buildByConfig()
|
||||
|
||||
|
||||
const buildByConfig2 = async () => {
|
||||
const build = await repo.startBuild('kevisual/cnb', {
|
||||
branch: 'main',
|
||||
env: {
|
||||
},
|
||||
event: 'api_trigger_events',
|
||||
config: config2,
|
||||
});
|
||||
console.log("build", showMore(build));
|
||||
}
|
||||
const config2 = `# .cnb.yml
|
||||
include:
|
||||
- https://cnb.cool/kevisual/cnb/-/blob/main/.cnb/template.yml
|
||||
|
||||
main:
|
||||
api_trigger_events:
|
||||
-
|
||||
docker:
|
||||
image: docker.cnb.cool/kevisual/dev-env:latest
|
||||
services:
|
||||
- vscode
|
||||
- docker
|
||||
stages:
|
||||
- name: test
|
||||
steps:
|
||||
- run: echo "hello world"
|
||||
`
|
||||
buildByConfig2()
|
||||
5
test/get-live-meta.ts
Normal file
5
test/get-live-meta.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { cnb, showMore } from './common'
|
||||
|
||||
const meta = await cnb.issue.getAliveMetadata('kevisual/kevisual', 34);
|
||||
|
||||
console.log('meta', showMore(meta));
|
||||
11
test/keep-cookie-get.ts
Normal file
11
test/keep-cookie-get.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// https://cnb.cool/kevisual/dev-env/-/workspace/vscode-web/cnb-708-1ji9sog7o-001
|
||||
import { Build } from "../src/index.ts";
|
||||
|
||||
import { cnb, showMore } from "./common.ts";
|
||||
|
||||
const repo = 'kevisual/dev-env';
|
||||
const pipelineId = 'cnb-708-1ji9sog7o-001';
|
||||
|
||||
const res = await cnb.workspace.getWorkspaceCookie(repo, pipelineId);
|
||||
|
||||
console.log('token', showMore(res));
|
||||
19
test/keep-file-live.ts
Normal file
19
test/keep-file-live.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { addKeepAliveData, createLiveData, getKeepAliveCache } from '../agent/routes/workspace/keep-file-live';
|
||||
|
||||
const repo = 'kevisual/dev-env';
|
||||
const pipelineId = 'cnb-708-1ji9sog7o-001';
|
||||
|
||||
const testData = createLiveData({
|
||||
wsUrl: "wss://cnb-708-1ji9sog7o-001.cnb.space:443?skipWebSocketFrames=false",
|
||||
cookie: "orange:workspace:cookie-session:cnb-708-1ji9sog7o-001=3dc03d84-5617-4e44-a6b9-38ce4398aea5",
|
||||
repo: repo,
|
||||
pipelineId: pipelineId
|
||||
});
|
||||
|
||||
addKeepAliveData(testData);
|
||||
|
||||
// 运行后可以在 ~/.cnb/kevisual_dev-env_cnb-708-1ji9sog7o-001.json 中看到保持存活的数据
|
||||
// 同时可以通过 pm2 list 命令看到对应的保持存活的进程
|
||||
|
||||
// 注意:如果要测试停止保持存活,可以调用 stopLive(testData.pm2Name) 来停止对应的进程
|
||||
// 例如:stopLive('kevisual_dev-env_cnb-708-1ji9sog7o-001');
|
||||
@@ -1,13 +1,16 @@
|
||||
import { createKeepAlive } from "@kevisual/cnb/keep";
|
||||
|
||||
// stable-9184b645cc7aa41b750e2f2ef956f2896512dd84 这个可以修改
|
||||
// reconnectionToken 不能修改
|
||||
// 但是可以删除 reconnectionToken=38837a9e-dd5a-4d28-9ec0-5e5b537a8b0f&skipWebSocketFrames=false
|
||||
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"
|
||||
"wss": "wss://cnb-708-1ji9sog7o-001.cnb.space:443?skipWebSocketFrames=false",
|
||||
"cookie": "orange:workspace:cookie-session:cnb-708-1ji9sog7o-001=3dc03d84-5617-4e44-a6b9-38ce4398aea5",
|
||||
"url": "https://cnb-708-1ji9sog7o-001.cnb.space/?folder=/workspace"
|
||||
}
|
||||
|
||||
createKeepAlive({
|
||||
wsUrl: config.wss,
|
||||
cookie: config.cookie,
|
||||
debug: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
10
test/user.ts
10
test/user.ts
@@ -4,10 +4,14 @@ import { token, showMore, cookie } from "./common.ts";
|
||||
|
||||
const user = new User({ token: token, cookie: cookie });
|
||||
|
||||
const currentUser = await user.getCurrentUser();
|
||||
// const currentUser = await user.getCurrentUser();
|
||||
|
||||
console.log("currentUser", showMore(currentUser));
|
||||
// console.log("currentUser", showMore(currentUser));
|
||||
|
||||
// const accessToken = await user.createAccessToken({ description: "Test Token from API" });
|
||||
|
||||
// console.log("accessToken", showMore(accessToken));
|
||||
// console.log("accessToken", showMore(accessToken));
|
||||
|
||||
const tokenUser = await user.getUser();
|
||||
|
||||
console.log("tokenUser", showMore(tokenUser));
|
||||
Reference in New Issue
Block a user