feat: 添加工作空间保持存活功能,更新相关依赖和配置

This commit is contained in:
2026-01-30 23:32:40 +08:00
parent 972d68b87e
commit 0d17d56628
12 changed files with 394 additions and 29 deletions

2
.gitignore vendored
View File

@@ -4,3 +4,5 @@ node_modules
.pnpm-store
dist
storage

View File

@@ -2,7 +2,6 @@ import { QueryRouterServer as App } from '@kevisual/router'
import { useContextKey } from '@kevisual/context'
import { useConfig, useKey } from '@kevisual/use-config'
import { CNB } from '../src/index.ts';
import { nanoid } from 'nanoid';
export const config = useConfig()
export const cnb = useContextKey<CNB>('cnb', () => {
@@ -13,9 +12,6 @@ export const cnb = useContextKey<CNB>('cnb', () => {
const cookie = useKey('CNB_COOKIE') as string
return new CNB({ token: token, cookie: cookie });
})
export const appId = nanoid();
export const app = useContextKey<App>('app', () => {
return new App({
appId
})
return new App({})
})

View File

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

View File

@@ -1,4 +1,4 @@
import { app, appId } from '../app.ts';
import { app } from '../app.ts';
import './cnb-env/check.ts'
import './repo/index.ts'
import './workspace/index.ts'
@@ -31,7 +31,7 @@ if (!app.hasRoute('auth')) {
path: 'auth',
}).define(async (ctx) => {
// ctx.body = 'Auth Route';
if (checkAppId(ctx, appId)) {
if (checkAppId(ctx, app.appId)) {
return;
}
}).addTo(app);
@@ -42,7 +42,7 @@ if (!app.hasRoute('auth')) {
middleware: ['auth'],
}).define(async (ctx) => {
// ctx.body = 'Admin Auth Route';
if (checkAppId(ctx, appId)) {
if (checkAppId(ctx, app.appId)) {
return;
}
}).addTo(app);

View File

@@ -2,6 +2,7 @@ import { createSkill, tool } from '@kevisual/router';
import { app, cnb } from '../../app.ts';
import z from 'zod';
import './skills.ts';
import './keep.ts';
// 启动工作空间
app.route({
@@ -113,7 +114,7 @@ app.route({
args: {
pipelineId: tool.schema.string().optional().describe('流水线 ID优先使用'),
sn: tool.schema.string().optional().describe('流水线构建号'),
sns: tool.schema.array(z.string()).optional().describe('流水线构建号'),
sns: tool.schema.array(z.string()).optional().describe('批量流水线构建号'),
},
})
}

View File

@@ -0,0 +1,121 @@
import { createSkill, tool } from '@kevisual/router';
import { app, cnb } from '../../app.ts';
import { createKeepAlive } from '../../../src/keep.ts';
type AliveInfo = {
startTime: number;
updatedTime?: number;
KeepAlive: ReturnType<typeof createKeepAlive>;
}
const keepAliveMap = new Map<string, AliveInfo>();
// 保持工作空间存活技能
app.route({
path: 'cnb',
key: 'keep-workspace-alive',
description: '保持工作空间存活技能,参数wsUrl:工作空间访问URLcookie:访问工作空间所需的cookie',
middleware: ['auth'],
metadata: {
tags: [],
...({
args: {
wsUrl: tool.schema.string().describe('工作空间的访问URL'),
cookie: tool.schema.string().describe('访问工作空间所需的cookie')
}
})
}
}).define(async (ctx) => {
const wsUrl = ctx.query?.wsUrl as string;
const cookie = ctx.query?.cookie as string;
if (!wsUrl) {
ctx.throw(400, '缺少工作空间访问URL参数');
}
if (!cookie) {
ctx.throw(400, '缺少访问工作空间所需的cookie参数');
}
// 检测是否已在运行
const existing = keepAliveMap.get(wsUrl);
if (existing) {
ctx.body = { message: `工作空间 ${wsUrl} 的保持存活任务已在运行中` };
return;
}
console.log(`启动保持工作空间 ${wsUrl} 存活的任务`);
const keep = createKeepAlive({
wsUrl,
cookie,
onConnect: () => {
console.log(`工作空间 ${wsUrl} 保持存活任务已连接`);
},
onMessage: (data) => {
// 可选:处理收到的消息
// console.log(`工作空间 ${wsUrl} 收到消息: ${data}`);
const aliveInfo = keepAliveMap.get(wsUrl);
if (aliveInfo) {
aliveInfo.updatedTime = Date.now();
}
},
debug: true,
onExit: (code) => {
console.log(`工作空间 ${wsUrl} 保持存活任务已退出,退出码: ${code}`);
keepAliveMap.delete(wsUrl);
}
});
keepAliveMap.set(wsUrl, { startTime: Date.now(), updatedTime: Date.now(), KeepAlive: keep });
ctx.body = { message: `已启动保持工作空间 ${wsUrl} 存活的任务` };
}).addTo(app);
// 获取保持工作空间存活任务列表技能
app.route({
path: 'cnb',
key: 'list-keep-alive-tasks',
description: '获取保持工作空间存活任务列表技能',
middleware: ['auth'],
metadata: {
tags: [],
}
}).define(async (ctx) => {
const list = Array.from(keepAliveMap.entries()).map(([wsUrl, info]) => ({
wsUrl,
startTime: info.startTime,
updatedTime: info.updatedTime
}));
ctx.body = { list };
}).addTo(app);
// 停止保持工作空间存活技能
app.route({
path: 'cnb',
key: 'stop-keep-workspace-alive',
description: '停止保持工作空间存活技能, 参数wsUrl:工作空间访问URL',
middleware: ['auth'],
metadata: {
tags: [],
...({
args: {
wsUrl: tool.schema.string().describe('工作空间的访问URL'),
}
})
}
}).define(async (ctx) => {
const wsUrl = ctx.query?.wsUrl as string;
if (!wsUrl) {
ctx.throw(400, '缺少工作空间访问URL参数');
}
const keepAlive = keepAliveMap.get(wsUrl);
if (keepAlive) {
const endTime = Date.now();
const duration = endTime - keepAlive.startTime;
keepAlive?.KeepAlive?.disconnect();
keepAliveMap.delete(wsUrl);
ctx.body = { message: `已停止保持工作空间 ${wsUrl} 存活的任务,持续时间: ${duration}ms` };
} else {
ctx.body = { message: `没有找到工作空间 ${wsUrl} 的保持存活任务` };
}
}).addTo(app);

View File

@@ -21,3 +21,4 @@ const buildFn = async (opts: { entry?: string, naming?: string }) => {
await buildFn({ naming: 'opencode', entry: 'agent/opencode.ts' });
await buildFn({ naming: 'keep', entry: 'src/keep.ts' });
await buildFn({ naming: 'routes', entry: 'agent/index.ts' });

View File

@@ -1,6 +1,6 @@
{
"name": "@kevisual/cnb",
"version": "0.0.10",
"version": "0.0.12",
"description": "",
"main": "index.js",
"scripts": {
@@ -30,12 +30,16 @@
"publishConfig": {
"access": "public"
},
"resolutions": {
"zod": "^4.3.6"
},
"dependencies": {
"@kevisual/query": "^0.0.38",
"@kevisual/router": "^0.0.63",
"@kevisual/router": "^0.0.64",
"@kevisual/use-config": "^1.0.28",
"es-toolkit": "^1.44.0",
"nanoid": "^5.1.6",
"unstorage": "^1.17.4",
"ws": "npm:@kevisual/ws",
"zod": "^4.3.6"
},
@@ -43,6 +47,7 @@
".": "./mod.ts",
"./opencode": "./dist/opencode.js",
"./keep": "./dist/keep.js",
"./routes": "./dist/routes.js",
"./src/*": "./src/*",
"./agent/*": "./agent/*"
}

238
pnpm-lock.yaml generated
View File

@@ -4,6 +4,9 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
zod: ^4.3.6
importers:
.:
@@ -12,8 +15,8 @@ importers:
specifier: ^0.0.38
version: 0.0.38
'@kevisual/router':
specifier: ^0.0.63
version: 0.0.63(typescript@5.9.3)
specifier: ^0.0.64
version: 0.0.64(typescript@5.9.3)
'@kevisual/use-config':
specifier: ^1.0.28
version: 1.0.28(dotenv@17.2.3)
@@ -23,6 +26,12 @@ importers:
nanoid:
specifier: ^5.1.6
version: 5.1.6
unstorage:
specifier: ^1.17.4
version: 1.17.4
ws:
specifier: npm:@kevisual/ws
version: '@kevisual/ws@8.19.0'
zod:
specifier: ^4.3.6
version: 4.3.6
@@ -30,6 +39,9 @@ importers:
'@kevisual/ai':
specifier: ^0.0.24
version: 0.0.24
'@kevisual/cnb':
specifier: ^0.0.10
version: 0.0.10(dotenv@17.2.3)(typescript@5.9.3)
'@kevisual/context':
specifier: ^0.0.4
version: 0.0.4
@@ -51,9 +63,6 @@ importers:
dotenv:
specifier: ^17.2.3
version: 17.2.3
ws:
specifier: npm:@kevisual/ws
version: '@kevisual/ws@8.19.0'
packages:
@@ -71,6 +80,9 @@ packages:
'@kevisual/ai@0.0.24':
resolution: {integrity: sha512-7jvZk1/L//VIClK7usuNgN4ZA9Etgbooka1Sj5quE/0UywR+NNnwqXVZ89Y1fBhI1TkhauDsdJBAtcQ7r/vbVw==}
'@kevisual/cnb@0.0.10':
resolution: {integrity: sha512-zwdJOj64FAkbUIGy+3CC+uQMUptWGf1uuQZXy0ZLfBfdFO2BcoHuzvomWhYxF0eexOBsLrz1F9jNWS0heBrp2Q==}
'@kevisual/context@0.0.4':
resolution: {integrity: sha512-HJeLeZQLU+7tCluSfOyvkgKLs0HjCZrdJlZgEgKRSa8XTwZfMAUt6J7qZTbrZAHBlPtX68EPu/PI8JMCeu3WAQ==}
@@ -93,6 +105,9 @@ packages:
'@kevisual/router@0.0.63':
resolution: {integrity: sha512-rM/FELiNtTJkjb00sdQ2f8NpWHzfOwrtgQJhsX9Np9KdzAQ5dP6pI5nAHlWvU2QyrC1VH5IibUmsBIeMMV3nWg==}
'@kevisual/router@0.0.64':
resolution: {integrity: sha512-EYz1MZxrltgySUL0Y+/MtZf2FEmqC5U8GmFAqvHNjgtS5FJdHpxRjo6zab4+0wSUlVyCxCpZXFY5vHB/g+nQBw==}
'@kevisual/types@0.0.12':
resolution: {integrity: sha512-zJXH2dosir3jVrQ6QG4i0+iLQeT9gJ3H+cKXs8ReWboxBSYzUZO78XssVeVrFPsJ33iaAqo4q3DWbSS1dWGn7Q==}
@@ -304,16 +319,36 @@ packages:
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
bun-types@1.3.8:
resolution: {integrity: sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q==}
chokidar@5.0.0:
resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
engines: {node: '>= 20.19.0'}
commondir@1.0.1:
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
cookie-es@1.2.2:
resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==}
crossws@0.3.5:
resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
destr@2.0.5:
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
dotenv@17.2.3:
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
engines: {node: '>=12'}
@@ -344,6 +379,9 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
h3@1.15.5:
resolution: {integrity: sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==}
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
@@ -352,6 +390,9 @@ packages:
resolution: {integrity: sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==}
engines: {node: '>=16.9.0'}
iron-webcrypto@1.2.1:
resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
is-core-module@2.16.1:
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'}
@@ -365,6 +406,10 @@ packages:
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
lru-cache@11.2.5:
resolution: {integrity: sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==}
engines: {node: 20 || >=22}
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
@@ -373,16 +418,40 @@ packages:
engines: {node: ^18 || >=20}
hasBin: true
node-fetch-native@1.6.7:
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
node-mock-http@1.0.4:
resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
ofetch@1.5.1:
resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==}
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
radix3@1.1.2:
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
readdirp@5.0.0:
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
engines: {node: '>= 20.19.0'}
resolve@1.22.11:
resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
engines: {node: '>= 0.4'}
@@ -412,11 +481,76 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
ufo@1.6.3:
resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}
uncrypto@0.1.3:
resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
zod@4.1.8:
resolution: {integrity: sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==}
unstorage@1.17.4:
resolution: {integrity: sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==}
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
peerDependenciesMeta:
'@azure/app-configuration':
optional: true
'@azure/cosmos':
optional: true
'@azure/data-tables':
optional: true
'@azure/identity':
optional: true
'@azure/keyvault-secrets':
optional: true
'@azure/storage-blob':
optional: true
'@capacitor/preferences':
optional: true
'@deno/kv':
optional: true
'@netlify/blobs':
optional: true
'@planetscale/database':
optional: true
'@upstash/redis':
optional: true
'@vercel/blob':
optional: true
'@vercel/functions':
optional: true
'@vercel/kv':
optional: true
aws4fetch:
optional: true
db0:
optional: true
idb-keyval:
optional: true
ioredis:
optional: true
uploadthing:
optional: true
zod@4.3.6:
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
@@ -441,6 +575,19 @@ snapshots:
'@kevisual/permission': 0.0.3
'@kevisual/query': 0.0.38
'@kevisual/cnb@0.0.10(dotenv@17.2.3)(typescript@5.9.3)':
dependencies:
'@kevisual/query': 0.0.38
'@kevisual/router': 0.0.63(typescript@5.9.3)
'@kevisual/use-config': 1.0.28(dotenv@17.2.3)
es-toolkit: 1.44.0
nanoid: 5.1.6
ws: '@kevisual/ws@8.19.0'
zod: 4.3.6
transitivePeerDependencies:
- dotenv
- typescript
'@kevisual/context@0.0.4': {}
'@kevisual/dts@0.0.3(typescript@5.9.3)':
@@ -473,6 +620,13 @@ snapshots:
transitivePeerDependencies:
- typescript
'@kevisual/router@0.0.64(typescript@5.9.3)':
dependencies:
'@kevisual/dts': 0.0.3(typescript@5.9.3)
hono: 4.11.7
transitivePeerDependencies:
- typescript
'@kevisual/types@0.0.12': {}
'@kevisual/use-config@1.0.28(dotenv@17.2.3)':
@@ -485,7 +639,7 @@ snapshots:
'@opencode-ai/plugin@1.1.44':
dependencies:
'@opencode-ai/sdk': 1.1.44
zod: 4.1.8
zod: 4.3.6
'@opencode-ai/sdk@1.1.44': {}
@@ -619,14 +773,33 @@ snapshots:
dependencies:
'@types/node': 25.1.0
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
bun-types@1.3.8:
dependencies:
'@types/node': 25.1.0
chokidar@5.0.0:
dependencies:
readdirp: 5.0.0
commondir@1.0.1: {}
cookie-es@1.2.2: {}
crossws@0.3.5:
dependencies:
uncrypto: 0.1.3
deepmerge@4.3.1: {}
defu@6.1.4: {}
destr@2.0.5: {}
dotenv@17.2.3: {}
es-toolkit@1.44.0: {}
@@ -644,12 +817,26 @@ snapshots:
function-bind@1.1.2: {}
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
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
hono@4.11.7: {}
iron-webcrypto@1.2.1: {}
is-core-module@2.16.1:
dependencies:
hasown: 2.0.2
@@ -663,19 +850,39 @@ snapshots:
js-tokens@4.0.0:
optional: true
lru-cache@11.2.5: {}
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
nanoid@5.1.6: {}
node-fetch-native@1.6.7: {}
node-mock-http@1.0.4: {}
normalize-path@3.0.0: {}
ofetch@1.5.1:
dependencies:
destr: 2.0.5
node-fetch-native: 1.6.7
ufo: 1.6.3
path-parse@1.0.7: {}
picocolors@1.1.1:
optional: true
picomatch@2.3.1: {}
picomatch@4.0.3: {}
radix3@1.1.2: {}
readdirp@5.0.0: {}
resolve@1.22.11:
dependencies:
is-core-module: 2.16.1
@@ -727,8 +934,21 @@ snapshots:
typescript@5.9.3: {}
ufo@1.6.3: {}
uncrypto@0.1.3: {}
undici-types@7.16.0: {}
zod@4.1.8: {}
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.5
node-fetch-native: 1.6.7
ofetch: 1.5.1
ufo: 1.6.3
zod@4.3.6: {}

View File

@@ -12,6 +12,7 @@ export interface KeepAliveConfig {
onDisconnect?: (code: number) => void;
onError?: (error: Error) => void;
onSign?: (data: { type: string; data: string; signedData: string }) => void;
onExit?: (code?: number) => void;
debug?: boolean;
}
@@ -38,11 +39,12 @@ export class WSKeepAlive {
reconnectInterval: config.reconnectInterval ?? 5000,
maxReconnectAttempts: config.maxReconnectAttempts ?? 3,
pingInterval: config.pingInterval ?? 30000,
onMessage: config.onMessage ?? (() => {}),
onConnect: config.onConnect ?? (() => {}),
onDisconnect: config.onDisconnect ?? (() => {}),
onError: config.onError ?? (() => {}),
onSign: config.onSign ?? (() => {}),
onMessage: config.onMessage ?? (() => { }),
onConnect: config.onConnect ?? (() => { }),
onDisconnect: config.onDisconnect ?? (() => { }),
onError: config.onError ?? (() => { }),
onSign: config.onSign ?? (() => { }),
onExit: config.onExit ?? (() => { }),
debug: config.debug ?? false,
};
this.url = new URL(this.config.wsUrl);
@@ -158,6 +160,7 @@ export class WSKeepAlive {
private handleReconnect() {
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
this.log(`Max reconnect attempts (${this.config.maxReconnectAttempts}) reached. Giving up.`);
this.config.onExit(1);
return;
}
this.reconnectAttempts++;
@@ -174,6 +177,7 @@ export class WSKeepAlive {
this.stopPing();
if (this.ws) {
this.ws.close();
this.config.onExit(0);
this.ws = null;
}
}

View File

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

View File

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