@relayflows/sdk 2.0.9 → 2.0.10

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 (475) hide show
  1. package/dist/adapters/base.d.ts +46 -0
  2. package/dist/adapters/base.d.ts.map +1 -0
  3. package/dist/adapters/base.js +15 -0
  4. package/dist/adapters/base.js.map +1 -0
  5. package/dist/adapters/claude.d.ts +5 -0
  6. package/dist/adapters/claude.d.ts.map +1 -0
  7. package/dist/adapters/claude.js +44 -0
  8. package/dist/adapters/claude.js.map +1 -0
  9. package/dist/adapters/codex.d.ts +5 -0
  10. package/dist/adapters/codex.d.ts.map +1 -0
  11. package/dist/adapters/codex.js +45 -0
  12. package/dist/adapters/codex.js.map +1 -0
  13. package/dist/adapters/index.d.ts +14 -0
  14. package/dist/adapters/index.d.ts.map +1 -0
  15. package/dist/adapters/index.js +29 -0
  16. package/dist/adapters/index.js.map +1 -0
  17. package/dist/adapters/wrapper.d.ts +12 -0
  18. package/dist/adapters/wrapper.d.ts.map +1 -0
  19. package/dist/adapters/wrapper.js +31 -0
  20. package/dist/adapters/wrapper.js.map +1 -0
  21. package/dist/agent-relay-transport.d.ts +84 -0
  22. package/dist/agent-relay-transport.d.ts.map +1 -0
  23. package/dist/agent-relay-transport.js +134 -0
  24. package/dist/agent-relay-transport.js.map +1 -0
  25. package/dist/authored-budget.d.ts +13 -0
  26. package/dist/authored-budget.d.ts.map +1 -0
  27. package/dist/authored-budget.js +78 -0
  28. package/dist/authored-budget.js.map +1 -0
  29. package/dist/authored-flow-error.d.ts +2 -21
  30. package/dist/authored-flow-error.d.ts.map +1 -1
  31. package/dist/authored-flow-error.js +1 -12
  32. package/dist/authored-flow-error.js.map +1 -1
  33. package/dist/authored-flow-executor.d.ts +13 -7
  34. package/dist/authored-flow-executor.d.ts.map +1 -1
  35. package/dist/authored-flow-executor.js +116 -235
  36. package/dist/authored-flow-executor.js.map +1 -1
  37. package/dist/authored-flow-loader.d.ts +10 -1
  38. package/dist/authored-flow-loader.d.ts.map +1 -1
  39. package/dist/authored-flow-loader.js +56 -3
  40. package/dist/authored-flow-loader.js.map +1 -1
  41. package/dist/authored-flow-operation.d.ts +9 -1
  42. package/dist/authored-flow-operation.d.ts.map +1 -1
  43. package/dist/authored-flow-operation.js +35 -4
  44. package/dist/authored-flow-operation.js.map +1 -1
  45. package/dist/authored-helper-effect.d.ts +12 -0
  46. package/dist/authored-helper-effect.d.ts.map +1 -0
  47. package/dist/authored-helper-effect.js +144 -0
  48. package/dist/authored-helper-effect.js.map +1 -0
  49. package/dist/authored-mcp.d.ts +17 -0
  50. package/dist/authored-mcp.d.ts.map +1 -0
  51. package/dist/authored-mcp.js +171 -0
  52. package/dist/authored-mcp.js.map +1 -0
  53. package/dist/authored-memory.d.ts +7 -0
  54. package/dist/authored-memory.d.ts.map +1 -0
  55. package/dist/authored-memory.js +60 -0
  56. package/dist/authored-memory.js.map +1 -0
  57. package/dist/authored-plugin-effect.d.ts +16 -0
  58. package/dist/authored-plugin-effect.d.ts.map +1 -0
  59. package/dist/authored-plugin-effect.js +163 -0
  60. package/dist/authored-plugin-effect.js.map +1 -0
  61. package/dist/authored-step-output.d.ts +23 -0
  62. package/dist/authored-step-output.d.ts.map +1 -0
  63. package/dist/authored-step-output.js +67 -0
  64. package/dist/authored-step-output.js.map +1 -0
  65. package/dist/authored-worker-step.d.ts +15 -0
  66. package/dist/authored-worker-step.d.ts.map +1 -0
  67. package/dist/authored-worker-step.js +136 -0
  68. package/dist/authored-worker-step.js.map +1 -0
  69. package/dist/budget-preflight.d.ts +5 -0
  70. package/dist/budget-preflight.d.ts.map +1 -0
  71. package/dist/budget-preflight.js +33 -0
  72. package/dist/budget-preflight.js.map +1 -0
  73. package/dist/budget.d.ts +14 -0
  74. package/dist/budget.d.ts.map +1 -0
  75. package/dist/budget.js +52 -0
  76. package/dist/budget.js.map +1 -0
  77. package/dist/bundle-transport.d.ts +18 -0
  78. package/dist/bundle-transport.d.ts.map +1 -0
  79. package/dist/bundle-transport.js +111 -0
  80. package/dist/bundle-transport.js.map +1 -0
  81. package/dist/bundle-typescript.d.ts +8 -0
  82. package/dist/bundle-typescript.d.ts.map +1 -0
  83. package/dist/bundle-typescript.js +108 -0
  84. package/dist/bundle-typescript.js.map +1 -0
  85. package/dist/bundle.d.ts +25 -0
  86. package/dist/bundle.d.ts.map +1 -0
  87. package/dist/bundle.js +199 -0
  88. package/dist/bundle.js.map +1 -0
  89. package/dist/child-stop.d.ts +59 -0
  90. package/dist/child-stop.d.ts.map +1 -0
  91. package/dist/child-stop.js +94 -0
  92. package/dist/child-stop.js.map +1 -0
  93. package/dist/cli/add.d.ts +6 -0
  94. package/dist/cli/add.d.ts.map +1 -0
  95. package/dist/cli/add.js +47 -0
  96. package/dist/cli/add.js.map +1 -0
  97. package/dist/cli/build.d.ts +13 -0
  98. package/dist/cli/build.d.ts.map +1 -0
  99. package/dist/cli/build.js +224 -0
  100. package/dist/cli/build.js.map +1 -0
  101. package/dist/cli/bundle-preflight.d.ts +5 -0
  102. package/dist/cli/bundle-preflight.d.ts.map +1 -0
  103. package/dist/cli/bundle-preflight.js +28 -0
  104. package/dist/cli/bundle-preflight.js.map +1 -0
  105. package/dist/cli/check-helper-body.d.ts +4 -0
  106. package/dist/cli/check-helper-body.d.ts.map +1 -0
  107. package/dist/cli/check-helper-body.js +15 -0
  108. package/dist/cli/check-helper-body.js.map +1 -0
  109. package/dist/cli/check-triggers.d.ts +14 -0
  110. package/dist/cli/check-triggers.d.ts.map +1 -0
  111. package/dist/cli/check-triggers.js +37 -0
  112. package/dist/cli/check-triggers.js.map +1 -0
  113. package/dist/cli/check-typescript.d.ts +20 -0
  114. package/dist/cli/check-typescript.d.ts.map +1 -0
  115. package/dist/cli/check-typescript.js +54 -0
  116. package/dist/cli/check-typescript.js.map +1 -0
  117. package/dist/cli/check.d.ts +36 -3
  118. package/dist/cli/check.d.ts.map +1 -1
  119. package/dist/cli/check.js +122 -11
  120. package/dist/cli/check.js.map +1 -1
  121. package/dist/cli/cloud-run.d.ts +8 -0
  122. package/dist/cli/cloud-run.d.ts.map +1 -0
  123. package/dist/cli/cloud-run.js +50 -0
  124. package/dist/cli/cloud-run.js.map +1 -0
  125. package/dist/cli/deploy.d.ts +9 -0
  126. package/dist/cli/deploy.d.ts.map +1 -0
  127. package/dist/cli/deploy.js +58 -0
  128. package/dist/cli/deploy.js.map +1 -0
  129. package/dist/cli/deterministic-failure.d.ts +5 -0
  130. package/dist/cli/deterministic-failure.d.ts.map +1 -0
  131. package/dist/cli/deterministic-failure.js +66 -0
  132. package/dist/cli/deterministic-failure.js.map +1 -0
  133. package/dist/cli/direct-run.d.ts.map +1 -1
  134. package/dist/cli/direct-run.js +83 -18
  135. package/dist/cli/direct-run.js.map +1 -1
  136. package/dist/cli/hn-monitor.d.ts.map +1 -1
  137. package/dist/cli/hn-monitor.js +2 -1
  138. package/dist/cli/hn-monitor.js.map +1 -1
  139. package/dist/cli/replay.d.ts +12 -0
  140. package/dist/cli/replay.d.ts.map +1 -0
  141. package/dist/cli/replay.js +82 -0
  142. package/dist/cli/replay.js.map +1 -0
  143. package/dist/cli/reuse.d.ts +8 -0
  144. package/dist/cli/reuse.d.ts.map +1 -0
  145. package/dist/cli/reuse.js +25 -0
  146. package/dist/cli/reuse.js.map +1 -0
  147. package/dist/cli/run-digest.d.ts +4 -0
  148. package/dist/cli/run-digest.d.ts.map +1 -0
  149. package/dist/cli/run-digest.js +24 -0
  150. package/dist/cli/run-digest.js.map +1 -0
  151. package/dist/cli/run.d.ts +16 -5
  152. package/dist/cli/run.d.ts.map +1 -1
  153. package/dist/cli/run.js +99 -17
  154. package/dist/cli/run.js.map +1 -1
  155. package/dist/cli/serve-webhook.d.ts +40 -0
  156. package/dist/cli/serve-webhook.d.ts.map +1 -0
  157. package/dist/cli/serve-webhook.js +233 -0
  158. package/dist/cli/serve-webhook.js.map +1 -0
  159. package/dist/cli/tick-runner.d.ts.map +1 -1
  160. package/dist/cli/tick-runner.js +2 -1
  161. package/dist/cli/tick-runner.js.map +1 -1
  162. package/dist/cli-adapter.d.ts +8 -14
  163. package/dist/cli-adapter.d.ts.map +1 -1
  164. package/dist/cli-adapter.js +19 -64
  165. package/dist/cli-adapter.js.map +1 -1
  166. package/dist/cli-watch.d.ts +13 -0
  167. package/dist/cli-watch.d.ts.map +1 -0
  168. package/dist/cli-watch.js +185 -0
  169. package/dist/cli-watch.js.map +1 -0
  170. package/dist/cli.d.ts +4 -0
  171. package/dist/cli.d.ts.map +1 -1
  172. package/dist/cli.js +387 -14
  173. package/dist/cli.js.map +1 -1
  174. package/dist/cloud-http.d.ts +22 -0
  175. package/dist/cloud-http.d.ts.map +1 -0
  176. package/dist/cloud-http.js +84 -0
  177. package/dist/cloud-http.js.map +1 -0
  178. package/dist/cloud-run.d.ts +38 -0
  179. package/dist/cloud-run.d.ts.map +1 -0
  180. package/dist/cloud-run.js +112 -0
  181. package/dist/cloud-run.js.map +1 -0
  182. package/dist/compile.d.ts +21 -3
  183. package/dist/compile.d.ts.map +1 -1
  184. package/dist/compile.js +97 -23
  185. package/dist/compile.js.map +1 -1
  186. package/dist/create-flow.d.ts +17 -0
  187. package/dist/create-flow.d.ts.map +1 -0
  188. package/dist/create-flow.js +72 -0
  189. package/dist/create-flow.js.map +1 -0
  190. package/dist/daemon-connection.d.ts +14 -0
  191. package/dist/daemon-connection.d.ts.map +1 -1
  192. package/dist/daemon-connection.js +33 -1
  193. package/dist/daemon-connection.js.map +1 -1
  194. package/dist/demo-hn-monitor.js +2 -1
  195. package/dist/demo-hn-monitor.js.map +1 -1
  196. package/dist/effect-channel.d.ts +30 -0
  197. package/dist/effect-channel.d.ts.map +1 -0
  198. package/dist/effect-channel.js +71 -0
  199. package/dist/effect-channel.js.map +1 -0
  200. package/dist/failure-kinds.d.ts +14 -3
  201. package/dist/failure-kinds.d.ts.map +1 -1
  202. package/dist/failure-kinds.js +27 -0
  203. package/dist/failure-kinds.js.map +1 -1
  204. package/dist/gate-contract.d.ts.map +1 -1
  205. package/dist/gate-contract.js +9 -0
  206. package/dist/gate-contract.js.map +1 -1
  207. package/dist/helper-preflight.d.ts +18 -0
  208. package/dist/helper-preflight.d.ts.map +1 -0
  209. package/dist/helper-preflight.js +32 -0
  210. package/dist/helper-preflight.js.map +1 -0
  211. package/dist/helper-storage.d.ts +5 -0
  212. package/dist/helper-storage.d.ts.map +1 -0
  213. package/dist/helper-storage.js +31 -0
  214. package/dist/helper-storage.js.map +1 -0
  215. package/dist/helper-writeback.d.ts +12 -0
  216. package/dist/helper-writeback.d.ts.map +1 -0
  217. package/dist/helper-writeback.js +95 -0
  218. package/dist/helper-writeback.js.map +1 -0
  219. package/dist/index.d.ts +10 -2
  220. package/dist/index.d.ts.map +1 -1
  221. package/dist/index.js +7 -1
  222. package/dist/index.js.map +1 -1
  223. package/dist/input-binding.d.ts +4 -0
  224. package/dist/input-binding.d.ts.map +1 -0
  225. package/dist/input-binding.js +78 -0
  226. package/dist/input-binding.js.map +1 -0
  227. package/dist/journal-client.d.ts +12 -4
  228. package/dist/journal-client.d.ts.map +1 -1
  229. package/dist/journal-client.js +17 -6
  230. package/dist/journal-client.js.map +1 -1
  231. package/dist/journal-offset.d.ts +3 -0
  232. package/dist/journal-offset.d.ts.map +1 -0
  233. package/dist/journal-offset.js +69 -0
  234. package/dist/journal-offset.js.map +1 -0
  235. package/dist/journal-reader.d.ts +30 -0
  236. package/dist/journal-reader.d.ts.map +1 -0
  237. package/dist/journal-reader.js +156 -0
  238. package/dist/journal-reader.js.map +1 -0
  239. package/dist/json-schema.d.ts +2 -0
  240. package/dist/json-schema.d.ts.map +1 -1
  241. package/dist/json-schema.js +38 -22
  242. package/dist/json-schema.js.map +1 -1
  243. package/dist/llm-worker.d.ts +16 -0
  244. package/dist/llm-worker.d.ts.map +1 -0
  245. package/dist/llm-worker.js +79 -0
  246. package/dist/llm-worker.js.map +1 -0
  247. package/dist/local-agent.d.ts +8 -0
  248. package/dist/local-agent.d.ts.map +1 -0
  249. package/dist/local-agent.js +25 -0
  250. package/dist/local-agent.js.map +1 -0
  251. package/dist/mcp-client.d.ts +15 -0
  252. package/dist/mcp-client.d.ts.map +1 -0
  253. package/dist/mcp-client.js +104 -0
  254. package/dist/mcp-client.js.map +1 -0
  255. package/dist/mcp-config.d.ts +4 -0
  256. package/dist/mcp-config.d.ts.map +1 -0
  257. package/dist/mcp-config.js +61 -0
  258. package/dist/mcp-config.js.map +1 -0
  259. package/dist/mcp-stdio.d.ts +24 -0
  260. package/dist/mcp-stdio.d.ts.map +1 -0
  261. package/dist/mcp-stdio.js +89 -0
  262. package/dist/mcp-stdio.js.map +1 -0
  263. package/dist/model-pricing.d.ts +22 -0
  264. package/dist/model-pricing.d.ts.map +1 -0
  265. package/dist/model-pricing.js +30 -0
  266. package/dist/model-pricing.js.map +1 -0
  267. package/dist/mount-registry.d.ts +9 -0
  268. package/dist/mount-registry.d.ts.map +1 -0
  269. package/dist/mount-registry.js +47 -0
  270. package/dist/mount-registry.js.map +1 -0
  271. package/dist/named-gate-lowering.d.ts +9 -0
  272. package/dist/named-gate-lowering.d.ts.map +1 -0
  273. package/dist/named-gate-lowering.js +152 -0
  274. package/dist/named-gate-lowering.js.map +1 -0
  275. package/dist/named-gates.d.ts +10 -0
  276. package/dist/named-gates.d.ts.map +1 -0
  277. package/dist/named-gates.js +69 -0
  278. package/dist/named-gates.js.map +1 -0
  279. package/dist/observer-link.d.ts +129 -0
  280. package/dist/observer-link.d.ts.map +1 -0
  281. package/dist/observer-link.js +274 -0
  282. package/dist/observer-link.js.map +1 -0
  283. package/dist/plugin-loader.d.ts +13 -0
  284. package/dist/plugin-loader.d.ts.map +1 -0
  285. package/dist/plugin-loader.js +110 -0
  286. package/dist/plugin-loader.js.map +1 -0
  287. package/dist/plugin-manifest.d.ts +27 -0
  288. package/dist/plugin-manifest.d.ts.map +1 -0
  289. package/dist/plugin-manifest.js +89 -0
  290. package/dist/plugin-manifest.js.map +1 -0
  291. package/dist/preflight.d.ts +26 -2
  292. package/dist/preflight.d.ts.map +1 -1
  293. package/dist/preflight.js +175 -1
  294. package/dist/preflight.js.map +1 -1
  295. package/dist/progress.d.ts +14 -0
  296. package/dist/progress.d.ts.map +1 -0
  297. package/dist/progress.js +39 -0
  298. package/dist/progress.js.map +1 -0
  299. package/dist/protocol.d.ts +11 -0
  300. package/dist/protocol.d.ts.map +1 -1
  301. package/dist/pty-sidechannel.d.ts +14 -0
  302. package/dist/pty-sidechannel.d.ts.map +1 -0
  303. package/dist/pty-sidechannel.js +103 -0
  304. package/dist/pty-sidechannel.js.map +1 -0
  305. package/dist/scope-compiler.d.ts +52 -0
  306. package/dist/scope-compiler.d.ts.map +1 -0
  307. package/dist/scope-compiler.js +62 -0
  308. package/dist/scope-compiler.js.map +1 -0
  309. package/dist/slack-preflight.d.ts +6 -0
  310. package/dist/slack-preflight.d.ts.map +1 -0
  311. package/dist/slack-preflight.js +28 -0
  312. package/dist/slack-preflight.js.map +1 -0
  313. package/dist/slack-writeback.d.ts +40 -0
  314. package/dist/slack-writeback.d.ts.map +1 -0
  315. package/dist/slack-writeback.js +60 -0
  316. package/dist/slack-writeback.js.map +1 -0
  317. package/dist/spec.d.ts +134 -4
  318. package/dist/spec.d.ts.map +1 -1
  319. package/dist/spec.js.map +1 -1
  320. package/dist/step-dependencies.d.ts.map +1 -1
  321. package/dist/step-dependencies.js +2 -1
  322. package/dist/step-dependencies.js.map +1 -1
  323. package/dist/step-fields.d.ts +4 -4
  324. package/dist/step-fields.d.ts.map +1 -1
  325. package/dist/step-fields.js +5 -2
  326. package/dist/step-fields.js.map +1 -1
  327. package/dist/trigger-executor.d.ts +7 -0
  328. package/dist/trigger-executor.d.ts.map +1 -0
  329. package/dist/trigger-executor.js +35 -0
  330. package/dist/trigger-executor.js.map +1 -0
  331. package/dist/validate.d.ts.map +1 -1
  332. package/dist/validate.js +53 -6
  333. package/dist/validate.js.map +1 -1
  334. package/dist/webhook-rate-limit.d.ts +29 -0
  335. package/dist/webhook-rate-limit.d.ts.map +1 -0
  336. package/dist/webhook-rate-limit.js +38 -0
  337. package/dist/webhook-rate-limit.js.map +1 -0
  338. package/dist/webhook-signature.d.ts +22 -0
  339. package/dist/webhook-signature.d.ts.map +1 -0
  340. package/dist/webhook-signature.js +53 -0
  341. package/dist/webhook-signature.js.map +1 -0
  342. package/dist/worker-cli.d.ts +13 -1
  343. package/dist/worker-cli.d.ts.map +1 -1
  344. package/dist/worker-cli.js +133 -12
  345. package/dist/worker-cli.js.map +1 -1
  346. package/dist/worker-input.d.ts +4 -0
  347. package/dist/worker-input.d.ts.map +1 -0
  348. package/dist/worker-input.js +14 -0
  349. package/dist/worker-input.js.map +1 -0
  350. package/dist/worker-lease.d.ts +5 -0
  351. package/dist/worker-lease.d.ts.map +1 -0
  352. package/dist/worker-lease.js +80 -0
  353. package/dist/worker-lease.js.map +1 -0
  354. package/dist/worker-spend.d.ts +19 -0
  355. package/dist/worker-spend.d.ts.map +1 -0
  356. package/dist/worker-spend.js +22 -0
  357. package/dist/worker-spend.js.map +1 -0
  358. package/dist/worker-usage.d.ts +7 -0
  359. package/dist/worker-usage.d.ts.map +1 -0
  360. package/dist/worker-usage.js +54 -0
  361. package/dist/worker-usage.js.map +1 -0
  362. package/dist/worker.d.ts +2 -0
  363. package/dist/worker.d.ts.map +1 -1
  364. package/dist/worker.js +22 -8
  365. package/dist/worker.js.map +1 -1
  366. package/dist/wrapper-session.d.ts +1 -1
  367. package/dist/wrapper-session.d.ts.map +1 -1
  368. package/dist/wrapper-session.js +56 -19
  369. package/dist/wrapper-session.js.map +1 -1
  370. package/dist/yaml-helper-effect.d.ts +8 -0
  371. package/dist/yaml-helper-effect.d.ts.map +1 -0
  372. package/dist/yaml-helper-effect.js +79 -0
  373. package/dist/yaml-helper-effect.js.map +1 -0
  374. package/dist/yaml-helpers.d.ts +18 -0
  375. package/dist/yaml-helpers.d.ts.map +1 -0
  376. package/dist/yaml-helpers.js +116 -0
  377. package/dist/yaml-helpers.js.map +1 -0
  378. package/package.json +19 -2
  379. package/src/adapters/base.ts +53 -0
  380. package/src/adapters/claude.ts +55 -0
  381. package/src/adapters/codex.ts +56 -0
  382. package/src/adapters/index.ts +36 -0
  383. package/src/adapters/wrapper.ts +42 -0
  384. package/src/agent-relay-transport.ts +187 -0
  385. package/src/authored-budget.ts +69 -0
  386. package/src/authored-flow-error.ts +10 -10
  387. package/src/authored-flow-executor.ts +171 -347
  388. package/src/authored-flow-loader.ts +60 -3
  389. package/src/authored-flow-operation.ts +38 -5
  390. package/src/authored-helper-effect.ts +143 -0
  391. package/src/authored-mcp.ts +166 -0
  392. package/src/authored-memory.ts +55 -0
  393. package/src/authored-plugin-effect.ts +152 -0
  394. package/src/authored-step-output.ts +107 -0
  395. package/src/authored-worker-step.ts +181 -0
  396. package/src/budget-preflight.ts +31 -0
  397. package/src/budget.ts +49 -0
  398. package/src/bundle-transport.ts +79 -0
  399. package/src/bundle-typescript.ts +98 -0
  400. package/src/bundle.ts +182 -0
  401. package/src/child-stop.ts +138 -0
  402. package/src/cli/add.ts +45 -0
  403. package/src/cli/build.ts +214 -0
  404. package/src/cli/bundle-preflight.ts +28 -0
  405. package/src/cli/check-helper-body.ts +14 -0
  406. package/src/cli/check-triggers.ts +41 -0
  407. package/src/cli/check-typescript.ts +63 -0
  408. package/src/cli/check.ts +132 -14
  409. package/src/cli/cloud-run.ts +47 -0
  410. package/src/cli/deploy.ts +52 -0
  411. package/src/cli/deterministic-failure.ts +69 -0
  412. package/src/cli/direct-run.ts +79 -18
  413. package/src/cli/hn-monitor.ts +2 -1
  414. package/src/cli/replay.ts +81 -0
  415. package/src/cli/reuse.ts +23 -0
  416. package/src/cli/run-digest.ts +24 -0
  417. package/src/cli/run.ts +104 -21
  418. package/src/cli/serve-webhook.ts +246 -0
  419. package/src/cli/tick-runner.ts +2 -1
  420. package/src/cli-adapter.ts +31 -74
  421. package/src/cli-watch.ts +174 -0
  422. package/src/cli.ts +396 -17
  423. package/src/cloud-http.ts +100 -0
  424. package/src/cloud-run.ts +137 -0
  425. package/src/compile.ts +102 -23
  426. package/src/create-flow.ts +82 -0
  427. package/src/daemon-connection.ts +32 -1
  428. package/src/demo-hn-monitor.ts +2 -1
  429. package/src/effect-channel.ts +97 -0
  430. package/src/failure-kinds.ts +38 -0
  431. package/src/gate-contract.ts +7 -0
  432. package/src/helper-preflight.ts +34 -0
  433. package/src/helper-storage.ts +23 -0
  434. package/src/helper-writeback.ts +83 -0
  435. package/src/index.ts +26 -0
  436. package/src/input-binding.ts +76 -0
  437. package/src/journal-client.ts +20 -7
  438. package/src/journal-offset.ts +60 -0
  439. package/src/journal-reader.ts +165 -0
  440. package/src/json-schema.ts +34 -18
  441. package/src/llm-worker.ts +82 -0
  442. package/src/local-agent.ts +30 -0
  443. package/src/mcp-client.ts +97 -0
  444. package/src/mcp-config.ts +42 -0
  445. package/src/mcp-stdio.ts +84 -0
  446. package/src/model-pricing.ts +32 -0
  447. package/src/mount-registry.ts +44 -0
  448. package/src/named-gate-lowering.ts +147 -0
  449. package/src/named-gates.ts +76 -0
  450. package/src/observer-link.ts +368 -0
  451. package/src/plugin-loader.ts +88 -0
  452. package/src/plugin-manifest.ts +76 -0
  453. package/src/preflight.ts +178 -4
  454. package/src/progress.ts +51 -0
  455. package/src/protocol.ts +12 -0
  456. package/src/pty-sidechannel.ts +92 -0
  457. package/src/scope-compiler.ts +105 -0
  458. package/src/slack-preflight.ts +25 -0
  459. package/src/slack-writeback.ts +63 -0
  460. package/src/spec.ts +112 -4
  461. package/src/step-dependencies.ts +3 -1
  462. package/src/step-fields.ts +5 -2
  463. package/src/trigger-executor.ts +34 -0
  464. package/src/validate.ts +47 -6
  465. package/src/webhook-rate-limit.ts +69 -0
  466. package/src/webhook-signature.ts +74 -0
  467. package/src/worker-cli.ts +156 -11
  468. package/src/worker-input.ts +15 -0
  469. package/src/worker-lease.ts +85 -0
  470. package/src/worker-spend.ts +23 -0
  471. package/src/worker-usage.ts +48 -0
  472. package/src/worker.ts +27 -9
  473. package/src/wrapper-session.ts +50 -15
  474. package/src/yaml-helper-effect.ts +78 -0
  475. package/src/yaml-helpers.ts +131 -0
