@tbox.cn/tboxcode-dsh-bundle 0.0.0-stage → 0.1.0-rc.1
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/cordis.patch.yml +105 -0
- package/lib/convergence-BfoAE48C.js +105 -0
- package/lib/convergence-CvqBcQv2.d.ts +8 -0
- package/lib/convergence-core-C1ZtU17g.d.ts +43 -0
- package/lib/convergence-core-CbS-hTjK.js +70 -0
- package/lib/convergence-core.d.ts +2 -0
- package/lib/convergence-core.js +3 -0
- package/lib/convergence.d.ts +2 -0
- package/lib/convergence.js +4 -0
- package/lib/index.d.ts +14 -0
- package/lib/index.js +15 -0
- package/lib/logger-BGwLHZXY.js +36 -0
- package/lib/logger-DcHqQwUI.d.ts +33 -0
- package/lib/logger.d.ts +2 -0
- package/lib/logger.js +3 -0
- package/lib/prompts.d.ts +22 -0
- package/lib/prompts.js +295 -0
- package/lib/remote-Ci33eojc.d.ts +197 -0
- package/lib/remote-DjawPq5z.js +353 -0
- package/lib/remote.d.ts +4 -0
- package/lib/remote.js +5 -0
- package/lib/retention-B2V8Plpx.d.ts +58 -0
- package/lib/retention-DNEdnIxC.js +162 -0
- package/lib/retention.d.ts +2 -0
- package/lib/retention.js +3 -0
- package/package.json +68 -3
- package/README.md +0 -3
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { n as isOwnedHome, r as startRetentionScheduler } from "./retention-DNEdnIxC.js";
|
|
2
|
+
import { t as createLogger } from "./logger-BGwLHZXY.js";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { RemoteError, TypertRemoteService, remoteMethods } from "@deepseek-ai/dsh-typert-protocol";
|
|
6
|
+
import { setSandboxMode } from "@deepseek-ai/dsh-sandbox-policy";
|
|
7
|
+
import { translateMode } from "@tbox.cn/dsh-acp-bridge-core";
|
|
8
|
+
|
|
9
|
+
//#region src/remote.ts
|
|
10
|
+
function requireAgent(services, agentId) {
|
|
11
|
+
const agent = services.agents.get(agentId);
|
|
12
|
+
if (agent === void 0) throw new RemoteError("tboxcode/agent-not-found", `会话 ${agentId} 无在册 agent(冷会话——prompt 路径会先 resume)`, { agentId });
|
|
13
|
+
return agent;
|
|
14
|
+
}
|
|
15
|
+
function handleCommandsList(services, request) {
|
|
16
|
+
const agent = requireAgent(services, request.agentId);
|
|
17
|
+
return services.commands.list(agent);
|
|
18
|
+
}
|
|
19
|
+
async function handleCommandsExecute(services, request, signal) {
|
|
20
|
+
const agent = requireAgent(services, request.agentId);
|
|
21
|
+
return services.commands.execute(agent, request.line, [], signal);
|
|
22
|
+
}
|
|
23
|
+
/** mode 通道(载体二):translateMode(bridge-core 语义映射单源)→ 双 knob 落地。 */
|
|
24
|
+
function handleMode(services, request) {
|
|
25
|
+
const agent = requireAgent(services, request.agentId);
|
|
26
|
+
const knobs = translateMode(request.mode);
|
|
27
|
+
if (knobs === void 0) throw new RemoteError("tboxcode/mode-unknown", `未知权限模式 "${request.mode}"(合法集 = bridge-core 语义映射表)`, { mode: request.mode });
|
|
28
|
+
const session = services.sessions.get(request.agentId);
|
|
29
|
+
if (session !== void 0) services.sandbox.setSandboxMode(session, knobs.sandbox);
|
|
30
|
+
services.approval.setPolicy(agent, knobs.approval);
|
|
31
|
+
return {
|
|
32
|
+
sandbox: knobs.sandbox,
|
|
33
|
+
approval: knobs.approval
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** 部署默认行(configOptions model 项 initialValue 面——未配置返回 undefined)。 */
|
|
37
|
+
function handleDefaultSelection(services) {
|
|
38
|
+
const selection = services.agentDefaultModel.currentSelection();
|
|
39
|
+
const provider = typeof selection.provider === "string" ? selection.provider : void 0;
|
|
40
|
+
const model = typeof selection.model === "string" ? selection.model : void 0;
|
|
41
|
+
if (provider === void 0 || model === void 0) return {};
|
|
42
|
+
return {
|
|
43
|
+
provider,
|
|
44
|
+
model,
|
|
45
|
+
...typeof selection.reasoningEffort === "string" ? { reasoningEffort: selection.reasoningEffort } : {}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async function handleSkillsUserInvocable(services, request) {
|
|
49
|
+
const agent = requireAgent(services, request.agentId);
|
|
50
|
+
return (await services.skills.snapshot({ cwd: agent.session.header.cwd })).skills;
|
|
51
|
+
}
|
|
52
|
+
async function handleSessionsFlush(services, request) {
|
|
53
|
+
const session = services.sessions.get(request.sessionId);
|
|
54
|
+
if (session === void 0) throw new RemoteError("tboxcode/agent-not-found", `会话 ${request.sessionId} 不在内存(flush 须热会话)`, { sessionId: request.sessionId });
|
|
55
|
+
await services.sessions.flush(session);
|
|
56
|
+
}
|
|
57
|
+
/** 会话头 stat(resume cwd 匹配面——冷会话返 null 不抛;网关 persistence.stat 对位)。 */
|
|
58
|
+
async function handleSessionsStat(services, request) {
|
|
59
|
+
const found = await services.persistence.stat(request.sessionId);
|
|
60
|
+
return found === void 0 ? null : { header: found.header };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* skills/change 转发流(P5-3 定案:skills/change 不在 $events allowlist——
|
|
64
|
+
* 零上游变更的进程内桥接;payload-free seq 递增,消费方重取 commands.list)。
|
|
65
|
+
*/
|
|
66
|
+
async function* handleSkillsWatch(services, signal) {
|
|
67
|
+
let pending = 0;
|
|
68
|
+
let wake;
|
|
69
|
+
let seq = 0;
|
|
70
|
+
const stop = services.on("skills/change", () => {
|
|
71
|
+
pending++;
|
|
72
|
+
wake?.();
|
|
73
|
+
});
|
|
74
|
+
const abort = () => {
|
|
75
|
+
wake?.();
|
|
76
|
+
};
|
|
77
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
78
|
+
try {
|
|
79
|
+
while (!signal.aborted) {
|
|
80
|
+
if (pending > 0) {
|
|
81
|
+
pending--;
|
|
82
|
+
yield { seq: ++seq };
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
await new Promise((resolve) => {
|
|
86
|
+
wake = resolve;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
} finally {
|
|
90
|
+
stop();
|
|
91
|
+
signal.removeEventListener("abort", abort);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* agent/inbox/claimed + agent/error 转发流(P5-3 定案:journal turn/start 仅
|
|
96
|
+
* {turn} 无 messageId——prompt 完成相关性与失败分支必须走进程事件;两类聚合
|
|
97
|
+
* 单流,sessionId 经 agent 句柄防御性提取)。
|
|
98
|
+
*/
|
|
99
|
+
async function* handleAgentWatch(services, signal) {
|
|
100
|
+
const queue = [];
|
|
101
|
+
let wake;
|
|
102
|
+
const push = (item) => {
|
|
103
|
+
queue.push(item);
|
|
104
|
+
wake?.();
|
|
105
|
+
};
|
|
106
|
+
const stopClaimed = services.on("agent/inbox/claimed", (payload) => {
|
|
107
|
+
const { agent, message, turn } = payload ?? {};
|
|
108
|
+
const sessionId = typeof agent?.session?.id === "string" ? agent.session.id : typeof agent?.id === "string" ? agent.id : "";
|
|
109
|
+
if (sessionId === "") return;
|
|
110
|
+
push({
|
|
111
|
+
kind: "inboxClaimed",
|
|
112
|
+
sessionId,
|
|
113
|
+
...typeof message?.id === "string" ? { messageId: message.id } : {},
|
|
114
|
+
turn: typeof turn === "number" ? turn : 0
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
const stopError = services.on("agent/error", (payload) => {
|
|
118
|
+
const { agent, error } = payload ?? {};
|
|
119
|
+
const sessionId = typeof agent?.session?.id === "string" ? agent.session.id : typeof agent?.id === "string" ? agent.id : "";
|
|
120
|
+
if (sessionId === "") return;
|
|
121
|
+
push({
|
|
122
|
+
kind: "error",
|
|
123
|
+
sessionId,
|
|
124
|
+
error: error instanceof Error ? error.message : String(error)
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
const abort = () => {
|
|
128
|
+
wake?.();
|
|
129
|
+
};
|
|
130
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
131
|
+
try {
|
|
132
|
+
while (!signal.aborted) {
|
|
133
|
+
const item = queue.shift();
|
|
134
|
+
if (item !== void 0) {
|
|
135
|
+
yield item;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
await new Promise((resolve) => {
|
|
139
|
+
wake = resolve;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
} finally {
|
|
143
|
+
stopClaimed();
|
|
144
|
+
stopError();
|
|
145
|
+
signal.removeEventListener("abort", abort);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** 热载触发(convergence reloadNow 包装——忙时 RETRY_AFTER_TURN 拒绝语义透传)。 */
|
|
149
|
+
async function handleReload(services) {
|
|
150
|
+
if (services.convergence === void 0) throw new RemoteError("tboxcode/reload-unavailable", "收敛环未挂载(reload 通道仅 config 热载形态可用)", {});
|
|
151
|
+
await services.convergence.reloadNow();
|
|
152
|
+
}
|
|
153
|
+
/** health(内存观测指标源——doctor --server 画像;会话/冷热分布经 api session/list 面)。 */
|
|
154
|
+
function handleHealth(services) {
|
|
155
|
+
const memory = (services.memoryUsage ?? (() => process.memoryUsage()))();
|
|
156
|
+
return {
|
|
157
|
+
memory: {
|
|
158
|
+
rss: memory.rss,
|
|
159
|
+
heapUsed: memory.heapUsed,
|
|
160
|
+
heapTotal: memory.heapTotal,
|
|
161
|
+
external: memory.external
|
|
162
|
+
},
|
|
163
|
+
uptimeMs: (services.uptime ?? (() => process.uptime() * 1e3))(),
|
|
164
|
+
...services.retention?.lastResult() === void 0 ? {} : { retentionLast: services.retention?.lastResult() }
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** retention.run(手动触发——调度面之外的运维通道)。 */
|
|
168
|
+
async function handleRetentionRun(services) {
|
|
169
|
+
if (services.retention === void 0) throw new RemoteError("tboxcode/retention-unavailable", "retention 调度未装载", {});
|
|
170
|
+
return services.retention.runOnce();
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 实施发现(切片 4):本仓 oxc 工具链(vitest vite:oxc / tsdown rolldown)不降级
|
|
174
|
+
* stage-3 decorator——`@Remote` 语法在构建产物原样泄漏(node 运行时 SyntaxError)。
|
|
175
|
+
* 壳改用协议标记描述符直写:REMOTE_METHOD_DESCRIPTOR('@deepseek-ai/dsh-typert-protocol/
|
|
176
|
+
* remote-methods',version:1 冻结格式——remoteMethods 的读取面)+ boot 自检
|
|
177
|
+
* (remoteMethods 回读校验方法数与 exportName 集——格式漂移即 boot 红,e2e 兜底)。
|
|
178
|
+
* 方法参数名由 Gateway SRC 从构建产物源码读取(纯方法声明,无语法依赖)。
|
|
179
|
+
*/
|
|
180
|
+
const REMOTE_METHOD_DESCRIPTOR = "@deepseek-ai/dsh-typert-protocol/remote-methods";
|
|
181
|
+
function resolveSessionsRoot(ctx) {
|
|
182
|
+
const root = (ctx.get?.("sessionPersistence"))?.config?.root;
|
|
183
|
+
if (typeof root === "string" && root !== "") return root;
|
|
184
|
+
return join(process.env["DSH_HOME"] ?? join(homedir(), ".dsh"), "sessions");
|
|
185
|
+
}
|
|
186
|
+
const name = "tboxcode-remote";
|
|
187
|
+
const inject = [
|
|
188
|
+
"agents",
|
|
189
|
+
"commands",
|
|
190
|
+
"skills",
|
|
191
|
+
"approval",
|
|
192
|
+
"agentDefaultModel",
|
|
193
|
+
"sessions",
|
|
194
|
+
"sessionPersistence",
|
|
195
|
+
"tboxcodeConvergence"
|
|
196
|
+
];
|
|
197
|
+
function stateOf(self) {
|
|
198
|
+
const carrier = self;
|
|
199
|
+
if (carrier._remoteState !== void 0) return carrier._remoteState;
|
|
200
|
+
const state = {
|
|
201
|
+
log: void 0,
|
|
202
|
+
retention: void 0,
|
|
203
|
+
services: void 0
|
|
204
|
+
};
|
|
205
|
+
carrier._remoteState = state;
|
|
206
|
+
return state;
|
|
207
|
+
}
|
|
208
|
+
/** 宿主服务行(结构行——四链恒挂;sandbox 写函数经 ctx 进程内直调)。 */
|
|
209
|
+
var TboxcodeRemoteGateway = class extends TypertRemoteService {
|
|
210
|
+
static inject = inject;
|
|
211
|
+
constructor(ctx) {
|
|
212
|
+
super(ctx, "tboxcode");
|
|
213
|
+
const remoteCtx = ctx;
|
|
214
|
+
stateOf(this).log = createLogger({ domain: "tboxcode.remote" });
|
|
215
|
+
const sessionsRoot = resolveSessionsRoot(remoteCtx);
|
|
216
|
+
if (sessionsRoot !== void 0) stateOf(this).retention = startRetentionScheduler({
|
|
217
|
+
sessionsRoot,
|
|
218
|
+
knobs: isOwnedHome() ? void 0 : {
|
|
219
|
+
sessionsMaxAgeDays: 0,
|
|
220
|
+
sessionsMaxBytes: 0,
|
|
221
|
+
sessionsActiveSkipMs: 24 * 36e5,
|
|
222
|
+
llmCallsRetentionDays: 14
|
|
223
|
+
},
|
|
224
|
+
log: (message, data) => stateOf(this).log.info(message, data)
|
|
225
|
+
});
|
|
226
|
+
stateOf(this).services = {
|
|
227
|
+
agents: remoteCtx.agents,
|
|
228
|
+
commands: remoteCtx.commands,
|
|
229
|
+
skills: remoteCtx.skills,
|
|
230
|
+
approval: remoteCtx.approval,
|
|
231
|
+
agentDefaultModel: remoteCtx.agentDefaultModel,
|
|
232
|
+
sessions: remoteCtx.sessions,
|
|
233
|
+
persistence: remoteCtx.sessionPersistence ?? { stat: async () => void 0 },
|
|
234
|
+
on: (event, listener) => remoteCtx.on(event, listener),
|
|
235
|
+
sandbox: { setSandboxMode: (session, mode) => setSandboxMode(session, mode) },
|
|
236
|
+
convergence: remoteCtx.tboxcodeConvergence,
|
|
237
|
+
retention: stateOf(this).retention,
|
|
238
|
+
memoryUsage: () => process.memoryUsage(),
|
|
239
|
+
uptime: () => process.uptime() * 1e3
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
commandsList(request) {
|
|
243
|
+
return handleCommandsList(stateOf(this).services, request);
|
|
244
|
+
}
|
|
245
|
+
commandsExecute(request, signal) {
|
|
246
|
+
return handleCommandsExecute(stateOf(this).services, request, signal);
|
|
247
|
+
}
|
|
248
|
+
mode(request) {
|
|
249
|
+
const outcome = handleMode(stateOf(this).services, request);
|
|
250
|
+
stateOf(this).log.info("mode 切换", {
|
|
251
|
+
agentId: request.agentId,
|
|
252
|
+
mode: request.mode
|
|
253
|
+
});
|
|
254
|
+
return outcome;
|
|
255
|
+
}
|
|
256
|
+
defaultSelection() {
|
|
257
|
+
return handleDefaultSelection(stateOf(this).services);
|
|
258
|
+
}
|
|
259
|
+
skillsUserInvocable(request) {
|
|
260
|
+
return handleSkillsUserInvocable(stateOf(this).services, request);
|
|
261
|
+
}
|
|
262
|
+
sessionsFlush(request) {
|
|
263
|
+
return handleSessionsFlush(stateOf(this).services, request);
|
|
264
|
+
}
|
|
265
|
+
sessionsStat(request) {
|
|
266
|
+
return handleSessionsStat(stateOf(this).services, request);
|
|
267
|
+
}
|
|
268
|
+
skillsWatch(signal) {
|
|
269
|
+
return handleSkillsWatch(stateOf(this).services, signal);
|
|
270
|
+
}
|
|
271
|
+
agentWatch(signal) {
|
|
272
|
+
return handleAgentWatch(stateOf(this).services, signal);
|
|
273
|
+
}
|
|
274
|
+
reload() {
|
|
275
|
+
return handleReload(stateOf(this).services);
|
|
276
|
+
}
|
|
277
|
+
health() {
|
|
278
|
+
return handleHealth(stateOf(this).services);
|
|
279
|
+
}
|
|
280
|
+
retentionRun() {
|
|
281
|
+
return handleRetentionRun(stateOf(this).services);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
const REMOTE_METHODS = [
|
|
285
|
+
{
|
|
286
|
+
method: "commandsList",
|
|
287
|
+
exportName: "commands.list"
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
method: "commandsExecute",
|
|
291
|
+
exportName: "commands.execute"
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
method: "mode",
|
|
295
|
+
exportName: "mode"
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
method: "defaultSelection",
|
|
299
|
+
exportName: "selection.default"
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
method: "skillsUserInvocable",
|
|
303
|
+
exportName: "skills.userInvocable"
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
method: "skillsWatch",
|
|
307
|
+
exportName: "skills.watch",
|
|
308
|
+
mode: "stream"
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
method: "agentWatch",
|
|
312
|
+
exportName: "agent.watch",
|
|
313
|
+
mode: "stream"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
method: "sessionsFlush",
|
|
317
|
+
exportName: "sessions.flush"
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
method: "sessionsStat",
|
|
321
|
+
exportName: "sessions.stat"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
method: "reload",
|
|
325
|
+
exportName: "reload"
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
method: "health",
|
|
329
|
+
exportName: "health"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
method: "retentionRun",
|
|
333
|
+
exportName: "retention.run"
|
|
334
|
+
}
|
|
335
|
+
];
|
|
336
|
+
Object.defineProperty(TboxcodeRemoteGateway.prototype, REMOTE_METHOD_DESCRIPTOR, {
|
|
337
|
+
configurable: true,
|
|
338
|
+
value: Object.freeze({
|
|
339
|
+
version: 1,
|
|
340
|
+
methods: Object.freeze(REMOTE_METHODS.map(({ method, exportName, mode }) => Object.freeze({
|
|
341
|
+
method,
|
|
342
|
+
...exportName === method ? {} : { exportName },
|
|
343
|
+
...mode === void 0 ? {} : { mode },
|
|
344
|
+
invocation: Object.freeze({ kind: "direct" })
|
|
345
|
+
})))
|
|
346
|
+
})
|
|
347
|
+
});
|
|
348
|
+
const markers = remoteMethods(Object.create(TboxcodeRemoteGateway.prototype));
|
|
349
|
+
if (markers.length !== REMOTE_METHODS.length || REMOTE_METHODS.some(({ exportName }) => !markers.some((marker) => (marker.exportName ?? marker.method) === exportName))) throw new Error("tboxcode-remote: 协议标记描述符自检失败(remoteMethods 回读与声明集失配——上游格式漂移)");
|
|
350
|
+
var remote_default = TboxcodeRemoteGateway;
|
|
351
|
+
|
|
352
|
+
//#endregion
|
|
353
|
+
export { handleDefaultSelection as a, handleReload as c, handleSessionsStat as d, handleSkillsUserInvocable as f, remote_default as g, name as h, handleCommandsList as i, handleRetentionRun as l, inject as m, handleAgentWatch as n, handleHealth as o, handleSkillsWatch as p, handleCommandsExecute as r, handleMode as s, TboxcodeRemoteGateway as t, handleSessionsFlush as u };
|
package/lib/remote.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import "./convergence-core-C1ZtU17g.js";
|
|
2
|
+
import "./retention-B2V8Plpx.js";
|
|
3
|
+
import { _ as handleSkillsWatch, a as TboxcodeRemoteServices, c as handleCommandsList, d as handleMode, f as handleReload, g as handleSkillsUserInvocable, h as handleSessionsStat, i as TboxcodeRemoteGateway, l as handleDefaultSelection, m as handleSessionsFlush, n as DefaultModelSelection, o as handleAgentWatch, p as handleRetentionRun, r as RemoteAgentLike, s as handleCommandsExecute, t as AgentWatchItem, u as handleHealth, v as inject, y as name } from "./remote-Ci33eojc.js";
|
|
4
|
+
export { AgentWatchItem, DefaultModelSelection, RemoteAgentLike, TboxcodeRemoteGateway, TboxcodeRemoteGateway as default, TboxcodeRemoteServices, handleAgentWatch, handleCommandsExecute, handleCommandsList, handleDefaultSelection, handleHealth, handleMode, handleReload, handleRetentionRun, handleSessionsFlush, handleSessionsStat, handleSkillsUserInvocable, handleSkillsWatch, inject, name };
|
package/lib/remote.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import "./retention-DNEdnIxC.js";
|
|
2
|
+
import "./logger-BGwLHZXY.js";
|
|
3
|
+
import { a as handleDefaultSelection, c as handleReload, d as handleSessionsStat, f as handleSkillsUserInvocable, g as remote_default, h as name, i as handleCommandsList, l as handleRetentionRun, m as inject, n as handleAgentWatch, o as handleHealth, p as handleSkillsWatch, r as handleCommandsExecute, s as handleMode, t as TboxcodeRemoteGateway, u as handleSessionsFlush } from "./remote-DjawPq5z.js";
|
|
4
|
+
|
|
5
|
+
export { TboxcodeRemoteGateway, remote_default as default, handleAgentWatch, handleCommandsExecute, handleCommandsList, handleDefaultSelection, handleHealth, handleMode, handleReload, handleRetentionRun, handleSessionsFlush, handleSessionsStat, handleSkillsUserInvocable, handleSkillsWatch, inject, name };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//#region src/retention.d.ts
|
|
2
|
+
interface RetentionKnobs {
|
|
3
|
+
/** sessions 钮:双阈值(天数 / 字节);0 = 关闭。 */
|
|
4
|
+
sessionsMaxAgeDays: number;
|
|
5
|
+
sessionsMaxBytes: number;
|
|
6
|
+
/** 活跃豁免窗口(mtime 近于此值的会话目录不删——保守代理)。 */
|
|
7
|
+
sessionsActiveSkipMs: number;
|
|
8
|
+
/** llm_calls 钮:天数;0 = 关闭。 */
|
|
9
|
+
llmCallsRetentionDays: number;
|
|
10
|
+
}
|
|
11
|
+
declare const DEFAULT_KNOBS: RetentionKnobs;
|
|
12
|
+
interface SweepResult {
|
|
13
|
+
/** 各钮清扫量(删除目录数 / 文件数)与跳过量(可观测面——doctor 画像源)。 */
|
|
14
|
+
sessionsDeleted: number;
|
|
15
|
+
sessionsSkippedActive: number;
|
|
16
|
+
llmCallsDeleted: number;
|
|
17
|
+
errors: string[];
|
|
18
|
+
}
|
|
19
|
+
/** 独立 home 判据:TBOXCODE_DSH_HOME 显式指向(本仓 env 域——领地隔离语义)。 */
|
|
20
|
+
declare function isOwnedHome(env?: NodeJS.ProcessEnv): boolean;
|
|
21
|
+
interface SessionsSweepFs {
|
|
22
|
+
readdir(dir: string): Promise<Array<{
|
|
23
|
+
name: string;
|
|
24
|
+
isDirectory(): boolean;
|
|
25
|
+
}>>;
|
|
26
|
+
stat(path: string): Promise<{
|
|
27
|
+
mtimeMs: number;
|
|
28
|
+
size: number;
|
|
29
|
+
isDirectory(): boolean;
|
|
30
|
+
}>;
|
|
31
|
+
rm(path: string, options: {
|
|
32
|
+
recursive: true;
|
|
33
|
+
force?: boolean;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* sessions 钮清扫(双阈值 LRU):逐会话目录计龄 + 求全量体量;超龄全删(活跃
|
|
38
|
+
* 豁免除外),未超龄但总量超 5GiB 时按最老先删至阈值内。两轮合一直接实现。
|
|
39
|
+
*/
|
|
40
|
+
declare function sweepSessions(sessionsRoot: string, knobs: RetentionKnobs, fs: SessionsSweepFs, now?: number): Promise<SweepResult>;
|
|
41
|
+
/** llm_calls 钮清扫(复用 llm 包双路径实现——会话私有 + orphan 兜底)。 */
|
|
42
|
+
declare function sweepLlmCalls(sessionsRoot: string, knobs: RetentionKnobs, cleanup: (sessionRoot: string, retentionDays: number, maxFiles?: number) => Promise<number>): Promise<SweepResult>;
|
|
43
|
+
/** 调度句柄(启动即扫 + 24h 周期;dispose 停表;末次结果观测面)。 */
|
|
44
|
+
interface RetentionScheduler {
|
|
45
|
+
runOnce(): Promise<SweepResult>;
|
|
46
|
+
lastResult(): SweepResult | undefined;
|
|
47
|
+
dispose(): void;
|
|
48
|
+
}
|
|
49
|
+
declare function startRetentionScheduler(options: {
|
|
50
|
+
sessionsRoot: string;
|
|
51
|
+
knobs?: RetentionKnobs;
|
|
52
|
+
intervalMs?: number;
|
|
53
|
+
fs?: SessionsSweepFs;
|
|
54
|
+
cleanupLlm?: (sessionRoot: string, retentionDays: number, maxFiles?: number) => Promise<number>;
|
|
55
|
+
log?: (message: string, data?: Record<string, unknown>) => void;
|
|
56
|
+
}): RetentionScheduler;
|
|
57
|
+
//#endregion
|
|
58
|
+
export { SweepResult as a, sweepLlmCalls as c, SessionsSweepFs as i, sweepSessions as l, RetentionKnobs as n, isOwnedHome as o, RetentionScheduler as r, startRetentionScheduler as s, DEFAULT_KNOBS as t };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { readdir, rm, stat } from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
//#region src/retention.ts
|
|
5
|
+
const DEFAULT_KNOBS = {
|
|
6
|
+
sessionsMaxAgeDays: 180,
|
|
7
|
+
sessionsMaxBytes: 5 * 1024 * 1024 * 1024,
|
|
8
|
+
sessionsActiveSkipMs: 24 * 36e5,
|
|
9
|
+
llmCallsRetentionDays: 14
|
|
10
|
+
};
|
|
11
|
+
/** 独立 home 判据:TBOXCODE_DSH_HOME 显式指向(本仓 env 域——领地隔离语义)。 */
|
|
12
|
+
function isOwnedHome(env = process.env) {
|
|
13
|
+
return env["TBOXCODE_DSH_HOME"] !== void 0;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* sessions 钮清扫(双阈值 LRU):逐会话目录计龄 + 求全量体量;超龄全删(活跃
|
|
17
|
+
* 豁免除外),未超龄但总量超 5GiB 时按最老先删至阈值内。两轮合一直接实现。
|
|
18
|
+
*/
|
|
19
|
+
async function sweepSessions(sessionsRoot, knobs, fs, now = Date.now()) {
|
|
20
|
+
const result = {
|
|
21
|
+
sessionsDeleted: 0,
|
|
22
|
+
sessionsSkippedActive: 0,
|
|
23
|
+
llmCallsDeleted: 0,
|
|
24
|
+
errors: []
|
|
25
|
+
};
|
|
26
|
+
if (knobs.sessionsMaxAgeDays <= 0 && knobs.sessionsMaxBytes <= 0) return result;
|
|
27
|
+
const cutoff = now - knobs.sessionsMaxAgeDays * 864e5;
|
|
28
|
+
const activeCutoff = now - knobs.sessionsActiveSkipMs;
|
|
29
|
+
const entries = [];
|
|
30
|
+
const projectDirs = await safeReaddir(fs, sessionsRoot, result.errors);
|
|
31
|
+
for (const project of projectDirs) {
|
|
32
|
+
if (!project.isDirectory()) continue;
|
|
33
|
+
const sessionDirs = await safeReaddir(fs, join(sessionsRoot, project.name), result.errors);
|
|
34
|
+
for (const session of sessionDirs) {
|
|
35
|
+
if (!session.isDirectory()) continue;
|
|
36
|
+
const path = join(sessionsRoot, project.name, session.name);
|
|
37
|
+
try {
|
|
38
|
+
const info = await fs.stat(path);
|
|
39
|
+
const size = info.isDirectory() ? await directorySize(fs, path, result.errors) : info.size;
|
|
40
|
+
entries.push({
|
|
41
|
+
path,
|
|
42
|
+
mtimeMs: info.mtimeMs,
|
|
43
|
+
size,
|
|
44
|
+
active: info.mtimeMs >= activeCutoff
|
|
45
|
+
});
|
|
46
|
+
} catch (error) {
|
|
47
|
+
result.errors.push(`stat ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const overAge = entries.filter((entry) => knobs.sessionsMaxAgeDays > 0 && entry.mtimeMs < cutoff);
|
|
52
|
+
for (const entry of overAge) {
|
|
53
|
+
if (entry.active) {
|
|
54
|
+
result.sessionsSkippedActive += 1;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
await fs.rm(entry.path, {
|
|
59
|
+
recursive: true,
|
|
60
|
+
force: true
|
|
61
|
+
});
|
|
62
|
+
result.sessionsDeleted += 1;
|
|
63
|
+
entry.size = 0;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
result.errors.push(`rm ${entry.path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (knobs.sessionsMaxBytes > 0) {
|
|
69
|
+
const survivors = entries.filter((entry) => entry.size > 0).sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
70
|
+
let total = survivors.reduce((sum, entry) => sum + entry.size, 0);
|
|
71
|
+
for (const entry of survivors) {
|
|
72
|
+
if (total <= knobs.sessionsMaxBytes) break;
|
|
73
|
+
if (entry.active) {
|
|
74
|
+
result.sessionsSkippedActive += 1;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await fs.rm(entry.path, {
|
|
79
|
+
recursive: true,
|
|
80
|
+
force: true
|
|
81
|
+
});
|
|
82
|
+
total -= entry.size;
|
|
83
|
+
result.sessionsDeleted += 1;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
result.errors.push(`rm ${entry.path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
async function directorySize(fs, dir, errors) {
|
|
92
|
+
let total = 0;
|
|
93
|
+
const children = await safeReaddir(fs, dir, errors);
|
|
94
|
+
for (const child of children) {
|
|
95
|
+
const path = join(dir, child.name);
|
|
96
|
+
try {
|
|
97
|
+
if (child.isDirectory()) total += await directorySize(fs, path, errors);
|
|
98
|
+
else total += (await fs.stat(path)).size;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
errors.push(`stat ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return total;
|
|
104
|
+
}
|
|
105
|
+
async function safeReaddir(fs, dir, errors) {
|
|
106
|
+
try {
|
|
107
|
+
return await fs.readdir(dir);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (error.code !== "ENOENT") errors.push(`readdir ${dir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** llm_calls 钮清扫(复用 llm 包双路径实现——会话私有 + orphan 兜底)。 */
|
|
114
|
+
async function sweepLlmCalls(sessionsRoot, knobs, cleanup) {
|
|
115
|
+
return {
|
|
116
|
+
sessionsDeleted: 0,
|
|
117
|
+
sessionsSkippedActive: 0,
|
|
118
|
+
llmCallsDeleted: knobs.llmCallsRetentionDays > 0 ? await cleanup(sessionsRoot, knobs.llmCallsRetentionDays) : 0,
|
|
119
|
+
errors: []
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function startRetentionScheduler(options) {
|
|
123
|
+
const knobs = options.knobs ?? DEFAULT_KNOBS;
|
|
124
|
+
const intervalMs = options.intervalMs ?? 24 * 36e5;
|
|
125
|
+
const fs = options.fs ?? {
|
|
126
|
+
readdir: async (dir) => readdir(dir, { withFileTypes: true }),
|
|
127
|
+
stat: async (path) => stat(path),
|
|
128
|
+
rm: async (path, opts) => rm(path, opts)
|
|
129
|
+
};
|
|
130
|
+
const cleanup = options.cleanupLlm ?? (async (root, days) => {
|
|
131
|
+
const { cleanupOldLlmLogs } = await import("@tbox.cn/tboxcode-dsh-llm/call-log");
|
|
132
|
+
return cleanupOldLlmLogs(root, days);
|
|
133
|
+
});
|
|
134
|
+
const log = options.log ?? (() => {});
|
|
135
|
+
let last;
|
|
136
|
+
const timer = setInterval(() => {
|
|
137
|
+
runOnce().catch((error) => log("retention 扫描异常", { error: error instanceof Error ? error.message : String(error) }));
|
|
138
|
+
}, intervalMs);
|
|
139
|
+
const runOnce = async () => {
|
|
140
|
+
const sessions = await sweepSessions(options.sessionsRoot, knobs, fs);
|
|
141
|
+
const llmCalls = await sweepLlmCalls(options.sessionsRoot, knobs, cleanup);
|
|
142
|
+
const merged = {
|
|
143
|
+
sessionsDeleted: sessions.sessionsDeleted,
|
|
144
|
+
sessionsSkippedActive: sessions.sessionsSkippedActive,
|
|
145
|
+
llmCallsDeleted: llmCalls.llmCallsDeleted,
|
|
146
|
+
errors: [...sessions.errors, ...llmCalls.errors]
|
|
147
|
+
};
|
|
148
|
+
last = merged;
|
|
149
|
+
log("retention 扫描完成", { ...merged });
|
|
150
|
+
return merged;
|
|
151
|
+
};
|
|
152
|
+
runOnce().catch((error) => log("retention 扫描异常", { error: error instanceof Error ? error.message : String(error) }));
|
|
153
|
+
timer.unref?.();
|
|
154
|
+
return {
|
|
155
|
+
runOnce,
|
|
156
|
+
lastResult: () => last,
|
|
157
|
+
dispose: () => clearInterval(timer)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
//#endregion
|
|
162
|
+
export { sweepSessions as a, sweepLlmCalls as i, isOwnedHome as n, startRetentionScheduler as r, DEFAULT_KNOBS as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as SweepResult, c as sweepLlmCalls, i as SessionsSweepFs, l as sweepSessions, n as RetentionKnobs, o as isOwnedHome, r as RetentionScheduler, s as startRetentionScheduler, t as DEFAULT_KNOBS } from "./retention-B2V8Plpx.js";
|
|
2
|
+
export { DEFAULT_KNOBS, RetentionKnobs, RetentionScheduler, SessionsSweepFs, SweepResult, isOwnedHome, startRetentionScheduler, sweepLlmCalls, sweepSessions };
|
package/lib/retention.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,71 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tbox.cn/tboxcode-dsh-bundle",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "0.1.0-rc.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "tboxcode 组合点(host 服务行 + agent 平面 host 行 + profile 模板三件):patch + prompts / reminders 插件 + tboxcode Remote 服务",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
14
|
+
"./prompts": {
|
|
15
|
+
"types": "./lib/prompts.d.ts",
|
|
16
|
+
"default": "./lib/prompts.js"
|
|
17
|
+
},
|
|
18
|
+
"./convergence": {
|
|
19
|
+
"types": "./lib/convergence.d.ts",
|
|
20
|
+
"default": "./lib/convergence.js"
|
|
21
|
+
},
|
|
22
|
+
"./convergence-core": {
|
|
23
|
+
"types": "./lib/convergence-core.d.ts",
|
|
24
|
+
"default": "./lib/convergence-core.js"
|
|
25
|
+
},
|
|
26
|
+
"./remote": {
|
|
27
|
+
"types": "./lib/remote.d.ts",
|
|
28
|
+
"default": "./lib/remote.js"
|
|
29
|
+
},
|
|
30
|
+
"./retention": {
|
|
31
|
+
"types": "./lib/retention.d.ts",
|
|
32
|
+
"default": "./lib/retention.js"
|
|
33
|
+
},
|
|
34
|
+
"./logger": {
|
|
35
|
+
"types": "./lib/logger.d.ts",
|
|
36
|
+
"default": "./lib/logger.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"lib",
|
|
41
|
+
"cordis.patch.yml"
|
|
42
|
+
],
|
|
43
|
+
"dsh": {
|
|
44
|
+
"bundle": {
|
|
45
|
+
"patch": "./cordis.patch.yml"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"zod": "4.6.7",
|
|
50
|
+
"@tbox.cn/dsh-acp-bridge-core": "0.1.0-rc.1",
|
|
51
|
+
"@tbox.cn/tboxcode-dsh-tools": "0.1.0-rc.1",
|
|
52
|
+
"@tbox.cn/tboxcode-dsh-runtime": "0.1.0-rc.1",
|
|
53
|
+
"@tbox.cn/tboxcode-dsh-llm": "0.1.0-rc.1"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@deepseek-ai/cordis": "4.0.5",
|
|
57
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-rc.2",
|
|
58
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-rc.2"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@deepseek-ai/cordis": "4.0.5",
|
|
62
|
+
"yaml": "^2.8.0",
|
|
63
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-rc.2",
|
|
64
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-rc.2",
|
|
65
|
+
"@deepseek-ai/dsh-commands": "0.2.1-rc.2"
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "tsdown",
|
|
69
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
70
|
+
}
|
|
6
71
|
}
|
package/README.md
DELETED