@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.
- package/LICENSE +21 -0
- package/README.md +353 -0
- package/VISION.md +249 -0
- package/bin/clawd.invoke.js +18 -0
- package/bin/lobster.js +24 -0
- package/bin/openclaw.invoke.js +21 -0
- package/dist/src/cli.js +793 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/commands_list.js +49 -0
- package/dist/src/commands/commands_list.js.map +1 -0
- package/dist/src/commands/registry.js +66 -0
- package/dist/src/commands/registry.js.map +1 -0
- package/dist/src/commands/stdlib/approve.js +77 -0
- package/dist/src/commands/stdlib/approve.js.map +1 -0
- package/dist/src/commands/stdlib/ask.js +171 -0
- package/dist/src/commands/stdlib/ask.js.map +1 -0
- package/dist/src/commands/stdlib/dedupe.js +55 -0
- package/dist/src/commands/stdlib/dedupe.js.map +1 -0
- package/dist/src/commands/stdlib/diff_last.js +35 -0
- package/dist/src/commands/stdlib/diff_last.js.map +1 -0
- package/dist/src/commands/stdlib/email_triage.js +279 -0
- package/dist/src/commands/stdlib/email_triage.js.map +1 -0
- package/dist/src/commands/stdlib/exec.js +130 -0
- package/dist/src/commands/stdlib/exec.js.map +1 -0
- package/dist/src/commands/stdlib/gog_gmail_search.js +94 -0
- package/dist/src/commands/stdlib/gog_gmail_search.js.map +1 -0
- package/dist/src/commands/stdlib/gog_gmail_send.js +104 -0
- package/dist/src/commands/stdlib/gog_gmail_send.js.map +1 -0
- package/dist/src/commands/stdlib/group_by.js +59 -0
- package/dist/src/commands/stdlib/group_by.js.map +1 -0
- package/dist/src/commands/stdlib/head.js +34 -0
- package/dist/src/commands/stdlib/head.js.map +1 -0
- package/dist/src/commands/stdlib/json.js +20 -0
- package/dist/src/commands/stdlib/json.js.map +1 -0
- package/dist/src/commands/stdlib/llm_invoke.js +758 -0
- package/dist/src/commands/stdlib/llm_invoke.js.map +1 -0
- package/dist/src/commands/stdlib/llm_task_invoke.js +2 -0
- package/dist/src/commands/stdlib/llm_task_invoke.js.map +1 -0
- package/dist/src/commands/stdlib/map.js +104 -0
- package/dist/src/commands/stdlib/map.js.map +1 -0
- package/dist/src/commands/stdlib/openclaw_invoke.js +136 -0
- package/dist/src/commands/stdlib/openclaw_invoke.js.map +1 -0
- package/dist/src/commands/stdlib/pick.js +45 -0
- package/dist/src/commands/stdlib/pick.js.map +1 -0
- package/dist/src/commands/stdlib/sort.js +86 -0
- package/dist/src/commands/stdlib/sort.js.map +1 -0
- package/dist/src/commands/stdlib/state.js +76 -0
- package/dist/src/commands/stdlib/state.js.map +1 -0
- package/dist/src/commands/stdlib/table.js +57 -0
- package/dist/src/commands/stdlib/table.js.map +1 -0
- package/dist/src/commands/stdlib/template.js +126 -0
- package/dist/src/commands/stdlib/template.js.map +1 -0
- package/dist/src/commands/stdlib/where.js +81 -0
- package/dist/src/commands/stdlib/where.js.map +1 -0
- package/dist/src/commands/types.js +2 -0
- package/dist/src/commands/types.js.map +1 -0
- package/dist/src/commands/workflows/workflows_list.js +24 -0
- package/dist/src/commands/workflows/workflows_list.js.map +1 -0
- package/dist/src/commands/workflows/workflows_run.js +74 -0
- package/dist/src/commands/workflows/workflows_run.js.map +1 -0
- package/dist/src/core/cost_tracker.js +119 -0
- package/dist/src/core/cost_tracker.js.map +1 -0
- package/dist/src/core/filters.js +102 -0
- package/dist/src/core/filters.js.map +1 -0
- package/dist/src/core/index.js +7 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/core/retry.js +89 -0
- package/dist/src/core/retry.js.map +1 -0
- package/dist/src/core/tool_runtime.js +289 -0
- package/dist/src/core/tool_runtime.js.map +1 -0
- package/dist/src/input_request.js +430 -0
- package/dist/src/input_request.js.map +1 -0
- package/dist/src/parser.js +145 -0
- package/dist/src/parser.js.map +1 -0
- package/dist/src/pipeline_resume_state.js +186 -0
- package/dist/src/pipeline_resume_state.js.map +1 -0
- package/dist/src/read_line.js +50 -0
- package/dist/src/read_line.js.map +1 -0
- package/dist/src/recipes/github/index.js +16 -0
- package/dist/src/recipes/github/index.js.map +1 -0
- package/dist/src/recipes/github/pr-monitor.js +248 -0
- package/dist/src/recipes/github/pr-monitor.js.map +1 -0
- package/dist/src/recipes/github/stages/pr-view.js +107 -0
- package/dist/src/recipes/github/stages/pr-view.js.map +1 -0
- package/dist/src/recipes/index.js +7 -0
- package/dist/src/recipes/index.js.map +1 -0
- package/dist/src/recipes/registry.js +30 -0
- package/dist/src/recipes/registry.js.map +1 -0
- package/dist/src/renderers/json.js +13 -0
- package/dist/src/renderers/json.js.map +1 -0
- package/dist/src/resume.js +179 -0
- package/dist/src/resume.js.map +1 -0
- package/dist/src/runtime.js +230 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/sdk/Lobster.js +402 -0
- package/dist/src/sdk/Lobster.js.map +1 -0
- package/dist/src/sdk/index.js +25 -0
- package/dist/src/sdk/index.js.map +1 -0
- package/dist/src/sdk/primitives/approve.js +47 -0
- package/dist/src/sdk/primitives/approve.js.map +1 -0
- package/dist/src/sdk/primitives/diff.js +156 -0
- package/dist/src/sdk/primitives/diff.js.map +1 -0
- package/dist/src/sdk/primitives/exec.js +167 -0
- package/dist/src/sdk/primitives/exec.js.map +1 -0
- package/dist/src/sdk/primitives/state.js +203 -0
- package/dist/src/sdk/primitives/state.js.map +1 -0
- package/dist/src/sdk/runtime.js +131 -0
- package/dist/src/sdk/runtime.js.map +1 -0
- package/dist/src/sdk/token.js +9 -0
- package/dist/src/sdk/token.js.map +1 -0
- package/dist/src/shell.js +39 -0
- package/dist/src/shell.js.map +1 -0
- package/dist/src/state/store.js +337 -0
- package/dist/src/state/store.js.map +1 -0
- package/dist/src/token.js +15 -0
- package/dist/src/token.js.map +1 -0
- package/dist/src/validation.js +38 -0
- package/dist/src/validation.js.map +1 -0
- package/dist/src/workflows/file.js +2405 -0
- package/dist/src/workflows/file.js.map +1 -0
- package/dist/src/workflows/github_pr_monitor.js +167 -0
- package/dist/src/workflows/github_pr_monitor.js.map +1 -0
- package/dist/src/workflows/graph.js +234 -0
- package/dist/src/workflows/graph.js.map +1 -0
- package/dist/src/workflows/registry.js +57 -0
- package/dist/src/workflows/registry.js.map +1 -0
- package/dist/test/approval_id.test.js +171 -0
- package/dist/test/approval_id.test.js.map +1 -0
- package/dist/test/approve_preview.test.js +38 -0
- package/dist/test/approve_preview.test.js.map +1 -0
- package/dist/test/clawd_invoke.test.js +124 -0
- package/dist/test/clawd_invoke.test.js.map +1 -0
- package/dist/test/clawd_invoke_legacy.test.js +63 -0
- package/dist/test/clawd_invoke_legacy.test.js.map +1 -0
- package/dist/test/cli_run_file_args_json.test.js +27 -0
- package/dist/test/cli_run_file_args_json.test.js.map +1 -0
- package/dist/test/commands_list.test.js +44 -0
- package/dist/test/commands_list.test.js.map +1 -0
- package/dist/test/condition_comparison.test.js +127 -0
- package/dist/test/condition_comparison.test.js.map +1 -0
- package/dist/test/core_tool_runtime.test.js +160 -0
- package/dist/test/core_tool_runtime.test.js.map +1 -0
- package/dist/test/cost_tracker.test.js +231 -0
- package/dist/test/cost_tracker.test.js.map +1 -0
- package/dist/test/dedupe.test.js +48 -0
- package/dist/test/dedupe.test.js.map +1 -0
- package/dist/test/diff_last.test.js +70 -0
- package/dist/test/diff_last.test.js.map +1 -0
- package/dist/test/doctor.test.js +19 -0
- package/dist/test/doctor.test.js.map +1 -0
- package/dist/test/dry_run.test.js +502 -0
- package/dist/test/dry_run.test.js.map +1 -0
- package/dist/test/email_triage.test.js +296 -0
- package/dist/test/email_triage.test.js.map +1 -0
- package/dist/test/exec_stdin.test.js +43 -0
- package/dist/test/exec_stdin.test.js.map +1 -0
- package/dist/test/for_each.test.js +228 -0
- package/dist/test/for_each.test.js.map +1 -0
- package/dist/test/github_pr_notify_format.test.js +19 -0
- package/dist/test/github_pr_notify_format.test.js.map +1 -0
- package/dist/test/github_pr_summary.test.js +41 -0
- package/dist/test/github_pr_summary.test.js.map +1 -0
- package/dist/test/group_by.test.js +43 -0
- package/dist/test/group_by.test.js.map +1 -0
- package/dist/test/llm_invoke.test.js +166 -0
- package/dist/test/llm_invoke.test.js.map +1 -0
- package/dist/test/llm_task_invoke.test.js +416 -0
- package/dist/test/llm_task_invoke.test.js.map +1 -0
- package/dist/test/map.test.js +41 -0
- package/dist/test/map.test.js.map +1 -0
- package/dist/test/multi_approval_resume.test.js +48 -0
- package/dist/test/multi_approval_resume.test.js.map +1 -0
- package/dist/test/on_error.test.js +151 -0
- package/dist/test/on_error.test.js.map +1 -0
- package/dist/test/openclaw_invoke_alias.test.js +13 -0
- package/dist/test/openclaw_invoke_alias.test.js.map +1 -0
- package/dist/test/parallel.test.js +184 -0
- package/dist/test/parallel.test.js.map +1 -0
- package/dist/test/parser.test.js +39 -0
- package/dist/test/parser.test.js.map +1 -0
- package/dist/test/read_line.test.js +25 -0
- package/dist/test/read_line.test.js.map +1 -0
- package/dist/test/request_input.test.js +946 -0
- package/dist/test/request_input.test.js.map +1 -0
- package/dist/test/resume.test.js +82 -0
- package/dist/test/resume.test.js.map +1 -0
- package/dist/test/sdk_lobster.test.js +177 -0
- package/dist/test/sdk_lobster.test.js.map +1 -0
- package/dist/test/shell.test.js +31 -0
- package/dist/test/shell.test.js.map +1 -0
- package/dist/test/sort.test.js +51 -0
- package/dist/test/sort.test.js.map +1 -0
- package/dist/test/state.test.js +336 -0
- package/dist/test/state.test.js.map +1 -0
- package/dist/test/step_retry.test.js +254 -0
- package/dist/test/step_retry.test.js.map +1 -0
- package/dist/test/step_timeout.test.js +154 -0
- package/dist/test/step_timeout.test.js.map +1 -0
- package/dist/test/template.test.js +46 -0
- package/dist/test/template.test.js.map +1 -0
- package/dist/test/template_filters.test.js +107 -0
- package/dist/test/template_filters.test.js.map +1 -0
- package/dist/test/tool_envelope_version.test.js +15 -0
- package/dist/test/tool_envelope_version.test.js.map +1 -0
- package/dist/test/tool_mode.test.js +83 -0
- package/dist/test/tool_mode.test.js.map +1 -0
- package/dist/test/validation.test.js +28 -0
- package/dist/test/validation.test.js.map +1 -0
- package/dist/test/workflow_args_env.test.js +41 -0
- package/dist/test/workflow_args_env.test.js.map +1 -0
- package/dist/test/workflow_composition.test.js +238 -0
- package/dist/test/workflow_composition.test.js.map +1 -0
- package/dist/test/workflow_file.test.js +1399 -0
- package/dist/test/workflow_file.test.js.map +1 -0
- package/dist/test/workflow_graph.test.js +97 -0
- package/dist/test/workflow_graph.test.js.map +1 -0
- package/dist/test/workflows.test.js +32 -0
- package/dist/test/workflows.test.js.map +1 -0
- package/package.json +75 -0
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
import { parsePipeline } from "./parser.js";
|
|
2
|
+
import { createDefaultRegistry } from "./commands/registry.js";
|
|
3
|
+
import { runPipeline } from "./runtime.js";
|
|
4
|
+
import { decodeResumeToken, parseResumeArgs, resolveApprovalId } from "./resume.js";
|
|
5
|
+
import { cleanupApprovalIndexByStateKey, deleteApprovalId } from "./state/store.js";
|
|
6
|
+
import { WorkflowResumeArgumentError, loadWorkflowFile, resolveWorkflowArgs, runWorkflowFile, } from "./workflows/file.js";
|
|
7
|
+
import { renderWorkflowGraph } from "./workflows/graph.js";
|
|
8
|
+
import { deleteStateJson } from "./state/store.js";
|
|
9
|
+
import { finalizePipelineToolRun, loadPipelineResumeState, validatePipelineInputResponse, } from "./pipeline_resume_state.js";
|
|
10
|
+
export async function runCli(argv) {
|
|
11
|
+
const registry = createDefaultRegistry();
|
|
12
|
+
if (argv.length === 0 || argv.includes("-h") || argv.includes("--help")) {
|
|
13
|
+
process.stdout.write(helpText());
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (argv[0] === "help") {
|
|
17
|
+
const topic = argv[1];
|
|
18
|
+
if (!topic) {
|
|
19
|
+
process.stdout.write(helpText());
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const cmd = registry.get(topic);
|
|
23
|
+
if (!cmd) {
|
|
24
|
+
process.stderr.write(`Unknown command: ${topic}\n`);
|
|
25
|
+
process.exitCode = 2;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
process.stdout.write(cmd.help());
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (argv[0] === "version" || argv[0] === "--version" || argv[0] === "-v") {
|
|
32
|
+
process.stdout.write(`${await readVersion()}\n`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (argv[0] === "doctor") {
|
|
36
|
+
await handleDoctor({ argv: argv.slice(1), registry });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (argv[0] === "graph") {
|
|
40
|
+
await handleGraph({ argv: argv.slice(1) });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (argv[0] === "run") {
|
|
44
|
+
await handleRun({ argv: argv.slice(1), registry });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (argv[0] === "resume") {
|
|
48
|
+
await handleResume({ argv: argv.slice(1), registry });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// Default: treat argv as a pipeline string.
|
|
52
|
+
await handleRun({ argv, registry });
|
|
53
|
+
}
|
|
54
|
+
async function handleGraph({ argv }) {
|
|
55
|
+
const parsed = parseGraphArgs(argv);
|
|
56
|
+
if (parsed.help) {
|
|
57
|
+
process.stdout.write(graphHelpText());
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (!parsed.filePath) {
|
|
61
|
+
process.stderr.write("graph requires a workflow file path (use --file <path>)\n");
|
|
62
|
+
process.exitCode = 2;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (!isWorkflowGraphFormat(parsed.format)) {
|
|
66
|
+
process.stderr.write("graph --format must be one of: mermaid, dot, ascii\n");
|
|
67
|
+
process.exitCode = 2;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
let argsJson = {};
|
|
71
|
+
if (parsed.argsJson) {
|
|
72
|
+
try {
|
|
73
|
+
const value = JSON.parse(parsed.argsJson);
|
|
74
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
75
|
+
process.stderr.write("graph --args-json must be a JSON object\n");
|
|
76
|
+
process.exitCode = 2;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
argsJson = value;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
process.stderr.write("graph --args-json must be valid JSON\n");
|
|
83
|
+
process.exitCode = 2;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let filePath;
|
|
88
|
+
try {
|
|
89
|
+
filePath = await resolveWorkflowFile(parsed.filePath);
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
process.stderr.write(`Error: ${err?.message ?? String(err)}\n`);
|
|
93
|
+
process.exitCode = 1;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const workflow = await loadWorkflowFile(filePath);
|
|
98
|
+
const args = resolveWorkflowArgs(workflow.args, argsJson);
|
|
99
|
+
const graph = renderWorkflowGraph({ workflow, format: parsed.format, args });
|
|
100
|
+
process.stdout.write(graph);
|
|
101
|
+
process.stdout.write("\n");
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
process.stderr.write(`Error: ${err?.message ?? String(err)}\n`);
|
|
105
|
+
process.exitCode = 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function isWorkflowGraphFormat(value) {
|
|
109
|
+
return value === "mermaid" || value === "dot" || value === "ascii";
|
|
110
|
+
}
|
|
111
|
+
async function handleRun({ argv, registry }) {
|
|
112
|
+
const parsed = parseRunArgs(argv);
|
|
113
|
+
const { mode, argsJson } = parsed;
|
|
114
|
+
const normalizedMode = normalizeMode(mode);
|
|
115
|
+
const { rest, filePath, dryRun } = await resolveRunTarget(parsed);
|
|
116
|
+
const workflowFile = filePath
|
|
117
|
+
? await resolveWorkflowFile(filePath)
|
|
118
|
+
: await detectWorkflowFile(rest);
|
|
119
|
+
if (workflowFile) {
|
|
120
|
+
let parsedArgs = {};
|
|
121
|
+
if (argsJson) {
|
|
122
|
+
try {
|
|
123
|
+
parsedArgs = JSON.parse(argsJson);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
if (mode === "tool") {
|
|
127
|
+
writeToolEnvelope({
|
|
128
|
+
ok: false,
|
|
129
|
+
error: { type: "parse_error", message: "run --args-json must be valid JSON" },
|
|
130
|
+
});
|
|
131
|
+
process.exitCode = 2;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
process.stderr.write("run --args-json must be valid JSON\n");
|
|
135
|
+
process.exitCode = 2;
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const output = await runWorkflowFile({
|
|
141
|
+
filePath: workflowFile,
|
|
142
|
+
args: parsedArgs,
|
|
143
|
+
ctx: {
|
|
144
|
+
stdin: process.stdin,
|
|
145
|
+
stdout: process.stdout,
|
|
146
|
+
stderr: process.stderr,
|
|
147
|
+
env: process.env,
|
|
148
|
+
mode: normalizedMode,
|
|
149
|
+
registry,
|
|
150
|
+
dryRun,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
if (normalizedMode === "tool") {
|
|
154
|
+
if (output.status === "needs_approval") {
|
|
155
|
+
writeToolEnvelope({
|
|
156
|
+
ok: true,
|
|
157
|
+
status: "needs_approval",
|
|
158
|
+
output: [],
|
|
159
|
+
requiresApproval: output.requiresApproval ?? null,
|
|
160
|
+
requiresInput: null,
|
|
161
|
+
});
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (output.status === "needs_input") {
|
|
165
|
+
writeToolEnvelope({
|
|
166
|
+
ok: true,
|
|
167
|
+
status: "needs_input",
|
|
168
|
+
output: [],
|
|
169
|
+
requiresApproval: null,
|
|
170
|
+
requiresInput: output.requiresInput ?? null,
|
|
171
|
+
});
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
writeToolEnvelope({
|
|
175
|
+
ok: true,
|
|
176
|
+
status: "ok",
|
|
177
|
+
output: output.output,
|
|
178
|
+
requiresApproval: null,
|
|
179
|
+
requiresInput: null,
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (output.status === "needs_approval" || output.status === "needs_input") {
|
|
184
|
+
process.stdout.write(JSON.stringify({
|
|
185
|
+
status: output.status,
|
|
186
|
+
output: [],
|
|
187
|
+
requiresApproval: output.requiresApproval ?? null,
|
|
188
|
+
requiresInput: output.requiresInput ?? null,
|
|
189
|
+
}, null, 2));
|
|
190
|
+
process.stdout.write("\n");
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (output.status === "ok" && output.output.length) {
|
|
194
|
+
process.stdout.write(JSON.stringify(output.output, null, 2));
|
|
195
|
+
process.stdout.write("\n");
|
|
196
|
+
}
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
if (normalizedMode === "tool") {
|
|
201
|
+
writeToolEnvelope({
|
|
202
|
+
ok: false,
|
|
203
|
+
error: { type: "runtime_error", message: err?.message ?? String(err) },
|
|
204
|
+
});
|
|
205
|
+
process.exitCode = 1;
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
process.stderr.write(`Error: ${err?.message ?? String(err)}\n`);
|
|
209
|
+
process.exitCode = 1;
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const pipelineString = rest.join(" ");
|
|
214
|
+
let pipeline;
|
|
215
|
+
try {
|
|
216
|
+
pipeline = parsePipeline(pipelineString);
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
if (mode === "tool") {
|
|
220
|
+
writeToolEnvelope({
|
|
221
|
+
ok: false,
|
|
222
|
+
error: { type: "parse_error", message: err?.message ?? String(err) },
|
|
223
|
+
});
|
|
224
|
+
process.exitCode = 2;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
process.stderr.write(`Parse error: ${err?.message ?? String(err)}\n`);
|
|
228
|
+
process.exitCode = 2;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
const output = await runPipeline({
|
|
233
|
+
pipeline,
|
|
234
|
+
registry,
|
|
235
|
+
input: [],
|
|
236
|
+
stdin: process.stdin,
|
|
237
|
+
stdout: process.stdout,
|
|
238
|
+
stderr: process.stderr,
|
|
239
|
+
env: process.env,
|
|
240
|
+
mode: normalizedMode,
|
|
241
|
+
dryRun,
|
|
242
|
+
});
|
|
243
|
+
if (normalizedMode === "tool") {
|
|
244
|
+
const finalized = await finalizePipelineToolRun({
|
|
245
|
+
env: process.env,
|
|
246
|
+
pipeline,
|
|
247
|
+
output,
|
|
248
|
+
});
|
|
249
|
+
writeToolEnvelope({
|
|
250
|
+
ok: true,
|
|
251
|
+
status: finalized.status,
|
|
252
|
+
output: finalized.output,
|
|
253
|
+
requiresApproval: finalized.requiresApproval,
|
|
254
|
+
requiresInput: finalized.requiresInput,
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (output.halted && isPipelineInputRequest(output.items)) {
|
|
259
|
+
throw new Error("requestInput requires --mode tool when stdin is not interactive");
|
|
260
|
+
}
|
|
261
|
+
// Human mode: if the last command didn't render, print JSON.
|
|
262
|
+
if (!output.rendered) {
|
|
263
|
+
process.stdout.write(JSON.stringify(output.items, null, 2));
|
|
264
|
+
process.stdout.write("\n");
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
if (normalizedMode === "tool") {
|
|
269
|
+
writeToolEnvelope({
|
|
270
|
+
ok: false,
|
|
271
|
+
error: { type: "runtime_error", message: err?.message ?? String(err) },
|
|
272
|
+
});
|
|
273
|
+
process.exitCode = 1;
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
process.stderr.write(`Error: ${err?.message ?? String(err)}\n`);
|
|
277
|
+
process.exitCode = 1;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function isPipelineInputRequest(items) {
|
|
281
|
+
return (items.length === 1 && items[0]?.type === "input_request" && items[0]?.commandInput !== undefined);
|
|
282
|
+
}
|
|
283
|
+
function parseRunArgs(argv) {
|
|
284
|
+
const rest = [];
|
|
285
|
+
let mode = "human";
|
|
286
|
+
let filePath = null;
|
|
287
|
+
let argsJson = null;
|
|
288
|
+
let dryRun = false;
|
|
289
|
+
for (let i = 0; i < argv.length; i++) {
|
|
290
|
+
const tok = argv[i];
|
|
291
|
+
// Treat --dry-run as a Lobster flag only before positional command/pipeline
|
|
292
|
+
// args begin. Once rest has started, the token may belong to the command.
|
|
293
|
+
// Trailing workflow-file --dry-run is handled later after we can prove the
|
|
294
|
+
// first positional token is actually a workflow file.
|
|
295
|
+
if (tok === "--dry-run" && rest.length === 0) {
|
|
296
|
+
dryRun = true;
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (tok === "--mode") {
|
|
300
|
+
const value = argv[i + 1];
|
|
301
|
+
if (value) {
|
|
302
|
+
mode = value;
|
|
303
|
+
i++;
|
|
304
|
+
}
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (tok.startsWith("--mode=")) {
|
|
308
|
+
mode = tok.slice("--mode=".length) || "human";
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (tok === "--file") {
|
|
312
|
+
const value = argv[i + 1];
|
|
313
|
+
if (value) {
|
|
314
|
+
filePath = value;
|
|
315
|
+
i++;
|
|
316
|
+
}
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (tok.startsWith("--file=")) {
|
|
320
|
+
filePath = tok.slice("--file=".length);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (tok === "--args-json") {
|
|
324
|
+
const value = argv[i + 1];
|
|
325
|
+
if (value) {
|
|
326
|
+
argsJson = value;
|
|
327
|
+
i++;
|
|
328
|
+
}
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (tok.startsWith("--args-json=")) {
|
|
332
|
+
argsJson = tok.slice("--args-json=".length);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
rest.push(tok);
|
|
336
|
+
}
|
|
337
|
+
return { mode, rest, filePath, argsJson, dryRun };
|
|
338
|
+
}
|
|
339
|
+
function parseGraphArgs(argv) {
|
|
340
|
+
const rest = [];
|
|
341
|
+
let filePath = null;
|
|
342
|
+
let format = "mermaid";
|
|
343
|
+
let argsJson = null;
|
|
344
|
+
let help = false;
|
|
345
|
+
for (let i = 0; i < argv.length; i++) {
|
|
346
|
+
const tok = argv[i];
|
|
347
|
+
if (tok === "-h" || tok === "--help") {
|
|
348
|
+
help = true;
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
if (tok === "--file") {
|
|
352
|
+
const value = argv[i + 1];
|
|
353
|
+
if (value) {
|
|
354
|
+
filePath = value;
|
|
355
|
+
i++;
|
|
356
|
+
}
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (tok.startsWith("--file=")) {
|
|
360
|
+
filePath = tok.slice("--file=".length);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (tok === "--format") {
|
|
364
|
+
const value = argv[i + 1];
|
|
365
|
+
if (value) {
|
|
366
|
+
format = value;
|
|
367
|
+
i++;
|
|
368
|
+
}
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (tok.startsWith("--format=")) {
|
|
372
|
+
format = tok.slice("--format=".length) || "mermaid";
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (tok === "--args-json") {
|
|
376
|
+
const value = argv[i + 1];
|
|
377
|
+
if (value) {
|
|
378
|
+
argsJson = value;
|
|
379
|
+
i++;
|
|
380
|
+
}
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (tok.startsWith("--args-json=")) {
|
|
384
|
+
argsJson = tok.slice("--args-json=".length);
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
rest.push(tok);
|
|
388
|
+
}
|
|
389
|
+
if (!filePath && rest.length > 0) {
|
|
390
|
+
filePath = rest[0];
|
|
391
|
+
}
|
|
392
|
+
return { filePath, format, argsJson, help };
|
|
393
|
+
}
|
|
394
|
+
async function resolveRunTarget(parsed) {
|
|
395
|
+
if (parsed.filePath)
|
|
396
|
+
return parsed;
|
|
397
|
+
const restWithoutDryRun = parsed.rest.filter((token) => token !== "--dry-run");
|
|
398
|
+
if (restWithoutDryRun.length === 1 && restWithoutDryRun.length !== parsed.rest.length) {
|
|
399
|
+
try {
|
|
400
|
+
const workflowFile = await resolveWorkflowFile(restWithoutDryRun[0]);
|
|
401
|
+
return { ...parsed, filePath: workflowFile, rest: [], dryRun: true };
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return parsed;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return parsed;
|
|
408
|
+
}
|
|
409
|
+
function normalizeMode(mode) {
|
|
410
|
+
return mode === "tool" ? "tool" : "human";
|
|
411
|
+
}
|
|
412
|
+
async function detectWorkflowFile(rest) {
|
|
413
|
+
if (rest.length !== 1)
|
|
414
|
+
return null;
|
|
415
|
+
const candidate = rest[0];
|
|
416
|
+
if (!candidate || candidate.includes("|"))
|
|
417
|
+
return null;
|
|
418
|
+
try {
|
|
419
|
+
return await resolveWorkflowFile(candidate);
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
async function resolveWorkflowFile(candidate) {
|
|
426
|
+
const { promises: fsp } = await import("node:fs");
|
|
427
|
+
const { resolve, extname, isAbsolute } = await import("node:path");
|
|
428
|
+
const resolved = isAbsolute(candidate) ? candidate : resolve(process.cwd(), candidate);
|
|
429
|
+
const stat = await fsp.stat(resolved);
|
|
430
|
+
if (!stat.isFile())
|
|
431
|
+
throw new Error("Workflow path is not a file");
|
|
432
|
+
const ext = extname(resolved).toLowerCase();
|
|
433
|
+
if (![".lobster", ".yaml", ".yml", ".json"].includes(ext)) {
|
|
434
|
+
throw new Error("Workflow file must end in .lobster, .yaml, .yml, or .json");
|
|
435
|
+
}
|
|
436
|
+
return resolved;
|
|
437
|
+
}
|
|
438
|
+
async function handleResume({ argv, registry }) {
|
|
439
|
+
const mode = "tool";
|
|
440
|
+
let approved;
|
|
441
|
+
let response = undefined;
|
|
442
|
+
let cancel = false;
|
|
443
|
+
let payload;
|
|
444
|
+
let resolvedApprovalId = null;
|
|
445
|
+
try {
|
|
446
|
+
const parsed = parseResumeArgs(argv);
|
|
447
|
+
approved = parsed.approved;
|
|
448
|
+
response = parsed.response;
|
|
449
|
+
cancel = parsed.cancel === true;
|
|
450
|
+
resolvedApprovalId = parsed.approvalId;
|
|
451
|
+
// Resolve short approval ID to token if provided
|
|
452
|
+
let token;
|
|
453
|
+
if (parsed.approvalId) {
|
|
454
|
+
token = await resolveApprovalId(parsed.approvalId, process.env);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
token = parsed.token;
|
|
458
|
+
}
|
|
459
|
+
payload = decodeResumeToken(token);
|
|
460
|
+
}
|
|
461
|
+
catch (err) {
|
|
462
|
+
writeToolEnvelope({
|
|
463
|
+
ok: false,
|
|
464
|
+
error: { type: "parse_error", message: err?.message ?? String(err) },
|
|
465
|
+
});
|
|
466
|
+
process.exitCode = 2;
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
// Helper: clean up approval ID index after successful use
|
|
470
|
+
const cleanupIndex = async () => {
|
|
471
|
+
if (resolvedApprovalId) {
|
|
472
|
+
await deleteApprovalId({ env: process.env, approvalId: resolvedApprovalId });
|
|
473
|
+
}
|
|
474
|
+
else if (payload.stateKey) {
|
|
475
|
+
await cleanupApprovalIndexByStateKey({ env: process.env, stateKey: payload.stateKey });
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
if (cancel === true) {
|
|
479
|
+
await cleanupIndex();
|
|
480
|
+
if (payload.kind === "workflow-file" && payload.stateKey) {
|
|
481
|
+
await deleteStateJson({ env: process.env, key: payload.stateKey });
|
|
482
|
+
}
|
|
483
|
+
if (payload.kind === "pipeline-resume" && payload.stateKey) {
|
|
484
|
+
await deleteStateJson({ env: process.env, key: payload.stateKey });
|
|
485
|
+
}
|
|
486
|
+
writeToolEnvelope({
|
|
487
|
+
ok: true,
|
|
488
|
+
status: "cancelled",
|
|
489
|
+
output: [],
|
|
490
|
+
requiresApproval: null,
|
|
491
|
+
requiresInput: null,
|
|
492
|
+
});
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
if (payload.kind === "workflow-file") {
|
|
496
|
+
try {
|
|
497
|
+
const output = await runWorkflowFile({
|
|
498
|
+
filePath: payload.filePath,
|
|
499
|
+
ctx: {
|
|
500
|
+
stdin: process.stdin,
|
|
501
|
+
stdout: process.stdout,
|
|
502
|
+
stderr: process.stderr,
|
|
503
|
+
env: process.env,
|
|
504
|
+
mode: "tool",
|
|
505
|
+
registry,
|
|
506
|
+
},
|
|
507
|
+
resume: payload,
|
|
508
|
+
approved,
|
|
509
|
+
response,
|
|
510
|
+
cancel,
|
|
511
|
+
});
|
|
512
|
+
if (output.status === "needs_approval") {
|
|
513
|
+
writeToolEnvelope({
|
|
514
|
+
ok: true,
|
|
515
|
+
status: "needs_approval",
|
|
516
|
+
output: [],
|
|
517
|
+
requiresApproval: output.requiresApproval ?? null,
|
|
518
|
+
requiresInput: null,
|
|
519
|
+
});
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
if (output.status === "needs_input") {
|
|
523
|
+
writeToolEnvelope({
|
|
524
|
+
ok: true,
|
|
525
|
+
status: "needs_input",
|
|
526
|
+
output: [],
|
|
527
|
+
requiresApproval: null,
|
|
528
|
+
requiresInput: output.requiresInput ?? null,
|
|
529
|
+
});
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
await cleanupIndex();
|
|
533
|
+
if (output.status === "cancelled") {
|
|
534
|
+
writeToolEnvelope({
|
|
535
|
+
ok: true,
|
|
536
|
+
status: "cancelled",
|
|
537
|
+
output: [],
|
|
538
|
+
requiresApproval: null,
|
|
539
|
+
requiresInput: null,
|
|
540
|
+
});
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
writeToolEnvelope({
|
|
544
|
+
ok: true,
|
|
545
|
+
status: "ok",
|
|
546
|
+
output: output.output,
|
|
547
|
+
requiresApproval: null,
|
|
548
|
+
requiresInput: null,
|
|
549
|
+
});
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
catch (err) {
|
|
553
|
+
if (err instanceof WorkflowResumeArgumentError) {
|
|
554
|
+
writeToolEnvelope({ ok: false, error: { type: "parse_error", message: err.message } });
|
|
555
|
+
process.exitCode = 2;
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
// Don't clean up index on error — allow retry by --id
|
|
559
|
+
writeToolEnvelope({
|
|
560
|
+
ok: false,
|
|
561
|
+
error: { type: "runtime_error", message: err?.message ?? String(err) },
|
|
562
|
+
});
|
|
563
|
+
process.exitCode = 1;
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const previousStateKey = payload.stateKey;
|
|
568
|
+
let resumeState;
|
|
569
|
+
try {
|
|
570
|
+
resumeState = await loadPipelineResumeState(process.env, previousStateKey);
|
|
571
|
+
}
|
|
572
|
+
catch (err) {
|
|
573
|
+
writeToolEnvelope({
|
|
574
|
+
ok: false,
|
|
575
|
+
error: { type: "runtime_error", message: err?.message ?? String(err) },
|
|
576
|
+
});
|
|
577
|
+
process.exitCode = 1;
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
if (resumeState.haltType === "input_request") {
|
|
581
|
+
if (approved !== undefined) {
|
|
582
|
+
writeToolEnvelope({
|
|
583
|
+
ok: false,
|
|
584
|
+
error: {
|
|
585
|
+
type: "parse_error",
|
|
586
|
+
message: "pipeline input resumes require --response-json <json>",
|
|
587
|
+
},
|
|
588
|
+
});
|
|
589
|
+
process.exitCode = 2;
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
if (response === undefined) {
|
|
593
|
+
writeToolEnvelope({
|
|
594
|
+
ok: false,
|
|
595
|
+
error: {
|
|
596
|
+
type: "parse_error",
|
|
597
|
+
message: "pipeline input resumes require --response-json <json>",
|
|
598
|
+
},
|
|
599
|
+
});
|
|
600
|
+
process.exitCode = 2;
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
validatePipelineInputResponse(resumeState.inputSchema, response);
|
|
605
|
+
}
|
|
606
|
+
catch (err) {
|
|
607
|
+
writeToolEnvelope({
|
|
608
|
+
ok: false,
|
|
609
|
+
error: { type: "parse_error", message: err?.message ?? String(err) },
|
|
610
|
+
});
|
|
611
|
+
process.exitCode = 2;
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
if (response !== undefined) {
|
|
617
|
+
writeToolEnvelope({
|
|
618
|
+
ok: false,
|
|
619
|
+
error: {
|
|
620
|
+
type: "parse_error",
|
|
621
|
+
message: "approval resumes require --approve yes|no, not --response-json",
|
|
622
|
+
},
|
|
623
|
+
});
|
|
624
|
+
process.exitCode = 2;
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
if (approved !== true) {
|
|
628
|
+
await cleanupIndex();
|
|
629
|
+
await deleteStateJson({ env: process.env, key: previousStateKey });
|
|
630
|
+
writeToolEnvelope({
|
|
631
|
+
ok: true,
|
|
632
|
+
status: "cancelled",
|
|
633
|
+
output: [],
|
|
634
|
+
requiresApproval: null,
|
|
635
|
+
requiresInput: null,
|
|
636
|
+
});
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
const isSameStageInput = resumeState.haltType === "input_request" && resumeState.resumeMode === "same_stage";
|
|
641
|
+
const remaining = resumeState.pipeline.slice(resumeState.resumeAtIndex);
|
|
642
|
+
const input = isSameStageInput
|
|
643
|
+
? resumeState.items
|
|
644
|
+
: resumeState.haltType === "input_request"
|
|
645
|
+
? [response]
|
|
646
|
+
: resumeState.items;
|
|
647
|
+
const requestInputResume = isSameStageInput
|
|
648
|
+
? {
|
|
649
|
+
state: resumeState.commandInput,
|
|
650
|
+
response,
|
|
651
|
+
onConsumed: async () => {
|
|
652
|
+
await cleanupIndex();
|
|
653
|
+
await deleteStateJson({ env: process.env, key: previousStateKey });
|
|
654
|
+
},
|
|
655
|
+
}
|
|
656
|
+
: undefined;
|
|
657
|
+
try {
|
|
658
|
+
const output = await runPipeline({
|
|
659
|
+
pipeline: remaining,
|
|
660
|
+
registry,
|
|
661
|
+
stdin: process.stdin,
|
|
662
|
+
stdout: process.stdout,
|
|
663
|
+
stderr: process.stderr,
|
|
664
|
+
env: process.env,
|
|
665
|
+
mode,
|
|
666
|
+
input,
|
|
667
|
+
requestInputResume,
|
|
668
|
+
});
|
|
669
|
+
await cleanupIndex();
|
|
670
|
+
const finalized = await finalizePipelineToolRun({
|
|
671
|
+
env: process.env,
|
|
672
|
+
pipeline: remaining,
|
|
673
|
+
output,
|
|
674
|
+
previousStateKey,
|
|
675
|
+
});
|
|
676
|
+
writeToolEnvelope({
|
|
677
|
+
ok: true,
|
|
678
|
+
status: finalized.status,
|
|
679
|
+
output: finalized.output,
|
|
680
|
+
requiresApproval: finalized.requiresApproval,
|
|
681
|
+
requiresInput: finalized.requiresInput,
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
catch (err) {
|
|
685
|
+
// Don't clean up index on error — allow retry by --id
|
|
686
|
+
writeToolEnvelope({
|
|
687
|
+
ok: false,
|
|
688
|
+
error: { type: "runtime_error", message: err?.message ?? String(err) },
|
|
689
|
+
});
|
|
690
|
+
process.exitCode = 1;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
async function readVersion() {
|
|
694
|
+
const { readFile } = await import("node:fs/promises");
|
|
695
|
+
const { fileURLToPath } = await import("node:url");
|
|
696
|
+
const { dirname, join } = await import("node:path");
|
|
697
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
698
|
+
const pkgPath = join(here, "..", "..", "package.json");
|
|
699
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
|
|
700
|
+
return pkg.version ?? "0.0.0";
|
|
701
|
+
}
|
|
702
|
+
async function handleDoctor({ argv, registry }) {
|
|
703
|
+
const mode = "tool";
|
|
704
|
+
const pipeline = "exec --json --shell 'echo [1]'";
|
|
705
|
+
const output = await (async () => {
|
|
706
|
+
try {
|
|
707
|
+
const parsed = parsePipeline(pipeline);
|
|
708
|
+
return await runPipeline({
|
|
709
|
+
pipeline: parsed,
|
|
710
|
+
registry,
|
|
711
|
+
input: [],
|
|
712
|
+
stdin: process.stdin,
|
|
713
|
+
stdout: process.stdout,
|
|
714
|
+
stderr: process.stderr,
|
|
715
|
+
env: process.env,
|
|
716
|
+
mode,
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
catch (err) {
|
|
720
|
+
return { error: err };
|
|
721
|
+
}
|
|
722
|
+
})();
|
|
723
|
+
if (output?.error) {
|
|
724
|
+
writeToolEnvelope({
|
|
725
|
+
ok: false,
|
|
726
|
+
error: { type: "doctor_error", message: output.error?.message ?? String(output.error) },
|
|
727
|
+
});
|
|
728
|
+
process.exitCode = 1;
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
writeToolEnvelope({
|
|
732
|
+
ok: true,
|
|
733
|
+
status: "ok",
|
|
734
|
+
output: [
|
|
735
|
+
{
|
|
736
|
+
toolMode: true,
|
|
737
|
+
protocolVersion: 1,
|
|
738
|
+
version: await readVersion(),
|
|
739
|
+
notes: argv.length ? argv : undefined,
|
|
740
|
+
},
|
|
741
|
+
],
|
|
742
|
+
requiresApproval: null,
|
|
743
|
+
requiresInput: null,
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
function writeToolEnvelope(payload) {
|
|
747
|
+
const envelope = {
|
|
748
|
+
protocolVersion: 1,
|
|
749
|
+
...payload,
|
|
750
|
+
};
|
|
751
|
+
process.stdout.write(JSON.stringify(envelope, null, 2));
|
|
752
|
+
process.stdout.write("\n");
|
|
753
|
+
}
|
|
754
|
+
function helpText() {
|
|
755
|
+
return (`lobster — OpenClaw-native typed shell\n\n` +
|
|
756
|
+
`Usage:\n` +
|
|
757
|
+
` lobster '<pipeline>'\n` +
|
|
758
|
+
` lobster run --mode tool '<pipeline>'\n` +
|
|
759
|
+
` lobster run path/to/workflow.lobster\n` +
|
|
760
|
+
` lobster run --file path/to/workflow.lobster --args-json '{...}'\n` +
|
|
761
|
+
` lobster run --dry-run --file path/to/workflow.lobster\n` +
|
|
762
|
+
` lobster run --dry-run '<pipeline>'\n` +
|
|
763
|
+
` lobster graph --file path/to/workflow.lobster --format mermaid\n` +
|
|
764
|
+
` lobster graph --file path/to/workflow.lobster --format dot\n` +
|
|
765
|
+
` lobster graph --file path/to/workflow.lobster --format ascii\n` +
|
|
766
|
+
` lobster resume --token <token> --approve yes|no\n` +
|
|
767
|
+
` lobster resume --token <token> --response-json '{...}'\n` +
|
|
768
|
+
` lobster resume --token <token> --cancel\n` +
|
|
769
|
+
` lobster doctor\n` +
|
|
770
|
+
` lobster version\n` +
|
|
771
|
+
` lobster help <command>\n\n` +
|
|
772
|
+
`Flags:\n` +
|
|
773
|
+
` --dry-run Validate and print the execution plan without running anything\n\n` +
|
|
774
|
+
`Modes:\n` +
|
|
775
|
+
` - human (default): renderers can write to stdout\n` +
|
|
776
|
+
` - tool: prints a single JSON envelope for easy integration\n\n` +
|
|
777
|
+
`Examples:\n` +
|
|
778
|
+
` lobster 'exec --json "echo [1,2,3]" | json'\n` +
|
|
779
|
+
` lobster run --mode tool 'exec --json "echo [1]" | approve --prompt "ok?"'\n\n` +
|
|
780
|
+
`Commands:\n` +
|
|
781
|
+
` exec, head, json, pick, table, where, approve, ask, openclaw.invoke, llm.invoke, llm_task.invoke, state.get, state.set, diff.last, commands.list, workflows.list, workflows.run, graph\n`);
|
|
782
|
+
}
|
|
783
|
+
function graphHelpText() {
|
|
784
|
+
return (`lobster graph — render workflow step graphs\n\n` +
|
|
785
|
+
`Usage:\n` +
|
|
786
|
+
` lobster graph --file path/to/workflow.lobster [--format mermaid|dot|ascii] [--args-json '{...}']\n` +
|
|
787
|
+
` lobster graph path/to/workflow.lobster [--format mermaid|dot|ascii]\n\n` +
|
|
788
|
+
`Flags:\n` +
|
|
789
|
+
` --file Workflow file path (.lobster, .yaml, .yml, .json)\n` +
|
|
790
|
+
` --format Output format: mermaid (default), dot, ascii\n` +
|
|
791
|
+
` --args-json JSON object used to resolve workflow args for labels\n`);
|
|
792
|
+
}
|
|
793
|
+
//# sourceMappingURL=cli.js.map
|