@openclaw/lobster 2026.9.5 → 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.
- package/node_modules/@clawdbot/lobster/README.md +20 -133
- package/node_modules/@clawdbot/lobster/VISION.md +15 -238
- package/node_modules/@clawdbot/lobster/bin/clawd.invoke.js +2 -21
- package/node_modules/@clawdbot/lobster/bin/invoke.js +19 -0
- package/node_modules/@clawdbot/lobster/bin/lobster.js +2 -21
- package/node_modules/@clawdbot/lobster/bin/openclaw.invoke.js +2 -24
- package/node_modules/@clawdbot/lobster/dist/src/cli.js +32 -48
- package/node_modules/@clawdbot/lobster/dist/src/cli.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/command_tokens.js +81 -0
- package/node_modules/@clawdbot/lobster/dist/src/command_tokens.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/dedupe.js +1 -12
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/dedupe.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/group_by.js +2 -14
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/group_by.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/llm_invoke.js +1 -337
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/llm_invoke.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/map.js +2 -13
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/map.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/sort.js +4 -21
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/sort.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/state.js +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/state.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/template.js +2 -13
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/template.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/workflows/workflows_run.js +0 -18
- package/node_modules/@clawdbot/lobster/dist/src/commands/workflows/workflows_run.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/core/llm_accounting.js +304 -0
- package/node_modules/@clawdbot/lobster/dist/src/core/llm_accounting.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/core/value_path.js +11 -0
- package/node_modules/@clawdbot/lobster/dist/src/core/value_path.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/parser.js +2 -67
- package/node_modules/@clawdbot/lobster/dist/src/parser.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/index.js +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/pr-monitor.js +5 -64
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/pr-monitor.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/read_pr.js +45 -0
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/read_pr.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/snapshot.js +45 -0
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/snapshot.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/stages/pr-view.js +9 -47
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/stages/pr-view.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/Lobster.js +18 -135
- package/node_modules/@clawdbot/lobster/dist/src/sdk/Lobster.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/index.js +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/approve.js +1 -2
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/approve.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/diff.js +2 -8
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/diff.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/exec.js +15 -81
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/exec.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/state.js +3 -9
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/state.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/runtime.js +0 -12
- package/node_modules/@clawdbot/lobster/dist/src/sdk/runtime.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/state_env.js +6 -0
- package/node_modules/@clawdbot/lobster/dist/src/sdk/state_env.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/state/atomic_file.js +192 -0
- package/node_modules/@clawdbot/lobster/dist/src/state/atomic_file.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/state/store.js +2 -188
- package/node_modules/@clawdbot/lobster/dist/src/state/store.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/workflows/dry_run.js +262 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/dry_run.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/expressions.js +345 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/expressions.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/file.js +21 -1036
- package/node_modules/@clawdbot/lobster/dist/src/workflows/file.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/workflows/github_pr_monitor.js +8 -77
- package/node_modules/@clawdbot/lobster/dist/src/workflows/github_pr_monitor.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/workflows/graph.js +1 -7
- package/node_modules/@clawdbot/lobster/dist/src/workflows/graph.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/workflows/load.js +347 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/load.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/types.js +2 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/types.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/values.js +93 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/values.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/cancellation.test.js +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/cancellation.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/cli_run_errors.test.js +53 -0
- package/node_modules/@clawdbot/lobster/dist/test/cli_run_errors.test.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/command_arguments.test.js +82 -0
- package/node_modules/@clawdbot/lobster/dist/test/command_arguments.test.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/cost_tracker.test.js +49 -19
- package/node_modules/@clawdbot/lobster/dist/test/cost_tracker.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/helpers/node_shell_fixture.js +17 -0
- package/node_modules/@clawdbot/lobster/dist/test/helpers/node_shell_fixture.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/llm_invoke.test.js +2 -0
- package/node_modules/@clawdbot/lobster/dist/test/llm_invoke.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/pipeline_resume_state.test.js +65 -0
- package/node_modules/@clawdbot/lobster/dist/test/pipeline_resume_state.test.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/process_output_limit.test.js +5 -3
- package/node_modules/@clawdbot/lobster/dist/test/process_output_limit.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/sdk_exec.test.js +72 -25
- package/node_modules/@clawdbot/lobster/dist/test/sdk_exec.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/workflow_graph.test.js +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/workflow_graph.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/package.json +6 -8
- package/package.json +5 -5
- package/node_modules/@clawdbot/lobster/dist/src/sdk/token.js +0 -9
- package/node_modules/@clawdbot/lobster/dist/src/sdk/token.js.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { stateEnv } from "../state_env.js";
|
|
1
2
|
/**
|
|
2
3
|
* Diff primitive - Compare current value against last stored value
|
|
3
4
|
*
|
|
4
5
|
* @example
|
|
5
|
-
* import { Lobster, diffLast } from 'lobster
|
|
6
|
+
* import { Lobster, diffLast } from '@clawdbot/lobster';
|
|
6
7
|
*
|
|
7
8
|
* new Lobster()
|
|
8
9
|
* .pipe(fetchPRStatus())
|
|
@@ -14,11 +15,6 @@
|
|
|
14
15
|
* });
|
|
15
16
|
*/
|
|
16
17
|
import { diffAndStore } from "../../state/store.js";
|
|
17
|
-
function stateEnv(ctx) {
|
|
18
|
-
return ctx?.stateDir
|
|
19
|
-
? { ...(ctx?.env ?? process.env), LOBSTER_STATE_DIR: ctx.stateDir }
|
|
20
|
-
: (ctx?.env ?? process.env);
|
|
21
|
-
}
|
|
22
18
|
/**
|
|
23
19
|
* Create a diff.last stage
|
|
24
20
|
*
|
|
@@ -38,7 +34,6 @@ export function diffLast(key, options = {}) {
|
|
|
38
34
|
type: "diff.last",
|
|
39
35
|
key,
|
|
40
36
|
async run({ input, ctx }) {
|
|
41
|
-
// Collect all input items
|
|
42
37
|
const items = [];
|
|
43
38
|
for await (const item of input) {
|
|
44
39
|
items.push(item);
|
|
@@ -50,7 +45,6 @@ export function diffLast(key, options = {}) {
|
|
|
50
45
|
value,
|
|
51
46
|
signal: ctx?.signal,
|
|
52
47
|
});
|
|
53
|
-
// Build result
|
|
54
48
|
const result = {
|
|
55
49
|
kind: "diff.last",
|
|
56
50
|
key,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../../src/sdk/primitives/diff.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../../src/sdk/primitives/diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAQ,EAAE;IAC9C,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAErD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,KAAK,IAAI,CAAC;IAEjD,OAAO;QACN,IAAI,EAAE,WAAW;QACjB,GAAG;QAEH,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE;YACvB,MAAM,KAAK,GAAG,EAAE,CAAC;YACjB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAEpD,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,YAAY,CAAC;gBACrD,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC;gBAClB,GAAG;gBACH,KAAK;gBACL,MAAM,EAAE,GAAG,EAAE,MAAM;aACnB,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG;gBACd,IAAI,EAAE,WAAW;gBACjB,GAAG;gBACH,OAAO;gBACP,MAAM;gBACN,KAAK;aACL,CAAC;YAEF,yDAAyD;YACzD,IAAI,WAAW,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC7B,OAAO;oBACN,MAAM,EAAE,CAAC,KAAK,SAAS,CAAC;wBACvB,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;oBACpE,CAAC,CAAC,EAAE;iBACJ,CAAC;YACH,CAAC;YAED,OAAO;gBACN,MAAM,EAAE,CAAC,KAAK,SAAS,CAAC;oBACvB,MAAM,MAAM,CAAC;gBACd,CAAC,CAAC,EAAE;aACJ,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,GAAQ,EAAE;IAChE,OAAO,YAAY,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -1,80 +1,18 @@
|
|
|
1
|
+
import { tokenizeCommand } from "../../command_tokens.js";
|
|
1
2
|
/**
|
|
2
3
|
* Exec primitive - Execute shell commands and return JSON output
|
|
3
4
|
*
|
|
4
5
|
* @example
|
|
5
|
-
* import { Lobster, exec } from 'lobster
|
|
6
|
+
* import { Lobster, exec } from '@clawdbot/lobster';
|
|
6
7
|
*
|
|
7
8
|
* new Lobster()
|
|
8
9
|
* .pipe(exec('gh pr view 123 --repo owner/repo --json title,url'))
|
|
9
10
|
* .pipe(items => items.filter(e => e.unread))
|
|
10
11
|
*/
|
|
11
12
|
import { runAbortableProcess } from "../../abortable_process.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* @param {string} command
|
|
16
|
-
* @param {string[]} argv
|
|
17
|
-
* @param {Object} options
|
|
18
|
-
* @returns {Promise<{stdout: string, stderr: string}>}
|
|
19
|
-
*/
|
|
20
|
-
async function runProcess(command, argv, { env, cwd, signal, forceTerminationSignal }) {
|
|
21
|
-
const { stdout, stderr, code } = await runAbortableProcess({
|
|
22
|
-
command,
|
|
23
|
-
argv,
|
|
24
|
-
env,
|
|
25
|
-
cwd,
|
|
26
|
-
signal,
|
|
27
|
-
forceTerminationSignal,
|
|
28
|
-
notFoundMessage: `Failed to execute ${command}: spawn ${command} ENOENT`,
|
|
29
|
-
});
|
|
30
|
-
if (code === 0) {
|
|
31
|
-
return { stdout, stderr };
|
|
32
|
-
}
|
|
33
|
-
throw new Error(`${command} exited with code ${code}: ${stderr.trim() || stdout.trim()}`);
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Parse a shell command string into command and arguments
|
|
37
|
-
* Simple parsing - for complex cases, use options.shell
|
|
38
|
-
* @param {string} cmdString
|
|
39
|
-
* @returns {{command: string, args: string[]}}
|
|
40
|
-
*/
|
|
41
|
-
function parseCommand(cmdString) {
|
|
42
|
-
const tokens = [];
|
|
43
|
-
let current = "";
|
|
44
|
-
let quote = null;
|
|
45
|
-
for (let i = 0; i < cmdString.length; i++) {
|
|
46
|
-
const ch = cmdString[i];
|
|
47
|
-
if (quote) {
|
|
48
|
-
if (ch === "\\" && cmdString[i + 1]) {
|
|
49
|
-
current += cmdString[i + 1];
|
|
50
|
-
i++;
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
if (ch === quote) {
|
|
54
|
-
quote = null;
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
current += ch;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (ch === '"' || ch === "'") {
|
|
61
|
-
quote = ch;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
if (ch === " " || ch === "\t") {
|
|
65
|
-
if (current.length > 0) {
|
|
66
|
-
tokens.push(current);
|
|
67
|
-
current = "";
|
|
68
|
-
}
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
current += ch;
|
|
72
|
-
}
|
|
73
|
-
if (current.length > 0) {
|
|
74
|
-
tokens.push(current);
|
|
75
|
-
}
|
|
76
|
-
const [command, ...args] = tokens;
|
|
77
|
-
return { command, args };
|
|
13
|
+
function parseCommand(command) {
|
|
14
|
+
const [executable, ...argv] = tokenizeCommand(command, "sdk");
|
|
15
|
+
return { command: executable, argv };
|
|
78
16
|
}
|
|
79
17
|
/**
|
|
80
18
|
* Create an exec stage
|
|
@@ -99,24 +37,20 @@ export function exec(cmdString, options = {}) {
|
|
|
99
37
|
// no-op
|
|
100
38
|
}
|
|
101
39
|
const env = ctx.env ?? process.env;
|
|
102
|
-
|
|
103
|
-
const
|
|
40
|
+
const invocation = useShell ? { shellCommand: cmdString } : parseCommand(cmdString);
|
|
41
|
+
const command = "command" in invocation ? invocation.command : cmdString;
|
|
42
|
+
const { stdout, stderr, code } = await runAbortableProcess({
|
|
43
|
+
...invocation,
|
|
104
44
|
env,
|
|
105
45
|
cwd,
|
|
106
46
|
signal: ctx.signal,
|
|
107
47
|
forceTerminationSignal: ctx.forceTerminationSignal,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
// Direct execution
|
|
117
|
-
const { command, args } = parseCommand(cmdString);
|
|
118
|
-
const result = await runProcess(command, args, processOptions);
|
|
119
|
-
stdout = result.stdout;
|
|
48
|
+
notFoundMessage: useShell
|
|
49
|
+
? "exec shell not found; check LOBSTER_SHELL or ComSpec"
|
|
50
|
+
: `Failed to execute ${command}: spawn ${command} ENOENT`,
|
|
51
|
+
});
|
|
52
|
+
if (code !== 0) {
|
|
53
|
+
throw new Error(`${command} exited with code ${code}: ${stderr.trim() || stdout.trim()}`);
|
|
120
54
|
}
|
|
121
55
|
// Parse output
|
|
122
56
|
let output;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../../src/sdk/primitives/exec.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../../src/sdk/primitives/exec.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { stateEnv } from "../state_env.js";
|
|
1
2
|
/**
|
|
2
3
|
* State primitives - Persistent state management
|
|
3
4
|
*
|
|
4
5
|
* @example
|
|
5
|
-
* import { Lobster, stateGet, stateSet } from 'lobster
|
|
6
|
+
* import { Lobster, stateGet, stateSet } from '@clawdbot/lobster';
|
|
6
7
|
*
|
|
7
8
|
* // Read state
|
|
8
9
|
* new Lobster()
|
|
@@ -15,11 +16,6 @@
|
|
|
15
16
|
* .pipe(stateSet('my-key'));
|
|
16
17
|
*/
|
|
17
18
|
import { readStateJsonWithLock, writeStateJson } from "../../state/store.js";
|
|
18
|
-
function stateEnv(ctx) {
|
|
19
|
-
return ctx?.stateDir
|
|
20
|
-
? { ...(ctx?.env ?? process.env), LOBSTER_STATE_DIR: ctx.stateDir }
|
|
21
|
-
: (ctx?.env ?? process.env);
|
|
22
|
-
}
|
|
23
19
|
/**
|
|
24
20
|
* Create a state.get stage
|
|
25
21
|
*
|
|
@@ -59,14 +55,12 @@ export function stateSet(key) {
|
|
|
59
55
|
type: "state.set",
|
|
60
56
|
key,
|
|
61
57
|
async run({ input, ctx }) {
|
|
62
|
-
// Collect all input items
|
|
63
58
|
const items = [];
|
|
64
59
|
for await (const item of input) {
|
|
65
60
|
items.push(item);
|
|
66
61
|
}
|
|
67
62
|
const value = items.length === 1 ? items[0] : items;
|
|
68
63
|
await writeStateJson({ env: stateEnv(ctx), key, value, signal: ctx?.signal });
|
|
69
|
-
// Pass through the value
|
|
70
64
|
return {
|
|
71
65
|
output: (async function* () {
|
|
72
66
|
yield value;
|
|
@@ -79,7 +73,7 @@ export function stateSet(key) {
|
|
|
79
73
|
* State namespace - provides get/set methods
|
|
80
74
|
*
|
|
81
75
|
* @example
|
|
82
|
-
* import { state } from 'lobster
|
|
76
|
+
* import { state } from '@clawdbot/lobster';
|
|
83
77
|
*
|
|
84
78
|
* new Lobster()
|
|
85
79
|
* .pipe(state.get('my-key'))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../../src/sdk/primitives/state.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../../src/sdk/primitives/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE7E;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAG;IAC3B,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAErD,OAAO;QACN,IAAI,EAAE,WAAW;QACjB,GAAG;QAEH,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE;YACvB,cAAc;YACd,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;gBACjC,QAAQ;YACT,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,qBAAqB,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;YAE5F,OAAO;gBACN,MAAM,EAAE,CAAC,KAAK,SAAS,CAAC;oBACvB,MAAM,KAAK,CAAC;gBACb,CAAC,CAAC,EAAE;aACJ,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAG;IAC3B,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAErD,OAAO;QACN,IAAI,EAAE,WAAW;QACjB,GAAG;QAEH,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE;YACvB,MAAM,KAAK,GAAG,EAAE,CAAC;YACjB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAEpD,MAAM,cAAc,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;YAE9E,OAAO;gBACN,MAAM,EAAE,CAAC,KAAK,SAAS,CAAC;oBACvB,MAAM,KAAK,CAAC;gBACb,CAAC,CAAC,EAAE;aACJ,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACpB,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,QAAQ;CACb,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAG,EAAE,GAAG,GAAQ,EAAE;IACjD,OAAO,qBAAqB,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,GAAQ,EAAE;IACzD,MAAM,cAAc,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;AAC/E,CAAC"}
|
|
@@ -5,18 +5,6 @@
|
|
|
5
5
|
* stay identical across CLI, tool mode, and SDK entry points.
|
|
6
6
|
*/
|
|
7
7
|
import { runPipeline as runCorePipeline } from "../runtime.js";
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {Object} StageResult
|
|
10
|
-
* @property {AsyncIterable|any[]} [output] - Output items
|
|
11
|
-
* @property {boolean} [halt] - Whether to halt the pipeline
|
|
12
|
-
* @property {boolean} [rendered] - Whether output was rendered
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* @typedef {Object} PipelineResult
|
|
16
|
-
* @property {any[]} items - Collected output items
|
|
17
|
-
* @property {boolean} halted - Whether pipeline halted
|
|
18
|
-
* @property {Object|null} haltedAt - Stage where halt occurred
|
|
19
|
-
*/
|
|
20
8
|
/**
|
|
21
9
|
* Collect async iterable to array
|
|
22
10
|
* @param {AsyncIterable} iterable
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/sdk/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,eAAe,CAAC;AAE/D
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/sdk/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state_env.js","sourceRoot":"","sources":["../../../src/sdk/state_env.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,QAAQ,CAAC,GAA+D;IACvF,OAAO,GAAG,EAAE,QAAQ;QACnB,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,iBAAiB,EAAE,GAAG,CAAC,QAAQ,EAAE;QACnE,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { promises as fsp } from "node:fs";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
function markAtomicWritePublished(err) {
|
|
5
|
+
if (err && (typeof err === "object" || typeof err === "function")) {
|
|
6
|
+
Object.defineProperty(err, "atomicWritePublished", {
|
|
7
|
+
value: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
return err;
|
|
12
|
+
}
|
|
13
|
+
export function atomicWriteWasPublished(err) {
|
|
14
|
+
return Boolean(err?.atomicWritePublished);
|
|
15
|
+
}
|
|
16
|
+
function isDirectorySyncUnsupportedError(err) {
|
|
17
|
+
return [
|
|
18
|
+
"EACCES",
|
|
19
|
+
"EBADF",
|
|
20
|
+
"EINVAL",
|
|
21
|
+
"EISDIR",
|
|
22
|
+
"ENOSYS",
|
|
23
|
+
"ENOTSUP",
|
|
24
|
+
"EOPNOTSUPP",
|
|
25
|
+
"EPERM",
|
|
26
|
+
].includes(err?.code);
|
|
27
|
+
}
|
|
28
|
+
async function syncParentDir(filePath) {
|
|
29
|
+
await syncDirectory(path.dirname(filePath));
|
|
30
|
+
}
|
|
31
|
+
async function syncDirectory(dir) {
|
|
32
|
+
let handle;
|
|
33
|
+
try {
|
|
34
|
+
handle = await fsp.open(dir, "r");
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
if (isDirectorySyncUnsupportedError(err))
|
|
38
|
+
return;
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
await handle.sync();
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (!isDirectorySyncUnsupportedError(err))
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
if (handle)
|
|
50
|
+
await handle.close().catch(() => { });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* On Windows, `fs.mkdir(..., { recursive: true })` reports the first created
|
|
55
|
+
* directory as an extended-length path (`\\?\C:\...`). `path.resolve` keeps
|
|
56
|
+
* that prefix, so such a path never compares equal to the plain drive path we
|
|
57
|
+
* walk toward and `path.relative` between the two yields an absolute path.
|
|
58
|
+
* Map the namespaces that have a plain equivalent back to it so both ends of the
|
|
59
|
+
* chain share one root form. The UNC marker is matched without regard to case,
|
|
60
|
+
* because Windows accepts a lowercase "unc" namespace component just as well.
|
|
61
|
+
*
|
|
62
|
+
* Device namespaces with no drive-letter or UNC equivalent, such as
|
|
63
|
+
* `\\?\Volume{GUID}\...`, are returned unchanged: stripping their prefix would
|
|
64
|
+
* leave a relative path and break an explicitly configured state directory.
|
|
65
|
+
*/
|
|
66
|
+
export function stripExtendedLengthPrefix(target) {
|
|
67
|
+
if (!target.startsWith("\\\\?\\"))
|
|
68
|
+
return target;
|
|
69
|
+
const rest = target.slice(4);
|
|
70
|
+
if (/^UNC\\/i.test(rest))
|
|
71
|
+
return `\\\\${rest.slice(4)}`;
|
|
72
|
+
if (/^[A-Za-z]:[\\/]/.test(rest))
|
|
73
|
+
return rest;
|
|
74
|
+
return target;
|
|
75
|
+
}
|
|
76
|
+
async function syncCreatedDirectoryChain(firstCreated, finalDir) {
|
|
77
|
+
const final = path.resolve(stripExtendedLengthPrefix(finalDir));
|
|
78
|
+
let current = path.resolve(stripExtendedLengthPrefix(firstCreated));
|
|
79
|
+
await syncDirectory(path.dirname(current));
|
|
80
|
+
while (current !== final) {
|
|
81
|
+
await syncDirectory(current);
|
|
82
|
+
const relative = path.relative(current, final);
|
|
83
|
+
const next = relative.split(path.sep)[0];
|
|
84
|
+
if (!next || next === "..")
|
|
85
|
+
break;
|
|
86
|
+
current = path.join(current, next);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export async function ensureDirectory(dir) {
|
|
90
|
+
const created = await fsp.mkdir(dir, { recursive: true });
|
|
91
|
+
if (created)
|
|
92
|
+
await syncCreatedDirectoryChain(created, dir);
|
|
93
|
+
}
|
|
94
|
+
function isLinkUnsupportedError(err) {
|
|
95
|
+
return ["ENOSYS", "ENOTSUP", "EOPNOTSUPP", "EPERM", "EXDEV"].includes(err?.code);
|
|
96
|
+
}
|
|
97
|
+
export function isAtomicExclusiveUnsupportedError(err) {
|
|
98
|
+
return err?.code === "ENOTSUP" && err?.cause && isLinkUnsupportedError(err.cause);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Write a file atomically: stage to a sibling temp file, fsync, then rename
|
|
102
|
+
* over the target. `rename(2)` is atomic on a single filesystem, so a reader
|
|
103
|
+
* (or a crash) never observes a truncated/partial file — it sees either the
|
|
104
|
+
* complete old content or the complete new content. Plain `fsp.writeFile`
|
|
105
|
+
* truncates the target up front, leaving a corruption window on SIGKILL/OOM/
|
|
106
|
+
* power loss. New state files are private by default; existing file modes are
|
|
107
|
+
* preserved across replacement. The temp file is removed on any failed path.
|
|
108
|
+
*/
|
|
109
|
+
export async function writeFileAtomic(filePath, data, options = {}) {
|
|
110
|
+
const renameFile = options.renameFile ?? fsp.rename;
|
|
111
|
+
const syncDir = options.syncParentDir ?? syncParentDir;
|
|
112
|
+
const dir = path.dirname(filePath);
|
|
113
|
+
const tmpPath = path.join(dir, `.${path.basename(filePath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
114
|
+
let mode = 0o600;
|
|
115
|
+
let handle;
|
|
116
|
+
let cleanup = true;
|
|
117
|
+
try {
|
|
118
|
+
try {
|
|
119
|
+
mode = (await fsp.stat(filePath)).mode & 0o777;
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
if (err?.code !== "ENOENT")
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
handle = await fsp.open(tmpPath, "wx", mode);
|
|
126
|
+
await handle.writeFile(data, "utf8");
|
|
127
|
+
await handle.chmod(mode);
|
|
128
|
+
await handle.sync();
|
|
129
|
+
await handle.close();
|
|
130
|
+
handle = undefined;
|
|
131
|
+
options.signal?.throwIfAborted();
|
|
132
|
+
await renameFile(tmpPath, filePath);
|
|
133
|
+
cleanup = false;
|
|
134
|
+
// The rename is the irreversible publication point. Keep propagating a
|
|
135
|
+
// directory-sync failure, but mark it so a state transition can reconcile
|
|
136
|
+
// the visible replacement before deciding whether dispatch is safe.
|
|
137
|
+
try {
|
|
138
|
+
await syncDir(filePath);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
throw markAtomicWritePublished(err);
|
|
142
|
+
}
|
|
143
|
+
return { signalAbortedAfterCommit: options.signal?.aborted === true };
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
if (handle)
|
|
147
|
+
await handle.close().catch(() => { });
|
|
148
|
+
if (cleanup)
|
|
149
|
+
await fsp.rm(tmpPath, { force: true }).catch(() => { });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export async function writeFileAtomicExclusive(filePath, data, options = {}) {
|
|
153
|
+
const linkFile = options.linkFile ?? fsp.link;
|
|
154
|
+
const syncDir = options.syncParentDir ?? syncParentDir;
|
|
155
|
+
const dir = path.dirname(filePath);
|
|
156
|
+
const tmpPath = path.join(dir, `.${path.basename(filePath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
157
|
+
let handle;
|
|
158
|
+
try {
|
|
159
|
+
handle = await fsp.open(tmpPath, "wx", 0o600);
|
|
160
|
+
await handle.writeFile(data, "utf8");
|
|
161
|
+
await handle.chmod(0o600);
|
|
162
|
+
await handle.sync();
|
|
163
|
+
await handle.close();
|
|
164
|
+
handle = undefined;
|
|
165
|
+
try {
|
|
166
|
+
await linkFile(tmpPath, filePath);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
if (!isLinkUnsupportedError(err))
|
|
170
|
+
throw err;
|
|
171
|
+
const unsupported = new Error("Atomic exclusive file creation requires hard-link support on this filesystem");
|
|
172
|
+
unsupported.code = "ENOTSUP";
|
|
173
|
+
unsupported.cause = err;
|
|
174
|
+
throw unsupported;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
await fsp.unlink(tmpPath);
|
|
178
|
+
await syncDir(filePath);
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
await fsp.unlink(filePath).catch(() => { });
|
|
182
|
+
await syncDir(filePath).catch(() => { });
|
|
183
|
+
throw err;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
if (handle)
|
|
188
|
+
await handle.close().catch(() => { });
|
|
189
|
+
await fsp.rm(tmpPath, { force: true }).catch(() => { });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=atomic_file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic_file.js","sourceRoot":"","sources":["../../../src/state/atomic_file.ts"],"names":[],"mappings":"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"}
|