taskmeld 0.1.2 → 0.1.41
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/README.md +176 -176
- package/README.zh-CN.md +176 -176
- package/dist/src/app/app-context-env.js +1 -1
- package/dist/src/app/create-app-context.js +3 -3
- package/dist/src/app/data-dir.js +13 -3
- package/dist/src/app/pipeline-config.js +4 -4
- package/dist/src/app/pipeline-registry.js +11 -11
- package/dist/src/app/pipeline-runtime.js +6 -9
- package/dist/src/app/runtime-store.js +3 -3
- package/dist/src/artifacts/artifact-cleanup.js +17 -17
- package/dist/src/artifacts/artifact-index.js +14 -14
- package/dist/src/artifacts/artifact-rebuilder.js +3 -3
- package/dist/src/artifacts/storage-service.js +18 -18
- package/dist/src/cli/bootstrap.js +7 -7
- package/dist/src/cli/commands/agent.js +12 -11
- package/dist/src/cli/commands/artifact.js +31 -30
- package/dist/src/cli/commands/init.js +49 -47
- package/dist/src/cli/commands/pipeline/result.js +9 -8
- package/dist/src/cli/commands/pipeline/selector.js +1 -1
- package/dist/src/cli/commands/pipeline/watch.js +2 -2
- package/dist/src/cli/commands/pipeline.js +54 -53
- package/dist/src/cli/commands/scheduler.js +9 -8
- package/dist/src/cli/commands/server.js +12 -11
- package/dist/src/cli/commands/system.js +4 -3
- package/dist/src/cli/errors.js +2 -2
- package/dist/src/cli/help.js +18 -17
- package/dist/src/cli/i18n.js +46 -0
- package/dist/src/cli/locales/en.json +244 -0
- package/dist/src/cli/locales/zh.json +244 -0
- package/dist/src/cli/output.js +3 -3
- package/dist/src/cli/renderers/engine/markdown.js +1 -1
- package/dist/src/cli/renderers/specs/index.js +1 -1
- package/dist/src/cli/router.js +1 -1
- package/dist/src/cli/server-runtime-client.js +54 -95
- package/dist/src/cli/ui-prompts.js +96 -0
- package/dist/src/cli/ws-runtime-client.js +51 -0
- package/dist/src/gateway/gateway-client.js +4 -4
- package/dist/src/index.js +28 -2
- package/dist/src/logs/run-log-reader.js +1 -1
- package/dist/src/pipeline/agent-activity.js +2 -2
- package/dist/src/pipeline/artifact-storage.js +11 -11
- package/dist/src/pipeline/diagnostics/dependency-diagnostic.js +11 -11
- package/dist/src/pipeline/dispatch/pipeline-inbound-queue.js +2 -2
- package/dist/src/pipeline/execution/group-item-executor.js +1 -1
- package/dist/src/pipeline/execution/node-item-executor.js +3 -3
- package/dist/src/pipeline/execution/node-runner.js +7 -7
- package/dist/src/pipeline/execution/readiness-state.js +1 -1
- package/dist/src/pipeline/execution/reject-handler.js +5 -5
- package/dist/src/pipeline/execution/rejected-artifact-archiver.js +1 -1
- package/dist/src/pipeline/execution/route-item-manager.js +4 -4
- package/dist/src/pipeline/execution/run-abort-controller.js +5 -5
- package/dist/src/pipeline/execution/run-state-helpers.js +2 -2
- package/dist/src/pipeline/execution/service.js +4 -4
- package/dist/src/pipeline/execution/structured-node-runner.js +24 -24
- package/dist/src/pipeline/execution-timeout.js +3 -3
- package/dist/src/pipeline/identity/index.js +3 -3
- package/dist/src/pipeline/item-batch-controller.js +6 -6
- package/dist/src/pipeline/scheduler/dependency-state.js +5 -5
- package/dist/src/pipeline/scheduler-service.js +24 -24
- package/dist/src/pipeline/state-machine.js +2 -2
- package/dist/src/pipeline/structured-output/contract.js +4 -4
- package/dist/src/pipeline/structured-output/index.js +2 -2
- package/dist/src/pipeline/structured-output/parser.js +5 -5
- package/dist/src/pipeline/structured-output/prompt.js +38 -38
- package/dist/src/pipeline/structured-output/waiter.js +6 -6
- package/dist/src/pipeline/template.js +5 -5
- package/dist/src/pipeline/timeline-log-store.js +5 -5
- package/dist/src/pipeline/tool-activity.js +3 -3
- package/dist/src/pipeline/types/pipeline-output.js +1 -1
- package/dist/src/pipeline/workflow/branch-rules.js +19 -19
- package/dist/src/pipeline/workflow/io.js +1 -1
- package/dist/src/pipeline/workflow/normalize.js +18 -18
- package/dist/src/pipeline/workflow/template-mapper.js +3 -3
- package/dist/src/pipeline/workflow/validate.js +39 -39
- package/dist/src/pipeline/workflow-graph.js +10 -10
- package/dist/src/server/http-handler.js +74 -0
- package/dist/src/services/agent-service.js +2 -2
- package/dist/src/services/gateway-read-helpers.js +1 -1
- package/dist/src/services/pipeline-service.js +19 -19
- package/dist/src/services/pipeline-status.js +4 -4
- package/dist/src/services/read-services.js +1 -1
- package/dist/src/services/session-service.js +6 -6
- package/dist/src/services/system-service.js +1 -1
- package/dist/src/transport/ws-broker.js +12 -1
- package/dist/src/transport/ws-handler.js +60 -0
- package/dist/src/transport/ws-methods/agents.js +144 -0
- package/dist/src/transport/ws-methods/artifacts.js +171 -0
- package/dist/src/transport/ws-methods/gateway.js +16 -0
- package/dist/src/transport/ws-methods/logs.js +43 -0
- package/dist/src/transport/ws-methods/pipeline-batch.js +68 -0
- package/dist/src/transport/ws-methods/pipeline-links.js +100 -0
- package/dist/src/transport/ws-methods/pipeline-queue.js +51 -0
- package/dist/src/transport/ws-methods/pipeline-runtime.js +151 -0
- package/dist/src/transport/ws-methods/pipeline-scheduler.js +48 -0
- package/dist/src/transport/ws-methods/pipeline-workflow.js +127 -0
- package/dist/src/transport/ws-methods/pipelines.js +56 -0
- package/dist/src/transport/ws-methods/register-all.js +32 -0
- package/dist/src/transport/ws-methods/sessions.js +154 -0
- package/dist/src/transport/ws-methods/timeline.js +10 -0
- package/dist/src/{server/routes/pipeline-identity.js → transport/ws-methods/utils.js} +14 -9
- package/dist/src/version.js +1 -1
- package/package.json +15 -7
- package/web/dist/assets/agent-DP6TMcLj.js +1 -0
- package/web/dist/assets/agent-DmJHzLyj.js +1 -0
- package/web/dist/assets/artifact-BqnoZy2M.js +1 -0
- package/web/dist/assets/artifact-DfDkgkno.js +1 -0
- package/web/dist/assets/common-DRMTVwE9.js +1 -0
- package/web/dist/assets/common-DeXccbr2.js +1 -0
- package/web/dist/assets/dispatch-CBskGCQI.js +1 -0
- package/web/dist/assets/dispatch-sk4Wp30e.js +1 -0
- package/web/dist/assets/index-C8wTjZvH.css +1 -0
- package/web/dist/assets/index-DYDQZRLk.js +58 -0
- package/web/dist/assets/log-DN8cjb0w.js +1 -0
- package/web/dist/assets/log-HSeA_dYy.js +1 -0
- package/web/dist/assets/modal-BdNai9jf.js +1 -0
- package/web/dist/assets/modal-D9_KDpFD.js +1 -0
- package/web/dist/assets/nav-BmF7oAKg.js +1 -0
- package/web/dist/assets/nav-IjC2xqXQ.js +1 -0
- package/web/dist/assets/node-detail-CENRXcrh.js +1 -0
- package/web/dist/assets/node-detail-bndPr0IM.js +1 -0
- package/web/dist/assets/overview-B87zWAxq.js +1 -0
- package/web/dist/assets/overview-gQvk-NOK.js +1 -0
- package/web/dist/assets/pipeline-D4dSJRDz.js +1 -0
- package/web/dist/assets/pipeline-DZzyOqQa.js +1 -0
- package/web/dist/assets/session-CUWvU14v.js +5 -0
- package/web/dist/assets/session-DQ6UuCaJ.js +5 -0
- package/web/dist/assets/timeline-8y_2_0Em.js +1 -0
- package/web/dist/assets/timeline-CAPsXUTC.js +1 -0
- package/web/dist/index.html +3 -3
- package/dist/src/app/pipeline-plugin-config.js +0 -2
- package/dist/src/server/api-handler.js +0 -163
- package/dist/src/server/http-utils.js +0 -34
- package/dist/src/server/middleware.js +0 -61
- package/dist/src/server/router.js +0 -105
- package/dist/src/server/routes/agents.js +0 -189
- package/dist/src/server/routes/artifacts.js +0 -163
- package/dist/src/server/routes/gateway.js +0 -18
- package/dist/src/server/routes/health.js +0 -16
- package/dist/src/server/routes/logs.js +0 -73
- package/dist/src/server/routes/pipeline-batch.js +0 -163
- package/dist/src/server/routes/pipeline-diagnostics.js +0 -33
- package/dist/src/server/routes/pipeline-links.js +0 -117
- package/dist/src/server/routes/pipeline-outputs.js +0 -27
- package/dist/src/server/routes/pipeline-queue.js +0 -62
- package/dist/src/server/routes/pipeline-runtime.js +0 -162
- package/dist/src/server/routes/pipeline-scheduler.js +0 -69
- package/dist/src/server/routes/pipeline-workflow.js +0 -180
- package/dist/src/server/routes/pipelines.js +0 -96
- package/dist/src/server/routes/sessions.js +0 -244
- package/dist/src/server/routes/timeline.js +0 -14
- package/dist/src/server/serve-static.js +0 -42
- package/web/dist/assets/index-CWnfhkn-.js +0 -65
- package/web/dist/assets/index-gZ0xOfSO.css +0 -1
- /package/dist/src/{server → transport/ws-methods}/types.js +0 -0
|
@@ -4,7 +4,7 @@ exports.ensureGatewayReadyForReadonly = void 0;
|
|
|
4
4
|
const ensureGatewayReadyForReadonly = async (app) => {
|
|
5
5
|
if (app.gateway.client.getStatus().status === "ready")
|
|
6
6
|
return;
|
|
7
|
-
//
|
|
7
|
+
// When a read-only command hits a gateway method, the link must be established first, otherwise sendReq would fail immediately.
|
|
8
8
|
await app.gateway.client.connect();
|
|
9
9
|
};
|
|
10
10
|
exports.ensureGatewayReadyForReadonly = ensureGatewayReadyForReadonly;
|
|
@@ -21,7 +21,7 @@ const extractKeywordPoolFromUnknown = (value, depth = 0) => {
|
|
|
21
21
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
22
22
|
return [];
|
|
23
23
|
const record = value;
|
|
24
|
-
//
|
|
24
|
+
// Remote keyword pool: prefer extracting from agreed fields first, compatible with existing list30/list/items etc. structures.
|
|
25
25
|
const priorityKeys = ["list30", "list", "keywords", "items", "pool"];
|
|
26
26
|
for (const key of priorityKeys) {
|
|
27
27
|
const candidates = (0, exports.extractKeywordPoolFromUnknown)(record[key], depth + 1);
|
|
@@ -84,34 +84,34 @@ const buildBatchRunId = (pipelineId, snapshot) => {
|
|
|
84
84
|
};
|
|
85
85
|
const markRunningRunStopped = (run) => {
|
|
86
86
|
const now = new Date().toISOString();
|
|
87
|
-
//
|
|
87
|
+
// Stop is a user-initiated abort; queued/blocked nodes should not remain schedulable.
|
|
88
88
|
for (const node of run.nodes) {
|
|
89
89
|
if (node.status === "success" || node.status === "failed" || node.status === "rejected" || node.status === "skipped")
|
|
90
90
|
continue;
|
|
91
91
|
node.status = "stopped";
|
|
92
92
|
node.finishedAt = node.finishedAt ?? now;
|
|
93
|
-
node.lastError = node.lastError ?? "
|
|
93
|
+
node.lastError = node.lastError ?? "Pipeline manually stopped by user";
|
|
94
94
|
}
|
|
95
95
|
for (const item of run.itemRuns ?? []) {
|
|
96
96
|
if (item.status === "success" || item.status === "failed" || item.status === "rejected" || item.status === "skipped")
|
|
97
97
|
continue;
|
|
98
98
|
item.status = "stopped";
|
|
99
99
|
item.finishedAt = item.finishedAt ?? now;
|
|
100
|
-
item.lastError = item.lastError ?? "
|
|
100
|
+
item.lastError = item.lastError ?? "Pipeline manually stopped by user";
|
|
101
101
|
}
|
|
102
102
|
for (const group of run.groups ?? []) {
|
|
103
103
|
if (group.status === "success" || group.status === "failed" || group.status === "rejected" || group.status === "skipped")
|
|
104
104
|
continue;
|
|
105
105
|
group.status = "stopped";
|
|
106
106
|
group.finishedAt = group.finishedAt ?? now;
|
|
107
|
-
group.lastError = group.lastError ?? "
|
|
107
|
+
group.lastError = group.lastError ?? "Pipeline manually stopped by user";
|
|
108
108
|
}
|
|
109
109
|
for (const groupItem of run.groupItemRuns ?? []) {
|
|
110
110
|
if (groupItem.status === "success" || groupItem.status === "failed" || groupItem.status === "rejected" || groupItem.status === "skipped")
|
|
111
111
|
continue;
|
|
112
112
|
groupItem.status = "stopped";
|
|
113
113
|
groupItem.finishedAt = groupItem.finishedAt ?? now;
|
|
114
|
-
groupItem.lastError = groupItem.lastError ?? "
|
|
114
|
+
groupItem.lastError = groupItem.lastError ?? "Pipeline manually stopped by user";
|
|
115
115
|
}
|
|
116
116
|
run.status = "stopped";
|
|
117
117
|
run.updatedAt = now;
|
|
@@ -174,7 +174,7 @@ const createPipelineService = (app) => {
|
|
|
174
174
|
const runtime = app.getPipelineRuntime(pipelineId);
|
|
175
175
|
if (!definition || !runtime)
|
|
176
176
|
return null;
|
|
177
|
-
//
|
|
177
|
+
// Read-only service directly passes through the runtime snapshot; it carries no write behavior.
|
|
178
178
|
return {
|
|
179
179
|
pipelineId: definition.id,
|
|
180
180
|
title: definition.title,
|
|
@@ -218,7 +218,7 @@ const createPipelineService = (app) => {
|
|
|
218
218
|
remotePayload = JSON.parse(text);
|
|
219
219
|
}
|
|
220
220
|
catch {
|
|
221
|
-
//
|
|
221
|
+
// Non-JSON responses are also allowed to continue parsing, compatible with comma/newline text pools.
|
|
222
222
|
remotePayload = text;
|
|
223
223
|
}
|
|
224
224
|
}
|
|
@@ -245,7 +245,7 @@ const createPipelineService = (app) => {
|
|
|
245
245
|
const normalizedError = started.error === "batch_run_in_progress" ? "batch_run_in_progress" : "batch_items_empty";
|
|
246
246
|
return { ok: false, pipelineId, error: normalizedError, state: started.snapshot, remoteUrl };
|
|
247
247
|
}
|
|
248
|
-
runtime.runtime.pushTimeline(`[${pipelineId}]
|
|
248
|
+
runtime.runtime.pushTimeline(`[${pipelineId}] Remote keyword pool batch started: ${items.length} items, batch size ${started.snapshot.batchSize}`, "info", { remoteUrl });
|
|
249
249
|
return {
|
|
250
250
|
ok: true,
|
|
251
251
|
mode: "remote_batch",
|
|
@@ -263,9 +263,9 @@ const createPipelineService = (app) => {
|
|
|
263
263
|
}
|
|
264
264
|
const run = runtime.runtime.seedRun(runtime.workflow.getTemplateNodes());
|
|
265
265
|
runtime.runtime.setRun(run);
|
|
266
|
-
runtime.runtime.pushTimeline(`[${pipelineId}]
|
|
266
|
+
runtime.runtime.pushTimeline(`[${pipelineId}] New run started: ${run.id}`);
|
|
267
267
|
runtime.runtime.emitPipeline();
|
|
268
|
-
// start
|
|
268
|
+
// start is only responsible for initiating the run; it does not promise completion upon return.
|
|
269
269
|
void runtime.pipeline.drainPipeline(`run:start:${run.id}`).then(() => {
|
|
270
270
|
runtime.runtime.touchRun(runtime.runtime.getRun());
|
|
271
271
|
});
|
|
@@ -290,7 +290,7 @@ const createPipelineService = (app) => {
|
|
|
290
290
|
error: "run_not_found",
|
|
291
291
|
};
|
|
292
292
|
}
|
|
293
|
-
// status
|
|
293
|
+
// The status command only expresses "whether it's currently running"; non-running states return a simplified union structure.
|
|
294
294
|
return attachIdentityToPipelineStatusResult((0, pipeline_status_1.buildPipelineStatusResult)({
|
|
295
295
|
pipelineId,
|
|
296
296
|
run: detail.run,
|
|
@@ -326,7 +326,7 @@ const createPipelineService = (app) => {
|
|
|
326
326
|
runtime.pipeline.abortRunControllers(runState.id);
|
|
327
327
|
}
|
|
328
328
|
markRunningRunStopped(runState);
|
|
329
|
-
runtime.runtime.pushTimeline(`[${pipelineId}]
|
|
329
|
+
runtime.runtime.pushTimeline(`[${pipelineId}] Stop requested for run: ${runState.id}`, "warn");
|
|
330
330
|
runtime.runtime.emitPipeline();
|
|
331
331
|
const stoppedStatus = (0, execution_status_1.buildPipelineExecutionStatus)({
|
|
332
332
|
pipelineId,
|
|
@@ -356,7 +356,7 @@ const createPipelineService = (app) => {
|
|
|
356
356
|
error: "batch_run_not_running",
|
|
357
357
|
};
|
|
358
358
|
}
|
|
359
|
-
//
|
|
359
|
+
// Abort the currently executing node, and also send /stop to the remote agent
|
|
360
360
|
if (runState.id) {
|
|
361
361
|
runtime.pipeline.abortRunControllers(runState.id);
|
|
362
362
|
}
|
|
@@ -385,7 +385,7 @@ const createPipelineService = (app) => {
|
|
|
385
385
|
};
|
|
386
386
|
};
|
|
387
387
|
const runPipeline = async (pipelineId) => {
|
|
388
|
-
// run
|
|
388
|
+
// run exists only as a compatibility entry point; semantically equivalent to start.
|
|
389
389
|
return startPipeline(pipelineId);
|
|
390
390
|
};
|
|
391
391
|
const startBatchRun = (input) => {
|
|
@@ -432,7 +432,7 @@ const createPipelineService = (app) => {
|
|
|
432
432
|
remotePayload = JSON.parse(text);
|
|
433
433
|
}
|
|
434
434
|
catch {
|
|
435
|
-
//
|
|
435
|
+
// Non-JSON text is also allowed to continue parsing, to prevent the entire batch-run entry from becoming unusable when upstream switches to a plain-text pool.
|
|
436
436
|
remotePayload = text;
|
|
437
437
|
}
|
|
438
438
|
}
|
|
@@ -445,7 +445,7 @@ const createPipelineService = (app) => {
|
|
|
445
445
|
detail: error instanceof Error ? error.message : "unknown_error",
|
|
446
446
|
};
|
|
447
447
|
}
|
|
448
|
-
// sourceField
|
|
448
|
+
// sourceField is treated as a "preferred path" not a strict dependency: fall back to full parsing when path is missing to maintain compatibility.
|
|
449
449
|
const preferredPayload = remoteBatchPlugin.sourceField
|
|
450
450
|
? (0, exports.readNestedValueByPath)(remotePayload, remoteBatchPlugin.sourceField)
|
|
451
451
|
: null;
|
|
@@ -465,7 +465,7 @@ const createPipelineService = (app) => {
|
|
|
465
465
|
remoteUrl,
|
|
466
466
|
};
|
|
467
467
|
}
|
|
468
|
-
runtime.runtime.pushTimeline(`[${input.pipelineId}]
|
|
468
|
+
runtime.runtime.pushTimeline(`[${input.pipelineId}] Remote keyword pool batch started: ${items.length} items, batch size ${started.snapshot.batchSize}`, "info", { remoteUrl });
|
|
469
469
|
return {
|
|
470
470
|
ok: true,
|
|
471
471
|
pipelineId: input.pipelineId,
|
|
@@ -478,7 +478,7 @@ const createPipelineService = (app) => {
|
|
|
478
478
|
const runtime = getRuntimeByPipelineId(app, input.pipelineId);
|
|
479
479
|
if (!runtime)
|
|
480
480
|
return { ok: false, pipelineId: input.pipelineId, error: "pipeline_not_found" };
|
|
481
|
-
//
|
|
481
|
+
// Abort the currently executing node to avoid old node conflicting with new retry (also send /stop to remote agent)
|
|
482
482
|
const runState = runtime.runtime.getRun();
|
|
483
483
|
if (runState.id) {
|
|
484
484
|
runtime.pipeline.abortRunControllers(runState.id);
|
|
@@ -45,7 +45,7 @@ const isBatchTerminal = (batchRun) => {
|
|
|
45
45
|
};
|
|
46
46
|
const readPipelineLastCompletedAt = (run, batchRun) => {
|
|
47
47
|
const candidates = [];
|
|
48
|
-
//
|
|
48
|
+
// Only allow updatedAt as a completion time candidate after confirming the run has reached a terminal state.
|
|
49
49
|
if (isRunTerminal(run) && isNonEmptyString(run.updatedAt)) {
|
|
50
50
|
candidates.push(run.updatedAt.trim());
|
|
51
51
|
}
|
|
@@ -53,7 +53,7 @@ const readPipelineLastCompletedAt = (run, batchRun) => {
|
|
|
53
53
|
candidates.push(...collectFinishedAtValues(run.itemRuns));
|
|
54
54
|
candidates.push(...collectFinishedAtValues(run.groups));
|
|
55
55
|
candidates.push(...collectFinishedAtValues(run.groupItemRuns));
|
|
56
|
-
//
|
|
56
|
+
// Batch-run completion time should come from the controller's real terminal state, not the running snapshot's update time.
|
|
57
57
|
if (isBatchTerminal(batchRun) && isNonEmptyString(batchRun.finishedAt)) {
|
|
58
58
|
candidates.push(batchRun.finishedAt.trim());
|
|
59
59
|
}
|
|
@@ -63,13 +63,13 @@ exports.readPipelineLastCompletedAt = readPipelineLastCompletedAt;
|
|
|
63
63
|
const buildPipelineStatusResult = (input) => {
|
|
64
64
|
const status = (0, execution_status_1.buildPipelineExecutionStatus)({
|
|
65
65
|
pipelineId: input.pipelineId,
|
|
66
|
-
//
|
|
66
|
+
// Reuse execution-status directly as the runtime snapshot constructor, keeping the status field source unique.
|
|
67
67
|
run: input.run,
|
|
68
68
|
scheduler: input.scheduler,
|
|
69
69
|
batchRun: input.batchRun,
|
|
70
70
|
});
|
|
71
71
|
if (status.running) {
|
|
72
|
-
// status.running=true
|
|
72
|
+
// When status.running=true, mode can only be single or remote_batch; explicitly narrow here for unified CLI/API consumption.
|
|
73
73
|
return {
|
|
74
74
|
ok: true,
|
|
75
75
|
status: {
|
|
@@ -18,7 +18,7 @@ const createAllServices = (app) => ({
|
|
|
18
18
|
const createReadonlyServices = (app) => {
|
|
19
19
|
const services = createAllServices(app);
|
|
20
20
|
return {
|
|
21
|
-
//
|
|
21
|
+
// Unified factory for mainline integration; the CLI entry only needs to pass in the registry to get all read-only services.
|
|
22
22
|
system: services.system,
|
|
23
23
|
pipeline: services.pipeline,
|
|
24
24
|
agent: services.agent,
|
|
@@ -42,7 +42,7 @@ const createSessionService = (app) => {
|
|
|
42
42
|
return toSessionListItems(refreshed.items);
|
|
43
43
|
}
|
|
44
44
|
catch {
|
|
45
|
-
//
|
|
45
|
+
// On refresh failure, use cache so the CLI can still read session info during gateway flapping.
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
return toSessionListItems(app.gateway.getSessionCache());
|
|
@@ -144,7 +144,7 @@ const createSessionService = (app) => {
|
|
|
144
144
|
return;
|
|
145
145
|
const stream = String(payload.stream ?? "").toLowerCase();
|
|
146
146
|
const data = (payload.data ?? {});
|
|
147
|
-
//
|
|
147
|
+
// Streaming text fragments (mix of accumulation and incremental; deduplicated with merge)
|
|
148
148
|
if (stream === "assistant") {
|
|
149
149
|
const text = typeof data.text === "string" ? data.text : "";
|
|
150
150
|
if (text) {
|
|
@@ -161,7 +161,7 @@ const createSessionService = (app) => {
|
|
|
161
161
|
}
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
164
|
-
//
|
|
164
|
+
// Compatible with non-stream-prefix direct text
|
|
165
165
|
const role = String(payload.role ?? data.role ?? "").toLowerCase();
|
|
166
166
|
if (role === "assistant") {
|
|
167
167
|
const text = typeof data.text === "string" ? data.text : "";
|
|
@@ -178,7 +178,7 @@ const createSessionService = (app) => {
|
|
|
178
178
|
emitIncremental(accumulated);
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
-
//
|
|
181
|
+
// Lifecycle end
|
|
182
182
|
if (stream === "lifecycle") {
|
|
183
183
|
const phase = String(data.phase ?? "").toLowerCase();
|
|
184
184
|
if (phase === "end" || phase === "done") {
|
|
@@ -192,12 +192,12 @@ const createSessionService = (app) => {
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
});
|
|
195
|
-
//
|
|
195
|
+
// Timeout safety net
|
|
196
196
|
timer = setTimeout(() => {
|
|
197
197
|
if (settled)
|
|
198
198
|
return;
|
|
199
199
|
cleanup();
|
|
200
|
-
//
|
|
200
|
+
// On timeout, try to backfill from history once
|
|
201
201
|
void getSessionHistory(sessionId).then((history) => {
|
|
202
202
|
const items = Array.isArray(history) ? history : [];
|
|
203
203
|
const lastAssistant = [...items].reverse().find((item) => {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createSystemService = void 0;
|
|
4
4
|
const createSystemService = (app) => {
|
|
5
5
|
const getSnapshot = () => {
|
|
6
|
-
//
|
|
6
|
+
// Reuse the registry's aggregated bootstrap result so the CLI sees the same system snapshot as existing frontends.
|
|
7
7
|
const bootstrap = app.getBootstrapPayload();
|
|
8
8
|
return {
|
|
9
9
|
generatedAt: new Date().toISOString(),
|
|
@@ -17,7 +17,6 @@ const createWsBroker = (options) => {
|
|
|
17
17
|
for (const peer of peers) {
|
|
18
18
|
if (peer.readyState !== 1)
|
|
19
19
|
continue;
|
|
20
|
-
// 慢消费者保护:高频消息在 buffer 堆积时跳过,避免 Node 进程 OOM
|
|
21
20
|
if (isHighFrequency && peer.bufferedAmount > 64 * 1024)
|
|
22
21
|
continue;
|
|
23
22
|
peer.send(message);
|
|
@@ -25,6 +24,18 @@ const createWsBroker = (options) => {
|
|
|
25
24
|
};
|
|
26
25
|
wsServer.on("connection", (socket) => {
|
|
27
26
|
peers.add(socket);
|
|
27
|
+
if (options.handleRequest) {
|
|
28
|
+
socket.on("message", (raw) => {
|
|
29
|
+
try {
|
|
30
|
+
options.handleRequest(socket, raw.toString());
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
// JSON-parsing failures from ws-handler are handled internally;
|
|
34
|
+
// unexpected handler bugs surface here so they don't vanish silently.
|
|
35
|
+
console.error("ws-message-handler-error", error);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
28
39
|
socket.send(JSON.stringify({ type: "bootstrap", payload: options.getBootstrapPayload() }));
|
|
29
40
|
socket.on("close", () => {
|
|
30
41
|
peers.delete(socket);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createWsRequestHandler = exports.createWsMethodRegistry = void 0;
|
|
4
|
+
const createWsMethodRegistry = () => {
|
|
5
|
+
const methods = new Map();
|
|
6
|
+
return {
|
|
7
|
+
register(method, handler) {
|
|
8
|
+
if (methods.has(method)) {
|
|
9
|
+
throw new Error(`duplicate_ws_method:${method}`);
|
|
10
|
+
}
|
|
11
|
+
methods.set(method, handler);
|
|
12
|
+
},
|
|
13
|
+
dispatch(method) {
|
|
14
|
+
return methods.get(method) ?? null;
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
exports.createWsMethodRegistry = createWsMethodRegistry;
|
|
19
|
+
const createWsRequestHandler = (app, services) => {
|
|
20
|
+
const registry = (0, exports.createWsMethodRegistry)();
|
|
21
|
+
const handleMessage = (socket, raw) => {
|
|
22
|
+
let frame;
|
|
23
|
+
try {
|
|
24
|
+
frame = JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (frame.type !== "req" || typeof frame.id !== "string" || typeof frame.method !== "string") {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const handler = registry.dispatch(frame.method);
|
|
33
|
+
const sendRes = (ok, payload, error) => {
|
|
34
|
+
socket.send(JSON.stringify({ type: "res", id: frame.id, ok, payload, error }));
|
|
35
|
+
};
|
|
36
|
+
if (!handler) {
|
|
37
|
+
sendRes(false, undefined, `unknown_method:${frame.method}`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const ctx = {
|
|
41
|
+
app,
|
|
42
|
+
params: Object.fromEntries(Object.entries(frame.params ?? {}).filter(([_, v]) => typeof v === "string")),
|
|
43
|
+
services,
|
|
44
|
+
};
|
|
45
|
+
try {
|
|
46
|
+
const result = handler(frame.params ?? {}, ctx);
|
|
47
|
+
if (result instanceof Promise) {
|
|
48
|
+
result.then((r) => sendRes(r.ok, r.payload, r.error), (err) => sendRes(false, undefined, err instanceof Error ? err.message : "internal_error"));
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
sendRes(result.ok, result.payload, result.error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
sendRes(false, undefined, err instanceof Error ? err.message : "internal_error");
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return { registry, handleMessage };
|
|
59
|
+
};
|
|
60
|
+
exports.createWsRequestHandler = createWsRequestHandler;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerAgentWsMethods = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
const toEpochMs = (value) => {
|
|
6
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
7
|
+
return value;
|
|
8
|
+
if (typeof value === "string" && value.trim()) {
|
|
9
|
+
const asNum = Number(value);
|
|
10
|
+
if (Number.isFinite(asNum))
|
|
11
|
+
return asNum;
|
|
12
|
+
const asDate = Date.parse(value);
|
|
13
|
+
if (Number.isFinite(asDate))
|
|
14
|
+
return asDate;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
};
|
|
18
|
+
const readSessionActivityMs = (raw) => {
|
|
19
|
+
const candidates = [raw.updatedAt, raw.endedAt, raw.startedAt, raw.timestamp, raw.ts];
|
|
20
|
+
let best = null;
|
|
21
|
+
for (const candidate of candidates) {
|
|
22
|
+
const ms = toEpochMs(candidate);
|
|
23
|
+
if (ms === null)
|
|
24
|
+
continue;
|
|
25
|
+
if (best === null || ms > best)
|
|
26
|
+
best = ms;
|
|
27
|
+
}
|
|
28
|
+
return best;
|
|
29
|
+
};
|
|
30
|
+
const inferAgentIdsFromSession = (session) => {
|
|
31
|
+
const out = new Set();
|
|
32
|
+
const id = session.id.trim();
|
|
33
|
+
if (id) {
|
|
34
|
+
const match = id.match(/^agent:([^:]+):/i);
|
|
35
|
+
if (match?.[1])
|
|
36
|
+
out.add(match[1]);
|
|
37
|
+
}
|
|
38
|
+
const directKeys = [session.raw.agentId, session.raw.agent_id, session.raw.executorAgentId, session.raw.ownerAgentId];
|
|
39
|
+
for (const value of directKeys) {
|
|
40
|
+
if (typeof value === "string" && value.trim())
|
|
41
|
+
out.add(value.trim());
|
|
42
|
+
}
|
|
43
|
+
return [...out];
|
|
44
|
+
};
|
|
45
|
+
const registerAgentWsMethods = (registry) => {
|
|
46
|
+
registry.register("agent.list", async (_params, ctx) => {
|
|
47
|
+
try {
|
|
48
|
+
const payload = await ctx.services.client.sendReq("agents.list");
|
|
49
|
+
const rawItems = ctx.services.pickArray(payload);
|
|
50
|
+
let sessionItems = ctx.services.getSessionCache();
|
|
51
|
+
try {
|
|
52
|
+
const refreshed = await ctx.services.refreshSessionsFromGateway();
|
|
53
|
+
sessionItems = refreshed.items;
|
|
54
|
+
}
|
|
55
|
+
catch { /* silent */ }
|
|
56
|
+
const lastActiveByAgentId = new Map();
|
|
57
|
+
for (const session of sessionItems) {
|
|
58
|
+
const activityMs = readSessionActivityMs(session.raw);
|
|
59
|
+
if (activityMs === null)
|
|
60
|
+
continue;
|
|
61
|
+
for (const agentId of inferAgentIdsFromSession(session)) {
|
|
62
|
+
const prev = lastActiveByAgentId.get(agentId);
|
|
63
|
+
if (prev === undefined || activityMs > prev) {
|
|
64
|
+
lastActiveByAgentId.set(agentId, activityMs);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const items = rawItems
|
|
69
|
+
.map((item) => {
|
|
70
|
+
const obj = (0, utils_1.asRecord)(item);
|
|
71
|
+
if (!obj)
|
|
72
|
+
return item;
|
|
73
|
+
const agentId = String(obj.id ?? obj.name ?? obj.key ?? "").trim();
|
|
74
|
+
const activeMs = agentId ? (lastActiveByAgentId.get(agentId) ?? null) : null;
|
|
75
|
+
return { ...obj, lastActiveAtMs: activeMs, lastActiveAt: activeMs ? new Date(activeMs).toISOString() : null };
|
|
76
|
+
})
|
|
77
|
+
.sort((a, b) => {
|
|
78
|
+
const aObj = (0, utils_1.asRecord)(a);
|
|
79
|
+
const bObj = (0, utils_1.asRecord)(b);
|
|
80
|
+
const aMs = toEpochMs(aObj?.lastActiveAtMs ?? aObj?.lastActiveAt) ?? -1;
|
|
81
|
+
const bMs = toEpochMs(bObj?.lastActiveAtMs ?? bObj?.lastActiveAt) ?? -1;
|
|
82
|
+
if (aMs !== bMs)
|
|
83
|
+
return bMs - aMs;
|
|
84
|
+
const aId = String(aObj?.id ?? aObj?.name ?? "");
|
|
85
|
+
const bId = String(bObj?.id ?? bObj?.name ?? "");
|
|
86
|
+
return aId.localeCompare(bId);
|
|
87
|
+
});
|
|
88
|
+
return { ok: true, payload: { items, raw: payload } };
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
return { ok: false, error: (0, utils_1.formatError)(error) };
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
registry.register("agent.files.list", async (params, ctx) => {
|
|
95
|
+
try {
|
|
96
|
+
const agentId = typeof params.agentId === "string" ? params.agentId.trim() : "";
|
|
97
|
+
if (!agentId)
|
|
98
|
+
return { ok: false, error: "invalid_agent_id" };
|
|
99
|
+
const payload = await ctx.services.client.sendReq("agents.files.list", { agentId });
|
|
100
|
+
const obj = (payload ?? {});
|
|
101
|
+
const files = Array.isArray(obj.files) ? obj.files : ctx.services.pickArray(payload);
|
|
102
|
+
return { ok: true, payload: { items: files, raw: payload } };
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return { ok: false, error: (0, utils_1.formatError)(error) };
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
registry.register("agent.files.get", async (params, ctx) => {
|
|
109
|
+
try {
|
|
110
|
+
const agentId = typeof params.agentId === "string" ? params.agentId.trim() : "";
|
|
111
|
+
const name = typeof params.name === "string" ? params.name.trim() : "";
|
|
112
|
+
if (!agentId)
|
|
113
|
+
return { ok: false, error: "invalid_agent_id" };
|
|
114
|
+
if (!name)
|
|
115
|
+
return { ok: false, error: "invalid_file_name" };
|
|
116
|
+
const payload = await ctx.services.client.sendReq("agents.files.get", { agentId, name });
|
|
117
|
+
const obj = (payload ?? {});
|
|
118
|
+
const file = (obj.file ?? payload);
|
|
119
|
+
return { ok: true, payload: { item: file ?? null, raw: payload } };
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return { ok: false, error: (0, utils_1.formatError)(error) };
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
registry.register("agent.files.set", async (params, ctx) => {
|
|
126
|
+
try {
|
|
127
|
+
const agentId = typeof params.agentId === "string" ? params.agentId.trim() : "";
|
|
128
|
+
const name = typeof params.name === "string" ? params.name.trim() : "";
|
|
129
|
+
const content = typeof params.content === "string" ? params.content : "";
|
|
130
|
+
if (!agentId)
|
|
131
|
+
return { ok: false, error: "invalid_agent_id" };
|
|
132
|
+
if (!name)
|
|
133
|
+
return { ok: false, error: "invalid_file_name" };
|
|
134
|
+
const payload = await ctx.services.client.sendReq("agents.files.set", { agentId, name, content }, { sideEffect: true });
|
|
135
|
+
const obj = (payload ?? {});
|
|
136
|
+
const file = (obj.file ?? payload);
|
|
137
|
+
return { ok: true, payload: { item: file ?? null, raw: payload } };
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
return { ok: false, error: (0, utils_1.formatError)(error) };
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
exports.registerAgentWsMethods = registerAgentWsMethods;
|