@@ -0,0 +1,246 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdir, open, rename, unlink, lstat } from 'node:fs/promises';
3
+ import { createServer, type Server, type ServerResponse } from 'node:http';
4
+ import { join, resolve } from 'node:path';
5
+ import { TextDecoder } from 'node:util';
6
+ import type { CliIo } from '../cli.js';
7
+ import { providerInboxEvent } from '../trigger-executor.js';
8
+ import { verifySignature, schemeFor } from '../webhook-signature.js';
9
+ import { TokenBucketLimiter, keyFor, type RateLimitConfig } from '../webhook-rate-limit.js';
10
+
11
+ const MAX_BODY_BYTES = 1024 * 1024;
12
+ const NAME = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
13
+
14
+ const DEFAULT_RATE_LIMIT: RateLimitConfig = { ratePerSecond: 20, burst: 60 };
15
+
16
+ export function parseWebhookArgs(args: readonly string[]): {
17
+ command: 'serve-webhook'; dataDir: string; port: number; admitted?: readonly string[];
18
+ } | undefined {
19
+ const values = new Map<string, string>();
20
+ for (let i = 0; i < args.length; i += 2) {
21
+ const flag = args[i]!;
22
+ const value = args[i + 1];
23
+ if (!['--data-dir', '--port', '--allow'].includes(flag) || values.has(flag)
24
+ || !value || value.startsWith('-')) return undefined;
25
+ values.set(flag, value);
26
+ }
27
+ const dataDir = values.get('--data-dir');
28
+ const portText = values.get('--port');
29
+ if (!dataDir || !portText || !/^\d+$/.test(portText)) return undefined;
30
+ const port = Number(portText);
31
+ if (!Number.isInteger(port) || port < 0 || port > 65535) return undefined;
32
+ const allow = values.get('--allow');
33
+ // --allow is comma-separated. Empty list is not admitted (would refuse everything);
34
+ // treat as parse failure so the author sees the typo rather than a mute receiver.
35
+ const admitted = allow === undefined ? undefined
36
+ : allow.split(',').map(entry => entry.trim());
37
+ if (admitted !== undefined && (admitted.length === 0 || admitted.some(entry => !NAME.test(entry)))) {
38
+ return undefined;
39
+ }
40
+ return admitted === undefined
41
+ ? { command: 'serve-webhook', dataDir, port }
42
+ : { command: 'serve-webhook', dataDir, port, admitted };
43
+ }
44
+
45
+ function reply(response: ServerResponse, status: number, body: object): void {
46
+ response.writeHead(status, { 'content-type': 'application/json' });
47
+ response.end(JSON.stringify(body));
48
+ }
49
+
50
+ /**
51
+ * Look up the shared secret for a provider from the process environment.
52
+ *
53
+ * `WEBHOOK_SECRET_<PROVIDER_UPPER>` (e.g. `WEBHOOK_SECRET_GITHUB`) is the
54
+ * canonical env var. When unset, the receiver runs unsigned — this is
55
+ * documented in #301 as the local-development posture.
56
+ */
57
+ export function providerSecret(provider: string | undefined, env: NodeJS.ProcessEnv = process.env): string | undefined {
58
+ if (!provider) return env.WEBHOOK_SECRET_DEFAULT;
59
+ const upper = provider.toUpperCase().replace(/[^A-Z0-9]/g, '_');
60
+ return env[`WEBHOOK_SECRET_${upper}`];
61
+ }
62
+
63
+ export interface WebhookServerOptions {
64
+ /**
65
+ * When set, unknown names refuse with `webhook_flow_unknown`. Closes the
66
+ * "any-name" ingress opened by slice E (#333) so the receiver only accepts
67
+ * inbox writes for triggers whose flows have been explicitly loaded — the
68
+ * loaded-flow admission half of #303. Durable handler execution against
69
+ * the journal is deferred; per #303 that half depends on kernel authored-
70
+ * handler registration + resume protocol.
71
+ */
72
+ admittedNames?: ReadonlySet<string>;
73
+ /** Optional rate limit config; defaults to 20/s with 60 burst per key. */
74
+ rateLimit?: RateLimitConfig;
75
+ /** Injectable env lookup for tests. */
76
+ env?: NodeJS.ProcessEnv;
77
+ }
78
+
79
+ /** POST /<name> accepts JSON; /providers/<provider> accepts typed event envelopes. */
80
+ export async function startWebhookServer(
81
+ dataDir: string,
82
+ port: number,
83
+ options: WebhookServerOptions = {},
84
+ ): Promise<Server> {
85
+ const inbox = join(resolve(dataDir), 'inbox');
86
+ await directory(inbox);
87
+ const admitted = options.admittedNames;
88
+ const env = options.env ?? process.env;
89
+ const limiter = new TokenBucketLimiter(options.rateLimit ?? DEFAULT_RATE_LIMIT);
90
+ // TODO https://github.com/AgentWorkforce/flows/issues/301: provider signatures,
91
+ // public ingress and Cloud mount provisioning belong to the deployment slice.
92
+ const server = createServer(async (request, response) => {
93
+ if (request.method !== 'POST') {
94
+ request.resume();
95
+ response.setHeader('allow', 'POST');
96
+ reply(response, 405, { error: 'method_not_allowed' });
97
+ return;
98
+ }
99
+ const providerRoute = request.url?.startsWith('/providers/') ?? false;
100
+ const name = request.url?.slice(providerRoute ? '/providers/'.length : 1);
101
+ if (!name || !NAME.test(name)) {
102
+ request.resume();
103
+ reply(response, 404, { error: 'invalid_webhook_name' });
104
+ return;
105
+ }
106
+ // Loaded-flow admission (#303) runs FIRST: refuse unknown names before
107
+ // spending any body-read or rate-limit budget. A rogue POST can't
108
+ // accumulate events in an inbox the daemon will never drain, and can't
109
+ // steal from the honest ratelimit token bucket.
110
+ if (admitted !== undefined && !admitted.has(name)) {
111
+ request.resume();
112
+ reply(response, 404, { error: 'webhook_flow_unknown', name });
113
+ return;
114
+ }
115
+ // #304 rate limiting — key by provider+name+source so an abusive caller
116
+ // cannot starve every route. Loopback callers share 127.0.0.1 as the
117
+ // source key by design (single-process, single-tenant).
118
+ const sourceAddress = request.socket.remoteAddress ?? '127.0.0.1';
119
+ const provider = providerRoute ? name : undefined;
120
+ const rateKey = keyFor(provider, name, sourceAddress);
121
+ const decision = limiter.consume(rateKey);
122
+ if (!decision.allowed) {
123
+ request.resume();
124
+ const retryAfterSeconds = Math.max(1, Math.ceil(decision.retryAfterMs / 1000));
125
+ response.setHeader('retry-after', String(retryAfterSeconds));
126
+ reply(response, 429, { error: 'webhook_rate_limited', retryAfterMs: decision.retryAfterMs });
127
+ return;
128
+ }
129
+ let temporary: string | undefined;
130
+ try {
131
+ let bytes = 0;
132
+ const chunks: Buffer[] = [];
133
+ for await (const chunk of request) {
134
+ const buffer = Buffer.from(chunk as Uint8Array);
135
+ bytes += buffer.length;
136
+ if (bytes > MAX_BODY_BYTES) {
137
+ reply(response, 413, { error: 'payload_too_large' });
138
+ return;
139
+ }
140
+ chunks.push(buffer);
141
+ }
142
+ const rawBody = Buffer.concat(chunks);
143
+ // #304 signature verification — only enforced when a secret is
144
+ // configured for this provider/name. Absent secret = development mode.
145
+ const secret = providerSecret(provider, env);
146
+ if (secret) {
147
+ const scheme = schemeFor(provider);
148
+ const headerValue = request.headers[scheme.header];
149
+ const suppliedHeader = Array.isArray(headerValue) ? headerValue[0] : headerValue;
150
+ const verdict = verifySignature(provider, rawBody, suppliedHeader, secret);
151
+ if (!verdict.ok) {
152
+ reply(response, 401, { error: 'webhook_signature_invalid', reason: verdict.reason });
153
+ return;
154
+ }
155
+ }
156
+ let payload: unknown;
157
+ try {
158
+ payload = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(rawBody), (_key, value: unknown) => {
159
+ if (typeof value === 'number' && !Number.isFinite(value)) throw new Error('non-finite JSON number');
160
+ return value;
161
+ });
162
+ } catch {
163
+ reply(response, 400, { error: 'invalid_json' });
164
+ return;
165
+ }
166
+ if (providerRoute) {
167
+ try {
168
+ payload = providerInboxEvent(name, payload);
169
+ } catch (error) {
170
+ // #304 payload-shape rejection: providers declare a closed event
171
+ // vocabulary; anything else is `webhook_payload_invalid`.
172
+ reply(response, 400, { error: 'webhook_payload_invalid',
173
+ message: error instanceof Error ? error.message : 'invalid provider event' });
174
+ return;
175
+ }
176
+ }
177
+ const target = join(inbox, name);
178
+ await directory(target);
179
+ const id = randomUUID();
180
+ temporary = join(target, `.${id}.tmp`);
181
+ const file = await open(temporary, 'wx', 0o600);
182
+ try {
183
+ await file.writeFile(JSON.stringify(payload));
184
+ await file.sync();
185
+ } finally {
186
+ await file.close();
187
+ }
188
+ await rename(temporary, join(target, `${id}.json`));
189
+ temporary = undefined;
190
+ const dir = await open(target, 'r');
191
+ try { await dir.sync(); } finally { await dir.close(); }
192
+ reply(response, 202, { accepted: true, id });
193
+ } catch {
194
+ if (temporary) await unlink(temporary).catch(() => undefined);
195
+ if (!response.headersSent) reply(response, 500, { error: 'inbox_write_failed' });
196
+ else response.destroy();
197
+ }
198
+ });
199
+ server.requestTimeout = 30_000;
200
+ server.headersTimeout = 10_000;
201
+ await new Promise<void>((accept, reject) => {
202
+ server.once('error', reject);
203
+ server.listen(port, '127.0.0.1', () => {
204
+ server.off('error', reject);
205
+ accept();
206
+ });
207
+ });
208
+ return server;
209
+ }
210
+
211
+ async function directory(path: string): Promise<void> {
212
+ await mkdir(path, { recursive: true });
213
+ if (!(await lstat(path)).isDirectory()) throw new Error('inbox directory must not be a symlink');
214
+ }
215
+
216
+ export async function runServeWebhook(
217
+ options: { dataDir: string; port: number; admitted?: readonly string[] }, io: CliIo,
218
+ ): Promise<0 | 1> {
219
+ try {
220
+ const admittedNames = options.admitted === undefined ? undefined : new Set(options.admitted);
221
+ const server = await startWebhookServer(options.dataDir, options.port, { admittedNames });
222
+ const address = server.address();
223
+ const port = typeof address === 'object' && address ? address.port : options.port;
224
+ io.stdout(`WEBHOOK http://127.0.0.1:${port}`);
225
+ if (admittedNames !== undefined) {
226
+ io.stdout(`ADMITTED ${[...admittedNames].sort().join(',')}`);
227
+ }
228
+ await new Promise<void>((accept, reject) => {
229
+ const stop = (): void => {
230
+ server.close(error => error ? reject(error) : accept());
231
+ server.closeAllConnections();
232
+ };
233
+ server.once('error', reject);
234
+ process.once('SIGINT', stop);
235
+ process.once('SIGTERM', stop);
236
+ server.once('close', () => {
237
+ process.off('SIGINT', stop);
238
+ process.off('SIGTERM', stop);
239
+ });
240
+ });
241
+ return 0;
242
+ } catch (error) {
243
+ io.stderr(`FAILED [webhook_server] ${error instanceof Error ? error.message : 'receiver failed'}`);
244
+ return 1;
245
+ }
246
+ }
@@ -37,6 +37,7 @@
37
37
 
