@plateforme-ai/lobster 2026.6.12

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 (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +353 -0
  3. package/VISION.md +249 -0
  4. package/bin/clawd.invoke.js +18 -0
  5. package/bin/lobster.js +24 -0
  6. package/bin/openclaw.invoke.js +21 -0
  7. package/dist/src/cli.js +793 -0
  8. package/dist/src/cli.js.map +1 -0
  9. package/dist/src/commands/commands_list.js +49 -0
  10. package/dist/src/commands/commands_list.js.map +1 -0
  11. package/dist/src/commands/registry.js +66 -0
  12. package/dist/src/commands/registry.js.map +1 -0
  13. package/dist/src/commands/stdlib/approve.js +77 -0
  14. package/dist/src/commands/stdlib/approve.js.map +1 -0
  15. package/dist/src/commands/stdlib/ask.js +171 -0
  16. package/dist/src/commands/stdlib/ask.js.map +1 -0
  17. package/dist/src/commands/stdlib/dedupe.js +55 -0
  18. package/dist/src/commands/stdlib/dedupe.js.map +1 -0
  19. package/dist/src/commands/stdlib/diff_last.js +35 -0
  20. package/dist/src/commands/stdlib/diff_last.js.map +1 -0
  21. package/dist/src/commands/stdlib/email_triage.js +279 -0
  22. package/dist/src/commands/stdlib/email_triage.js.map +1 -0
  23. package/dist/src/commands/stdlib/exec.js +130 -0
  24. package/dist/src/commands/stdlib/exec.js.map +1 -0
  25. package/dist/src/commands/stdlib/gog_gmail_search.js +94 -0
  26. package/dist/src/commands/stdlib/gog_gmail_search.js.map +1 -0
  27. package/dist/src/commands/stdlib/gog_gmail_send.js +104 -0
  28. package/dist/src/commands/stdlib/gog_gmail_send.js.map +1 -0
  29. package/dist/src/commands/stdlib/group_by.js +59 -0
  30. package/dist/src/commands/stdlib/group_by.js.map +1 -0
  31. package/dist/src/commands/stdlib/head.js +34 -0
  32. package/dist/src/commands/stdlib/head.js.map +1 -0
  33. package/dist/src/commands/stdlib/json.js +20 -0
  34. package/dist/src/commands/stdlib/json.js.map +1 -0
  35. package/dist/src/commands/stdlib/llm_invoke.js +758 -0
  36. package/dist/src/commands/stdlib/llm_invoke.js.map +1 -0
  37. package/dist/src/commands/stdlib/llm_task_invoke.js +2 -0
  38. package/dist/src/commands/stdlib/llm_task_invoke.js.map +1 -0
  39. package/dist/src/commands/stdlib/map.js +104 -0
  40. package/dist/src/commands/stdlib/map.js.map +1 -0
  41. package/dist/src/commands/stdlib/openclaw_invoke.js +136 -0
  42. package/dist/src/commands/stdlib/openclaw_invoke.js.map +1 -0
  43. package/dist/src/commands/stdlib/pick.js +45 -0
  44. package/dist/src/commands/stdlib/pick.js.map +1 -0
  45. package/dist/src/commands/stdlib/sort.js +86 -0
  46. package/dist/src/commands/stdlib/sort.js.map +1 -0
  47. package/dist/src/commands/stdlib/state.js +76 -0
  48. package/dist/src/commands/stdlib/state.js.map +1 -0
  49. package/dist/src/commands/stdlib/table.js +57 -0
  50. package/dist/src/commands/stdlib/table.js.map +1 -0
  51. package/dist/src/commands/stdlib/template.js +126 -0
  52. package/dist/src/commands/stdlib/template.js.map +1 -0
  53. package/dist/src/commands/stdlib/where.js +81 -0
  54. package/dist/src/commands/stdlib/where.js.map +1 -0
  55. package/dist/src/commands/types.js +2 -0
  56. package/dist/src/commands/types.js.map +1 -0
  57. package/dist/src/commands/workflows/workflows_list.js +24 -0
  58. package/dist/src/commands/workflows/workflows_list.js.map +1 -0
  59. package/dist/src/commands/workflows/workflows_run.js +74 -0
  60. package/dist/src/commands/workflows/workflows_run.js.map +1 -0
  61. package/dist/src/core/cost_tracker.js +119 -0
  62. package/dist/src/core/cost_tracker.js.map +1 -0
  63. package/dist/src/core/filters.js +102 -0
  64. package/dist/src/core/filters.js.map +1 -0
  65. package/dist/src/core/index.js +7 -0
  66. package/dist/src/core/index.js.map +1 -0
  67. package/dist/src/core/retry.js +89 -0
  68. package/dist/src/core/retry.js.map +1 -0
  69. package/dist/src/core/tool_runtime.js +289 -0
  70. package/dist/src/core/tool_runtime.js.map +1 -0
  71. package/dist/src/input_request.js +430 -0
  72. package/dist/src/input_request.js.map +1 -0
  73. package/dist/src/parser.js +145 -0
  74. package/dist/src/parser.js.map +1 -0
  75. package/dist/src/pipeline_resume_state.js +186 -0
  76. package/dist/src/pipeline_resume_state.js.map +1 -0
  77. package/dist/src/read_line.js +50 -0
  78. package/dist/src/read_line.js.map +1 -0
  79. package/dist/src/recipes/github/index.js +16 -0
  80. package/dist/src/recipes/github/index.js.map +1 -0
  81. package/dist/src/recipes/github/pr-monitor.js +248 -0
  82. package/dist/src/recipes/github/pr-monitor.js.map +1 -0
  83. package/dist/src/recipes/github/stages/pr-view.js +107 -0
  84. package/dist/src/recipes/github/stages/pr-view.js.map +1 -0
  85. package/dist/src/recipes/index.js +7 -0
  86. package/dist/src/recipes/index.js.map +1 -0
  87. package/dist/src/recipes/registry.js +30 -0
  88. package/dist/src/recipes/registry.js.map +1 -0
  89. package/dist/src/renderers/json.js +13 -0
  90. package/dist/src/renderers/json.js.map +1 -0
  91. package/dist/src/resume.js +179 -0
  92. package/dist/src/resume.js.map +1 -0
  93. package/dist/src/runtime.js +230 -0
  94. package/dist/src/runtime.js.map +1 -0
  95. package/dist/src/sdk/Lobster.js +402 -0
  96. package/dist/src/sdk/Lobster.js.map +1 -0
  97. package/dist/src/sdk/index.js +25 -0
  98. package/dist/src/sdk/index.js.map +1 -0
  99. package/dist/src/sdk/primitives/approve.js +47 -0
  100. package/dist/src/sdk/primitives/approve.js.map +1 -0
  101. package/dist/src/sdk/primitives/diff.js +156 -0
  102. package/dist/src/sdk/primitives/diff.js.map +1 -0
  103. package/dist/src/sdk/primitives/exec.js +167 -0
  104. package/dist/src/sdk/primitives/exec.js.map +1 -0
  105. package/dist/src/sdk/primitives/state.js +203 -0
  106. package/dist/src/sdk/primitives/state.js.map +1 -0
  107. package/dist/src/sdk/runtime.js +131 -0
  108. package/dist/src/sdk/runtime.js.map +1 -0
  109. package/dist/src/sdk/token.js +9 -0
  110. package/dist/src/sdk/token.js.map +1 -0
  111. package/dist/src/shell.js +39 -0
  112. package/dist/src/shell.js.map +1 -0
  113. package/dist/src/state/store.js +337 -0
  114. package/dist/src/state/store.js.map +1 -0
  115. package/dist/src/token.js +15 -0
  116. package/dist/src/token.js.map +1 -0
  117. package/dist/src/validation.js +38 -0
  118. package/dist/src/validation.js.map +1 -0
  119. package/dist/src/workflows/file.js +2405 -0
  120. package/dist/src/workflows/file.js.map +1 -0
  121. package/dist/src/workflows/github_pr_monitor.js +167 -0
  122. package/dist/src/workflows/github_pr_monitor.js.map +1 -0
  123. package/dist/src/workflows/graph.js +234 -0
  124. package/dist/src/workflows/graph.js.map +1 -0
  125. package/dist/src/workflows/registry.js +57 -0
  126. package/dist/src/workflows/registry.js.map +1 -0
  127. package/dist/test/approval_id.test.js +171 -0
  128. package/dist/test/approval_id.test.js.map +1 -0
  129. package/dist/test/approve_preview.test.js +38 -0
  130. package/dist/test/approve_preview.test.js.map +1 -0
  131. package/dist/test/clawd_invoke.test.js +124 -0
  132. package/dist/test/clawd_invoke.test.js.map +1 -0
  133. package/dist/test/clawd_invoke_legacy.test.js +63 -0
  134. package/dist/test/clawd_invoke_legacy.test.js.map +1 -0
  135. package/dist/test/cli_run_file_args_json.test.js +27 -0
  136. package/dist/test/cli_run_file_args_json.test.js.map +1 -0
  137. package/dist/test/commands_list.test.js +44 -0
  138. package/dist/test/commands_list.test.js.map +1 -0
  139. package/dist/test/condition_comparison.test.js +127 -0
  140. package/dist/test/condition_comparison.test.js.map +1 -0
  141. package/dist/test/core_tool_runtime.test.js +160 -0
  142. package/dist/test/core_tool_runtime.test.js.map +1 -0
  143. package/dist/test/cost_tracker.test.js +231 -0
  144. package/dist/test/cost_tracker.test.js.map +1 -0
  145. package/dist/test/dedupe.test.js +48 -0
  146. package/dist/test/dedupe.test.js.map +1 -0
  147. package/dist/test/diff_last.test.js +70 -0
  148. package/dist/test/diff_last.test.js.map +1 -0
  149. package/dist/test/doctor.test.js +19 -0
  150. package/dist/test/doctor.test.js.map +1 -0
  151. package/dist/test/dry_run.test.js +502 -0
  152. package/dist/test/dry_run.test.js.map +1 -0
  153. package/dist/test/email_triage.test.js +296 -0
  154. package/dist/test/email_triage.test.js.map +1 -0
  155. package/dist/test/exec_stdin.test.js +43 -0
  156. package/dist/test/exec_stdin.test.js.map +1 -0
  157. package/dist/test/for_each.test.js +228 -0
  158. package/dist/test/for_each.test.js.map +1 -0
  159. package/dist/test/github_pr_notify_format.test.js +19 -0
  160. package/dist/test/github_pr_notify_format.test.js.map +1 -0
  161. package/dist/test/github_pr_summary.test.js +41 -0
  162. package/dist/test/github_pr_summary.test.js.map +1 -0
  163. package/dist/test/group_by.test.js +43 -0
  164. package/dist/test/group_by.test.js.map +1 -0
  165. package/dist/test/llm_invoke.test.js +166 -0
  166. package/dist/test/llm_invoke.test.js.map +1 -0
  167. package/dist/test/llm_task_invoke.test.js +416 -0
  168. package/dist/test/llm_task_invoke.test.js.map +1 -0
  169. package/dist/test/map.test.js +41 -0
  170. package/dist/test/map.test.js.map +1 -0
  171. package/dist/test/multi_approval_resume.test.js +48 -0
  172. package/dist/test/multi_approval_resume.test.js.map +1 -0
  173. package/dist/test/on_error.test.js +151 -0
  174. package/dist/test/on_error.test.js.map +1 -0
  175. package/dist/test/openclaw_invoke_alias.test.js +13 -0
  176. package/dist/test/openclaw_invoke_alias.test.js.map +1 -0
  177. package/dist/test/parallel.test.js +184 -0
  178. package/dist/test/parallel.test.js.map +1 -0
  179. package/dist/test/parser.test.js +39 -0
  180. package/dist/test/parser.test.js.map +1 -0
  181. package/dist/test/read_line.test.js +25 -0
  182. package/dist/test/read_line.test.js.map +1 -0
  183. package/dist/test/request_input.test.js +946 -0
  184. package/dist/test/request_input.test.js.map +1 -0
  185. package/dist/test/resume.test.js +82 -0
  186. package/dist/test/resume.test.js.map +1 -0
  187. package/dist/test/sdk_lobster.test.js +177 -0
  188. package/dist/test/sdk_lobster.test.js.map +1 -0
  189. package/dist/test/shell.test.js +31 -0
  190. package/dist/test/shell.test.js.map +1 -0
  191. package/dist/test/sort.test.js +51 -0
  192. package/dist/test/sort.test.js.map +1 -0
  193. package/dist/test/state.test.js +336 -0
  194. package/dist/test/state.test.js.map +1 -0
  195. package/dist/test/step_retry.test.js +254 -0
  196. package/dist/test/step_retry.test.js.map +1 -0
  197. package/dist/test/step_timeout.test.js +154 -0
  198. package/dist/test/step_timeout.test.js.map +1 -0
  199. package/dist/test/template.test.js +46 -0
  200. package/dist/test/template.test.js.map +1 -0
  201. package/dist/test/template_filters.test.js +107 -0
  202. package/dist/test/template_filters.test.js.map +1 -0
  203. package/dist/test/tool_envelope_version.test.js +15 -0
  204. package/dist/test/tool_envelope_version.test.js.map +1 -0
  205. package/dist/test/tool_mode.test.js +83 -0
  206. package/dist/test/tool_mode.test.js.map +1 -0
  207. package/dist/test/validation.test.js +28 -0
  208. package/dist/test/validation.test.js.map +1 -0
  209. package/dist/test/workflow_args_env.test.js +41 -0
  210. package/dist/test/workflow_args_env.test.js.map +1 -0
  211. package/dist/test/workflow_composition.test.js +238 -0
  212. package/dist/test/workflow_composition.test.js.map +1 -0
  213. package/dist/test/workflow_file.test.js +1399 -0
  214. package/dist/test/workflow_file.test.js.map +1 -0
  215. package/dist/test/workflow_graph.test.js +97 -0
  216. package/dist/test/workflow_graph.test.js.map +1 -0
  217. package/dist/test/workflows.test.js +32 -0
  218. package/dist/test/workflows.test.js.map +1 -0
  219. package/package.json +75 -0
@@ -0,0 +1,336 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { mkdtempSync, promises as fsp } from "node:fs";
6
+ import { createDefaultRegistry } from "../src/commands/registry.js";
7
+ import { runPipeline } from "../src/runtime.js";
8
+ import { diffLast, diffAndStoreValue } from "../src/sdk/primitives/diff.js";
9
+ import { stateSet, readState, writeState } from "../src/sdk/primitives/state.js";
10
+ import { createApprovalIndex, diffAndStore, writeStateJson, readStateJson, writeFileAtomic, writeFileAtomicExclusive, } from "../src/state/store.js";
11
+ function streamOf(items) {
12
+ return (async function* () {
13
+ for (const item of items)
14
+ yield item;
15
+ })();
16
+ }
17
+ test("state.set writes and state.get reads", async () => {
18
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-state-"));
19
+ const registry = createDefaultRegistry();
20
+ const env = { ...process.env, LOBSTER_STATE_DIR: tmp };
21
+ // write
22
+ const setCmd = registry.get("state.set");
23
+ await setCmd.run({
24
+ input: streamOf([{ a: 1 }]),
25
+ args: { _: ["demo-key"] },
26
+ ctx: {
27
+ stdin: process.stdin,
28
+ stdout: process.stdout,
29
+ stderr: process.stderr,
30
+ env,
31
+ registry,
32
+ mode: "tool",
33
+ render: { json() { }, lines() { } },
34
+ },
35
+ });
36
+ // read
37
+ const getCmd = registry.get("state.get");
38
+ const res = await getCmd.run({
39
+ input: streamOf([]),
40
+ args: { _: ["demo-key"] },
41
+ ctx: {
42
+ stdin: process.stdin,
43
+ stdout: process.stdout,
44
+ stderr: process.stderr,
45
+ env,
46
+ registry,
47
+ mode: "tool",
48
+ render: { json() { }, lines() { } },
49
+ },
50
+ });
51
+ const items = [];
52
+ for await (const it of res.output)
53
+ items.push(it);
54
+ assert.deepEqual(items, [{ a: 1 }]);
55
+ });
56
+ test("state.get returns null for missing key", async () => {
57
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-state-"));
58
+ const registry = createDefaultRegistry();
59
+ const env = { ...process.env, LOBSTER_STATE_DIR: tmp };
60
+ const output = await runPipeline({
61
+ pipeline: [{ name: "state.get", args: { _: ["missing"] }, raw: "state.get missing" }],
62
+ registry,
63
+ input: [],
64
+ stdin: process.stdin,
65
+ stdout: process.stdout,
66
+ stderr: process.stderr,
67
+ env,
68
+ mode: "tool",
69
+ });
70
+ assert.deepEqual(output.items, [null]);
71
+ });
72
+ // --- Atomic-write behavior proofs (issues #108, #109) ---
73
+ //
74
+ // Plain fsp.writeFile truncates the target before writing, so a concurrent
75
+ // reader (or a crash mid-write) can observe an empty/partial file and fail to
76
+ // JSON.parse it. These tests drive many large writes while reading in parallel
77
+ // and assert the reader NEVER sees a truncated value. They fail against the
78
+ // pre-fix non-atomic writeFile and pass with writeFileAtomic (stage + rename).
79
+ test("writeStateJson is atomic: concurrent reads never observe truncated state (#108)", async () => {
80
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-store-"));
81
+ const env = { LOBSTER_STATE_DIR: tmp };
82
+ const key = "pipeline-resume";
83
+ const payload = "x".repeat(256 * 1024); // large enough that writeFile is not instantaneous
84
+ await writeStateJson({ env, key, value: { payload, n: 0 } });
85
+ let readErrors = 0;
86
+ let partialReads = 0;
87
+ const reader = (async () => {
88
+ for (let i = 0; i < 500; i++) {
89
+ try {
90
+ const v = await readStateJson({ env, key });
91
+ if (!v || v.payload !== payload)
92
+ partialReads++;
93
+ }
94
+ catch {
95
+ readErrors++; // JSON.parse on truncated content throws SyntaxError
96
+ }
97
+ }
98
+ })();
99
+ const writer = (async () => {
100
+ for (let n = 1; n <= 150; n++) {
101
+ await writeStateJson({ env, key, value: { payload, n } });
102
+ }
103
+ })();
104
+ await Promise.all([reader, writer]);
105
+ assert.equal(readErrors, 0, "reader must never hit a parse/IO error mid-write");
106
+ assert.equal(partialReads, 0, "reader must never observe truncated/empty state");
107
+ const leftovers = (await fsp.readdir(tmp)).filter((f) => f.includes(".tmp"));
108
+ assert.deepEqual(leftovers, [], "atomic write must not leave temp files behind");
109
+ });
110
+ test("writeFileAtomic creates private files and preserves existing modes", async () => {
111
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-mode-"));
112
+ const freshPath = path.join(tmp, "fresh.json");
113
+ const existingPath = path.join(tmp, "existing.json");
114
+ await writeFileAtomic(freshPath, '{"ok":true}\n');
115
+ assert.equal((await fsp.stat(freshPath)).mode & 0o777, 0o600);
116
+ await fsp.writeFile(existingPath, '{"old":true}\n', { mode: 0o640 });
117
+ await fsp.chmod(existingPath, 0o640);
118
+ await writeFileAtomic(existingPath, '{"ok":true}\n');
119
+ assert.equal((await fsp.stat(existingPath)).mode & 0o777, 0o640);
120
+ });
121
+ test("writeFileAtomic removes temp files when replacement fails", async () => {
122
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-cleanup-"));
123
+ const targetDir = path.join(tmp, "state.json");
124
+ await fsp.mkdir(targetDir);
125
+ await assert.rejects(() => writeFileAtomic(targetDir, '{"ok":true}\n'));
126
+ const leftovers = (await fsp.readdir(tmp)).filter((f) => f.includes(".tmp"));
127
+ assert.deepEqual(leftovers, []);
128
+ });
129
+ test("writeFileAtomic leaves existing target untouched when publish fails", async () => {
130
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-fault-"));
131
+ const target = path.join(tmp, "state.json");
132
+ await fsp.writeFile(target, '{"old":true}\n', { mode: 0o600 });
133
+ const fault = Object.assign(new Error("rename failed"), { code: "EIO" });
134
+ await assert.rejects(() => writeFileAtomic(target, '{"new":true}\n', {
135
+ async renameFile() {
136
+ throw fault;
137
+ },
138
+ }), (err) => err?.code === "EIO");
139
+ assert.equal(await fsp.readFile(target, "utf8"), '{"old":true}\n');
140
+ const leftovers = (await fsp.readdir(tmp)).filter((f) => f.includes(".tmp"));
141
+ assert.deepEqual(leftovers, []);
142
+ });
143
+ test("writeFileAtomic propagates parent directory sync failures", async () => {
144
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-dir-sync-"));
145
+ const target = path.join(tmp, "state.json");
146
+ const fault = Object.assign(new Error("dir sync failed"), { code: "EIO" });
147
+ await assert.rejects(() => writeFileAtomic(target, '{"ok":true}\n', {
148
+ async syncParentDir() {
149
+ throw fault;
150
+ },
151
+ }), (err) => err?.code === "EIO");
152
+ assert.equal(await fsp.readFile(target, "utf8"), '{"ok":true}\n');
153
+ const leftovers = (await fsp.readdir(tmp)).filter((f) => f.includes(".tmp"));
154
+ assert.deepEqual(leftovers, []);
155
+ });
156
+ test("readStateJson surfaces malformed authoritative state", async () => {
157
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-state-corrupt-"));
158
+ const env = { LOBSTER_STATE_DIR: tmp };
159
+ await fsp.writeFile(path.join(tmp, "resume.json"), '{"partial"', "utf8");
160
+ await assert.rejects(() => readStateJson({ env, key: "resume" }), SyntaxError);
161
+ });
162
+ test("writeFileAtomicExclusive creates private files without replacing existing targets", async () => {
163
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-exclusive-"));
164
+ const target = path.join(tmp, "approval_deadbeef.json");
165
+ await writeFileAtomicExclusive(target, '{"stateKey":"original"}\n');
166
+ assert.equal((await fsp.stat(target)).mode & 0o777, 0o600);
167
+ await assert.rejects(() => writeFileAtomicExclusive(target, '{"stateKey":"replacement"}\n'), (err) => err?.code === "EEXIST");
168
+ assert.equal(await fsp.readFile(target, "utf8"), '{"stateKey":"original"}\n');
169
+ const leftovers = (await fsp.readdir(tmp)).filter((f) => f.includes(".tmp"));
170
+ assert.deepEqual(leftovers, []);
171
+ });
172
+ test("writeFileAtomicExclusive removes temp link before final directory sync", async () => {
173
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-exclusive-sync-order-"));
174
+ const target = path.join(tmp, "approval_deadbeef.json");
175
+ let filesAtSync = [];
176
+ await writeFileAtomicExclusive(target, '{"stateKey":"original"}\n', {
177
+ async syncParentDir() {
178
+ filesAtSync = await fsp.readdir(tmp);
179
+ },
180
+ });
181
+ assert.equal(await fsp.readFile(target, "utf8"), '{"stateKey":"original"}\n');
182
+ assert.ok(filesAtSync.includes("approval_deadbeef.json"));
183
+ assert.deepEqual(filesAtSync.filter((file) => file.includes(".tmp")), []);
184
+ });
185
+ test("writeFileAtomicExclusive rejects unsupported hard links without a partial target", async () => {
186
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-exclusive-unsupported-"));
187
+ const target = path.join(tmp, "approval_deadbeef.json");
188
+ const unsupported = Object.assign(new Error("operation not supported"), { code: "ENOTSUP" });
189
+ const options = {
190
+ async linkFile() {
191
+ throw unsupported;
192
+ },
193
+ };
194
+ await assert.rejects(() => writeFileAtomicExclusive(target, '{"stateKey":"original"}\n', options), (err) => err?.code === "ENOTSUP");
195
+ await assert.rejects(() => fsp.stat(target), (err) => err?.code === "ENOENT");
196
+ const leftovers = (await fsp.readdir(tmp)).filter((f) => f.includes(".tmp"));
197
+ assert.deepEqual(leftovers, []);
198
+ });
199
+ test("writeFileAtomicExclusive removes published target when parent directory sync fails", async () => {
200
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-exclusive-dir-sync-"));
201
+ const target = path.join(tmp, "approval_deadbeef.json");
202
+ const fault = Object.assign(new Error("dir sync failed"), { code: "EIO" });
203
+ await assert.rejects(() => writeFileAtomicExclusive(target, '{"stateKey":"original"}\n', {
204
+ async syncParentDir() {
205
+ throw fault;
206
+ },
207
+ }), (err) => err?.code === "EIO");
208
+ await assert.rejects(() => fsp.stat(target), (err) => err?.code === "ENOENT");
209
+ const leftovers = (await fsp.readdir(tmp)).filter((f) => f.includes(".tmp"));
210
+ assert.deepEqual(leftovers, []);
211
+ });
212
+ test("createApprovalIndex omits short ID when atomic exclusive publish is unsupported", async () => {
213
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-approval-index-unsupported-"));
214
+ const env = { LOBSTER_STATE_DIR: tmp };
215
+ const unsupported = Object.assign(new Error("operation not supported"), { code: "ENOTSUP" });
216
+ const approvalId = await createApprovalIndex({
217
+ env,
218
+ stateKey: "workflow_resume_1",
219
+ options: {
220
+ async linkFile() {
221
+ throw unsupported;
222
+ },
223
+ },
224
+ });
225
+ assert.equal(approvalId, null);
226
+ const files = await fsp.readdir(tmp);
227
+ assert.deepEqual(files, []);
228
+ });
229
+ test("createApprovalIndex omits short ID when approval index durability fails", async () => {
230
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-approval-index-sync-fails-"));
231
+ const env = { LOBSTER_STATE_DIR: tmp };
232
+ const fault = Object.assign(new Error("dir sync failed"), { code: "EIO" });
233
+ const approvalId = await createApprovalIndex({
234
+ env,
235
+ stateKey: "workflow_resume_1",
236
+ options: {
237
+ async syncParentDir() {
238
+ throw fault;
239
+ },
240
+ },
241
+ });
242
+ assert.equal(approvalId, null);
243
+ const files = await fsp.readdir(tmp);
244
+ assert.deepEqual(files, []);
245
+ });
246
+ test("diffAndStore treats corrupt previous state as a miss and rewrites atomically (#112)", async () => {
247
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-diff-corrupt-"));
248
+ const env = { LOBSTER_STATE_DIR: tmp };
249
+ await fsp.writeFile(path.join(tmp, "snapshot.json"), '{"partial"', "utf8");
250
+ const result = await diffAndStore({ env, key: "snapshot", value: { ok: true } });
251
+ assert.equal(result.before, null);
252
+ assert.equal(result.changed, true);
253
+ assert.deepEqual(await readStateJson({ env, key: "snapshot" }), { ok: true });
254
+ });
255
+ test("SDK diff primitives treat corrupt previous state as a miss (#112)", async () => {
256
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-sdk-diff-corrupt-"));
257
+ const ctx = { env: { LOBSTER_STATE_DIR: tmp } };
258
+ await fsp.writeFile(path.join(tmp, "sdk-snapshot.json"), '{"partial"', "utf8");
259
+ const direct = await diffAndStoreValue("sdk-snapshot", { next: true }, ctx);
260
+ assert.equal(direct.before, null);
261
+ assert.equal(direct.changed, true);
262
+ await fsp.writeFile(path.join(tmp, "stage-snapshot.json"), '{"partial"', "utf8");
263
+ const stage = diffLast("stage-snapshot");
264
+ const result = await stage.run({ input: streamOf([{ next: true }]), ctx });
265
+ const output = [];
266
+ for await (const item of result.output)
267
+ output.push(item);
268
+ assert.deepEqual(output, [
269
+ {
270
+ kind: "diff.last",
271
+ key: "stage-snapshot",
272
+ changed: true,
273
+ before: null,
274
+ after: { next: true },
275
+ },
276
+ ]);
277
+ });
278
+ test("SDK stateSet/readState is atomic under concurrent reads (#109)", async () => {
279
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-sdk-"));
280
+ const ctx = { env: { LOBSTER_STATE_DIR: tmp } };
281
+ const key = "sdk-state";
282
+ const payload = "y".repeat(256 * 1024);
283
+ const writeOnce = async (n) => {
284
+ const prim = stateSet(key);
285
+ const input = (async function* () {
286
+ yield { payload, n };
287
+ })();
288
+ const res = await prim.run({ input, ctx });
289
+ for await (const _ of res.output) {
290
+ void _;
291
+ }
292
+ };
293
+ await writeOnce(0);
294
+ let readErrors = 0;
295
+ let partialReads = 0;
296
+ const reader = (async () => {
297
+ for (let i = 0; i < 500; i++) {
298
+ try {
299
+ const v = await readState(key, ctx);
300
+ if (!v || v.payload !== payload)
301
+ partialReads++;
302
+ }
303
+ catch {
304
+ readErrors++;
305
+ }
306
+ }
307
+ })();
308
+ const writer = (async () => {
309
+ for (let n = 1; n <= 120; n++) {
310
+ await writeOnce(n);
311
+ }
312
+ })();
313
+ await Promise.all([reader, writer]);
314
+ assert.equal(readErrors, 0, "SDK reader must never hit a parse/IO error mid-write");
315
+ assert.equal(partialReads, 0, "SDK reader must never observe truncated/empty state");
316
+ });
317
+ test("SDK writeState preserves restricted state-file mode", async () => {
318
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-sdk-mode-"));
319
+ const ctx = { env: { LOBSTER_STATE_DIR: tmp } };
320
+ const filePath = path.join(tmp, "sdk-state.json");
321
+ await fsp.mkdir(tmp, { recursive: true });
322
+ await fsp.writeFile(filePath, '{"old":true}\n', { mode: 0o600 });
323
+ await fsp.chmod(filePath, 0o600);
324
+ await writeState("sdk-state", { ok: true }, ctx);
325
+ assert.equal((await fsp.stat(filePath)).mode & 0o777, 0o600);
326
+ assert.deepEqual(await readState("sdk-state", ctx), { ok: true });
327
+ });
328
+ test("SDK writeState removes temp files when replacement fails", async () => {
329
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "lobster-atomic-sdk-cleanup-"));
330
+ const ctx = { env: { LOBSTER_STATE_DIR: tmp } };
331
+ await fsp.mkdir(path.join(tmp, "sdk-state.json"));
332
+ await assert.rejects(() => writeState("sdk-state", { ok: true }, ctx));
333
+ const leftovers = (await fsp.readdir(tmp)).filter((f) => f.includes(".tmp"));
334
+ assert.deepEqual(leftovers, []);
335
+ });
336
+ //# sourceMappingURL=state.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.test.js","sourceRoot":"","sources":["../../test/state.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,254 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { promises as fsp } from "node:fs";
4
+ import path from "node:path";
5
+ import os from "node:os";
6
+ import { PassThrough } from "node:stream";
7
+ import { runWorkflowFile, loadWorkflowFile } from "../src/workflows/file.js";
8
+ import { withRetry, resolveRetryConfig } from "../src/core/retry.js";
9
+ // --- Retry utility unit tests ---
10
+ test("resolveRetryConfig fills defaults", () => {
11
+ const config = resolveRetryConfig(undefined);
12
+ assert.equal(config.max, 1);
13
+ assert.equal(config.backoff, "fixed");
14
+ assert.equal(config.delay_ms, 1000);
15
+ assert.equal(config.max_delay_ms, 30000);
16
+ assert.equal(config.jitter, false);
17
+ });
18
+ test("resolveRetryConfig preserves provided values", () => {
19
+ const config = resolveRetryConfig({ max: 5, backoff: "exponential", jitter: true });
20
+ assert.equal(config.max, 5);
21
+ assert.equal(config.backoff, "exponential");
22
+ assert.equal(config.jitter, true);
23
+ assert.equal(config.delay_ms, 1000); // default
24
+ });
25
+ test("withRetry succeeds on first try", async () => {
26
+ let calls = 0;
27
+ const result = await withRetry(async () => {
28
+ calls++;
29
+ return "ok";
30
+ }, resolveRetryConfig({ max: 3 }));
31
+ assert.equal(result, "ok");
32
+ assert.equal(calls, 1);
33
+ });
34
+ test("withRetry retries and succeeds", async () => {
35
+ let calls = 0;
36
+ const result = await withRetry(async () => {
37
+ calls++;
38
+ if (calls < 3)
39
+ throw new Error("transient");
40
+ return "ok";
41
+ }, resolveRetryConfig({ max: 3, delay_ms: 10 }));
42
+ assert.equal(result, "ok");
43
+ assert.equal(calls, 3);
44
+ });
45
+ test("withRetry throws after max exhausted", async () => {
46
+ let calls = 0;
47
+ await assert.rejects(withRetry(async () => {
48
+ calls++;
49
+ throw new Error("always fails");
50
+ }, resolveRetryConfig({ max: 2, delay_ms: 10 })), /always fails/);
51
+ assert.equal(calls, 2);
52
+ });
53
+ test("withRetry never retries external abort errors", async () => {
54
+ let calls = 0;
55
+ const controller = new AbortController();
56
+ controller.abort();
57
+ const abortErr = new DOMException("aborted", "AbortError");
58
+ await assert.rejects(withRetry(async () => {
59
+ calls++;
60
+ throw abortErr;
61
+ }, resolveRetryConfig({ max: 3, delay_ms: 10 }), { signal: controller.signal }), (err) => err.name === "AbortError");
62
+ assert.equal(calls, 1);
63
+ });
64
+ test("withRetry retries per-attempt timeout AbortErrors when external signal is not aborted", async () => {
65
+ let calls = 0;
66
+ const timeoutAbortErr = new DOMException("step timed out", "AbortError");
67
+ // No external signal — per-attempt timeout abort should be retriable
68
+ const result = await withRetry(async () => {
69
+ calls++;
70
+ if (calls < 3)
71
+ throw timeoutAbortErr;
72
+ return "recovered";
73
+ }, resolveRetryConfig({ max: 3, delay_ms: 10 }));
74
+ assert.equal(result, "recovered");
75
+ assert.equal(calls, 3);
76
+ });
77
+ test("withRetry calls onRetry callback", async () => {
78
+ const retries = [];
79
+ let calls = 0;
80
+ await withRetry(async () => {
81
+ calls++;
82
+ if (calls < 3)
83
+ throw new Error("fail");
84
+ return "ok";
85
+ }, resolveRetryConfig({ max: 3, delay_ms: 10 }), { onRetry: (attempt) => retries.push(attempt) });
86
+ assert.deepEqual(retries, [1, 2]);
87
+ });
88
+ // --- Validation tests ---
89
+ async function loadWorkflow(workflow) {
90
+ const tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), "lobster-retry-"));
91
+ const filePath = path.join(tmpDir, "workflow.lobster");
92
+ await fsp.writeFile(filePath, JSON.stringify(workflow), "utf8");
93
+ return loadWorkflowFile(filePath);
94
+ }
95
+ test("retry validation rejects non-object", async () => {
96
+ await assert.rejects(loadWorkflow({ name: "bad", steps: [{ id: "x", command: "echo", retry: "yes" }] }), /retry must be an object/);
97
+ });
98
+ test("retry validation rejects non-integer max", async () => {
99
+ await assert.rejects(loadWorkflow({ name: "bad", steps: [{ id: "x", command: "echo", retry: { max: 1.5 } }] }), /retry.max must be a positive integer/);
100
+ });
101
+ test("retry validation rejects max < 1", async () => {
102
+ await assert.rejects(loadWorkflow({ name: "bad", steps: [{ id: "x", command: "echo", retry: { max: 0 } }] }), /retry.max must be a positive integer/);
103
+ });
104
+ test("retry validation rejects invalid backoff", async () => {
105
+ await assert.rejects(loadWorkflow({
106
+ name: "bad",
107
+ steps: [{ id: "x", command: "echo", retry: { backoff: "linear" } }],
108
+ }), /retry.backoff must be "fixed" or "exponential"/);
109
+ });
110
+ test("retry validation rejects negative delay_ms", async () => {
111
+ await assert.rejects(loadWorkflow({ name: "bad", steps: [{ id: "x", command: "echo", retry: { delay_ms: -1 } }] }), /retry.delay_ms must be a finite non-negative number/);
112
+ });
113
+ test("retry validation rejects non-boolean jitter", async () => {
114
+ await assert.rejects(loadWorkflow({ name: "bad", steps: [{ id: "x", command: "echo", retry: { jitter: "yes" } }] }), /retry.jitter must be a boolean/);
115
+ });
116
+ test("retry validation accepts valid config", async () => {
117
+ const wf = await loadWorkflow({
118
+ name: "ok",
119
+ steps: [
120
+ {
121
+ id: "x",
122
+ command: "echo",
123
+ retry: { max: 3, backoff: "exponential", delay_ms: 500, jitter: true },
124
+ },
125
+ ],
126
+ });
127
+ assert.ok(wf.steps[0].retry);
128
+ });
129
+ // --- Integration tests ---
130
+ async function runWorkflow(workflow) {
131
+ const tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), "lobster-retry-"));
132
+ const stateDir = path.join(tmpDir, "state");
133
+ const filePath = path.join(tmpDir, "workflow.lobster");
134
+ await fsp.writeFile(filePath, JSON.stringify(workflow, null, 2), "utf8");
135
+ const stderrChunks = [];
136
+ const stderr = new PassThrough();
137
+ stderr.on("data", (d) => stderrChunks.push(d.toString()));
138
+ const result = await runWorkflowFile({
139
+ filePath,
140
+ ctx: {
141
+ stdin: process.stdin,
142
+ stdout: process.stdout,
143
+ stderr,
144
+ env: { ...process.env, LOBSTER_STATE_DIR: stateDir },
145
+ mode: "tool",
146
+ },
147
+ });
148
+ return { result, stderrOutput: stderrChunks.join("") };
149
+ }
150
+ test("step retries and succeeds after transient failure", async () => {
151
+ // Script that fails twice then succeeds on third run using a counter file
152
+ const tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), "lobster-retry-"));
153
+ const counterFile = path.join(tmpDir, "counter");
154
+ await fsp.writeFile(counterFile, "0", "utf8");
155
+ const workflow = {
156
+ name: "retry-succeed",
157
+ steps: [
158
+ {
159
+ id: "flaky",
160
+ command: `node -e "const fs=require('fs');const c=Number(fs.readFileSync('${counterFile}','utf8'))+1;fs.writeFileSync('${counterFile}',String(c));if(c<3){process.exit(1);}process.stdout.write(JSON.stringify({attempt:c}))"`,
161
+ retry: { max: 3, delay_ms: 50 },
162
+ },
163
+ ],
164
+ };
165
+ const { result, stderrOutput } = await runWorkflow(workflow);
166
+ assert.equal(result.status, "ok");
167
+ const output = result.output;
168
+ assert.equal(output[0].attempt, 3);
169
+ assert.ok(stderrOutput.includes("[RETRY]"), "should log retry attempts");
170
+ });
171
+ test("step with timeout_ms + retry retries on per-attempt timeout (issue #105)", async () => {
172
+ // Behavior proof: a step that hangs past timeout_ms on its first two attempts
173
+ // must be retried (per-attempt timeout produces an AbortError that should NOT
174
+ // bypass retry) and succeed on the third. Pre-fix, withRetry short-circuited on
175
+ // any AbortError, so retry.max was inert for timed-out steps and this ran once.
176
+ const tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), "lobster-retry-"));
177
+ const counterFile = path.join(tmpDir, "counter");
178
+ await fsp.writeFile(counterFile, "0", "utf8");
179
+ // The counter is incremented synchronously before the hang, so each timed-out
180
+ // (SIGKILLed) attempt is still recorded. Attempts 1-2 hang 8s (killed by the
181
+ // 3000ms timeout); attempt 3 returns immediately. The timeout leaves enough
182
+ // room for slow CI machines to start Node and write the counter before kill.
183
+ const workflow = {
184
+ name: "retry-timeout",
185
+ steps: [
186
+ {
187
+ id: "slow",
188
+ command: `node -e "const fs=require('fs');const c=Number(fs.readFileSync('${counterFile}','utf8'))+1;fs.writeFileSync('${counterFile}',String(c));if(c<3){setTimeout(()=>{},6000);}else{process.stdout.write(JSON.stringify({attempt:c}));}"`,
189
+ timeout_ms: 3000,
190
+ retry: { max: 3, delay_ms: 50 },
191
+ },
192
+ ],
193
+ };
194
+ const { result, stderrOutput } = await runWorkflow(workflow);
195
+ assert.equal(result.status, "ok");
196
+ const output = result.output;
197
+ assert.equal(output[0].attempt, 3);
198
+ assert.ok(stderrOutput.includes("[RETRY]"), "should log retry attempts on timeout");
199
+ });
200
+ test("step exhausts retries and throws", async () => {
201
+ const workflow = {
202
+ name: "retry-exhaust",
203
+ steps: [
204
+ {
205
+ id: "fail",
206
+ command: 'node -e "process.exit(1)"',
207
+ retry: { max: 2, delay_ms: 50 },
208
+ },
209
+ ],
210
+ };
211
+ await assert.rejects(runWorkflow(workflow).then((r) => r.result), /workflow command failed/);
212
+ });
213
+ test("step without retry fails immediately (no retry)", async () => {
214
+ const workflow = {
215
+ name: "no-retry",
216
+ steps: [{ id: "fail", command: 'node -e "process.exit(1)"' }],
217
+ };
218
+ await assert.rejects(runWorkflow(workflow).then((r) => r.result), /workflow command failed/);
219
+ });
220
+ test("dry-run renders retry config", async () => {
221
+ const tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), "lobster-retry-"));
222
+ const stateDir = path.join(tmpDir, "state");
223
+ const filePath = path.join(tmpDir, "workflow.lobster");
224
+ const workflow = {
225
+ name: "dry-run-retry",
226
+ steps: [
227
+ {
228
+ id: "fetch",
229
+ command: "curl https://example.com",
230
+ retry: { max: 3, backoff: "exponential", delay_ms: 1000 },
231
+ },
232
+ ],
233
+ };
234
+ await fsp.writeFile(filePath, JSON.stringify(workflow, null, 2), "utf8");
235
+ const stderrChunks = [];
236
+ const stderr = new PassThrough();
237
+ stderr.on("data", (d) => stderrChunks.push(d.toString()));
238
+ await runWorkflowFile({
239
+ filePath,
240
+ ctx: {
241
+ stdin: process.stdin,
242
+ stdout: process.stdout,
243
+ stderr,
244
+ env: { ...process.env, LOBSTER_STATE_DIR: stateDir },
245
+ mode: "tool",
246
+ dryRun: true,
247
+ },
248
+ });
249
+ const output = stderrChunks.join("");
250
+ assert.ok(output.includes("retry:"), "should show retry config");
251
+ assert.ok(output.includes("3 attempts"), "should show max attempts");
252
+ assert.ok(output.includes("exponential"), "should show backoff type");
253
+ });
254
+ //# sourceMappingURL=step_retry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step_retry.test.js","sourceRoot":"","sources":["../../test/step_retry.test.ts"],"names":[],"mappings":"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"}