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
package/README.zh-CN.md CHANGED
@@ -1,176 +1,176 @@
1
- <p align="center">
2
- <a href="./README.md">English</a>
3
- &nbsp;·&nbsp;
4
- <strong>简体中文</strong>
5
- &nbsp;·&nbsp;
6
- <a href="https://taskmeld.com">Website</a>
7
- </p>
8
-
9
- <p align="center">
10
- <a href="https://www.npmjs.com/package/taskmeld"><img src="https://img.shields.io/npm/v/taskmeld.svg?style=flat-square&color=cb3837&labelColor=161b22&logo=npm&logoColor=white" alt="npm version"/></a>
11
- <a href="./package.json"><img src="https://img.shields.io/node/v/taskmeld.svg?style=flat-square&color=5fa04e&labelColor=161b22&logo=nodedotjs&logoColor=white" alt="node"/></a>
12
- </p>
13
-
14
- <br/>
15
-
16
- <h3 align="center">Agent 流水线编排平台</h3>
17
- <p align="center">将 OpenClaw Agent 编排为可执行流水线——定义、运行、观察、迭代。文件持久化,零外部数据库。</p>
18
-
19
- <br/>
20
-
21
- > [!TIP]
22
- > TaskMeld 是 OpenClaw 的流水线运行时。OpenClaw 负责 Agent 执行,TaskMeld 负责将 Agent 串成 DAG 工作流,含路由分流、重试和产物追踪。
23
-
24
- <br/>
25
-
26
- ## 环境要求
27
-
28
- - Node ≥ 18
29
- - OpenClaw ≥ 5.20
30
- - Windows(macOS 和 Linux 尚未测试验证)
31
-
32
- ## 安装
33
-
34
- ~~~bash
35
- npm install -g taskmeld
36
- ~~~
37
-
38
- <br/>
39
-
40
- ## 快速开始
41
-
42
- ~~~bash
43
- # 初始化 — 引导式配置 OpenClaw Gateway 连接
44
- taskmeld init
45
-
46
- # 启动后端守护进程
47
- taskmeld server start
48
-
49
- # 查看可用流水线
50
- taskmeld pipeline list
51
-
52
- # 运行一条流水线
53
- taskmeld pipeline start <pipelineId>
54
-
55
- # 实时监听流水线运行
56
- taskmeld pipeline watch <pipelineId>
57
- ~~~
58
-
59
- | 命令 | 场景 |
60
- |---|---|
61
- | `taskmeld pipeline list` | 查看有哪些流水线 |
62
- | `taskmeld pipeline start <id>` | 启动一条流水线 |
63
- | `taskmeld pipeline watch <id>` | 通过 WebSocket 实时跟踪运行 |
64
- | `taskmeld pipeline status <id>` | 一次性状态快照 |
65
- | `taskmeld pipeline stop <id>` | 停止运行中的流水线 |
66
- | `taskmeld pipeline retry-node <id> <node>` | 重试失败的节点 |
67
- | `taskmeld server start` | 启动后端守护进程 |
68
- | `taskmeld agent list` | 列出已注册的 Agent |
69
- | `taskmeld artifact list` | 浏览流水线产物 |
70
-
71
- 完整命令参考:`taskmeld --help` 或 [CLI 文档](docs/cli.md)。
72
-
73
- <br/>
74
-
75
- ## 特性
76
-
77
- - **DAG 流水线引擎** — 节点依赖图、并行组、路由分支、节点级重试、状态持久化
78
- - **CLI 工具** — 全生命周期管理:list, run, status, stop, retry, watch(WebSocket 流式监听)
79
- - **HTTP + WebSocket API** — REST 端点用于控制面,WS 广播用于实时可观测性
80
- - **Web 控制台** — React 19 仪表盘,含 DAG 可视化、Agent 会话、产物浏览器、日志查看器
81
- - **Gateway 集成** — WebSocket 客户端,对接 OpenClaw Gateway 鉴权、事件中继与 Agent 会话委托
82
- - **文件持久化** — 所有状态以 JSON 和日志文件默认存储在 `~/.taskmeld/` 下,可用 `TASKMELD_DATA_DIR` 覆盖,无需外部数据库
83
-
84
- <br/>
85
-
86
- ## 架构
87
-
88
- ```
89
- CLI (taskmeld) · Web 控制台 (React)
90
- │ │
91
- HTTP API ─────── WS Broker
92
- │ │
93
- App Assembly (注册表 + 运行时)
94
-
95
- Pipeline Engine (DAG · 调度器 · 状态机)
96
-
97
- Gateway Client (OpenClaw — 鉴权、事件、会话)
98
- ```
99
-
100
- | 目录 | 说明 |
101
- |------|------|
102
- | `src/cli/` | CLI 入口、路由、输出渲染 |
103
- | `src/pipeline/` | 流水线引擎(运行时、调度器、执行、DAG) |
104
- | `src/server/` | HTTP API 服务 + 路由模块 |
105
- | `src/transport/` | WebSocket 广播 |
106
- | `src/gateway/` | 外部 Gateway WebSocket 客户端 |
107
- | `src/services/` | 服务层(读写 facade) |
108
- | `src/app/` | 应用装配(注册表、运行时、上下文) |
109
- | `src/artifacts/` | 产物存储 |
110
- | `src/logs/` | 时间线日志 |
111
- | `web/` | React 管理前端 |
112
-
113
- <br/>
114
-
115
- ## 开发状态
116
-
117
- > [!WARNING]
118
- > TaskMeld 当前处于初始测试阶段。功能正在逐步构建,API 可能在版本之间变化,部分界面仍较为粗糙。生产环境使用请自行评估——欢迎早期用户试用和反馈。
119
-
120
- <br/>
121
-
122
- ## 后续规划
123
-
124
- ### 现状
125
-
126
- - **流水线执行** — 节点驱动模式,每个节点绑定一个 OpenClaw Agent。CLI 已暴露完整命令集,外部 Agent 可通过编程方式调用(`pipeline list`、`pipeline start`、`pipeline status` 等)。
127
- - **Agent 管理** — 以只读操作为主(对话、编辑核心文件如 `agent.md` / `memory.md` / `soul.md`)。创建 Agent、配置 Skill 等操作仍需切换到 OpenClaw 中完成。
128
- - **数据存储** — 基于文件持久化(默认 `~/.taskmeld/` 下的 JSON + 日志文件),零外部依赖。
129
-
130
- ### 计划
131
-
132
- - **内置自主 Agent** — 作为一等运行时组件,全权负责流水线的完整生命周期:调度运行、创建与审查流水线定义、故障分类、产物整理。目标是从*你来操作流水线*过渡到*Agent 操作流水线,你来指挥*。
133
- - **Agent 生命周期管理** — 创建 Agent、配置 Skill、编辑核心文件等操作统一收敛到 TaskMeld 内,由内置 Agent 驱动,不再需要切换到 OpenClaw。
134
- - **数据库存储层** — 从文件持久化演进为数据库存储,提升查询性能、并发访问和可扩展性,同时保留单节点零依赖的轻量体验。
135
-
136
- <br/>
137
-
138
- ## 开发
139
-
140
- ```bash
141
- npm install # 安装依赖
142
- npm run build # 构建
143
- npm run typecheck # 仅类型检查
144
- npm run lint # 代码检查
145
- npm test # 运行测试
146
- npm run dev:web # 启动前端开发服务器(Vite HMR)
147
- ```
148
-
149
- | 层面 | 技术 |
150
- |------|------|
151
- | 语言 | TypeScript(strict, CommonJS) |
152
- | 运行时 | Node.js |
153
- | 后端 HTTP | Node.js 内置 `http` |
154
- | WebSocket | `ws` |
155
- | 前端 | React 19 + Vite 7 |
156
- | CSS | Tailwind CSS 4 |
157
- | 测试 | Vitest |
158
- | Lint | ESLint 9 |
159
-
160
- <br/>
161
-
162
- ## 文档
163
-
164
- - [CLI 参考](docs/cli.md)
165
- - [后端架构](docs/backend.md)
166
- - [前端架构](docs/web.md)
167
- - [流水线引擎](docs/pipeline/)
168
- - [参与贡献](CONTRIBUTING.md)
169
-
170
- <br/>
171
-
172
- ---
173
-
174
- <p align="center">
175
- <sub>MIT — 详见 <a href="./LICENSE">LICENSE</a></sub>
176
- </p>
1
+ <p align="center">
2
+ <a href="./README.md">English</a>
3
+ &nbsp;·&nbsp;
4
+ <strong>简体中文</strong>
5
+ &nbsp;·&nbsp;
6
+ <a href="https://taskmeld.com">Website</a>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/taskmeld"><img src="https://img.shields.io/npm/v/taskmeld.svg?style=flat-square&color=cb3837&labelColor=161b22&logo=npm&logoColor=white" alt="npm version"/></a>
11
+ <a href="./package.json"><img src="https://img.shields.io/node/v/taskmeld.svg?style=flat-square&color=5fa04e&labelColor=161b22&logo=nodedotjs&logoColor=white" alt="node"/></a>
12
+ </p>
13
+
14
+ <br/>
15
+
16
+ <h3 align="center">Agent 流水线编排平台</h3>
17
+ <p align="center">将 OpenClaw Agent 编排为可执行流水线——定义、运行、观察、迭代。文件持久化,零外部数据库。</p>
18
+
19
+ <br/>
20
+
21
+ > [!TIP]
22
+ > TaskMeld 是 OpenClaw 的流水线运行时。OpenClaw 负责 Agent 执行,TaskMeld 负责将 Agent 串成 DAG 工作流,含路由分流、重试和产物追踪。
23
+
24
+ <br/>
25
+
26
+ ## 环境要求
27
+
28
+ - Node ≥ 18
29
+ - OpenClaw ≥ 5.20
30
+ - Windows(macOS 和 Linux 尚未测试验证)
31
+
32
+ ## 安装
33
+
34
+ ~~~bash
35
+ npm install -g taskmeld
36
+ ~~~
37
+
38
+ <br/>
39
+
40
+ ## 快速开始
41
+
42
+ ~~~bash
43
+ # 初始化 — 引导式配置 OpenClaw Gateway 连接
44
+ taskmeld init
45
+
46
+ # 启动后端守护进程
47
+ taskmeld server start
48
+
49
+ # 查看可用流水线
50
+ taskmeld pipeline list
51
+
52
+ # 运行一条流水线
53
+ taskmeld pipeline start <pipelineId>
54
+
55
+ # 实时监听流水线运行
56
+ taskmeld pipeline watch <pipelineId>
57
+ ~~~
58
+
59
+ | 命令 | 场景 |
60
+ |---|---|
61
+ | `taskmeld pipeline list` | 查看有哪些流水线 |
62
+ | `taskmeld pipeline start <id>` | 启动一条流水线 |
63
+ | `taskmeld pipeline watch <id>` | 通过 WebSocket 实时跟踪运行 |
64
+ | `taskmeld pipeline status <id>` | 一次性状态快照 |
65
+ | `taskmeld pipeline stop <id>` | 停止运行中的流水线 |
66
+ | `taskmeld pipeline retry-node <id> <node>` | 重试失败的节点 |
67
+ | `taskmeld server start` | 启动后端守护进程 |
68
+ | `taskmeld agent list` | 列出已注册的 Agent |
69
+ | `taskmeld artifact list` | 浏览流水线产物 |
70
+
71
+ 完整命令参考:`taskmeld --help` 或 [CLI 文档](docs/cli.md)。
72
+
73
+ <br/>
74
+
75
+ ## 特性
76
+
77
+ - **DAG 流水线引擎** — 节点依赖图、并行组、路由分支、节点级重试、状态持久化
78
+ - **CLI 工具** — 全生命周期管理:list, run, status, stop, retry, watch(WebSocket 流式监听)
79
+ - **WebSocket API** — 统一 WS 传输层,用于控制面和实时可观测性
80
+ - **Web 控制台** — React 19 仪表盘,含 DAG 可视化、Agent 会话、产物浏览器、日志查看器
81
+ - **Gateway 集成** — WebSocket 客户端,对接 OpenClaw Gateway 鉴权、事件中继与 Agent 会话委托
82
+ - **文件持久化** — 所有状态以 JSON 和日志文件默认存储在 `~/.taskmeld/` 下,可用 `TASKMELD_DATA_DIR` 覆盖,无需外部数据库
83
+
84
+ <br/>
85
+
86
+ ## 架构
87
+
88
+ ```
89
+ CLI (taskmeld) · Web 控制台 (React)
90
+ │ │
91
+ WS RPC ─────── WS Broker
92
+ │ │
93
+ App Assembly (注册表 + 运行时)
94
+
95
+ Pipeline Engine (DAG · 调度器 · 状态机)
96
+
97
+ Gateway Client (OpenClaw — 鉴权、事件、会话)
98
+ ```
99
+
100
+ | 目录 | 说明 |
101
+ |------|------|
102
+ | `src/cli/` | CLI 入口、路由、输出渲染 |
103
+ | `src/pipeline/` | 流水线引擎(运行时、调度器、执行、DAG) |
104
+ | `src/server/` | HTTP 服务(健康检查 + 静态文件) |
105
+ | `src/transport/` | WebSocket 传输层(广播 + RPC 方法) |
106
+ | `src/gateway/` | 外部 Gateway WebSocket 客户端 |
107
+ | `src/services/` | 服务层(读写 facade) |
108
+ | `src/app/` | 应用装配(注册表、运行时、上下文) |
109
+ | `src/artifacts/` | 产物存储 |
110
+ | `src/logs/` | 时间线日志 |
111
+ | `web/` | React 管理前端 |
112
+
113
+ <br/>
114
+
115
+ ## 开发状态
116
+
117
+ > [!WARNING]
118
+ > TaskMeld 当前处于初始测试阶段。功能正在逐步构建,API 可能在版本之间变化,部分界面仍较为粗糙。生产环境使用请自行评估——欢迎早期用户试用和反馈。
119
+
120
+ <br/>
121
+
122
+ ## 后续规划
123
+
124
+ ### 现状
125
+
126
+ - **流水线执行** — 节点驱动模式,每个节点绑定一个 OpenClaw Agent。CLI 已暴露完整命令集,外部 Agent 可通过编程方式调用(`pipeline list`、`pipeline start`、`pipeline status` 等)。
127
+ - **Agent 管理** — 以只读操作为主(对话、编辑核心文件如 `agent.md` / `memory.md` / `soul.md`)。创建 Agent、配置 Skill 等操作仍需切换到 OpenClaw 中完成。
128
+ - **数据存储** — 基于文件持久化(默认 `~/.taskmeld/` 下的 JSON + 日志文件),零外部依赖。
129
+
130
+ ### 计划
131
+
132
+ - **内置自主 Agent** — 作为一等运行时组件,全权负责流水线的完整生命周期:调度运行、创建与审查流水线定义、故障分类、产物整理。目标是从*你来操作流水线*过渡到*Agent 操作流水线,你来指挥*。
133
+ - **Agent 生命周期管理** — 创建 Agent、配置 Skill、编辑核心文件等操作统一收敛到 TaskMeld 内,由内置 Agent 驱动,不再需要切换到 OpenClaw。
134
+ - **数据库存储层** — 从文件持久化演进为数据库存储,提升查询性能、并发访问和可扩展性,同时保留单节点零依赖的轻量体验。
135
+
136
+ <br/>
137
+
138
+ ## 开发
139
+
140
+ ```bash
141
+ npm install # 安装依赖
142
+ npm run build # 构建
143
+ npm run typecheck # 仅类型检查
144
+ npm run lint # 代码检查
145
+ npm test # 运行测试
146
+ npm run dev:web # 启动前端开发服务器(Vite HMR)
147
+ ```
148
+
149
+ | 层面 | 技术 |
150
+ |------|------|
151
+ | 语言 | TypeScript(strict, CommonJS) |
152
+ | 运行时 | Node.js |
153
+ | 后端 HTTP | Node.js 内置 `http` |
154
+ | WebSocket | `ws` |
155
+ | 前端 | React 19 + Vite 7 |
156
+ | CSS | Tailwind CSS 4 |
157
+ | 测试 | Vitest |
158
+ | Lint | ESLint 9 |
159
+
160
+ <br/>
161
+
162
+ ## 文档
163
+
164
+ - [CLI 参考](docs/cli.md)
165
+ - [后端架构](docs/backend.md)
166
+ - [前端架构](docs/web.md)
167
+ - [流水线引擎](docs/pipeline/)
168
+ - [参与贡献](CONTRIBUTING.md)
169
+
170
+ <br/>
171
+
172
+ ---
173
+
174
+ <p align="center">
175
+ <sub>MIT — 详见 <a href="./LICENSE">LICENSE</a></sub>
176
+ </p>
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveAppContextConfig = exports.DEFAULT_ITEM_KEYS = exports.DEFAULT_GATEWAY_SCOPES = exports.DEFAULT_WEB_ORIGIN = exports.DEFAULT_API_HOST = exports.DEFAULT_API_PORT = void 0;
4
- exports.DEFAULT_API_PORT = 3100;
4
+ exports.DEFAULT_API_PORT = 54320;
5
5
  exports.DEFAULT_API_HOST = "0.0.0.0";
