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.
Files changed (154) hide show
  1. package/README.md +176 -176
  2. package/README.zh-CN.md +176 -176
  3. package/dist/src/app/app-context-env.js +1 -1
  4. package/dist/src/app/create-app-context.js +3 -3
  5. package/dist/src/app/data-dir.js +13 -3
  6. package/dist/src/app/pipeline-config.js +4 -4
  7. package/dist/src/app/pipeline-registry.js +11 -11
  8. package/dist/src/app/pipeline-runtime.js +6 -9
  9. package/dist/src/app/runtime-store.js +3 -3
  10. package/dist/src/artifacts/artifact-cleanup.js +17 -17
  11. package/dist/src/artifacts/artifact-index.js +14 -14
  12. package/dist/src/artifacts/artifact-rebuilder.js +3 -3
  13. package/dist/src/artifacts/storage-service.js +18 -18
  14. package/dist/src/cli/bootstrap.js +7 -7
  15. package/dist/src/cli/commands/agent.js +12 -11
  16. package/dist/src/cli/commands/artifact.js +31 -30
  17. package/dist/src/cli/commands/init.js +49 -47
  18. package/dist/src/cli/commands/pipeline/result.js +9 -8
  19. package/dist/src/cli/commands/pipeline/selector.js +1 -1
  20. package/dist/src/cli/commands/pipeline/watch.js +2 -2
  21. package/dist/src/cli/commands/pipeline.js +54 -53
  22. package/dist/src/cli/commands/scheduler.js +9 -8
  23. package/dist/src/cli/commands/server.js +12 -11
  24. package/dist/src/cli/commands/system.js +4 -3
  25. package/dist/src/cli/errors.js +2 -2
  26. package/dist/src/cli/help.js +18 -17
  27. package/dist/src/cli/i18n.js +46 -0
  28. package/dist/src/cli/locales/en.json +244 -0
  29. package/dist/src/cli/locales/zh.json +244 -0
  30. package/dist/src/cli/output.js +3 -3
  31. package/dist/src/cli/renderers/engine/markdown.js +1 -1
  32. package/dist/src/cli/renderers/specs/index.js +1 -1
  33. package/dist/src/cli/router.js +1 -1
  34. package/dist/src/cli/server-runtime-client.js +54 -95
  35. package/dist/src/cli/ui-prompts.js +96 -0
  36. package/dist/src/cli/ws-runtime-client.js +51 -0
  37. package/dist/src/gateway/gateway-client.js +4 -4
  38. package/dist/src/index.js +28 -2
  39. package/dist/src/logs/run-log-reader.js +1 -1
  40. package/dist/src/pipeline/agent-activity.js +2 -2
  41. package/dist/src/pipeline/artifact-storage.js +11 -11
  42. package/dist/src/pipeline/diagnostics/dependency-diagnostic.js +11 -11
  43. package/dist/src/pipeline/dispatch/pipeline-inbound-queue.js +2 -2
  44. package/dist/src/pipeline/execution/group-item-executor.js +1 -1
  45. package/dist/src/pipeline/execution/node-item-executor.js +3 -3
  46. package/dist/src/pipeline/execution/node-runner.js +7 -7
  47. package/dist/src/pipeline/execution/readiness-state.js +1 -1
  48. package/dist/src/pipeline/execution/reject-handler.js +5 -5
  49. package/dist/src/pipeline/execution/rejected-artifact-archiver.js +1 -1
  50. package/dist/src/pipeline/execution/route-item-manager.js +4 -4
  51. package/dist/src/pipeline/execution/run-abort-controller.js +5 -5
  52. package/dist/src/pipeline/execution/run-state-helpers.js +2 -2
  53. package/dist/src/pipeline/execution/service.js +4 -4
  54. package/dist/src/pipeline/execution/structured-node-runner.js +24 -24
  55. package/dist/src/pipeline/execution-timeout.js +3 -3
  56. package/dist/src/pipeline/identity/index.js +3 -3
  57. package/dist/src/pipeline/item-batch-controller.js +6 -6
  58. package/dist/src/pipeline/scheduler/dependency-state.js +5 -5
  59. package/dist/src/pipeline/scheduler-service.js +24 -24
  60. package/dist/src/pipeline/state-machine.js +2 -2
  61. package/dist/src/pipeline/structured-output/contract.js +4 -4
  62. package/dist/src/pipeline/structured-output/index.js +2 -2
  63. package/dist/src/pipeline/structured-output/parser.js +5 -5
  64. package/dist/src/pipeline/structured-output/prompt.js +38 -38
  65. package/dist/src/pipeline/structured-output/waiter.js +6 -6
  66. package/dist/src/pipeline/template.js +5 -5
  67. package/dist/src/pipeline/timeline-log-store.js +5 -5
  68. package/dist/src/pipeline/tool-activity.js +3 -3
  69. package/dist/src/pipeline/types/pipeline-output.js +1 -1
  70. package/dist/src/pipeline/workflow/branch-rules.js +19 -19
  71. package/dist/src/pipeline/workflow/io.js +1 -1
  72. package/dist/src/pipeline/workflow/normalize.js +18 -18
  73. package/dist/src/pipeline/workflow/template-mapper.js +3 -3
  74. package/dist/src/pipeline/workflow/validate.js +39 -39
  75. package/dist/src/pipeline/workflow-graph.js +10 -10
  76. package/dist/src/server/http-handler.js +74 -0
  77. package/dist/src/services/agent-service.js +2 -2
  78. package/dist/src/services/gateway-read-helpers.js +1 -1
  79. package/dist/src/services/pipeline-service.js +19 -19
  80. package/dist/src/services/pipeline-status.js +4 -4
  81. package/dist/src/services/read-services.js +1 -1
  82. package/dist/src/services/session-service.js +6 -6
  83. package/dist/src/services/system-service.js +1 -1
  84. package/dist/src/transport/ws-broker.js +12 -1
  85. package/dist/src/transport/ws-handler.js +60 -0
  86. package/dist/src/transport/ws-methods/agents.js +144 -0
  87. package/dist/src/transport/ws-methods/artifacts.js +171 -0
  88. package/dist/src/transport/ws-methods/gateway.js +16 -0
  89. package/dist/src/transport/ws-methods/logs.js +43 -0
  90. package/dist/src/transport/ws-methods/pipeline-batch.js +68 -0
  91. package/dist/src/transport/ws-methods/pipeline-links.js +100 -0
  92. package/dist/src/transport/ws-methods/pipeline-queue.js +51 -0
  93. package/dist/src/transport/ws-methods/pipeline-runtime.js +151 -0
  94. package/dist/src/transport/ws-methods/pipeline-scheduler.js +48 -0
  95. package/dist/src/transport/ws-methods/pipeline-workflow.js +127 -0
  96. package/dist/src/transport/ws-methods/pipelines.js +56 -0
  97. package/dist/src/transport/ws-methods/register-all.js +32 -0
  98. package/dist/src/transport/ws-methods/sessions.js +154 -0
  99. package/dist/src/transport/ws-methods/timeline.js +10 -0
  100. package/dist/src/{server/routes/pipeline-identity.js → transport/ws-methods/utils.js} +14 -9
  101. package/dist/src/version.js +1 -1
  102. package/package.json +15 -7
  103. package/web/dist/assets/agent-DP6TMcLj.js +1 -0
  104. package/web/dist/assets/agent-DmJHzLyj.js +1 -0
  105. package/web/dist/assets/artifact-BqnoZy2M.js +1 -0
  106. package/web/dist/assets/artifact-DfDkgkno.js +1 -0
  107. package/web/dist/assets/common-DRMTVwE9.js +1 -0
  108. package/web/dist/assets/common-DeXccbr2.js +1 -0
  109. package/web/dist/assets/dispatch-CBskGCQI.js +1 -0
  110. package/web/dist/assets/dispatch-sk4Wp30e.js +1 -0
  111. package/web/dist/assets/index-C8wTjZvH.css +1 -0
  112. package/web/dist/assets/index-DYDQZRLk.js +58 -0
  113. package/web/dist/assets/log-DN8cjb0w.js +1 -0
  114. package/web/dist/assets/log-HSeA_dYy.js +1 -0
  115. package/web/dist/assets/modal-BdNai9jf.js +1 -0
  116. package/web/dist/assets/modal-D9_KDpFD.js +1 -0
  117. package/web/dist/assets/nav-BmF7oAKg.js +1 -0
  118. package/web/dist/assets/nav-IjC2xqXQ.js +1 -0
  119. package/web/dist/assets/node-detail-CENRXcrh.js +1 -0
  120. package/web/dist/assets/node-detail-bndPr0IM.js +1 -0
  121. package/web/dist/assets/overview-B87zWAxq.js +1 -0
  122. package/web/dist/assets/overview-gQvk-NOK.js +1 -0
  123. package/web/dist/assets/pipeline-D4dSJRDz.js +1 -0
  124. package/web/dist/assets/pipeline-DZzyOqQa.js +1 -0
  125. package/web/dist/assets/session-CUWvU14v.js +5 -0
  126. package/web/dist/assets/session-DQ6UuCaJ.js +5 -0
  127. package/web/dist/assets/timeline-8y_2_0Em.js +1 -0
  128. package/web/dist/assets/timeline-CAPsXUTC.js +1 -0
  129. package/web/dist/index.html +3 -3
  130. package/dist/src/app/pipeline-plugin-config.js +0 -2
  131. package/dist/src/server/api-handler.js +0 -163
  132. package/dist/src/server/http-utils.js +0 -34
  133. package/dist/src/server/middleware.js +0 -61
  134. package/dist/src/server/router.js +0 -105
  135. package/dist/src/server/routes/agents.js +0 -189
  136. package/dist/src/server/routes/artifacts.js +0 -163
  137. package/dist/src/server/routes/gateway.js +0 -18
  138. package/dist/src/server/routes/health.js +0 -16
  139. package/dist/src/server/routes/logs.js +0 -73
  140. package/dist/src/server/routes/pipeline-batch.js +0 -163
  141. package/dist/src/server/routes/pipeline-diagnostics.js +0 -33
  142. package/dist/src/server/routes/pipeline-links.js +0 -117
  143. package/dist/src/server/routes/pipeline-outputs.js +0 -27
  144. package/dist/src/server/routes/pipeline-queue.js +0 -62
  145. package/dist/src/server/routes/pipeline-runtime.js +0 -162
  146. package/dist/src/server/routes/pipeline-scheduler.js +0 -69
  147. package/dist/src/server/routes/pipeline-workflow.js +0 -180
  148. package/dist/src/server/routes/pipelines.js +0 -96
  149. package/dist/src/server/routes/sessions.js +0 -244
  150. package/dist/src/server/routes/timeline.js +0 -14
  151. package/dist/src/server/serve-static.js +0 -42
  152. package/web/dist/assets/index-CWnfhkn-.js +0 -65
  153. package/web/dist/assets/index-gZ0xOfSO.css +0 -1
  154. /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
