@kevisual/cnb 0.0.8 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/app.ts +1 -5
- package/agent/opencode.ts +1 -1
- package/agent/routes/index.ts +3 -3
- package/agent/routes/workspace/index.ts +2 -1
- package/agent/routes/workspace/keep.ts +121 -0
- package/dist/keep.d.ts +2 -0
- package/dist/keep.js +13 -2851
- package/dist/opencode.js +7944 -20047
- package/dist/routes.d.ts +744 -0
- package/dist/routes.js +54326 -0
- package/package.json +9 -4
- package/src/workspace/keep-live.ts +16 -6
- package/src/workspace/keep-worker.ts +13 -0
package/agent/app.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { QueryRouterServer as App } from '@kevisual/router'
|
|
|
2
2
|
import { useContextKey } from '@kevisual/context'
|
|
3
3
|
import { useConfig, useKey } from '@kevisual/use-config'
|
|
4
4
|
import { CNB } from '../src/index.ts';
|
|
5
|
-
import { nanoid } from 'nanoid';
|
|
6
5
|
|
|
7
6
|
export const config = useConfig()
|
|
8
7
|
export const cnb = useContextKey<CNB>('cnb', () => {
|
|
@@ -13,9 +12,6 @@ export const cnb = useContextKey<CNB>('cnb', () => {
|
|
|
13
12
|
const cookie = useKey('CNB_COOKIE') as string
|
|
14
13
|
return new CNB({ token: token, cookie: cookie });
|
|
15
14
|
})
|
|
16
|
-
export const appId = nanoid();
|
|
17
15
|
export const app = useContextKey<App>('app', () => {
|
|
18
|
-
return new App({
|
|
19
|
-
appId
|
|
20
|
-
})
|
|
16
|
+
return new App({})
|
|
21
17
|
})
|
package/agent/opencode.ts
CHANGED
package/agent/routes/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { app
|
|
1
|
+
import { app } from '../app.ts';
|
|
2
2
|
import './cnb-env/check.ts'
|
|
3
3
|
import './repo/index.ts'
|
|
4
4
|
import './workspace/index.ts'
|
|
@@ -31,7 +31,7 @@ if (!app.hasRoute('auth')) {
|
|
|
31
31
|
path: 'auth',
|
|
32
32
|
}).define(async (ctx) => {
|
|
33
33
|
// ctx.body = 'Auth Route';
|
|
34
|
-
if (checkAppId(ctx, appId)) {
|
|
34
|
+
if (checkAppId(ctx, app.appId)) {
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
}).addTo(app);
|
|
@@ -42,7 +42,7 @@ if (!app.hasRoute('auth')) {
|
|
|
42
42
|
middleware: ['auth'],
|
|
43
43
|
}).define(async (ctx) => {
|
|
44
44
|
// ctx.body = 'Admin Auth Route';
|
|
45
|
-
if (checkAppId(ctx, appId)) {
|
|
45
|
+
if (checkAppId(ctx, app.appId)) {
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
}).addTo(app);
|
|
@@ -2,6 +2,7 @@ import { createSkill, tool } from '@kevisual/router';
|
|
|
2
2
|
import { app, cnb } from '../../app.ts';
|
|
3
3
|
import z from 'zod';
|
|
4
4
|
import './skills.ts';
|
|
5
|
+
import './keep.ts';
|
|
5
6
|
|
|
6
7
|
// 启动工作空间
|
|
7
8
|
app.route({
|
|
@@ -113,7 +114,7 @@ app.route({
|
|
|
113
114
|
args: {
|
|
114
115
|
pipelineId: tool.schema.string().optional().describe('流水线 ID,优先使用'),
|
|
115
116
|
sn: tool.schema.string().optional().describe('流水线构建号'),
|
|
116
|
-
sns: tool.schema.array(z.string()).optional().describe('
|
|
117
|
+
sns: tool.schema.array(z.string()).optional().describe('批量流水线构建号'),
|
|
117
118
|
},
|
|
118
119
|
})
|
|
119
120
|
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createSkill, tool } from '@kevisual/router';
|
|
2
|
+
import { app, cnb } from '../../app.ts';
|
|
3
|
+
|
|
4
|
+
import { createKeepAlive } from '../../../src/keep.ts';
|
|
5
|
+
|
|
6
|
+
type AliveInfo = {
|
|
7
|
+
startTime: number;
|
|
8
|
+
updatedTime?: number;
|
|
9
|
+
KeepAlive: ReturnType<typeof createKeepAlive>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const keepAliveMap = new Map<string, AliveInfo>();
|
|
13
|
+
|
|
14
|
+
// 保持工作空间存活技能
|
|
15
|
+
app.route({
|
|
16
|
+
path: 'cnb',
|
|
17
|
+
key: 'keep-workspace-alive',
|
|
18
|
+
description: '保持工作空间存活技能,参数wsUrl:工作空间访问URL,cookie:访问工作空间所需的cookie',
|
|
19
|
+
middleware: ['auth'],
|
|
20
|
+
metadata: {
|
|
21
|
+
tags: [],
|
|
22
|
+
...({
|
|
23
|
+
args: {
|
|
24
|
+
wsUrl: tool.schema.string().describe('工作空间的访问URL'),
|
|
25
|
+
cookie: tool.schema.string().describe('访问工作空间所需的cookie')
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
}).define(async (ctx) => {
|
|
30
|
+
const wsUrl = ctx.query?.wsUrl as string;
|
|
31
|
+
const cookie = ctx.query?.cookie as string;
|
|
32
|
+
if (!wsUrl) {
|
|
33
|
+
ctx.throw(400, '缺少工作空间访问URL参数');
|
|
34
|
+
}
|
|
35
|
+
if (!cookie) {
|
|
36
|
+
ctx.throw(400, '缺少访问工作空间所需的cookie参数');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 检测是否已在运行
|
|
40
|
+
const existing = keepAliveMap.get(wsUrl);
|
|
41
|
+
if (existing) {
|
|
42
|
+
ctx.body = { message: `工作空间 ${wsUrl} 的保持存活任务已在运行中` };
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log(`启动保持工作空间 ${wsUrl} 存活的任务`);
|
|
47
|
+
const keep = createKeepAlive({
|
|
48
|
+
wsUrl,
|
|
49
|
+
cookie,
|
|
50
|
+
onConnect: () => {
|
|
51
|
+
console.log(`工作空间 ${wsUrl} 保持存活任务已连接`);
|
|
52
|
+
},
|
|
53
|
+
onMessage: (data) => {
|
|
54
|
+
// 可选:处理收到的消息
|
|
55
|
+
// console.log(`工作空间 ${wsUrl} 收到消息: ${data}`);
|
|
56
|
+
const aliveInfo = keepAliveMap.get(wsUrl);
|
|
57
|
+
if (aliveInfo) {
|
|
58
|
+
aliveInfo.updatedTime = Date.now();
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
debug: true,
|
|
62
|
+
onExit: (code) => {
|
|
63
|
+
console.log(`工作空间 ${wsUrl} 保持存活任务已退出,退出码: ${code}`);
|
|
64
|
+
keepAliveMap.delete(wsUrl);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
keepAliveMap.set(wsUrl, { startTime: Date.now(), updatedTime: Date.now(), KeepAlive: keep });
|
|
69
|
+
|
|
70
|
+
ctx.body = { message: `已启动保持工作空间 ${wsUrl} 存活的任务` };
|
|
71
|
+
}).addTo(app);
|
|
72
|
+
|
|
73
|
+
// 获取保持工作空间存活任务列表技能
|
|
74
|
+
app.route({
|
|
75
|
+
path: 'cnb',
|
|
76
|
+
key: 'list-keep-alive-tasks',
|
|
77
|
+
description: '获取保持工作空间存活任务列表技能',
|
|
78
|
+
middleware: ['auth'],
|
|
79
|
+
metadata: {
|
|
80
|
+
tags: [],
|
|
81
|
+
}
|
|
82
|
+
}).define(async (ctx) => {
|
|
83
|
+
const list = Array.from(keepAliveMap.entries()).map(([wsUrl, info]) => ({
|
|
84
|
+
wsUrl,
|
|
85
|
+
startTime: info.startTime,
|
|
86
|
+
updatedTime: info.updatedTime
|
|
87
|
+
}));
|
|
88
|
+
ctx.body = { list };
|
|
89
|
+
}).addTo(app);
|
|
90
|
+
|
|
91
|
+
// 停止保持工作空间存活技能
|
|
92
|
+
app.route({
|
|
93
|
+
path: 'cnb',
|
|
94
|
+
key: 'stop-keep-workspace-alive',
|
|
95
|
+
description: '停止保持工作空间存活技能, 参数wsUrl:工作空间访问URL',
|
|
96
|
+
middleware: ['auth'],
|
|
97
|
+
metadata: {
|
|
98
|
+
tags: [],
|
|
99
|
+
...({
|
|
100
|
+
args: {
|
|
101
|
+
wsUrl: tool.schema.string().describe('工作空间的访问URL'),
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
}).define(async (ctx) => {
|
|
106
|
+
const wsUrl = ctx.query?.wsUrl as string;
|
|
107
|
+
if (!wsUrl) {
|
|
108
|
+
ctx.throw(400, '缺少工作空间访问URL参数');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const keepAlive = keepAliveMap.get(wsUrl);
|
|
112
|
+
if (keepAlive) {
|
|
113
|
+
const endTime = Date.now();
|
|
114
|
+
const duration = endTime - keepAlive.startTime;
|
|
115
|
+
keepAlive?.KeepAlive?.disconnect();
|
|
116
|
+
keepAliveMap.delete(wsUrl);
|
|
117
|
+
ctx.body = { message: `已停止保持工作空间 ${wsUrl} 存活的任务,持续时间: ${duration}ms` };
|
|
118
|
+
} else {
|
|
119
|
+
ctx.body = { message: `没有找到工作空间 ${wsUrl} 的保持存活任务` };
|
|
120
|
+
}
|
|
121
|
+
}).addTo(app);
|
package/dist/keep.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ interface KeepAliveConfig {
|
|
|
2
2
|
wsUrl: string;
|
|
3
3
|
cookie: string;
|
|
4
4
|
reconnectInterval?: number;
|
|
5
|
+
maxReconnectAttempts?: number;
|
|
5
6
|
pingInterval?: number;
|
|
6
7
|
onMessage?: (data: Buffer | string) => void;
|
|
7
8
|
onConnect?: () => void;
|
|
@@ -12,6 +13,7 @@ interface KeepAliveConfig {
|
|
|
12
13
|
data: string;
|
|
13
14
|
signedData: string;
|
|
14
15
|
}) => void;
|
|
16
|
+
onExit?: (code?: number) => void;
|
|
15
17
|
debug?: boolean;
|
|
16
18
|
}
|
|
17
19
|
interface ParsedMessage {
|