6
6
  exports.DEFAULT_WEB_ORIGIN = "*";
7
7
  exports.DEFAULT_GATEWAY_SCOPES = ["operator.read", "operator.write", "operator.admin"];
@@ -35,8 +35,8 @@ const createAppContext = (options = {}) => {
35
35
  const ensureGatewayClient = () => {
36
36
  if (clientRef)
37
37
  return clientRef;
38
- // dev/server 进程允许先在“网关未配置/未就绪”状态下启动 HTTP/WS
39
- // 真正触发 connect/sendReq 时再校验凭据,才能兼容本地调试与需要网关的命令两类路径。
38
+ // dev/server processes allow HTTP/WS to start even before gateway is configured/ready,
39
+ // deferring credential validation to when connect/sendReq actually fires, to support both local debugging and gateway-required commands.
40
40
  const credentials = resolveGatewayCredentials(options, env);
41
41
  clientRef = (0, gateway_1.createGatewayClient)({
42
42
  gatewayUrl: credentials.url,
@@ -83,7 +83,7 @@ const createAppContext = (options = {}) => {
83
83
  });
84
84
  appRef = app;
85
85
  const appServices = (0, services_1.createAppServices)(app);
86
- // 网关握手成功后需要把 hello 回灌到 registry,保证运行态上下文完整。
86
+ // After the gateway handshake succeeds, feed the hello payload back into the registry so runtime context stays complete.
87
87
  const connect = async () => {
88
88
  const hello = await client.connect();
89
89
  app.onGatewayReady(hello);
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveTaskMeldDataPath = exports.getTaskMeldDataDir = exports.isTaskMeldTestRuntime = void 0;
3
+ exports.resolveTaskMeldDataPath = exports.getTaskMeldDataDir = exports.isTaskMeldDevRuntime = exports.isTaskMeldTestRuntime = void 0;
4
4
  const node_os_1 = require("node:os");
5
5
  const node_path_1 = require("node:path");
6
6
  const TEST_ARG_PATTERN = /(^|[\\/])(test|dist[\\/]test)[\\/]/;
7
+ // Detect tsx / ts-node etc. dev-mode run (e.g. `npx tsx src/index.ts`)
8
+ const DEV_ARG_PATTERN = /(^|[\\/])tsx[\\/]|(^|[\\/])(src[\\/]index\.ts)$/;
7
9
  const isTaskMeldTestRuntime = (options = {}) => {
8
10
  const env = options.env ?? process.env;
9
11
  const argv = options.argv ?? process.argv;
@@ -13,13 +15,21 @@ const isTaskMeldTestRuntime = (options = {}) => {
13
15
  return argv.some((arg) => TEST_ARG_PATTERN.test(arg));
14
16
  };
15
17
  exports.isTaskMeldTestRuntime = isTaskMeldTestRuntime;
18
+ const isTaskMeldDevRuntime = (options = {}) => {
19
+ const env = options.env ?? process.env;
20
+ const argv = options.argv ?? process.argv;
21
+ if (env.NODE_ENV === "development")
22
+ return true;
23
+ return argv.some((arg) => DEV_ARG_PATTERN.test(arg));
24
+ };
25
+ exports.isTaskMeldDevRuntime = isTaskMeldDevRuntime;
16
26
  const getTaskMeldDataDir = (options = {}) => {
17
27
  const env = options.env ?? process.env;
18
28
  const override = env.TASKMELD_DATA_DIR?.trim();
19
29
  if (override)
20
30
  return override;
21
- // 测试用例默认隔离到当前仓库,避免污染用户真实的 ~/.taskmeld 数据。
22
- if ((0, exports.isTaskMeldTestRuntime)({ env, argv: options.argv })) {
31
+ // Test/dev cases default to isolation in the current repo to avoid polluting the user's real ~/.taskmeld data.
32
+ if ((0, exports.isTaskMeldTestRuntime)({ env, argv: options.argv }) || (0, exports.isTaskMeldDevRuntime)({ env, argv: options.argv })) {
23
33
  return (0, node_path_1.join)(options.cwd ?? process.cwd(), ".data");
24
34
  }
25
35
  return (0, node_path_1.join)(options.homeDir ?? (0, node_os_1.homedir)(), ".taskmeld");
@@ -15,7 +15,7 @@ const createPipelineDefinition = (id, title) => {
15
15
  const baseDir = (0, node_path_1.join)(PIPELINE_ROOT_DIR, id);
16
16
  return {
17
17
  id,
18
- title: title?.trim() || `流水线 DAG-${id}`,
18
+ title: title?.trim() || `Pipeline ${id}`,
19
19
  workflowFilePath: (0, node_path_1.join)(baseDir, "workflow.json"),
20
20
  runStateFile: (0, node_path_1.join)(baseDir, "run-state.json"),
21
21
  artifactDir: (0, node_path_1.join)(baseDir, "artifacts"),
@@ -27,7 +27,7 @@ const createDefaultDefinitionsDocument = () => ({
27
27
  defaultPipelineId: DEFAULT_PIPELINE_ID_FALLBACK,
28
28
  items: DEFAULT_PIPELINE_IDS.map((pipelineId) => ({
29
29
  id: pipelineId,
30
- title: `流水线 DAG-${pipelineId}`,
30
+ title: `Pipeline ${pipelineId}`,
31
31
  })),
32
32
  });
33
33
  const isValidPipelineId = (value) => typeof value === "string" && PIPELINE_ID_PATTERN.test(value.trim());
@@ -43,7 +43,7 @@ const normalizeDefinitionItems = (items) => {
43
43
  if (!(0, exports.isValidPipelineId)(record.id))
44
44
  continue;
45
45
  const id = record.id.trim();
46
- const title = typeof record.title === "string" && record.title.trim() ? record.title.trim() : `流水线 DAG-${id}`;
46
+ const title = typeof record.title === "string" && record.title.trim() ? record.title.trim() : `Pipeline ${id}`;
47
47
  deduped.set(id, { id, title });
48
48
  }
49
49
  return [...deduped.values()];
@@ -82,7 +82,7 @@ exports.savePipelineDefinitions = savePipelineDefinitions;
82
82
  const ensurePipelineDefinitions = () => {
83
83
  const fromDisk = readDefinitionsDocumentFromDisk();
84
84
  if (fromDisk) {
85
- // 每次启动都按当前约束回写一遍,避免 defaultPipelineId 指向失效项或 title 为空。
85
+ // Rewrite on every startup according to current constraints, to prevent defaultPipelineId pointing to an invalid entry or title being empty.
86
86
  return (0, exports.savePipelineDefinitions)(fromDisk);
87
87
  }
88
88
  return (0, exports.savePipelineDefinitions)(createDefaultDefinitionsDocument());
@@ -30,7 +30,7 @@ const buildArchivedPipelineDirPath = (pipelineId) => {
30
30
  (0, node_fs_1.mkdirSync)(deletedRootDir, { recursive: true });
31
31
  let archiveDirPath = (0, node_path_1.join)(deletedRootDir, createArchivedPipelineDirName(pipelineId));
32
32
  let suffix = 1;
33
- // 归档目录需要保证唯一,避免同秒内重复删除时互相覆盖。
33
+ // Archive directory must be unique to avoid collisions when multiple deletes happen within the same second.
34
34
  while ((0, node_fs_1.existsSync)(archiveDirPath)) {
35
35
  archiveDirPath = (0, node_path_1.join)(deletedRootDir, `${createArchivedPipelineDirName(pipelineId)}-${suffix}`);
36
36
  suffix += 1;
@@ -184,7 +184,7 @@ const createPipelineRegistry = (options) => {
184
184
  });
185
185
  return;
186
186
  }
187
- // 网关状态/握手是全局共享事件,只透传主流水线的一份,避免前端收到重复广播。
187
+ // Gateway status/handshake is a globally shared event; only relay one copy from the primary pipeline to avoid duplicate broadcasts to frontends.
188
188
  if ((event.type === "gateway.status" || event.type === "gateway.ready" || event.type === "gateway.frame") &&
189
189
  definition.id !== getPrimaryPipelineId()) {
190
190
  return;
@@ -241,7 +241,7 @@ const createPipelineRegistry = (options) => {
241
241
  timelineHasMore: combined.length > MAX_BOOTSTRAP_TIMELINE,
242
242
  status: getPrimaryRuntime().gateway.getLatestStatus() ?? options.client.getStatus(),
243
243
  hello: getPrimaryRuntime().gateway.getLatestHello(),
244
- // 保留旧字段兜底,避免前端分阶段改造时直接失效。
244
+ // Keep the old top-level fields as a fallback to avoid breaking frontends during phased migration.
245
245
  run: primary?.run,
246
246
  pipeline: primary?.pipeline,
247
247
  runId: primary?.runId,
@@ -249,8 +249,8 @@ const createPipelineRegistry = (options) => {
249
249
  };
250
250
  };
251
251
  const broadcastBootstrapPayload = () => {
252
- // 流水线资源发生新增、删除、重命名时,直接广播完整 bootstrap
253
- // 这样已连接前端可以一次性拿到最新 definitions + 运行态快照,避免自己拼补丁遗漏字段。
252
+ // When pipeline resources are added, deleted, or renamed, broadcast a full bootstrap immediately;
253
+ // this way connected frontends get the latest definitions + runtime snapshot in one message, avoiding hand-rolled patch merging with missing fields.
254
254
  broadcast({
255
255
  type: "bootstrap",
256
256
  payload: getBootstrapPayload(),
@@ -285,7 +285,7 @@ const createPipelineRegistry = (options) => {
285
285
  };
286
286
  const initializePipelineWorkflowFile = (definition, cloneFrom) => {
287
287
  if (cloneFrom) {
288
- // 克隆只复制 workflow 定义,运行态和产物目录仍由新流水线独立初始化。
288
+ // Clone only copies the workflow definition; runtime state and artifact directories are independently initialized for the new pipeline.
289
289
  const sourceWorkflow = (0, template_1.loadWorkflowDefinitionWithStorage)({ workflowFilePath: cloneFrom.workflowFilePath });
290
290
  (0, template_1.saveWorkflowDefinitionWithStorage)(sourceWorkflow, { workflowFilePath: definition.workflowFilePath });
291
291
  return;
@@ -309,7 +309,7 @@ const createPipelineRegistry = (options) => {
309
309
  continue;
310
310
  await runtime.initialize();
311
311
  }
312
- // 启动时扫描所有流水线,对已有 pending job 的队列触发 drain
312
+ // On startup, scan all pipelines and trigger drain for any queue that already has pending jobs.
313
313
  for (const definition of pipelineDefinitions) {
314
314
  const pendingCount = inboundQueue.getPendingCount(definition.id);
315
315
  if (pendingCount > 0) {
@@ -356,8 +356,8 @@ const createPipelineRegistry = (options) => {
356
356
  persistDefinitions([...currentDocument.items, { id: definition.id, title: definition.title }], currentDocument.defaultPipelineId);
357
357
  let runtime = null;
358
358
  try {
359
- // 先把目标流水线的 workflow 文件初始化到位,再创建 runtime
360
- // 否则 runtime 构造阶段会先读到默认 workflow,导致“磁盘已克隆、内存仍是默认”的假克隆问题。
359
+ // Initialize the target pipeline's workflow file on disk first, then create the runtime;
360
+ // otherwise the runtime constructor would read the default workflow, causing a "disk already cloned, memory still default" phantom-clone issue.
361
361
  initializePipelineWorkflowFile(definition, cloneSourceDefinition ?? undefined);
362
362
  runtime = createRuntimeForDefinition(definition);
363
363
  bindRuntimeBroadcast(definition, runtime);
@@ -419,7 +419,7 @@ const createPipelineRegistry = (options) => {
419
419
  archivePipelineDirectory(pipelineDefinitions[definitionIndex]);
420
420
  }
421
421
  catch (error) {
422
- // 归档失败时立即回滚 definitions,避免页面列表先把流水线删掉但目录仍保持原位。
422
+ // If archiving fails, immediately rollback definitions so the page listing doesn't remove the pipeline while the directory stays in place.
423
423
  (0, pipeline_config_1.savePipelineDefinitions)(currentDocument);
424
424
  throw error;
425
425
  }
@@ -484,7 +484,7 @@ const createPipelineRegistry = (options) => {
484
484
  if (routed)
485
485
  return;
486
486
  }
487
- // 无法判定 sessionKey 或无匹配 runtime 时,投递全部
487
+ // When sessionKey can't be determined or no runtime matches, deliver to all
488
488
  for (const runtime of runtimeById.values()) {
489
489
  runtime.onGatewayRawFrame(rawFrame);
490
490
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createPipelineRuntime = void 0;
4
- const http_utils_1 = require("../server/http-utils");
5
4
  const template_1 = require("../pipeline/template");
6
5
  const execution_timeout_1 = require("../pipeline/execution-timeout");
7
6
  const runtime_model_1 = require("../pipeline/runtime-model");
@@ -45,7 +44,6 @@ const createPipelineRuntime = (options) => {
45
44
  getBatchRunId = () => schedulerService.getBatchRunState().batchRunId;
46
45
  schedulerStateAccessor = schedulerService.getSchedulerState;
47
46
  batchRunStateGetter = () => schedulerService.getBatchRunState();
48
- const sendJson = (res, code, data) => (0, http_utils_1.sendJson)(res, code, data, options.webOrigin);
49
47
  const setRun = (nextRun) => {
50
48
  runtimeStore.setRun(nextRun);
51
49
  graph.syncRunGroupsFromWorkflow(nextRun);
@@ -64,7 +62,7 @@ const createPipelineRuntime = (options) => {
64
62
  };
65
63
  const onGatewayStatus = (status) => {
66
64
  runtimeStore.setLatestStatus(status);
67
- runtimeStore.pushTimeline(`网关状态: ${status.status}`, status.status.includes("failed") ? "error" : "info");
65
+ runtimeStore.pushTimeline(`Gateway status: ${status.status}`, status.status.includes("failed") ? "error" : "info");
68
66
  runtimeStore.broadcast({
69
67
  type: "gateway.status",
70
68
  payload: status,
@@ -76,7 +74,7 @@ const createPipelineRuntime = (options) => {
76
74
  const isSilentEvent = isHealthEvent || isTickEvent;
77
75
  runtimeStore.setLastFrame(frame);
78
76
  if (frame.type === "event" && !isSilentEvent) {
79
- runtimeStore.pushTimeline(`事件: ${frame.event}`, "info", {
77
+ runtimeStore.pushTimeline(`Event: ${frame.event}`, "info", {
80
78
  type: "event",
81
79
  event: frame.event,
82
80
  seq: frame.seq ?? null,
@@ -84,8 +82,8 @@ const createPipelineRuntime = (options) => {
84
82
  payload: frame.payload ?? null,
85
83
  });
86
84
  }
87
- // health / tick 都是高频事件,只保留为 lastFrame,不推送到 timeline / ws,
88
- // 避免前端与日志被刷屏,同时不影响依赖最后一帧做诊断的调试能力。
85
+ // health / tick are high-frequency events; only store as lastFrame, don't push to timeline/WS,
86
+ // to avoid flooding the frontend and logs, while still allowing diagnostic debugging that depends on the last frame.
89
87
  if (!isSilentEvent) {
90
88
  runtimeStore.broadcast({
91
89
  type: "gateway.frame",
@@ -97,7 +95,7 @@ const createPipelineRuntime = (options) => {
97
95
  executionService.onGatewayFrame(rawFrame);
98
96
  };
99
97
  const onGatewayError = (error) => {
100
- runtimeStore.pushTimeline(`网关错误: ${String(error)}`, "error");
98
+ runtimeStore.pushTimeline(`Gateway error: ${String(error)}`, "error");
101
99
  runtimeStore.broadcast({
102
100
  type: "gateway.error",
103
101
  payload: { message: String(error) },
@@ -105,7 +103,7 @@ const createPipelineRuntime = (options) => {
105
103
  };
106
104
  const onGatewayReady = (hello) => {
107
105
  runtimeStore.setLatestHello(hello);
108
- runtimeStore.pushTimeline("网关握手完成");
106
+ runtimeStore.pushTimeline("Gateway handshake completed");
109
107
  runtimeStore.emitPipeline();
110
108
  runtimeStore.broadcast({
111
109
  type: "gateway.ready",
@@ -135,7 +133,6 @@ const createPipelineRuntime = (options) => {
135
133
  return {
136
134
  initialize,
137
135
  dispose: () => executionService.dispose(),
138
- sendJson,
139
136
  getBootstrapPayload,
140
137
  onGatewayStatus,
141
138
  onGatewayFrame,
@@ -31,7 +31,7 @@ const createRuntimeStore = (options) => {
31
31
  }
32
32
  catch (error) {
33
33
  // Persistence failures should not break pipeline execution.
34
- pushTimeline(`持久化运行状态失败: ${error instanceof Error ? error.message : String(error)}`, "warn");
34
+ pushTimeline(`Failed to persist run state: ${error instanceof Error ? error.message : String(error)}`, "warn");
35
35
  }
36
36
  })().finally(() => {
37
37
  persistRunStateInFlight = null;
@@ -69,7 +69,7 @@ const createRuntimeStore = (options) => {
69
69
  return savedRun;
70
70
  }
71
71
  catch (error) {
72
- pushTimeline(`加载持久化运行状态失败: ${error instanceof Error ? error.message : String(error)}`, "warn");
72
+ pushTimeline(`Failed to load persisted run state: ${error instanceof Error ? error.message : String(error)}`, "warn");
73
73
  return null;
74
74
  }
75
75
  };
@@ -107,7 +107,7 @@ const createRuntimeStore = (options) => {
107
107
  options.graph.syncRunGroupsFromWorkflow(run);
108
108
  (0, runtime_model_1.syncRunNodeStatusFromItemRuns)(run);
109
109
  (0, runtime_model_1.touchRun)(run);
110
- pushTimeline(`已恢复上次运行状态: ${run.id}`);
110
+ pushTimeline(`Restored previous run state: ${run.id}`);
111
111
  emitPipeline();
112
112
  };
113
113
  const bootstrapRun = () => {