- // 只读命令一旦命中网关方法,也必须先建好链路,否则 sendReq 会直接失败。
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
- // 远端关键词池优先按约定字段提取,兼容现有 list30/list/items 等结构。
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
- // 停止是用户主动中止,不应继续让 queued/blocked 节点保持可调度状态。
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
- // 只读 service 直接透出运行态快照,不承担任何写入行为。
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
- // JSON 返回也允许继续尝试解析,兼容逗号/换行文本池。
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}] 远程关键词池批跑已启动: ${items.length} 个关键词, 每批 ${started.snapshot.batchSize} 个`, "info", { remoteUrl });
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}] 已启动新运行: ${run.id}`);
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}] 已请求停止单次运行: ${runState.id}`, "warn");
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
- // 中止当前正在执行的节点,同时向远端 agent 发送 /stop 命令
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 仅作为兼容入口,语义等价到 start
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
- // JSON 文本也允许继续解析,避免上游切到纯文本池时整条批跑入口不可用。
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}] 远程关键词池批跑已启动: ${items.length} 个关键词, 每批 ${started.snapshot.batchSize} 个`, "info", { remoteUrl });
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
- // 中止当前正在执行的节点,避免旧节点与新重试冲突(同时向远端 agent 发送 /stop 命令)
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
- // 只有确认 run 已进入终态后,才允许把 updatedAt 视作完成时间候选。
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
- // 这里直接复用 execution-status 作为运行态快照构造器,保证 status 字段来源仍然唯一。
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 时,mode 只能是 single remote_batch;这里显式收窄给 CLI/API 统一消费。
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
- // 统一工厂用于主线集成,CLI 入口只需传入 registry 即可拿到全部只读 service。
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
- // 刷新失败时使用缓存,确保 CLI 在网关抖动时仍可读到会话信息。
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
- // 流式文本片段(累积+增量混合,用 merge 去重)
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
- // 兼容非 stream 前缀的直接文本
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
- // 超时时尝试从 history 补一次
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
- // 复用 registry bootstrap 聚合结果,保证 CLI 与现有前端看到的是同一份系统快照。
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;