38
38
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
39
39
  import { dirname, isAbsolute, join, resolve } from 'node:path';
40
+ import { socketPathFor } from '../daemon-connection.js';
40
41
  import { JournalClient } from '../journal-client.js';
41
42
  import {
42
43
  DEFAULT_MAX_CATCH_UP,
@@ -269,7 +270,7 @@ export async function runTickRunner(args: TickRunnerArgs, io: CliIo): Promise<nu
269
270
  const resuming = state.lastEmittedSlot !== undefined;
270
271
  const cursor: TickCursor = resuming ? { lastEmittedSlot: state.lastEmittedSlot } : {};
271
272
 
272
- const socketPath = join(args.dataDir, 'relayflowd.sock');
273
+ const socketPath = socketPathFor(args.dataDir);
273
274
  let client: TickRunnerClient;
274
275
  try {
275
276
  client = args.connectClient
@@ -1,50 +1,40 @@
1
- import { basename } from 'node:path';
2
-
3
- export type CliAdapterKind = 'claude' | 'codex' | 'relayflows-wrapper-v1';
4
-
5
- export interface CliInvocation {
6
- args: string[];
7
- timeoutMs: number;
8
- /** Set only for wrapper readiness probes; raw providers receive a model flag. */
9
- modelEnv?: string;
10
- }
1
+ /**
2
+ * Legacy dispatch surface — retained so existing callers (`worker-cli.ts`,
3
+ * `cli/check.ts`, real-cli-adapters tests) compile unchanged. The per-CLI
4
+ * knowledge now lives in `./adapters/{claude,codex,wrapper}.ts` behind the
5
+ * `HeadlessAdapter` interface (flows#141). New CLIs implement the interface
6
+ * and register in `./adapters/index.ts`; this file no longer needs edits.
7
+ */
8
+ import {
9
+ resolveAdapterKind,
10
+ registeredAdapters,
11
+ } from './adapters/index.js';
12
+ import type { CliAdapterKind } from './adapters/index.js';
13
+ import type { CliInvocation, CliAdapterIdentification } from './adapters/base.js';
11
14
 
12
- export interface CliAdapterIdentification {
13
- invocation: CliInvocation;
14
- expectedStdout?: string;
15
- }
15
+ export type { CliAdapterKind } from './adapters/index.js';
16
+ export type { CliInvocation, CliAdapterIdentification, HeadlessAdapter } from './adapters/base.js';
16
17
 
17
- export const WRAPPER_IDENTIFY_ARG = '--relayflows-adapter-v1';
18
- export const WRAPPER_IDENTIFY_TOKEN = 'relayflows-agent-cli-v1';
19
- export const WRAPPER_EXECUTE_TOKEN = 'relayflows-agent-cli-v1-execute';
18
+ export {
19
+ WRAPPER_IDENTIFY_ARG,
20
+ WRAPPER_IDENTIFY_TOKEN,
21
+ WRAPPER_EXECUTE_TOKEN,
22
+ } from './adapters/wrapper.js';
20
23
 
21
- const MODEL_PROBE_PROMPT = 'Reply with exactly RELAYFLOWS_MODEL_READY and nothing else.';
24
+ export { resolveAdapter, resolveAdapterKind, registeredAdapters } from './adapters/index.js';
22
25
 
23
26
  /** Select a closed adapter from the resolved executable's basename. */
24
27
  export function cliAdapterKind(executable: string): CliAdapterKind {
25
- const name = basename(executable).replace(/\.exe$/i, '');
26
- if (name === 'claude') return 'claude';
27
- if (name === 'codex') return 'codex';
28
- return 'relayflows-wrapper-v1';
28
+ return resolveAdapterKind(executable);
29
29
  }
30
30
 
31
31
  /** Prove the adapter command shape before classifying an auth failure. */
32
32
  export function adapterIdentification(kind: CliAdapterKind): CliAdapterIdentification {
33
- if (kind === 'claude') {
34
- return { invocation: { args: ['auth', 'status', '--help'], timeoutMs: 10_000 } };
35
- }
36
- if (kind === 'codex') {
37
- return { invocation: { args: ['login', 'status', '--help'], timeoutMs: 10_000 } };
38
- }
39
- return {
40
- invocation: { args: [WRAPPER_IDENTIFY_ARG], timeoutMs: 10_000 },
41
- expectedStdout: WRAPPER_IDENTIFY_TOKEN,
42
- };
33
+ return registeredAdapters()[kind].buildIdentification();
43
34
  }
44
35
 
45
36
  export function authenticationProbe(kind: CliAdapterKind): CliInvocation {
46
- if (kind === 'codex') return { args: ['login', 'status'], timeoutMs: 10_000 };
47
- return { args: ['auth', 'status'], timeoutMs: 10_000 };
37
+ return registeredAdapters()[kind].buildAuthProbe();
48
38
  }
49
39
 
50
40
  /**
@@ -53,29 +43,7 @@ export function authenticationProbe(kind: CliAdapterKind): CliInvocation {
53
43
  * exact model through its explicitly identified environment contract.
54
44
  */
55
45
  export function modelReadinessProbe(kind: CliAdapterKind, model: string): CliInvocation {
56
- if (kind === 'claude') {
57
- return {
58
- args: [
59
- '-p', '--model', model, '--tools', '', '--no-session-persistence',
60
- MODEL_PROBE_PROMPT,
61
- ],
62
- timeoutMs: 60_000,
63
- };
64
- }
65
- if (kind === 'codex') {
66
- return {
67
- args: [
68
- 'exec', '--ephemeral', '--sandbox', 'read-only', '--skip-git-repo-check',
69
- '--model', model, MODEL_PROBE_PROMPT,
70
- ],
71
- timeoutMs: 60_000,
72
- };
73
- }
74
- return {
75
- args: ['auth', 'status'],
76
- timeoutMs: 60_000,
77
- modelEnv: model,
78
- };
46
+ return registeredAdapters()[kind].buildModelReadinessProbe(model);
79
47
  }
80
48
 
81
49
  /** Build the actual worker argv; this is shared contract, not probe-only lore. */
@@ -84,23 +52,12 @@ export function agentExecution(
84
52
  instruction: string,
85
53
  model?: string,
86
54
  ): CliInvocation {
87
- if (kind === 'claude') {
88
- return {
89
- args: ['-p', ...(model === undefined ? [] : ['--model', model]), instruction],
90
- timeoutMs: 0,
91
- };
92
- }
93
- if (kind === 'codex') {
94
- return {
95
- args: [
96
- 'exec', '--ephemeral', '--skip-git-repo-check',
97
- ...(model === undefined ? [] : ['--model', model]),
98
- instruction,
99
- ],
100
- timeoutMs: 0,
101
- };
102
- }
103
- throw new Error('custom wrapper execution requires the runAgentCli same-process session');
55
+ return registeredAdapters()[kind].buildAgentInvocation(instruction, model);
56
+ }
57
+
58
+ /** Reuse the workspace-free model probe's provider flags for a real LLM call. */
59
+ export function llmExecution(kind: CliAdapterKind, prompt: string, model?: string): CliInvocation {
60
+ return registeredAdapters()[kind].buildLlmInvocation(prompt, model);
104
61
  }
105
62
 
106
63
  export function displayInvocation(cli: string, invocation: CliInvocation): string {
@@ -0,0 +1,174 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { accessSync, constants, existsSync, readFileSync, watch, type FSWatcher } from 'node:fs';
3
+ import { dirname, join, resolve, sep } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { parse as parseYaml } from 'yaml';
6
+ import type { CliIo } from './cli.js';
7
+
8
+ const DEBOUNCE_MS = 150;
9
+ type ExitCode = 0 | 1 | 2 | 3;
10
+
11
+ /** A runner around the ordinary CLI, with a fresh module cache for every check. */
12
+ export async function watchCheck(path: string, json: boolean, io: CliIo): Promise<ExitCode> {
13
+ const controller = new AbortController();
14
+ const stop = (): void => controller.abort();
15
+ process.once('SIGINT', stop);
16
+ process.once('SIGTERM', stop);
17
+ try {
18
+ return await watchChecks({
19
+ path,
20
+ signal: controller.signal,
21
+ check: () => checkOnce(path, json, io),
22
+ clear: () => { if (!json) io.stdout('\x1b[2J\x1b[H'); },
23
+ });
24
+ } catch (error) {
25
+ io.stderr(`REFUSED [input_unreadable] Could not watch "${path}": ${error instanceof Error ? error.message : String(error)}`);
26
+ return 2;
27
+ } finally {
28
+ process.off('SIGINT', stop);
29
+ process.off('SIGTERM', stop);
30
+ }
31
+ }
32
+
33
+ /** Inject the check and cancellation, just as the SDK pollers inject their I/O. */
34
+ export async function watchChecks(options: {
35
+ path: string;
36
+ signal: AbortSignal;
37
+ check: () => Promise<ExitCode>;
38
+ clear: () => void;
39
+ }): Promise<ExitCode> {
40
+ const watchers = new Map<string, FSWatcher>();
41
+ const imports = new Map<string, string[]>();
42
+ let paths = new Set<string>();
43
+ let lastCode: ExitCode = 0;
44
+ let timer: ReturnType<typeof setTimeout> | undefined;
45
+ let pending = false;
46
+ let stopped = options.signal.aborted;
47
+ let wake: (() => void) | undefined;
48
+ let watchError: Error | undefined;
49
+
50
+ const changed = (): void => {
51
+ if (stopped) return;
52
+ pending = true;
53
+ clearTimeout(timer);
54
+ timer = setTimeout(() => { timer = undefined; wake?.(); }, DEBOUNCE_MS);
55
+ };
56
+ const stop = (): void => {
57
+ stopped = true;
58
+ clearTimeout(timer);
59
+ timer = undefined;
60
+ wake?.();
61
+ };
62
+ const refresh = (): void => {
63
+ paths = discoverWatchPaths(options.path, imports);
64
+ const directories = new Set<string>();
65
+ for (const path of paths) {
66
+ let directory = dirname(path);
67
+ while (!existsSync(directory) && dirname(directory) !== directory) directory = dirname(directory);
68
+ directories.add(directory);
69
+ if (watchers.has(directory)) continue;
70
+ // Watch directories so atomic editor saves and delete/recreate keep working.
71
+ const watcher = watch(directory, (_event, filename) => {
72
+ const changedPath = filename === null ? undefined : resolve(directory, filename.toString());
73
+ if (changedPath === undefined || [...paths].some((target) =>
74
+ target === changedPath || target.startsWith(`${changedPath}${sep}`))) changed();
75
+ });
76
+ watcher.on('error', (error) => { watchError = error; stop(); });
77
+ watchers.set(directory, watcher);
78
+ }
79
+ for (const [directory, watcher] of watchers) {
80
+ if (!directories.has(directory)) { watcher.close(); watchers.delete(directory); }
81
+ }
82
+ };
83
+
84
+ options.signal.addEventListener('abort', stop, { once: true });
85
+ try {
86
+ refresh();
87
+ let first = true;
88
+ while (!stopped) {
89
+ if (!first && (!pending || timer !== undefined)) {
90
+ await new Promise<void>((resolveWake) => { wake = resolveWake; });
91
+ wake = undefined;
92
+ continue;
93
+ }
94
+ pending = false;
95
+ refresh();
96
+ if (!first) options.clear();
97
+ first = false;
98
+ lastCode = await options.check();
99
+ // A change while checking remains pending. Never overlap checks or lose
100
+ // a save merely because its debounce deadline elapsed during a check.
101
+ if (!stopped) refresh();
102
+ }
103
+ if (watchError !== undefined) throw watchError;
104
+ return lastCode;
105
+ } finally {
106
+ options.signal.removeEventListener('abort', stop);
107
+ clearTimeout(timer);
108
+ for (const watcher of watchers.values()) watcher.close();
109
+ }
110
+ }
111
+
112
+ function discoverWatchPaths(path: string, imports: Map<string, string[]>): Set<string> {
113
+ const paths = new Set<string>();
114
+ const visit = (file: string): void => {
115
+ if (paths.has(file)) return;
116
+ paths.add(file);
117
+ try {
118
+ const value: unknown = parseYaml(readFileSync(file, 'utf8'));
119
+ const use = typeof value === 'object' && value !== null && !Array.isArray(value)
120
+ ? (value as Record<string, unknown>)['use'] : undefined;
121
+ imports.set(file, Array.isArray(use) ? use.filter((entry): entry is string =>
122
+ typeof entry === 'string' && (entry.startsWith('./') || entry.startsWith('../'))) : []);
123
+ } catch {
124
+ // Keep the previous graph while an editor has left this file incomplete.
125
+ // The ordinary check owns all parse and validation diagnostics.
126
+ }
127
+ for (const dependency of imports.get(file) ?? []) visit(resolve(dirname(file), dependency));
128
+ };
129
+ visit(resolve(path));
130
+ for (const file of imports.keys()) { if (!paths.has(file)) imports.delete(file); }
131
+ let directory = dirname(resolve(path));
132
+ while (true) {
133
+ const candidate = join(directory, 'flows.json');
134
+ paths.add(candidate);
135
+ try { accessSync(candidate, constants.R_OK); break; }
136
+ catch { /* Match check's nearest-readable-config discovery. */ }
137
+ const parent = dirname(directory);
138
+ if (parent === directory) break;
139
+ directory = parent;
140
+ }
141
+ return paths;
142
+ }
143
+
144
+ function checkOnce(path: string, json: boolean, io: CliIo): Promise<ExitCode> {
145
+ // Importing an authored TS file in this process again would return its stale
146
+ // cached definition (including transitive imports). A fresh CLI process uses
147
+ // precisely the same check pipeline and reporting as a one-shot invocation.
148
+ const entry = fileURLToPath(new URL(import.meta.url.endsWith('.ts') ? './cli.ts' : './cli.js', import.meta.url));
149
+ return new Promise((resolveCode, reject) => {
150
+ const child = spawn(process.execPath, [...process.execArgv, entry, 'check', ...(json ? ['--json'] : []), path], {
151
+ stdio: ['ignore', 'pipe', 'pipe'],
152
+ // Ctrl-C belongs to the watcher; let an active check finish so its report
153
+ // and exit status remain a pair, then release all resources.
154
+ detached: true,
155
+ });
156
+ const forward = (stream: NodeJS.ReadableStream, write: (line: string) => void): void => {
157
+ let pending = '';
158
+ stream.setEncoding('utf8');
159
+ stream.on('data', (chunk: string) => {
160
+ pending += chunk;
161
+ let end: number;
162
+ while ((end = pending.indexOf('\n')) >= 0) {
163
+ write(pending.slice(0, end));
164
+ pending = pending.slice(end + 1);
165
+ }
166
+ });
167
+ stream.on('end', () => { if (pending) write(pending); });
168
+ };
169
+ forward(child.stdout, io.stdout);
170
+ forward(child.stderr, io.stderr);
171
+ child.once('error', reject);
172
+ child.once('close', (code) => resolveCode(code === 0 || code === 2 || code === 3 ? code : 1));
173
+ });
174
+ }