@openclaw/lobster 2026.9.4 → 2026.9.6

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 (367) hide show
  1. package/assets/activity.svg +4 -0
  2. package/assets/icon.png +0 -0
  3. package/dist/index.js +39 -10
  4. package/node_modules/@clawdbot/lobster/README.md +129 -131
  5. package/node_modules/@clawdbot/lobster/VISION.md +15 -238
  6. package/node_modules/@clawdbot/lobster/bin/clawd.invoke.js +2 -15
  7. package/node_modules/@clawdbot/lobster/bin/invoke.js +19 -0
  8. package/node_modules/@clawdbot/lobster/bin/lobster.js +2 -21
  9. package/node_modules/@clawdbot/lobster/bin/openclaw.invoke.js +2 -18
  10. package/node_modules/@clawdbot/lobster/dist/src/abortable_process.js +297 -0
  11. package/node_modules/@clawdbot/lobster/dist/src/abortable_process.js.map +1 -0
  12. package/node_modules/@clawdbot/lobster/dist/src/cli.js +128 -296
  13. package/node_modules/@clawdbot/lobster/dist/src/cli.js.map +1 -1
  14. package/node_modules/@clawdbot/lobster/dist/src/command_tokens.js +81 -0
  15. package/node_modules/@clawdbot/lobster/dist/src/command_tokens.js.map +1 -0
  16. package/node_modules/@clawdbot/lobster/dist/src/commands/commands_list.js.map +1 -1
  17. package/node_modules/@clawdbot/lobster/dist/src/commands/registry.js +2 -0
  18. package/node_modules/@clawdbot/lobster/dist/src/commands/registry.js.map +1 -1
  19. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/approve.js +2 -0
  20. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/approve.js.map +1 -1
  21. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/ask.js +3 -1
  22. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/ask.js.map +1 -1
  23. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/dedupe.js +1 -12
  24. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/dedupe.js.map +1 -1
  25. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/diff_last.js +6 -1
  26. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/diff_last.js.map +1 -1
  27. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/email_triage.js.map +1 -1
  28. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/exec.js +30 -34
  29. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/exec.js.map +1 -1
  30. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/gog_gmail_search.js +12 -25
  31. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/gog_gmail_search.js.map +1 -1
  32. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/gog_gmail_send.js +19 -25
  33. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/gog_gmail_send.js.map +1 -1
  34. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/group_by.js +2 -14
  35. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/group_by.js.map +1 -1
  36. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/head.js.map +1 -1
  37. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/json.js.map +1 -1
  38. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/llm_invoke.js +260 -42
  39. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/llm_invoke.js.map +1 -1
  40. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/map.js +2 -13
  41. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/map.js.map +1 -1
  42. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/openclaw_agent.js +139 -0
  43. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/openclaw_agent.js.map +1 -0
  44. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/openclaw_invoke.js +20 -2
  45. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/openclaw_invoke.js.map +1 -1
  46. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/pick.js.map +1 -1
  47. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/sort.js +4 -21
  48. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/sort.js.map +1 -1
  49. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/state.js +48 -20
  50. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/state.js.map +1 -1
  51. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/table.js.map +1 -1
  52. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/template.js +2 -13
  53. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/template.js.map +1 -1
  54. package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/where.js.map +1 -1
  55. package/node_modules/@clawdbot/lobster/dist/src/commands/workflows/workflows_list.js.map +1 -1
  56. package/node_modules/@clawdbot/lobster/dist/src/commands/workflows/workflows_run.js +0 -18
  57. package/node_modules/@clawdbot/lobster/dist/src/commands/workflows/workflows_run.js.map +1 -1
  58. package/node_modules/@clawdbot/lobster/dist/src/core/cost_tracker.js +45 -3
  59. package/node_modules/@clawdbot/lobster/dist/src/core/cost_tracker.js.map +1 -1
  60. package/node_modules/@clawdbot/lobster/dist/src/core/filters.js.map +1 -1
  61. package/node_modules/@clawdbot/lobster/dist/src/core/llm_accounting.js +304 -0
  62. package/node_modules/@clawdbot/lobster/dist/src/core/llm_accounting.js.map +1 -0
  63. package/node_modules/@clawdbot/lobster/dist/src/core/retry.js.map +1 -1
  64. package/node_modules/@clawdbot/lobster/dist/src/core/tool_runtime.js +147 -21
  65. package/node_modules/@clawdbot/lobster/dist/src/core/tool_runtime.js.map +1 -1
  66. package/node_modules/@clawdbot/lobster/dist/src/core/value_path.js +11 -0
  67. package/node_modules/@clawdbot/lobster/dist/src/core/value_path.js.map +1 -0
  68. package/node_modules/@clawdbot/lobster/dist/src/input_request.js +4 -2
  69. package/node_modules/@clawdbot/lobster/dist/src/input_request.js.map +1 -1
  70. package/node_modules/@clawdbot/lobster/dist/src/parser.js +5 -69
  71. package/node_modules/@clawdbot/lobster/dist/src/parser.js.map +1 -1
  72. package/node_modules/@clawdbot/lobster/dist/src/pipeline_resume_state.js +241 -74
  73. package/node_modules/@clawdbot/lobster/dist/src/pipeline_resume_state.js.map +1 -1
  74. package/node_modules/@clawdbot/lobster/dist/src/read_line.js +52 -7
  75. package/node_modules/@clawdbot/lobster/dist/src/read_line.js.map +1 -1
  76. package/node_modules/@clawdbot/lobster/dist/src/read_response_text.js +86 -0
  77. package/node_modules/@clawdbot/lobster/dist/src/read_response_text.js.map +1 -0
  78. package/node_modules/@clawdbot/lobster/dist/src/recipes/github/index.js +1 -1
  79. package/node_modules/@clawdbot/lobster/dist/src/recipes/github/pr-monitor.js +9 -66
  80. package/node_modules/@clawdbot/lobster/dist/src/recipes/github/pr-monitor.js.map +1 -1
  81. package/node_modules/@clawdbot/lobster/dist/src/recipes/github/read_pr.js +45 -0
  82. package/node_modules/@clawdbot/lobster/dist/src/recipes/github/read_pr.js.map +1 -0
  83. package/node_modules/@clawdbot/lobster/dist/src/recipes/github/snapshot.js +45 -0
  84. package/node_modules/@clawdbot/lobster/dist/src/recipes/github/snapshot.js.map +1 -0
  85. package/node_modules/@clawdbot/lobster/dist/src/recipes/github/stages/pr-view.js +13 -65
  86. package/node_modules/@clawdbot/lobster/dist/src/recipes/github/stages/pr-view.js.map +1 -1
  87. package/node_modules/@clawdbot/lobster/dist/src/recipes/registry.js.map +1 -1
  88. package/node_modules/@clawdbot/lobster/dist/src/renderers/json.js.map +1 -1
  89. package/node_modules/@clawdbot/lobster/dist/src/resume.js.map +1 -1
  90. package/node_modules/@clawdbot/lobster/dist/src/runtime.js +187 -17
  91. package/node_modules/@clawdbot/lobster/dist/src/runtime.js.map +1 -1
  92. package/node_modules/@clawdbot/lobster/dist/src/sdk/Lobster.js +25 -134
  93. package/node_modules/@clawdbot/lobster/dist/src/sdk/Lobster.js.map +1 -1
  94. package/node_modules/@clawdbot/lobster/dist/src/sdk/index.js +1 -1
  95. package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/approve.js +1 -2
  96. package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/approve.js.map +1 -1
  97. package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/diff.js +11 -86
  98. package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/diff.js.map +1 -1
  99. package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/exec.js +20 -96
  100. package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/exec.js.map +1 -1
  101. package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/state.js +8 -106
  102. package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/state.js.map +1 -1
  103. package/node_modules/@clawdbot/lobster/dist/src/sdk/runtime.js +2 -13
  104. package/node_modules/@clawdbot/lobster/dist/src/sdk/runtime.js.map +1 -1
  105. package/node_modules/@clawdbot/lobster/dist/src/sdk/state_env.js +6 -0
  106. package/node_modules/@clawdbot/lobster/dist/src/sdk/state_env.js.map +1 -0
  107. package/node_modules/@clawdbot/lobster/dist/src/shell.js.map +1 -1
  108. package/node_modules/@clawdbot/lobster/dist/src/state/atomic_file.js +192 -0
  109. package/node_modules/@clawdbot/lobster/dist/src/state/atomic_file.js.map +1 -0
  110. package/node_modules/@clawdbot/lobster/dist/src/state/store.js +550 -136
  111. package/node_modules/@clawdbot/lobster/dist/src/state/store.js.map +1 -1
  112. package/node_modules/@clawdbot/lobster/dist/src/token.js.map +1 -1
  113. package/node_modules/@clawdbot/lobster/dist/src/validation.js.map +1 -1
  114. package/node_modules/@clawdbot/lobster/dist/src/workflows/dry_run.js +262 -0
  115. package/node_modules/@clawdbot/lobster/dist/src/workflows/dry_run.js.map +1 -0
  116. package/node_modules/@clawdbot/lobster/dist/src/workflows/expressions.js +345 -0
  117. package/node_modules/@clawdbot/lobster/dist/src/workflows/expressions.js.map +1 -0
  118. package/node_modules/@clawdbot/lobster/dist/src/workflows/file.js +950 -1285
  119. package/node_modules/@clawdbot/lobster/dist/src/workflows/file.js.map +1 -1
  120. package/node_modules/@clawdbot/lobster/dist/src/workflows/github_pr_monitor.js +20 -90
  121. package/node_modules/@clawdbot/lobster/dist/src/workflows/github_pr_monitor.js.map +1 -1
  122. package/node_modules/@clawdbot/lobster/dist/src/workflows/graph.js +9 -8
  123. package/node_modules/@clawdbot/lobster/dist/src/workflows/graph.js.map +1 -1
  124. package/node_modules/@clawdbot/lobster/dist/src/workflows/load.js +347 -0
  125. package/node_modules/@clawdbot/lobster/dist/src/workflows/load.js.map +1 -0
  126. package/node_modules/@clawdbot/lobster/dist/src/workflows/registry.js.map +1 -1
  127. package/node_modules/@clawdbot/lobster/dist/src/workflows/types.js +2 -0
  128. package/node_modules/@clawdbot/lobster/dist/src/workflows/types.js.map +1 -0
  129. package/node_modules/@clawdbot/lobster/dist/src/workflows/values.js +93 -0
  130. package/node_modules/@clawdbot/lobster/dist/src/workflows/values.js.map +1 -0
  131. package/node_modules/@clawdbot/lobster/dist/test/approval_id.test.js.map +1 -1
  132. package/node_modules/@clawdbot/lobster/dist/test/approve_preview.test.js.map +1 -1
  133. package/node_modules/@clawdbot/lobster/dist/test/bin_smoke.test.js +25 -0
  134. package/node_modules/@clawdbot/lobster/dist/test/bin_smoke.test.js.map +1 -0
  135. package/node_modules/@clawdbot/lobster/dist/test/cancellation.test.js +5171 -0
  136. package/node_modules/@clawdbot/lobster/dist/test/cancellation.test.js.map +1 -0
  137. package/node_modules/@clawdbot/lobster/dist/test/clawd_invoke.test.js +22 -0
  138. package/node_modules/@clawdbot/lobster/dist/test/clawd_invoke.test.js.map +1 -1
  139. package/node_modules/@clawdbot/lobster/dist/test/clawd_invoke_legacy.test.js.map +1 -1
  140. package/node_modules/@clawdbot/lobster/dist/test/cli_run_errors.test.js +53 -0
  141. package/node_modules/@clawdbot/lobster/dist/test/cli_run_errors.test.js.map +1 -0
  142. package/node_modules/@clawdbot/lobster/dist/test/cli_run_file_args_json.test.js.map +1 -1
  143. package/node_modules/@clawdbot/lobster/dist/test/command_arguments.test.js +82 -0
  144. package/node_modules/@clawdbot/lobster/dist/test/command_arguments.test.js.map +1 -0
  145. package/node_modules/@clawdbot/lobster/dist/test/commands_list.test.js.map +1 -1
  146. package/node_modules/@clawdbot/lobster/dist/test/condition_comparison.test.js.map +1 -1
  147. package/node_modules/@clawdbot/lobster/dist/test/core_tool_runtime.test.js.map +1 -1
  148. package/node_modules/@clawdbot/lobster/dist/test/cost_tracker.test.js +1774 -12
  149. package/node_modules/@clawdbot/lobster/dist/test/cost_tracker.test.js.map +1 -1
  150. package/node_modules/@clawdbot/lobster/dist/test/dedupe.test.js.map +1 -1
  151. package/node_modules/@clawdbot/lobster/dist/test/diff_last.test.js.map +1 -1
  152. package/node_modules/@clawdbot/lobster/dist/test/doctor.test.js.map +1 -1
  153. package/node_modules/@clawdbot/lobster/dist/test/dry_run.test.js.map +1 -1
  154. package/node_modules/@clawdbot/lobster/dist/test/email_triage.test.js.map +1 -1
  155. package/node_modules/@clawdbot/lobster/dist/test/exec_stdin.test.js.map +1 -1
  156. package/node_modules/@clawdbot/lobster/dist/test/for_each.test.js +254 -0
  157. package/node_modules/@clawdbot/lobster/dist/test/for_each.test.js.map +1 -1
  158. package/node_modules/@clawdbot/lobster/dist/test/github_pr_notify_format.test.js.map +1 -1
  159. package/node_modules/@clawdbot/lobster/dist/test/github_pr_summary.test.js.map +1 -1
  160. package/node_modules/@clawdbot/lobster/dist/test/github_pr_view.test.js +176 -0
  161. package/node_modules/@clawdbot/lobster/dist/test/github_pr_view.test.js.map +1 -0
  162. package/node_modules/@clawdbot/lobster/dist/test/gog_gmail_send.test.js +100 -0
  163. package/node_modules/@clawdbot/lobster/dist/test/gog_gmail_send.test.js.map +1 -0
  164. package/node_modules/@clawdbot/lobster/dist/test/group_by.test.js.map +1 -1
  165. package/node_modules/@clawdbot/lobster/dist/test/helpers/node_shell_fixture.js +17 -0
  166. package/node_modules/@clawdbot/lobster/dist/test/helpers/node_shell_fixture.js.map +1 -0
  167. package/node_modules/@clawdbot/lobster/dist/test/helpers/wait_for_pid.js +19 -0
  168. package/node_modules/@clawdbot/lobster/dist/test/helpers/wait_for_pid.js.map +1 -0
  169. package/node_modules/@clawdbot/lobster/dist/test/http_response_body_cap.test.js +318 -0
  170. package/node_modules/@clawdbot/lobster/dist/test/http_response_body_cap.test.js.map +1 -0
  171. package/node_modules/@clawdbot/lobster/dist/test/llm_invoke.test.js +948 -1
  172. package/node_modules/@clawdbot/lobster/dist/test/llm_invoke.test.js.map +1 -1
  173. package/node_modules/@clawdbot/lobster/dist/test/llm_task_invoke.test.js +103 -0
  174. package/node_modules/@clawdbot/lobster/dist/test/llm_task_invoke.test.js.map +1 -1
  175. package/node_modules/@clawdbot/lobster/dist/test/map.test.js.map +1 -1
  176. package/node_modules/@clawdbot/lobster/dist/test/multi_approval_resume.test.js.map +1 -1
  177. package/node_modules/@clawdbot/lobster/dist/test/on_error.test.js.map +1 -1
  178. package/node_modules/@clawdbot/lobster/dist/test/openclaw_agent.test.js +169 -0
  179. package/node_modules/@clawdbot/lobster/dist/test/openclaw_agent.test.js.map +1 -0
  180. package/node_modules/@clawdbot/lobster/dist/test/openclaw_dispatch_retry.test.js +96 -0
  181. package/node_modules/@clawdbot/lobster/dist/test/openclaw_dispatch_retry.test.js.map +1 -0
  182. package/node_modules/@clawdbot/lobster/dist/test/openclaw_invoke_alias.test.js.map +1 -1
  183. package/node_modules/@clawdbot/lobster/dist/test/parallel.test.js.map +1 -1
  184. package/node_modules/@clawdbot/lobster/dist/test/parser.test.js +57 -0
  185. package/node_modules/@clawdbot/lobster/dist/test/parser.test.js.map +1 -1
  186. package/node_modules/@clawdbot/lobster/dist/test/pipeline_resume_state.test.js +65 -0
  187. package/node_modules/@clawdbot/lobster/dist/test/pipeline_resume_state.test.js.map +1 -0
  188. package/node_modules/@clawdbot/lobster/dist/test/process_output_limit.test.js +175 -0
  189. package/node_modules/@clawdbot/lobster/dist/test/process_output_limit.test.js.map +1 -0
  190. package/node_modules/@clawdbot/lobster/dist/test/read_line.test.js +54 -0
  191. package/node_modules/@clawdbot/lobster/dist/test/read_line.test.js.map +1 -1
  192. package/node_modules/@clawdbot/lobster/dist/test/release_changelog.test.js +39 -0
  193. package/node_modules/@clawdbot/lobster/dist/test/release_changelog.test.js.map +1 -0
  194. package/node_modules/@clawdbot/lobster/dist/test/request_input.test.js +1 -1
  195. package/node_modules/@clawdbot/lobster/dist/test/request_input.test.js.map +1 -1
  196. package/node_modules/@clawdbot/lobster/dist/test/resume.test.js.map +1 -1
  197. package/node_modules/@clawdbot/lobster/dist/test/sdk_exec.test.js +248 -0
  198. package/node_modules/@clawdbot/lobster/dist/test/sdk_exec.test.js.map +1 -0
  199. package/node_modules/@clawdbot/lobster/dist/test/sdk_lobster.test.js.map +1 -1
  200. package/node_modules/@clawdbot/lobster/dist/test/shell.test.js.map +1 -1
  201. package/node_modules/@clawdbot/lobster/dist/test/sort.test.js.map +1 -1
  202. package/node_modules/@clawdbot/lobster/dist/test/state.test.js +584 -1
  203. package/node_modules/@clawdbot/lobster/dist/test/state.test.js.map +1 -1
  204. package/node_modules/@clawdbot/lobster/dist/test/step_retry.test.js.map +1 -1
  205. package/node_modules/@clawdbot/lobster/dist/test/step_stdin_epipe.test.js +44 -0
  206. package/node_modules/@clawdbot/lobster/dist/test/step_stdin_epipe.test.js.map +1 -0
  207. package/node_modules/@clawdbot/lobster/dist/test/step_timeout.test.js +67 -1
  208. package/node_modules/@clawdbot/lobster/dist/test/step_timeout.test.js.map +1 -1
  209. package/node_modules/@clawdbot/lobster/dist/test/template.test.js.map +1 -1
  210. package/node_modules/@clawdbot/lobster/dist/test/template_filters.test.js +37 -0
  211. package/node_modules/@clawdbot/lobster/dist/test/template_filters.test.js.map +1 -1
  212. package/node_modules/@clawdbot/lobster/dist/test/tool_envelope_version.test.js.map +1 -1
  213. package/node_modules/@clawdbot/lobster/dist/test/tool_mode.test.js.map +1 -1
  214. package/node_modules/@clawdbot/lobster/dist/test/validation.test.js.map +1 -1
  215. package/node_modules/@clawdbot/lobster/dist/test/where.test.js +94 -0
  216. package/node_modules/@clawdbot/lobster/dist/test/where.test.js.map +1 -0
  217. package/node_modules/@clawdbot/lobster/dist/test/workflow_args_env.test.js +5 -3
  218. package/node_modules/@clawdbot/lobster/dist/test/workflow_args_env.test.js.map +1 -1
  219. package/node_modules/@clawdbot/lobster/dist/test/workflow_composition.test.js.map +1 -1
  220. package/node_modules/@clawdbot/lobster/dist/test/workflow_file.test.js +647 -1
  221. package/node_modules/@clawdbot/lobster/dist/test/workflow_file.test.js.map +1 -1
  222. package/node_modules/@clawdbot/lobster/dist/test/workflow_graph.test.js +13 -3
  223. package/node_modules/@clawdbot/lobster/dist/test/workflow_graph.test.js.map +1 -1
  224. package/node_modules/@clawdbot/lobster/dist/test/workflows.test.js.map +1 -1
  225. package/node_modules/@clawdbot/lobster/package.json +72 -73
  226. package/node_modules/typebox/build/compile/validator.d.mts +1 -1
  227. package/node_modules/typebox/build/compile/validator.mjs +1 -1
  228. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +7 -5
  229. package/node_modules/typebox/build/format/iri.mjs +5 -1
  230. package/node_modules/typebox/build/guard/emit.mjs +2 -4
  231. package/node_modules/typebox/build/guard/guard.mjs +6 -18
  232. package/node_modules/typebox/build/guard/string.mjs +16 -24
  233. package/node_modules/typebox/build/schema/build.mjs +2 -2
  234. package/node_modules/typebox/build/schema/check.mjs +1 -1
  235. package/node_modules/typebox/build/schema/compile.d.mts +3 -2
  236. package/node_modules/typebox/build/schema/compile.mjs +8 -8
  237. package/node_modules/typebox/build/schema/engine/_context.d.mts +6 -7
  238. package/node_modules/typebox/build/schema/engine/_context.mjs +23 -15
  239. package/node_modules/typebox/build/schema/engine/_functions.d.mts +2 -2
  240. package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -1
  241. package/node_modules/typebox/build/schema/engine/_reducer.d.mts +3 -3
  242. package/node_modules/typebox/build/schema/engine/_refine.d.mts +5 -5
  243. package/node_modules/typebox/build/schema/engine/_refine.mjs +4 -7
  244. package/node_modules/typebox/build/schema/engine/_stack.d.mts +34 -16
  245. package/node_modules/typebox/build/schema/engine/_stack.mjs +148 -104
  246. package/node_modules/typebox/build/schema/engine/additionalItems.d.mts +4 -4
  247. package/node_modules/typebox/build/schema/engine/additionalProperties.d.mts +8 -8
  248. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +26 -18
  249. package/node_modules/typebox/build/schema/engine/allOf.d.mts +4 -4
  250. package/node_modules/typebox/build/schema/engine/allOf.mjs +3 -3
  251. package/node_modules/typebox/build/schema/engine/anyOf.d.mts +4 -4
  252. package/node_modules/typebox/build/schema/engine/anyOf.mjs +5 -10
  253. package/node_modules/typebox/build/schema/engine/boolean.d.mts +4 -4
  254. package/node_modules/typebox/build/schema/engine/boolean.mjs +2 -6
  255. package/node_modules/typebox/build/schema/engine/const.d.mts +4 -4
  256. package/node_modules/typebox/build/schema/engine/const.mjs +2 -6
  257. package/node_modules/typebox/build/schema/engine/contains.d.mts +4 -4
  258. package/node_modules/typebox/build/schema/engine/contains.mjs +2 -6
  259. package/node_modules/typebox/build/schema/engine/dependencies.d.mts +4 -4
  260. package/node_modules/typebox/build/schema/engine/dependencies.mjs +3 -6
  261. package/node_modules/typebox/build/schema/engine/dependentRequired.d.mts +4 -4
  262. package/node_modules/typebox/build/schema/engine/dependentRequired.mjs +2 -6
  263. package/node_modules/typebox/build/schema/engine/dependentSchemas.d.mts +4 -4
  264. package/node_modules/typebox/build/schema/engine/dynamicRef.d.mts +4 -4
  265. package/node_modules/typebox/build/schema/engine/dynamicRef.mjs +11 -7
  266. package/node_modules/typebox/build/schema/engine/enum.d.mts +4 -4
  267. package/node_modules/typebox/build/schema/engine/enum.mjs +2 -6
  268. package/node_modules/typebox/build/schema/engine/exclusiveMaximum.d.mts +4 -4
  269. package/node_modules/typebox/build/schema/engine/exclusiveMaximum.mjs +2 -6
  270. package/node_modules/typebox/build/schema/engine/exclusiveMinimum.d.mts +4 -4
  271. package/node_modules/typebox/build/schema/engine/exclusiveMinimum.mjs +2 -6
  272. package/node_modules/typebox/build/schema/engine/format.d.mts +4 -4
  273. package/node_modules/typebox/build/schema/engine/format.mjs +2 -6
  274. package/node_modules/typebox/build/schema/engine/if.d.mts +4 -4
  275. package/node_modules/typebox/build/schema/engine/if.mjs +7 -15
  276. package/node_modules/typebox/build/schema/engine/items.d.mts +4 -4
  277. package/node_modules/typebox/build/schema/engine/items.mjs +1 -1
  278. package/node_modules/typebox/build/schema/engine/maxContains.d.mts +4 -4
  279. package/node_modules/typebox/build/schema/engine/maxContains.mjs +2 -6
  280. package/node_modules/typebox/build/schema/engine/maxItems.d.mts +4 -4
  281. package/node_modules/typebox/build/schema/engine/maxItems.mjs +2 -6
  282. package/node_modules/typebox/build/schema/engine/maxLength.d.mts +4 -4
  283. package/node_modules/typebox/build/schema/engine/maxLength.mjs +2 -6
  284. package/node_modules/typebox/build/schema/engine/maxProperties.d.mts +4 -4
  285. package/node_modules/typebox/build/schema/engine/maxProperties.mjs +2 -6
  286. package/node_modules/typebox/build/schema/engine/maximum.d.mts +4 -4
  287. package/node_modules/typebox/build/schema/engine/maximum.mjs +2 -6
  288. package/node_modules/typebox/build/schema/engine/minContains.d.mts +4 -4
  289. package/node_modules/typebox/build/schema/engine/minContains.mjs +2 -6
  290. package/node_modules/typebox/build/schema/engine/minItems.d.mts +5 -5
  291. package/node_modules/typebox/build/schema/engine/minItems.mjs +2 -6
  292. package/node_modules/typebox/build/schema/engine/minLength.d.mts +4 -4
  293. package/node_modules/typebox/build/schema/engine/minLength.mjs +2 -6
  294. package/node_modules/typebox/build/schema/engine/minProperties.d.mts +4 -4
  295. package/node_modules/typebox/build/schema/engine/minProperties.mjs +2 -6
  296. package/node_modules/typebox/build/schema/engine/minimum.d.mts +4 -4
  297. package/node_modules/typebox/build/schema/engine/minimum.mjs +2 -6
  298. package/node_modules/typebox/build/schema/engine/multipleOf.d.mts +4 -4
  299. package/node_modules/typebox/build/schema/engine/multipleOf.mjs +2 -6
  300. package/node_modules/typebox/build/schema/engine/not.d.mts +4 -4
  301. package/node_modules/typebox/build/schema/engine/not.mjs +4 -8
  302. package/node_modules/typebox/build/schema/engine/oneOf.d.mts +4 -4
  303. package/node_modules/typebox/build/schema/engine/oneOf.mjs +6 -11
  304. package/node_modules/typebox/build/schema/engine/pattern.d.mts +4 -4
  305. package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -7
  306. package/node_modules/typebox/build/schema/engine/patternProperties.d.mts +4 -4
  307. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +1 -1
  308. package/node_modules/typebox/build/schema/engine/prefixItems.d.mts +4 -4
  309. package/node_modules/typebox/build/schema/engine/prefixItems.mjs +1 -1
  310. package/node_modules/typebox/build/schema/engine/properties.d.mts +4 -4
  311. package/node_modules/typebox/build/schema/engine/properties.mjs +1 -1
  312. package/node_modules/typebox/build/schema/engine/propertyNames.d.mts +4 -4
  313. package/node_modules/typebox/build/schema/engine/propertyNames.mjs +4 -10
  314. package/node_modules/typebox/build/schema/engine/recursiveRef.d.mts +4 -4
  315. package/node_modules/typebox/build/schema/engine/recursiveRef.mjs +11 -7
  316. package/node_modules/typebox/build/schema/engine/ref.d.mts +4 -4
  317. package/node_modules/typebox/build/schema/engine/ref.mjs +19 -15
  318. package/node_modules/typebox/build/schema/engine/required.d.mts +4 -4
  319. package/node_modules/typebox/build/schema/engine/required.mjs +2 -6
  320. package/node_modules/typebox/build/schema/engine/schema.d.mts +7 -7
  321. package/node_modules/typebox/build/schema/engine/schema.mjs +137 -133
  322. package/node_modules/typebox/build/schema/engine/type.d.mts +4 -4
  323. package/node_modules/typebox/build/schema/engine/type.mjs +2 -6
  324. package/node_modules/typebox/build/schema/engine/unevaluatedItems.d.mts +4 -4
  325. package/node_modules/typebox/build/schema/engine/unevaluatedItems.mjs +5 -9
  326. package/node_modules/typebox/build/schema/engine/unevaluatedProperties.d.mts +4 -4
  327. package/node_modules/typebox/build/schema/engine/unevaluatedProperties.mjs +5 -9
  328. package/node_modules/typebox/build/schema/engine/uniqueItems.d.mts +4 -4
  329. package/node_modules/typebox/build/schema/engine/uniqueItems.mjs +2 -6
  330. package/node_modules/typebox/build/schema/errors.d.mts +2 -2
  331. package/node_modules/typebox/build/schema/errors.mjs +8 -14
  332. package/node_modules/typebox/build/schema/intern/index.d.mts +1 -0
  333. package/node_modules/typebox/build/schema/intern/index.mjs +1 -0
  334. package/node_modules/typebox/build/schema/intern/intern.d.mts +11 -0
  335. package/node_modules/typebox/build/schema/intern/intern.mjs +293 -0
  336. package/node_modules/typebox/build/schema/meta/draft_2019_09.d.mts +2 -0
  337. package/node_modules/typebox/build/schema/meta/draft_2019_09.mjs +288 -0
  338. package/node_modules/typebox/build/schema/meta/draft_2020_12.d.mts +2 -0
  339. package/node_modules/typebox/build/schema/meta/draft_2020_12.mjs +300 -0
  340. package/node_modules/typebox/build/schema/meta/draft_3.d.mts +2 -0
  341. package/node_modules/typebox/build/schema/meta/draft_3.mjs +143 -0
  342. package/node_modules/typebox/build/schema/meta/draft_4.d.mts +2 -0
  343. package/node_modules/typebox/build/schema/meta/draft_4.mjs +149 -0
  344. package/node_modules/typebox/build/schema/meta/draft_6.d.mts +2 -0
  345. package/node_modules/typebox/build/schema/meta/draft_6.mjs +155 -0
  346. package/node_modules/typebox/build/schema/meta/draft_7.d.mts +2 -0
  347. package/node_modules/typebox/build/schema/meta/draft_7.mjs +172 -0
  348. package/node_modules/typebox/build/schema/meta/index.d.mts +2 -0
  349. package/node_modules/typebox/build/schema/meta/index.mjs +2 -0
  350. package/node_modules/typebox/build/schema/meta/meta.d.mts +9 -0
  351. package/node_modules/typebox/build/schema/meta/meta.mjs +15 -0
  352. package/node_modules/typebox/build/schema/meta/schema.d.mts +6 -0
  353. package/node_modules/typebox/build/schema/meta/schema.mjs +1 -0
  354. package/node_modules/typebox/build/schema/parse.d.mts +1 -0
  355. package/node_modules/typebox/build/schema/parse.mjs +10 -5
  356. package/node_modules/typebox/build/schema/resolve/resolve.d.mts +8 -3
  357. package/node_modules/typebox/build/schema/resolve/resolve.mjs +268 -139
  358. package/node_modules/typebox/build/schema/schema.d.mts +2 -0
  359. package/node_modules/typebox/build/schema/schema.mjs +2 -0
  360. package/node_modules/typebox/build/value/errors/errors.d.mts +2 -12
  361. package/node_modules/typebox/build/value/errors/errors.mjs +2 -7
  362. package/node_modules/typebox/package.json +1 -1
  363. package/node_modules/typebox/readme.md +59 -89
  364. package/openclaw.plugin.json +1 -1
  365. package/package.json +10 -7
  366. package/node_modules/@clawdbot/lobster/dist/src/sdk/token.js +0 -9
  367. package/node_modules/@clawdbot/lobster/dist/src/sdk/token.js.map +0 -1
