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
@@ -12,8 +12,8 @@ const DEFAULT_RETENTION_DAYS = {
12
12
  };
13
13
  const ONE_DAY_MS = 24 * 60 * 60 * 1000;
14
14
  /**
15
- * 生成清理计划,不删除任何文件。
16
- * 默认只清理 success 状态,可通过 options 指定其他状态。
15
+ * Generate a cleanup plan without deleting any files.
16
+ * Defaults to only cleaning "success" status; other statuses can be specified via options.
17
17
  */
18
18
  const planCleanup = async (definition, options = {}) => {
19
19
  const statuses = options.statuses?.length ? options.statuses : ["success"];
@@ -22,7 +22,7 @@ const planCleanup = async (definition, options = {}) => {
22
22
  const kindFilter = options.kinds?.length ? new Set(options.kinds) : null;
23
23
  const olderThanDays = options.olderThanDays ?? Math.max(...safeStatuses.map((s) => DEFAULT_RETENTION_DAYS[s] ?? 90));
24
24
  const cutoffMs = Date.now() - olderThanDays * ONE_DAY_MS;
25
- // 优先从索引获取文件列表,索引缺失时回退扫描
25
+ // Prefer index for file list; fall back to scan when index is missing
26
26
  let items = await (0, artifact_index_1.readIndexRecords)(definition.artifactDir);
27
27
  if (items.length === 0) {
28
28
  const scanned = await (0, storage_service_1.scanStoredArtifacts)([definition]);
@@ -85,7 +85,7 @@ const planCleanup = async (definition, options = {}) => {
85
85
  };
86
86
  exports.planCleanup = planCleanup;
87
87
  /**
88
- * 执行清理:删除文件 + 清理空目录 + 清理临时文件 + 重建索引。
88
+ * Execute cleanup: delete files + clean empty directories + clean temp files + rebuild index.
89
89
  */
90
90
  const executeCleanup = async (definition, plan) => {
91
91
  const warnings = [];
@@ -94,10 +94,10 @@ const executeCleanup = async (definition, plan) => {
94
94
  for (const file of plan.files) {
95
95
  const rootAbs = (0, node_path_1.resolve)(definition.artifactDir);
96
96
  const absPath = (0, node_path_1.resolve)(rootAbs, file.relativePath);
97
- // 路径穿越保护:目标必须位于当前 pipeline artifactDir
97
+ // Path traversal protection: target must be within the current pipeline's artifactDir
98
98
  if (absPath !== rootAbs && !absPath.startsWith(`${rootAbs}${node_path_1.sep}`)) {
99
99
  failed += 1;
100
- warnings.push(`拒绝删除越界文件 (${definition.id}:${file.relativePath})`);
100
+ warnings.push(`Refused to delete out-of-bounds file (${definition.id}:${file.relativePath})`);
101
101
  continue;
102
102
  }
103
103
  try {
@@ -106,27 +106,27 @@ const executeCleanup = async (definition, plan) => {
106
106
  }
107
107
  catch (error) {
108
108
  failed += 1;
109
- warnings.push(`删除失败 (${definition.id}:${file.relativePath}): ${error instanceof Error ? error.message : String(error)}`);
109
+ warnings.push(`Delete failed (${definition.id}:${file.relativePath}): ${error instanceof Error ? error.message : String(error)}`);
110
110
  }
111
111
  }
112
- // 清理空目录
112
+ // Clean empty directories
113
113
  await (0, exports.cleanupEmptyDirs)(definition.artifactDir);
114
- // 清理临时文件
114
+ // Clean temp files
115
115
  const tmpResult = await (0, exports.cleanupTempFiles)(definition.artifactDir);
116
116
  warnings.push(...tmpResult.warnings);
117
- // 重建索引以移除已删文件
117
+ // Rebuild index to remove deleted file entries
118
118
  try {
119
119
  const { rebuildArtifactIndex } = await import("./artifact-index.js");
120
120
  await rebuildArtifactIndex(definition, (d) => (0, storage_service_1.scanStoredArtifacts)([d]));
121
121
  }
122
122
  catch {
123
- warnings.push(`删除后自动重建索引失败 (${definition.id}),请手动执行 rebuild-index`);
123
+ warnings.push(`Auto-rebuild index after delete failed (${definition.id}), please run rebuild-index manually`);
124
124
  }
125
125
  return { deleted, failed, warnings };
126
126
  };
127
127
  exports.executeCleanup = executeCleanup;
128
128
  /**
129
- * 递归清理空目录。保留 artifactDir 根目录本身。
129
+ * Recursively clean empty directories. Preserves the artifactDir root directory itself.
130
130
  */
131
131
  const cleanupEmptyDirs = async (artifactDir) => {
132
132
  const warnings = [];
@@ -139,7 +139,7 @@ const cleanupEmptyDirs = async (artifactDir) => {
139
139
  await walkAndRemove(`${dirPath}${node_path_1.sep}${entry.name}`);
140
140
  }
141
141
  }
142
- // 不删除 artifacts 根目录本身
142
+ // Don't delete the artifacts root directory itself
143
143
  const isRoot = dirPath === artifactDir || dirPath.endsWith(`${node_path_1.sep}artifacts`) && dirPath.replace(/\\/g, "/").endsWith("/artifacts");
144
144
  if (isRoot)
145
145
  return;
@@ -150,7 +150,7 @@ const cleanupEmptyDirs = async (artifactDir) => {
150
150
  }
151
151
  }
152
152
  catch {
153
- // 目录不存在或无权访问
153
+ // Directory doesn't exist or cannot be accessed
154
154
  }
155
155
  };
156
156
  await walkAndRemove(artifactDir);
@@ -158,7 +158,7 @@ const cleanupEmptyDirs = async (artifactDir) => {
158
158
  };
159
159
  exports.cleanupEmptyDirs = cleanupEmptyDirs;
160
160
  /**
161
- * 清理 persistArtifactFile 遗留的 .tmp-*.json 临时文件(原子写入失败残留)。
161
+ * Clean up .tmp-*.json temp files left behind by persistArtifactFile (atomic write failure residue).
162
162
  */
163
163
  const cleanupTempFiles = async (artifactDir) => {
164
164
  const warnings = [];
@@ -177,13 +177,13 @@ const cleanupTempFiles = async (artifactDir) => {
177
177
  cleaned += 1;
178
178
  }
179
179
  catch (error) {
180
- warnings.push(`清理临时文件失败: ${error instanceof Error ? error.message : String(error)}`);
180
+ warnings.push(`Failed to clean temp files: ${error instanceof Error ? error.message : String(error)}`);
181
181
  }
182
182
  }
183
183
  }
184
184
  }
185
185
  catch {
186
- // 目录不存在
186
+ // Directory doesn't exist
187
187
  }
188
188
  };