@@ -1,13 +1,4 @@
1
- function getByPath(obj, path) {
2
- const parts = path.split(".").filter(Boolean);
3
- let cur = obj;
4
- for (const p of parts) {
5
- if (cur == null)
6
- return undefined;
7
- cur = cur[p];
8
- }
9
- return cur;
10
- }
1
+ import { getByPath } from "../../core/value_path.js";
11
2
  export const groupByCommand = {
12
3
  name: "groupBy",
13
4
  meta: {
@@ -36,20 +27,17 @@ export const groupByCommand = {
36
27
  if (!keyPath)
37
28
  throw new Error("groupBy requires --key");
38
29
  const groups = new Map();
39
- const order = [];
40
30
  for await (const item of input) {
41
31
  const keyVal = getByPath(item, keyPath);
42
32
  const k = JSON.stringify(keyVal);
43
33
  if (!groups.has(k)) {
44
34
  groups.set(k, { key: keyVal, items: [] });
45
- order.push(k);
46
35
  }
47
36
  groups.get(k).items.push(item);
48
37
  }
49
38
  return {
50
39
  output: (async function* () {
51
- for (const k of order) {
52
- const g = groups.get(k);
40
+ for (const g of groups.values()) {
53
41
  yield { key: g.key, items: g.items, count: g.items.length };
54
42
  }
55
43
  })(),
@@ -1 +1 @@
1
- {"version":3,"file":"group_by.js","sourceRoot":"","sources":["../../../../src/commands/stdlib/group_by.ts"],"names":[],"mappings":"AAAA,SAAS,SAAS,CAAC,GAAQ,EAAE,IAAY;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,GAAG,GAAQ,GAAG,CAAC;IACnB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,GAAG,IAAI,IAAI;YAAE,OAAO,SAAS,CAAC;QAClC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,SAAS;IACf,IAAI,EAAE;QACJ,WAAW,EAAE,2CAA2C;QACxD,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qCAAqC,EAAE;gBAC3E,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;aAChD;YACD,QAAQ,EAAE,CAAC,KAAK,CAAC;SAClB;QACD,WAAW,EAAE,EAAE;KAChB;IACD,IAAI;QACF,OAAO,CACL,oCAAoC;YACpC,UAAU;YACV,gCAAgC;YAChC,WAAW;YACX,+CAA+C;YAC/C,UAAU;YACV,0DAA0D,CAC3D,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAO;QAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAExD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsC,CAAC;QAC7D,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACxC,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnB,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC1C,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChB,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;QAED,OAAO;YACL,MAAM,EAAE,CAAC,KAAK,SAAS,CAAC;gBACtB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;oBACtB,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;oBACzB,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC9D,CAAC;YACH,CAAC,CAAC,EAAE;SACL,CAAC;IACJ,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"group_by.js","sourceRoot":"","sources":["../../../../src/commands/stdlib/group_by.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,IAAI,EAAE,SAAS;IACf,IAAI,EAAE;QACL,WAAW,EAAE,2CAA2C;QACxD,UAAU,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACX,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qCAAqC,EAAE;gBAC3E,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;aAC/C;YACD,QAAQ,EAAE,CAAC,KAAK,CAAC;SACjB;QACD,WAAW,EAAE,EAAE;KACf;IACD,IAAI;QACH,OAAO,CACN,oCAAoC;YACpC,UAAU;YACV,gCAAgC;YAChC,WAAW;YACX,+CAA+C;YAC/C,UAAU;YACV,0DAA0D,CAC1D,CAAC;IACH,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAO;QAC7B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAExD,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8C,CAAC;QAErE,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACxC,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpB,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YAC3C,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QAED,OAAO;YACN,MAAM,EAAE,CAAC,KAAK,SAAS,CAAC;gBACvB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;oBACjC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC7D,CAAC;YACF,CAAC,CAAC,EAAE;SACJ,CAAC;IACH,CAAC;CACD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"head.js","sourceRoot":"","sources":["../../../../src/commands/stdlib/head.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE;QACJ,WAAW,EAAE,oBAAoB;QACjC,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yBAAyB,EAAE,OAAO,EAAE,EAAE,EAAE;gBAC1E,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;aAChD;YACD,QAAQ,EAAE,EAAE;SACb;QACD,WAAW,EAAE,EAAE;KAChB;IACD,IAAI;QACF,OAAO,sDAAsD,CAAC;IAChE,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;QACvB,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAE5F,OAAO;YACL,MAAM,EAAE,CAAC,KAAK,SAAS,CAAC;gBACtB,IAAI,CAAC,GAAG,CAAC,CAAC;gBACV,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBAC/B,IAAI,CAAC,EAAE,IAAI,CAAC;wBAAE,MAAM;oBACpB,MAAM,IAAI,CAAC;gBACb,CAAC;YACH,CAAC,CAAC,EAAE;SACL,CAAC;IACJ,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"head.js","sourceRoot":"","sources":["../../../../src/commands/stdlib/head.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE;QACL,WAAW,EAAE,oBAAoB;QACjC,UAAU,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACX,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yBAAyB,EAAE,OAAO,EAAE,EAAE,EAAE;gBAC1E,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;aAC/C;YACD,QAAQ,EAAE,EAAE;SACZ;QACD,WAAW,EAAE,EAAE;KACf;IACD,IAAI;QACH,OAAO,sDAAsD,CAAC;IAC/D,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;QACxB,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAE5F,OAAO;YACN,MAAM,EAAE,CAAC,KAAK,SAAS,CAAC;gBACvB,IAAI,CAAC,GAAG,CAAC,CAAC;gBACV,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBAChC,IAAI,CAAC,EAAE,IAAI,CAAC;wBAAE,MAAM;oBACpB,MAAM,IAAI,CAAC;gBACZ,CAAC;YACF,CAAC,CAAC,EAAE;SACJ,CAAC;IACH,CAAC;CACD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"json.js","sourceRoot":"","sources":["../../../../src/commands/stdlib/json.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE;QACJ,WAAW,EAAE,gCAAgC;QAC7C,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC5D,WAAW,EAAE,EAAE;KAChB;IACD,IAAI;QACF,OAAO,iEAAiE,CAAC;IAC3E,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE;QACtB,MAAM,KAAK,GAAG,EAAE,CAAC;QACjB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACnD,CAAC;CACF,CAAC;AAEF,KAAK,SAAS,CAAC,CAAC,WAAW,KAAI,CAAC"}
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../../../src/commands/stdlib/json.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE;QACL,WAAW,EAAE,gCAAgC;QAC7C,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC5D,WAAW,EAAE,EAAE;KACf;IACD,IAAI;QACH,OAAO,iEAAiE,CAAC;IAC1E,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE;QACvB,MAAM,KAAK,GAAG,EAAE,CAAC;QACjB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAClD,CAAC;CACD,CAAC;AAEF,KAAK,SAAS,CAAC,CAAC,WAAW,KAAI,CAAC"}
@@ -1,9 +1,11 @@
1
+ import { markLlmItem } from "../../core/llm_accounting.js";
1
2
  import path from "node:path";
2
3
  import { promises as fsp } from "node:fs";
3
4
  import { createHash } from "node:crypto";
4
5
  import { Ajv } from "ajv";
5
- import { ensureDirectory, isJsonSyntaxError, readStateJson, stableStringify, writeFileAtomic, writeStateJson, } from "../../state/store.js";
6
+ import { diffAndStore, ensureDirectory, atomicWriteWasPublished, isJsonSyntaxError, readStateJsonWithLock, stableStringify, withFileLock, writeFileAtomic, } from "../../state/store.js";
6
7
  import { createCompileCached } from "../../validation.js";
8
+ import { httpResponseLimitFromEnv, readResponseTextCapped } from "../../read_response_text.js";
7
9
  const ajv = new Ajv({ allErrors: true, strict: false });
8
10
  const compileCachedLocal = createCompileCached(ajv);
9
11
  const artifactSchema = {
@@ -89,6 +91,33 @@ const validatePayload = ajv.compile(payloadSchema);
89
91
  const validateResponseEnvelope = ajv.compile(responseSchema);
90
92
  const DEFAULT_MAX_VALIDATION_RETRIES = 1;
91
93
  const STATE_VERSION = 1;
94
+ // Identity version of the response cache key. See computeCacheKey.
95
+ const CACHE_KEY_VERSION = 2;
96
+ /**
97
+ * Opens a charge for a live call, and only for one that costs something. An answer that reports
98
+ * no usage is billed nowhere however many times it is replayed, so a charge for it would sit in
99
+ * the ledger with nothing to settle it -- and would be handed to the next caller with a real
100
+ * call to account for, leaving that one's charge open to be billed a second time.
101
+ */
102
+ function recordLiveCharge(ctx, cacheKey, item) {
103
+ const cost = chargeCostOf(item);
104
+ if (!cost)
105
+ return;
106
+ ledgerFrom(ctx)?.record(cacheKey, cost);
107
+ }
108
+ /** The cost of a live call, read from the item this module just built for it. */
109
+ function chargeCostOf(item) {
110
+ const usage = item?.usage;
111
+ if (!usage || typeof usage !== "object")
112
+ return undefined;
113
+ return { model: typeof item?.model === "string" ? item.model : null, usage };
114
+ }
115
+ function ledgerFrom(ctx) {
116
+ const ledger = ctx?.llmSpendLedger;
117
+ if (!ledger || typeof ledger !== "object")
118
+ return null;
119
+ return typeof ledger.record === "function" && typeof ledger.claim === "function" ? ledger : null;
120
+ }
92
121
  export const llmInvokeCommand = createLlmInvokeCommand({
93
122
  name: "llm.invoke",
94
123
  itemKind: "llm.invoke",
@@ -164,7 +193,7 @@ export function createLlmInvokeCommand(config) {
164
193
  "schema-version": { type: "string", description: "Logical schema version for caching" },
165
194
  "max-validation-retries": {
166
195
  type: "number",
167
- description: "Retries when schema validation fails",
196
+ description: "Extra model calls allowed after the first when schema validation fails (default 1)",
168
197
  },
169
198
  temperature: { type: "number", description: "Sampling temperature" },
170
199
  "max-output-tokens": { type: "number", description: "Max completion tokens" },
@@ -190,7 +219,8 @@ export function createLlmInvokeCommand(config) {
190
219
  "Features:",
191
220
  " - Typed payload validation before invoking the adapter.",
192
221
  " - Run-state + file cache so resumes do not re-call the LLM.",
193
- " - Optional JSON-schema enforcement with bounded retries.",
222
+ " - Optional JSON-schema enforcement, retried at most --max-validation-retries times",
223
+ " after the first call.",
194
224
  "",
195
225
  "Config:",
196
226
  ...config.helpConfig.map((line) => ` - ${line}`),
@@ -204,6 +234,10 @@ export function createLlmInvokeCommand(config) {
204
234
  }
205
235
  async function runLlmInvoke({ input, args, ctx, config, }) {
206
236
  const env = ctx.env ?? process.env;
237
+ const signal = ctx?.signal;
238
+ // Run-state and cache hits return before any adapter call, so a cancelled run
239
+ // would otherwise still finish as a success.
240
+ throwIfCancelled(signal);
207
241
  const provider = resolveProvider(args, env, config.defaultProvider, ctx);
208
242
  const adapter = resolveAdapter({ provider, env, args, config, ctx });
209
243
  const prompt = extractPrompt(args);
@@ -236,6 +270,9 @@ async function runLlmInvoke({ input, args, ctx, config, }) {
236
270
  const inputArtifacts = [];
237
271
  for await (const item of input)
238
272
  inputArtifacts.push(item);
273
+ // Draining pipeline input waits on the upstream step, so a timeout can fire
274
+ // here. Re-check before the reuse lookups below can answer with a success.
275
+ throwIfCancelled(signal);
239
276
  const normalizedArtifacts = [...inputArtifacts, ...providedArtifacts].map(normalizeArtifact);
240
277
  const artifactHashes = normalizedArtifacts.map(hashArtifact);
241
278
  const cacheKey = computeCacheKey({
@@ -245,21 +282,29 @@ async function runLlmInvoke({ input, args, ctx, config, }) {
245
282
  schemaVersion,
246
283
  artifactHashes,
247
284
  outputSchema: userOutputSchema,
285
+ temperature,
286
+ maxOutputTokens,
248
287
  });
249
288
  if (stateKey && !forceRefresh) {
250
- const stored = await readReusableLlmState(env, stateKey);
289
+ const stored = await readReusableLlmState(env, stateKey, ctx.signal);
290
+ // Reading run state is I/O of unbounded duration; a signal that aborted
291
+ // during it must not be overtaken by the replay below.
292
+ throwIfCancelled(signal);
251
293
  const reused = pickReusableState(stored, cacheKey, config.stateType);
252
294
  if (reused) {
295
+ const replay = { cacheKey, replayed: true };
253
296
  return {
254
- output: streamOf(reused.items.map((item) => ({ ...item, source: "run_state", cached: true }))),
297
+ output: streamOf(reused.items.map((item) => markLlmItem({ ...item, source: "run_state", cached: true, replayed: true }, replay))),
255
298
  };
256
299
  }
257
300
  }
258
301
  if (!disableCache && !forceRefresh) {
259
- const cache = await readCacheEntry(env, cacheKey, config.cacheNamespace);
302
+ const cache = await readCacheEntry(env, cacheKey, config.cacheNamespace, ctx.signal);
303
+ throwIfCancelled(signal);
260
304
  if (cache) {
305
+ const replay = { cacheKey, replayed: true };
261
306
  return {
262
- output: streamOf(cache.items.map((item) => ({ ...item, source: "cache", cached: true }))),
307
+ output: streamOf(cache.items.map((item) => markLlmItem({ ...item, source: "cache", cached: true, replayed: true }, replay))),
263
308
  };
264
309
  }
265
310
  }
@@ -286,6 +331,7 @@ async function runLlmInvoke({ input, args, ctx, config, }) {
286
331
  let attempt = 0;
287
332
  let lastValidationErrors = [];
288
333
  while (true) {
334
+ throwIfCancelled(signal);
289
335
  attempt += 1;
290
336
  if (attempt > 1) {
291
337
  payload.retryContext = {
@@ -298,9 +344,16 @@ async function runLlmInvoke({ input, args, ctx, config, }) {
298
344
  }
299
345
  let responseEnvelope;
300
346
  try {
301
- responseEnvelope = await adapter.invoke({ env, args, payload });
347
+ ctx.signal?.throwIfAborted();
348
+ responseEnvelope = await abortable(adapter.invoke({ env, args, payload, signal: ctx.signal }), signal);
349
+ ctx.signal?.throwIfAborted();
302
350
  }
303
351
  catch (err) {
352
+ // Cancellation is the caller's error, not an adapter failure: surface it
353
+ // as an abort so workflow timeout and abort handling still recognizes it,
354
+ // rather than wrapping it in a "request failed" adapter error.
355
+ if (signal?.aborted)
356
+ throw asCancellation(err, signal);
304
357
  throw new Error(`${config.name} request failed: ${err?.message ?? String(err)}`);
305
358
  }
306
359
  if (!validateResponseEnvelope(responseEnvelope)) {
@@ -319,37 +372,112 @@ async function runLlmInvoke({ input, args, ctx, config, }) {
319
372
  attempt,
320
373
  itemKind: config.itemKind,
321
374
  });
375
+ const live = { cacheKey, replayed: false };
376
+ for (const item of normalized)
377
+ markLlmItem(item, live);
378
+ // The provider has answered and been paid, so the charge is opened here rather than at
379
+ // either of the returns below. An attempt the local validator rejects never reaches one
380
+ // of them -- it goes round the loop and asks again -- and its call was as real as the one
381
+ // that eventually satisfies the schema. Opening it here also puts it before the writes
382
+ // that store the answer: either can fail once run state already holds a replayable copy,
383
+ // and the retry that replays it must still find a charge to settle.
384
+ recordLiveCharge(ctx, cacheKey, normalized[0]);
322
385
  if (!validator) {
386
+ ctx.signal?.throwIfAborted();
323
387
  await persistOutputs({
324
388
  env,
325
389
  stateKey,
326
390
  cacheKey,
327
391
  items: normalized,
328
392
  stateType: config.stateType,
393
+ signal: ctx.signal,
394
+ afterStore: disableCache
395
+ ? undefined
396
+ : () => writeCacheEntry(env, cacheKey, normalized, config.cacheNamespace, ctx.signal),
329
397
  });
330
- if (!disableCache)
331
- await writeCacheEntry(env, cacheKey, normalized, config.cacheNamespace);
332
398
  return { output: streamOf(normalized) };
333
399
  }
334
400
  const structured = normalized[0]?.output?.data ?? null;
335
401
  if (validator(structured)) {
402
+ ctx.signal?.throwIfAborted();
336
403
  await persistOutputs({
337
404
  env,
338
405
  stateKey,
339
406
  cacheKey,
340
407
  items: normalized,
341
408
  stateType: config.stateType,
409
+ signal: ctx.signal,
410
+ afterStore: disableCache
411
+ ? undefined
412
+ : () => writeCacheEntry(env, cacheKey, normalized, config.cacheNamespace, ctx.signal),
342
413
  });
343
- if (!disableCache)
344
- await writeCacheEntry(env, cacheKey, normalized, config.cacheNamespace);
345
414
  return { output: streamOf(normalized) };
346
415
  }
347
416
  lastValidationErrors = collectAjvErrors(validator.errors);
348
- if (attempt > maxValidationRetries + 1) {
417
+ if (attempt > maxValidationRetries) {
349
418
  throw new Error(`${config.name} output failed schema validation: ${lastValidationErrors.join("; ")}`);
350
419
  }
351
420
  }
352
421
  }
422
+ /**
423
+ * Build the error a cancelled run rejects with.
424
+ *
425
+ * The workflow runner only treats an error named `AbortError` or coded
426
+ * `ABORT_ERR` as cancellation; everything else follows the step's retry and
427
+ * `on_error` policy. A host may abort with any reason it likes, so passing
428
+ * `signal.reason` straight through means `controller.abort(new Error("stop"))`
429
+ * leaves a cancelled step looking like an ordinary failure -- retried, or
430
+ * swallowed by `on_error: continue` and reported as a successful run. Keep the
431
+ * host's message and hang its reason off `cause`, but mark the rejection so the
432
+ * runner recognizes it.
433
+ */
434
+ function cancellationError(signal) {
435
+ const reason = signal.reason;
436
+ if (reason === undefined || reason === null) {
437
+ return new DOMException("The operation was aborted.", "AbortError");
438
+ }
439
+ if (reason?.name === "AbortError" || reason?.code === "ABORT_ERR")
440
+ return reason;
441
+ const message = typeof reason?.message === "string" && reason.message ? reason.message : String(reason);
442
+ const error = new Error(message, { cause: reason });
443
+ error.name = "AbortError";
444
+ error.code = "ABORT_ERR";
445
+ return error;
446
+ }
447
+ /** Rethrow `err` when it already reads as cancellation, else the run's reason. */
448
+ function asCancellation(err, signal) {
449
+ if (err?.name === "AbortError" || err?.code === "ABORT_ERR")
450
+ return err;
451
+ return cancellationError(signal);
452
+ }
453
+ function throwIfCancelled(signal) {
454
+ if (signal?.aborted)
455
+ throw cancellationError(signal);
456
+ }
457
+ /**
458
+ * Reject as soon as the run is cancelled instead of waiting for `promise`.
459
+ * HTTP adapters are cancelled at the socket, but an injected `ctx.llmAdapters`
460
+ * adapter may ignore `ctx.signal` entirely; without this, one of those keeps a
461
+ * timed-out step waiting for as long as it likes.
462
+ */
463
+ function abortable(promise, signal) {
464
+ if (!signal)
465
+ return promise;
466
+ return new Promise((resolve, reject) => {
467
+ const onAbort = () => reject(cancellationError(signal));
468
+ // Observe `promise` before anything can settle the wrapper, including the
469
+ // already-aborted case below. An adapter can cancel the run from inside its own
470
+ // `invoke` and reject afterwards; rejecting here without watching that promise
471
+ // leaves the rejection unhandled, which ends the process under Node's default
472
+ // handling -- long after this step was cancelled cleanly.
473
+ promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
474
+ if (signal.aborted) {
475
+ onAbort();
476
+ return;
477
+ }
478
+ signal.addEventListener("abort", onAbort, { once: true });
479
+ });
480
+ }
353
481
  function resolveProvider(args, env, defaultProvider, ctx) {
354
482
  const explicit = String(args.provider ?? env.LOBSTER_LLM_PROVIDER ?? "")
355
483
  .trim()
@@ -385,11 +513,12 @@ function resolveAdapter({ provider, env, args, config, ctx, }) {
385
513
  return {
386
514
  provider,
387
515
  source: typeof direct === "function" ? provider : (direct.source ?? provider),
388
- async invoke({ payload }) {
389
- return invoke({ env, args, payload, ctx });
516
+ async invoke({ payload, signal }) {
517
+ return invoke({ env, args, payload, ctx, signal });
390
518
  },
391
519
  };
392
520
  }
521
+ const maxResponseBytes = httpResponseLimitFromEnv(env);
393
522
  if (provider === "openclaw") {
394
523
  const openclawUrl = String(env.OPENCLAW_URL ?? env.CLAWD_URL ?? "").trim();
395
524
  if (!openclawUrl) {
@@ -400,8 +529,8 @@ function resolveAdapter({ provider, env, args, config, ctx, }) {
400
529
  return {
401
530
  provider,
402
531
  source: config.sourceForProvider?.(provider) ?? "openclaw",
403
- async invoke({ payload }) {
404
- return invokeOpenClawAdapter({ endpoint, token, payload });
532
+ async invoke({ payload, signal }) {
533
+ return invokeOpenClawAdapter({ endpoint, token, payload, signal, maxResponseBytes });
405
534
  },
406
535
  };
407
536
  }
@@ -414,8 +543,14 @@ function resolveAdapter({ provider, env, args, config, ctx, }) {
414
543
  return {
415
544
  provider,
416
545
  source: config.sourceForProvider?.(provider) ?? "pi",
417
- async invoke({ payload }) {
418
- return invokeHttpAdapter({ endpoint: buildAdapterEndpoint(adapterUrl), token, payload });
546
+ async invoke({ payload, signal }) {
547
+ return invokeHttpAdapter({
548
+ endpoint: buildAdapterEndpoint(adapterUrl),
549
+ maxResponseBytes,
550
+ token,
551
+ payload,
552
+ signal,
553
+ });
419
554
  },
420
555
  };
421
556
  }
@@ -427,8 +562,14 @@ function resolveAdapter({ provider, env, args, config, ctx, }) {
427
562
  return {
428
563
  provider,
429
564
  source: config.sourceForProvider?.(provider) ?? "http",
430
- async invoke({ payload }) {
431
- return invokeHttpAdapter({ endpoint: buildAdapterEndpoint(adapterUrl), token, payload });
565
+ async invoke({ payload, signal }) {
566
+ return invokeHttpAdapter({
567
+ endpoint: buildAdapterEndpoint(adapterUrl),
568
+ maxResponseBytes,
569
+ token,
570
+ payload,
571
+ signal,
572
+ });
432
573
  },
433
574
  };
434
575
  }
@@ -451,9 +592,10 @@ function buildAdapterEndpoint(rawUrl) {
451
592
  }
452
593
  return endpoint;
453
594
  }
454
- async function invokeOpenClawAdapter({ endpoint, token, payload, }) {
595
+ async function invokeOpenClawAdapter({ endpoint, token, payload, signal, maxResponseBytes, }) {
455
596
  const res = await fetch(endpoint, {
456
597
  method: "POST",
598
+ signal,
457
599
  headers: {
458
600
  "content-type": "application/json",
459
601
  ...(token ? { authorization: `Bearer ${token}` } : null),
@@ -464,7 +606,7 @@ async function invokeOpenClawAdapter({ endpoint, token, payload, }) {
464
606
  args: payload,
465
607
  }),
466
608
  });
467
- const text = await res.text();
609
+ const text = await readResponseTextCapped(res, maxResponseBytes);
468
610
  if (!res.ok) {
469
611
  throw new Error(`${res.status} ${res.statusText}: ${text.slice(0, 400)}`);
470
612
  }
@@ -488,16 +630,17 @@ async function invokeOpenClawAdapter({ endpoint, token, payload, }) {
488
630
  }
489
631
  return { ok: true, result: parsed };
490
632
  }
491
- async function invokeHttpAdapter({ endpoint, token, payload, }) {
633
+ async function invokeHttpAdapter({ endpoint, token, payload, signal, maxResponseBytes, }) {
492
634
  const res = await fetch(endpoint, {
493
635
  method: "POST",
636
+ signal,
494
637
  headers: {
495
638
  "content-type": "application/json",
496
639
  ...(token ? { authorization: `Bearer ${token}` } : null),
497
640
  },
498
641
  body: JSON.stringify(payload),
499
642
  });
500
- const text = await res.text();
643
+ const text = await readResponseTextCapped(res, maxResponseBytes);
501
644
  if (!res.ok) {
502
645
  throw new Error(`${res.status} ${res.statusText}: ${text.slice(0, 400)}`);
503
646
  }
@@ -603,14 +746,24 @@ function hashArtifact(artifact) {
603
746
  const stable = stableStringify(artifact);
604
747
  return createHash("sha256").update(stable).digest("hex");
605
748
  }
606
- function computeCacheKey({ provider, prompt, model, schemaVersion, artifactHashes, outputSchema, }) {
749
+ function computeCacheKey({ provider, prompt, model, schemaVersion, artifactHashes, outputSchema, temperature, maxOutputTokens, }) {
750
+ // `null` is the identity of an omitted parameter, distinct from any value a caller can
751
+ // pass, so an omitted request can never resolve to an entry written with an explicit one.
752
+ // The version separates this identity from the keys earlier releases wrote, where the two
753
+ // parameters were absent from the payload and a sampled answer therefore shared a key with
754
+ // an unsampled request. Bump it whenever the fields below change: entries under older
755
+ // versions become unreachable, which costs one re-invocation and never a wrong replay.
607
756
  const payload = {
757
+ cacheKeyVersion: CACHE_KEY_VERSION,
608
758
  provider,
609
759
  prompt,
610
760
  model: model || `${provider}-default`,
611
761
  schemaVersion,
612
762
  artifactHashes,
613
763
  outputSchema: outputSchema ?? null,
764
+ // The same predicate that decides whether each parameter is sent to the adapter.
765
+ temperature: Number.isFinite(temperature ?? NaN) ? Number(temperature) : null,
766
+ maxOutputTokens: Number.isFinite(maxOutputTokens ?? NaN) ? Number(maxOutputTokens) : null,
614
767
  };
615
768
  return createHash("sha256").update(stableStringify(payload)).digest("hex");
616
769
  }
@@ -646,9 +799,11 @@ function normalizeResult({ envelope, cacheKey, schemaVersion, artifactHashes, so
646
799
  };
647
800
  return [item];
648
801
  }
649
- async function persistOutputs({ env, stateKey, cacheKey, items, stateType, }) {
650
- if (!stateKey)
802
+ async function persistOutputs({ env, stateKey, cacheKey, items, stateType, signal, afterStore, }) {
803
+ if (!stateKey) {
804
+ await afterStore?.();
651
805
  return;
806
+ }
652
807
  const record = {
653
808
  type: stateType,
654
809
  version: STATE_VERSION,
@@ -656,11 +811,17 @@ async function persistOutputs({ env, stateKey, cacheKey, items, stateType, }) {
656
811
  items,
657
812
  storedAt: new Date().toISOString(),
658
813
  };
659
- await writeStateJson({ env, key: stateKey, value: record });
814
+ await diffAndStore({
815
+ env,
816
+ key: stateKey,
817
+ value: record,
818
+ signal,
819
+ afterStore: afterStore ? () => afterStore() : undefined,
820
+ });
660
821
  }
661
- async function readReusableLlmState(env, stateKey) {
822
+ async function readReusableLlmState(env, stateKey, signal) {
662
823
  try {
663
- return await readStateJson({ env, key: stateKey });
824
+ return await readStateJsonWithLock({ env, key: stateKey, signal });
664
825
  }
665
826
  catch (err) {
666
827
  if (isJsonSyntaxError(err))
@@ -684,28 +845,85 @@ function collectAjvErrors(errors) {
684
845
  return [];
685
846
  return errors.map((err) => `${err.instancePath || "/"} ${err.message ?? ""}`.trim());
686
847
  }
687
- async function readCacheEntry(env, key, cacheNamespace) {
848
+ async function readCacheEntry(env, key, cacheNamespace, signal) {
688
849
  const filePath = path.join(getCacheDir(env), cacheNamespace, `${key}.json`);
850
+ const read = async () => {
851
+ try {
852
+ const text = await fsp.readFile(filePath, "utf8");
853
+ const parsed = JSON.parse(text);
854
+ if (parsed?.cacheKey !== key || !Array.isArray(parsed.items))
855
+ return null;
856
+ return parsed;
857
+ }
858
+ catch (err) {
859
+ if (err?.code === "ENOENT")
860
+ return null;
861
+ if (isJsonSyntaxError(err))
862
+ return null;
863
+ throw err;
864
+ }
865
+ };
689
866
  try {
690
- const text = await fsp.readFile(filePath, "utf8");
691
- const parsed = JSON.parse(text);
692
- if (parsed?.cacheKey !== key || !Array.isArray(parsed.items))
693
- return null;
694
- return parsed;
867
+ return await withFileLock({ filePath, signal, task: read });
695
868
  }
696
869
  catch (err) {
697
- if (err?.code === "ENOENT")
698
- return null;
699
- if (isJsonSyntaxError(err))
870
+ if (err?.code === "ENOENT" || err?.code === "ENOTDIR")
700
871
  return null;
872
+ // A cache mounted read-only cannot have an active local writer because a
873
+ // writer first creates the same coordination lock. Preserve its reusable
874
+ // entries instead of requiring a lock-directory write for a read.
875
+ if (["EACCES", "EPERM", "EROFS"].includes(err?.code))
876
+ return read();
701
877
  throw err;
702
878
  }
703
879
  }
704
- async function writeCacheEntry(env, key, items, cacheNamespace) {
880
+ async function writeCacheEntry(env, key, items, cacheNamespace, signal) {
705
881
  const dir = path.join(getCacheDir(env), cacheNamespace);
882
+ signal?.throwIfAborted();
706
883
  await ensureDirectory(dir);
707
884
  const filePath = path.join(dir, `${key}.json`);
708
- await writeFileAtomic(filePath, JSON.stringify({ items, cacheKey: key, storedAt: new Date().toISOString() }, null, 2) + "\n");
885
+ const content = JSON.stringify({ items, cacheKey: key, storedAt: new Date().toISOString() }, null, 2) + "\n";
886
+ await withFileLock({
887
+ filePath,
888
+ signal,
889
+ task: async () => {
890
+ let previousContent = null;
891
+ try {
892
+ previousContent = await fsp.readFile(filePath);
893
+ }
894
+ catch (err) {
895
+ if (err?.code !== "ENOENT")
896
+ throw err;
897
+ }
898
+ const restorePreviousContent = async () => {
899
+ // No cache reader or competing cache writer can observe this entry
900
+ // until this lock is released. Restore an entry replaced by a refresh;
901
+ // only remove the just-published file when none existed beforehand.
902
+ if (previousContent === null)
903
+ await fsp.rm(filePath, { force: true });
904
+ else
905
+ await writeFileAtomic(filePath, previousContent);
906
+ };
907
+ let cacheWasPublished = false;
908
+ try {
909
+ signal?.throwIfAborted();
910
+ const result = await writeFileAtomic(filePath, content, { signal });
911
+ cacheWasPublished = true;
912
+ if (result?.signalAbortedAfterCommit || signal?.aborted) {
913
+ await restorePreviousContent();
914
+ cacheWasPublished = false;
915
+ signal?.throwIfAborted();
916
+ throw new Error("LLM cache publication cancelled");
917
+ }
918
+ }
919
+ catch (err) {
920
+ if (cacheWasPublished || atomicWriteWasPublished(err)) {
921
+ await restorePreviousContent();
922
+ }
923
+ throw err;
924
+ }
925
+ },
926
+ });
709
927
  }
710
928
  function getCacheDir(env) {
711
929
  if (env?.LOBSTER_CACHE_DIR)