189
189
  await walkAndClean(artifactDir);
@@ -9,7 +9,7 @@ const INDEX_FILE_NAME = "index.jsonl";
9
9
  const getIndexPath = (artifactDir) => (0, node_path_1.resolve)(artifactDir, INDEX_FILE_NAME);
10
10
  exports.getIndexPath = getIndexPath;
11
11
  const encodeCursorV2 = (cursor) => `v2:${Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url")}`;
12
- /** 兼容解码 v2 和旧版 cursor。返回 null 表示无效。 */
12
+ /** Compatible decoding for v2 and legacy cursors. Returns null for invalid input. */
13
13
  const decodeAnyCursor = (cursor) => {
14
14
  if (cursor.startsWith("v2:")) {
15
15
  try {
@@ -23,7 +23,7 @@ const decodeAnyCursor = (cursor) => {
23
23
  return null;
24
24
  }
25
25
  }
26
- // v1 cursor 格式: base64url(updatedAt|artifactId)
26
+ // Legacy v1 cursor format: base64url(updatedAt|artifactId)
27
27
  try {
28
28
  const raw = Buffer.from(cursor, "base64url").toString("utf8");
29
29
  const idx = raw.lastIndexOf("|");
@@ -44,7 +44,7 @@ const getIndexUpdatedAt = async (artifactDir) => {
44
44
  return "";
45
45
  }
46
46
  };
47
- /** 追加一条索引记录到 index.jsonl。追加失败不抛异常,返回失败信息供调用方观测。 */
47
+ /** Append one index record to index.jsonl. Does not throw on failure; returns failure info for caller observation. */
48
48
  const appendIndexRecord = async (artifactDir, record) => {
49
49
  const indexPath = (0, exports.getIndexPath)(artifactDir);
50
50
  try {
@@ -65,7 +65,7 @@ const appendIndexRecord = async (artifactDir, record) => {
65
65
  }
66
66
  };
67
67
  exports.appendIndexRecord = appendIndexRecord;
68
- /** 读取索引文件所有行,损坏行跳过。使用流式读取避免大索引一次性内存占用。 */
68
+ /** Read all lines from the index file, skipping corrupt lines. Uses streaming reads to avoid loading a large index entirely into memory. */
69
69
  const readIndexRecords = async (artifactDir) => {
70
70
  const indexPath = (0, exports.getIndexPath)(artifactDir);
71
71
  const records = [];
@@ -81,7 +81,7 @@ const readIndexRecords = async (artifactDir) => {
81
81
  records.push(JSON.parse(line));
82
82
  }
83
83
  catch {
84
- // 损坏行跳过
84
+ // Skip corrupt lines
85
85
  }
86
86
  }
87
87
  }
@@ -101,7 +101,7 @@ const dedupeLatestByArtifactId = (records) => {
101
101
  }
102
102
  return [...latest.values()];
103
103
  };
104
- /** 从索引读取、过滤、排序、分页。基于 readIndexRecords 之上提供查询能力。 */
104
+ /** Read from index, filter, sort, paginate. Builds on top of readIndexRecords to provide query capabilities. */
105
105
  const listIndexRecords = async (artifactDir, filter) => {
106
106
  const records = dedupeLatestByArtifactId(await (0, exports.readIndexRecords)(artifactDir));
107
107
  const limitRaw = filter.limit ?? 100;
@@ -133,7 +133,7 @@ const listIndexRecords = async (artifactDir, filter) => {
133
133
  continue;
134
134
  matched.push(record);
135
135
  }
136
- // updatedAt DESC, artifactId ASC 排序
136
+ // Sort by updatedAt DESC, artifactId ASC
137
137
  matched.sort((a, b) => {
138
138
  const dateDiff = Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
139
139
  if (dateDiff !== 0)
@@ -141,13 +141,13 @@ const listIndexRecords = async (artifactDir, filter) => {
141
141
  return a.artifactId.localeCompare(b.artifactId);
142
142
  });
143
143
  const total = matched.length;
144
- // cursor 分页:v2 cursor indexUpdatedAt,索引重建后旧 cursor 自动从头开始
144
+ // cursor pagination: v2 cursor includes indexUpdatedAt; after index rebuild, old cursors automatically restart from the beginning
145
145
  let startIndex = 0;
146
146
  const currentIndexUpdatedAt = await getIndexUpdatedAt(artifactDir);
147
147
  if (filter.cursor) {
148
148
  const decoded = decodeAnyCursor(filter.cursor);
149
149
  if (decoded) {
150
- // 索引代际变化 → cursor 过期,从头开始
150
+ // Index generation changed → cursor expired, restart from the beginning
151
151
  if (decoded.indexUpdatedAt && decoded.indexUpdatedAt !== currentIndexUpdatedAt) {
152
152
  startIndex = 0;
153
153
  }
@@ -165,7 +165,7 @@ const listIndexRecords = async (artifactDir, filter) => {
165
165
  return { items: page, nextCursor, total };
166
166
  };
167
167
  exports.listIndexRecords = listIndexRecords;
168
- /** StoredArtifactIndexRecord 转换为 StoredArtifactItem */
168
+ /** Convert from StoredArtifactIndexRecord to StoredArtifactItem. */
169
169
  const toStoredArtifactItem = (record, pipelineId, pipelineTitle) => ({
170
170
  pipelineId,
171
171
  pipelineTitle,
@@ -181,7 +181,7 @@ const toStoredArtifactItem = (record, pipelineId, pipelineTitle) => ({
181
181
  });
182
182
  exports.toStoredArtifactItem = toStoredArtifactItem;
183
183
  /**
184
- * 扫描一条流水线的产物目录,重建 index.jsonl
184
+ * Scan a single pipeline's artifact directory and rebuild its index.jsonl.
185
185
  */
186
186
  const rebuildArtifactIndex = async (definition, scan) => {
187
187
  const warnings = [];
@@ -207,12 +207,12 @@ const rebuildArtifactIndex = async (definition, scan) => {
207
207
  await (0, promises_1.rename)(tmpPath, indexPath);
208
208
  }
209
209
  catch (error) {
210
- warnings.push(`重建索引失败 (pipeline ${definition.id}): ${error instanceof Error ? error.message : String(error)}`);
210
+ warnings.push(`Failed to rebuild index (pipeline ${definition.id}): ${error instanceof Error ? error.message : String(error)}`);
211
211
  }
212
212
  return { indexed, skipped, warnings };
213
213
  };
214
214
  exports.rebuildArtifactIndex = rebuildArtifactIndex;
215
- /** StoredArtifactItem + 读取文件内容 补充为完整的 IndexRecord */
215
+ /** Enrich a StoredArtifactItem with its file content to produce a complete IndexRecord. */
216
216
  const enrichItemToIndexRecord = async (item, definition) => {
217
217
  const filePath = (0, node_path_1.resolve)(definition.artifactDir, item.relativePath);
218
218
  let parsed = null;
@@ -221,7 +221,7 @@ const enrichItemToIndexRecord = async (item, definition) => {
221
221
  parsed = JSON.parse(raw);
222
222
  }
223
223
  catch {
224
- // 无法读取时用路径启发式信息
224
+ // When unreadable, use path-based heuristics
225
225
  }
226
226
  const artifactObj = parsed && typeof parsed.artifact === "object" && parsed.artifact !== null
227
227
  ? parsed.artifact
@@ -5,8 +5,8 @@ const promises_1 = require("node:fs/promises");
5
5
  const node_path_1 = require("node:path");
6
6
  const artifact_index_1 = require("./artifact-index");
7
7
  /**
8
- * 增量重建产物索引扫描磁盘目录,仅对缺失索引的文件补充记录。
9
- * 已有索引记录的文件(按 relativePath 匹配)不会重复添加。
8
+ * Incrementally rebuild artifact index scan the disk directory, only append records for files missing from the index.
9
+ * Files already present in the index (matched by relativePath) are not re-added.
10
10
  */
11
11
  const rebuildArtifactIndexIncremental = async (rootDir, pipelineId) => {
12
12
  const warnings = [];
@@ -36,7 +36,7 @@ const rebuildArtifactIndexIncremental = async (rootDir, pipelineId) => {
36
36
  }
37
37
  }
38
38
  catch {
39
- warnings.push(`无法扫描产物根目录: ${rootDir}`);
39
+ warnings.push(`Failed to scan artifact root directory: ${rootDir}`);
40
40
  }
41
41
  for (const dir of dirs) {
42
42
  const existingRecords = await (0, artifact_index_1.readIndexRecords)(dir.artifactDir);
@@ -39,7 +39,7 @@ const parseNodeIdFromFileName = (fileName, runId, relativePath) => {
39
39
  return null;
40
40
  const normalizedPath = toUnixPath(relativePath).toLowerCase();
41
41
  if (normalizedPath.includes("/envelopes/")) {
42
- // envelope 文件名格式: <runId>-<nodeId>-node-<nodeId>-<uuid>-envelope.json
42
+ // envelope filename format: <runId>-<nodeId>-node-<nodeId>-<uuid>-envelope.json
43
43
  const marker = "-node-";
44
44
  const markerIndex = tail.indexOf(marker);
45
45
  if (markerIndex > 0) {
@@ -48,13 +48,13 @@ const parseNodeIdFromFileName = (fileName, runId, relativePath) => {
48
48
  }
49
49
  return null;
50
50
  }
51
- // group/adpater 聚合文件格式不稳定(含 itemKey),这里不参与节点筛选,避免误判。
51
+ // group/adapter aggregate file format is unstable (includes itemKey); skip node filtering here to avoid false positives.
52
52
  if (tail.endsWith("-adapter-output") || tail.endsWith("-group-output"))
53
53
  return null;
54
54
  const tokens = tail.split("-").filter(Boolean);
55
55
  if (tokens.length < 3)
56
56
  return null;
57
- // 结构化节点产物命名格式: <runId>-<nodeId>-<artifactIndex>-<safeType>.json
57
+ // Structured node artifact naming format: <runId>-<nodeId>-<artifactIndex>-<safeType>.json
58
58
  let artifactIndexPos = -1;
59
59
  for (let i = tokens.length - 1; i >= 0; i -= 1) {
60
60
  if (NUMERIC_TOKEN_RE.test(tokens[i])) {
@@ -102,7 +102,7 @@ const parseArtifactPathMeta = (artifactRootAbs, filePathAbs, updatedAtIso) => {
102
102
  const dateBucket = DATE_BUCKET_RE.test(dateRaw) ? dateRaw : formatDateBucketFromIso(updatedAtIso);
103
103
  let runId = null;
104
104
  if (segments[2]?.startsWith("batch-")) {
105
- // 批跑产物路径: status/date/batchRunId/runId/...
105
+ // Batch run artifact path: status/date/batchRunId/runId/...
106
106
  runId = segments[3]?.startsWith("run-") ? segments[3] : null;
107
107
  }
108
108
  else {
@@ -116,7 +116,7 @@ const shouldIncludeArtifactFile = (relativePath) => {
116
116
  return false;
117
117
  return true;
118
118
  };
119
- /** 文件系统扫描(降级路径 / 索引重建用)。 */
119
+ /** Filesystem scan (fallback path / for index rebuild). */
120
120
  const scanStoredArtifacts = async (definitions, options) => {
121
121
  const pipelineIdFilter = options?.pipelineIds?.length ? new Set(options.pipelineIds) : null;
122
122
  const nodeIdFilter = options?.nodeIds?.length ? new Set(options.nodeIds) : null;
@@ -150,11 +150,11 @@ const scanStoredArtifacts = async (definitions, options) => {
150
150
  continue;
151
151
  if (runIdFilter && effectiveRunId !== runIdFilter)
152
152
  continue;
153
- // batchRunId 扫描过滤:路径含 batch-xxx
153
+ // batchRunId scan filter: path contains batch-xxx segment
154
154
  if (batchRunIdFilter && !meta.relativePath.includes(`/${batchRunIdFilter}/`))
155
155
  continue;
156
156
  const nodeId = parseNodeIdFromFileName(fileName, effectiveRunId, meta.relativePath);
157
- // kind 扫描过滤:通过文件名后缀和路径判断
157
+ // kind scan filter: determined by filename suffix and path
158
158
  if (kindFilter) {
159
159
  const scanKind = meta.relativePath.includes("/envelopes/") ? "envelope"
160
160
  : fileName.includes("-adapter-output") ? "adapter"
@@ -203,11 +203,11 @@ const decodeListCursor = (cursor) => {
203
203
  return null;
204
204
  }
205
205
  };
206
- /** 优先读索引,索引缺失时降级为文件系统扫描。支持 cursor 分页。 */
206
+ /** Prefer reading from index; fall back to filesystem scan when index is missing. Supports cursor pagination. */
207
207
  const listStoredArtifacts = async (definitions, options) => {
208
208
  const pipelineIdFilter = options?.pipelineIds?.length ? new Set(options.pipelineIds) : null;
209
209
  const limit = Number.isFinite(options?.limit) ? Math.max(1, Math.min(5000, Math.trunc(options?.limit))) : 100;
210
- // 尝试从索引读取(不传 cursor,由外层统一分页)
210
+ // Try reading from index (without cursor; pagination is handled uniformly by the outer layer)
211
211
  const indexItems = [];
212
212
  let indexAvailable = false;
213
213
  for (const definition of definitions) {
@@ -222,7 +222,7 @@ const listStoredArtifacts = async (definitions, options) => {
222
222
  dateTo: options?.dateTo,
223
223
  batchRunId: options?.batchRunId,
224
224
  runId: options?.runId,
225
- limit: 0, // 0 = 无限制,全量读出后统一排序分页
225
+ limit: 0, // 0 = unlimited; read all then sort and paginate uniformly
226
226
  };
227
227
  const result = await (0, artifact_index_1.listIndexRecords)(definition.artifactDir, filter);
228
228
  if (result.total > 0 || result.items.length > 0) {
@@ -232,7 +232,7 @@ const listStoredArtifacts = async (definitions, options) => {
232
232
  }
233
233
  }
234
234
  }
235
- // 统一的排序与 cursor 分页(index scan 使用相同编码)
235
+ // Unified sort and cursor pagination (index and scan use the same encoding)
236
236
  const sortItems = (items) => items.sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt));
237
237
  const paginateWithCursor = (items, source) => {
238
238
  let startIndex = 0;
@@ -255,7 +255,7 @@ const listStoredArtifacts = async (definitions, options) => {
255
255
  if (indexAvailable) {
256
256
  return paginateWithCursor(sortItems(indexItems), "index");
257
257
  }
258
- // 降级:文件系统扫描
258
+ // Fallback: filesystem scan
259
259
  const scanItems = await (0, exports.scanStoredArtifacts)(definitions, {
260
260
  pipelineIds: options?.pipelineIds,
261
261
  nodeIds: options?.nodeIds,
@@ -272,7 +272,7 @@ exports.listStoredArtifacts = listStoredArtifacts;
272
272
  const resolveArtifactFilePath = (definition, relativePath) => {
273
273
  const rootAbs = (0, node_path_1.resolve)(definition.artifactDir);
274
274
  const targetAbs = (0, node_path_1.resolve)(rootAbs, relativePath);
275
- // 防止路径穿越,只允许读取当前流水线产物目录下文件。
275
+ // Prevent path traversal; only allow reading files within the current pipeline's artifact directory.
276
276
  if (targetAbs !== rootAbs && !targetAbs.startsWith(`${rootAbs}${node_path_1.sep}`))
277
277
  return null;
278
278
  return targetAbs;
@@ -293,7 +293,7 @@ const readStoredArtifactContent = async (definition, relativePath) => {
293
293
  parsed = null;
294
294
  }
295
295
  const parsedObj = toRecord(parsed);
296
- // 优先按 kind 字段分发,无 kind 时回退到旧启发式判断(向后兼容)
296
+ // Prefer dispatching by kind field; fall back to the old heuristic when kind is absent (backward compatible)
297
297
  const kind = typeof parsedObj?.kind === "string" ? parsedObj.kind : null;
298
298
  const isEnvelope = kind === "envelope" || (!kind && parsedObj && "envelope" in parsedObj);
299
299
  const envelopeObj = isEnvelope ? toRecord(parsedObj?.envelope) : null;
@@ -307,7 +307,7 @@ const readStoredArtifactContent = async (definition, relativePath) => {
307
307
  .map((item) => toRecord(item)?.content)
308
308
  .filter((item) => item !== undefined);
309
309
  const logs = Array.isArray(envelopeObj.logs) ? envelopeObj.logs : [];
310
- // envelope 预览返回 contents + logs,便于排错。
310
+ // envelope preview returns contents + logs for easier troubleshooting.
311
311
  return {
312
312
  contents,
313
313
  logs,
@@ -325,7 +325,7 @@ const readStoredArtifactContent = async (definition, relativePath) => {
325
325
  };
326
326
  exports.readStoredArtifactContent = readStoredArtifactContent;
327
327
  const exportStoredArtifactContents = async (definitions, options) => {
328
- // 优先从索引选候选文件,减少全量 scan;索引缺失时 listStoredArtifacts 自动降级 scan
328
+ // Prefer index for candidate files to reduce full scan; listStoredArtifacts auto-falls-back to scan when index is missing
329
329
  const limit = Number.isFinite(options?.limit) ? Math.max(1, Math.trunc(options?.limit)) : 20000;
330
330
  const listResult = await (0, exports.listStoredArtifacts)(definitions, {
331
331
  pipelineIds: options?.pipelineIds,
@@ -357,8 +357,8 @@ const exportStoredArtifactContents = async (definitions, options) => {
357
357
  out[dateKey][pipelineKey] = {};
358
358
  if (!out[dateKey][pipelineKey][nodeKey])
359
359
  out[dateKey][pipelineKey][nodeKey] = [];
360
- // 导出只保留产物内容,不包含 runId、文件名等元信息。
361
- // content 本身是数组,则展开写入,避免导出结果出现“数组套数组”。
360
+ // Export keeps only artifact content; no runId, filename or other metadata.
361
+ // If content is itself an array, flatten it to avoid "array inside array" in the export result.
362
362
  if (Array.isArray(content.content)) {
363
363
  out[dateKey][pipelineKey][nodeKey].push(...content.content);
364
364
  }
@@ -18,7 +18,7 @@ const normalizePipelineSelector = (selector) => {
18
18
  const requirePipelineId = (selector) => {
19
19
  if (selector.pipelineId)
20
20
  return selector.pipelineId;
21
- // 内嵌 runtime service 只能通过 pipelineId 定位 pipeline 实例;runId/batchRunId 在此层仅用于二次匹配。
21
+ // The embedded runtime service can only locate a pipeline instance by pipelineId; runId/batchRunId at this layer is only used for secondary matching.
22
22
  throw new errors_1.CliError("Missing pipelineId for local runtime selector", {
23
23
  code: "INVALID_ARGUMENT",
24
24
  exitCode: 2,
@@ -64,7 +64,7 @@ const buildCliAppContext = (appContext) => {
64
64
  runPipeline: async (pipelineId) => writableServices.pipeline.runPipeline(pipelineId),
65
65
  retryNode: async (input) => writableServices.pipeline.retryNode(input),
66
66
  diagnoseNode: async (input) => {
67
- const runtimeApiClient = (0, server_runtime_client_1.createPipelineRuntimeApiClient)();
67
+ const runtimeApiClient = (0, server_runtime_client_1.createPipelineRuntimeApiClientWs)();
68
68
  return runtimeApiClient.diagnoseNode(input.pipelineId, input.nodeId, input.itemKey);
69
69
  },
70
70
  getOutput: async (pipelineId, runId) => writableServices.pipeline.getOutput(pipelineId, runId),
@@ -127,7 +127,7 @@ const buildCliAppContext = (appContext) => {
127
127
  };
128
128
  };
129
129
  const buildRuntimeApiOnlyContext = () => {
130
- const runtimeApiClient = (0, server_runtime_client_1.createPipelineRuntimeApiClient)();
130
+ const runtimeApiClient = (0, server_runtime_client_1.createPipelineRuntimeApiClientWs)();
131
131
  const serverLifecycleClient = (0, server_runtime_client_1.createServerLifecycleClient)();
132
132
  const unsupported = async () => {
133
133
  throw new Error("unsupported_cli_runtime_api_context");
@@ -187,10 +187,10 @@ const createMainCliBootstrap = () => {
187
187
  return async ({ route }) => {
188
188
  const bootstrap = route.bootstrap;
189
189
  if (bootstrap?.runtimeApiOnly) {
190
- // runtime-api only 路由必须复用 daemon 提供的 API 语义,避免误落到内嵌 service 路径。
190
+ // runtime-api-only routes must reuse the daemon-provided API semantics to avoid incorrectly falling to the embedded service path.
191
191
  if (bootstrap.ensureServerReady) {
192
192
  const serverLifecycleClient = (0, server_runtime_client_1.createServerLifecycleClient)();
193
- // 运行类命令必须先绑定到持久执行宿主,避免 CLI 临时进程误充当后台宿主。
193
+ // Run-class commands must bind to a persistent execution host first, to avoid the CLI temporary process inadvertently acting as the daemon host.
194
194
  await serverLifecycleClient.ensureServerReady();
195
195
  }
196
196
  return {
@@ -207,12 +207,12 @@ const createMainCliBootstrap = () => {
207
207
  await appContext.gateway.connect();
208
208
  }
209
209
  else if (bootstrap?.gateway === "warmup") {
210
- // 系统快照尽量尝试建链,失败时退回当前缓存状态,避免只读命令被完全阻断。
210
+ // For system snapshots, try connecting first; fall back to cached state on failure so read-only commands aren't completely blocked.
211
211
  try {
212
212
  await appContext.gateway.connect();
213
213
  }
214
214
  catch {
215
- // 保持只读命令可退化执行。
215
+ // Allow read-only commands to degrade gracefully.
216
216
  }
217
217
  }
218
218
  return {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.agentRoutes = exports.agentSendCommand = exports.agentSessionCommand = exports.agentListCommand = void 0;
4
4
  const errors_1 = require("../errors");
5
+ const i18n_1 = require("../i18n");
5
6
  const readFlagAsPositiveInteger = (value, fallback) => {
6
7
  if (typeof value !== "string")
7
8
  return fallback;
@@ -84,42 +85,42 @@ exports.agentRoutes = [
84
85
  {
85
86
  key: "agent.list",
86
87
  path: ["agent", "list"],
87
- description: "输出智能体列表",
88
+ description: (0, i18n_1.t)("agent.list.description"),
88
89
  handler: exports.agentListCommand,
89
90
  bootstrap: { gateway: "required" },
90
91
  help: {
91
92
  usage: "taskmeld agent list [--format <json|md>]",
92
- summary: "输出智能体列表",
93
+ summary: (0, i18n_1.t)("agent.list.summary"),
93
94
  },
94
95
  },
95
96
  {
96
97
  key: "agent.session",
97
98
  path: ["agent", "session"],
98
- description: "输出会话列表(全部或按 agent 过滤)",
99
+ description: (0, i18n_1.t)("agent.session.description"),
99
100
  handler: exports.agentSessionCommand,
100
101
  bootstrap: { gateway: "required" },
101
102
  help: {
102
103
  usage: "taskmeld agent session [agentId] [--format <json|md>]",
103
- summary: "输出会话列表(全部或按 agent 过滤)",
104
- args: [{ name: "agentId", required: false, description: "按 Agent ID 过滤会话" }],
104
+ summary: (0, i18n_1.t)("agent.session.summary"),
105
+ args: [{ name: "agentId", required: false, description: (0, i18n_1.t)("agent.session.argAgentId") }],
105
106
  },
106
107
  },
107
108
  {
108
109
  key: "agent.send",
109
110
  path: ["agent", "send"],
110
- description: "向指定 agent 会话发送消息",
111
+ description: (0, i18n_1.t)("agent.send.description"),
111
112
  handler: exports.agentSendCommand,
112
113
  bootstrap: { gateway: "required" },
113
114
  help: {
114
115
  usage: "taskmeld agent send <agentId> <message> [--session <id>] [--format <json|md>]",
115
- summary: "向指定 agent 会话发送消息并等待回复",
116
+ summary: (0, i18n_1.t)("agent.send.summary"),
116
117
  args: [
117
- { name: "agentId", required: true, description: "Agent ID" },
118
- { name: "message", required: true, description: "消息内容" },
118
+ { name: "agentId", required: true, description: (0, i18n_1.t)("agent.send.argAgentId") },
119
+ { name: "message", required: true, description: (0, i18n_1.t)("agent.send.argMessage") },
119
120
  ],
120
121
  options: [
121
- { flags: ["--session"], valueName: "id", description: "会话 ID(默认 main)" },
122
- { flags: ["--stream"], description: "流式输出回复内容" },
122
+ { flags: ["--session"], valueName: "id", description: (0, i18n_1.t)("agent.send.optSession") },
123
+ { flags: ["--stream"], description: (0, i18n_1.t)("agent.send.optStream") },
123
124
  ],
124
125
  },
125
126
  },