@openclaw/lobster 2026.9.5 → 2026.9.7
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/assets/icon.png +0 -0
- package/dist/index.js +8 -294
- 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/node_modules/fast-uri/README.md +1 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/SECURITY.md +62 -13
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/index.js +40 -9
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/lib/schemes.js +76 -16
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/lib/utils.js +54 -10
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/package.json +1 -1
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/fixtures/uri-js-parse.json +1 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/ipv6-validation.test.js +130 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/mailto-authority-security.test.js +125 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/mailto-field-name-security.test.js +151 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/security-normalization.test.js +38 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/types/index.d.ts +16 -0
- package/node_modules/@clawdbot/lobster/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/@clawdbot/lobster/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
- package/node_modules/@clawdbot/lobster/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/@clawdbot/lobster/node_modules/yaml/dist/nodes/Alias.js +25 -23
- package/node_modules/@clawdbot/lobster/node_modules/yaml/package.json +1 -1
- package/node_modules/@clawdbot/lobster/package.json +6 -8
- package/node_modules/typebox/build/compile/code.mjs +1 -2
- package/node_modules/typebox/build/compile/validator.mjs +5 -1
- package/node_modules/typebox/build/format/iri.mjs +16 -9
- package/node_modules/typebox/build/guard/emit.mjs +2 -2
- package/node_modules/typebox/build/guard/guard.d.mts +7 -5
- package/node_modules/typebox/build/guard/guard.mjs +14 -9
- package/node_modules/typebox/build/guard/index.mjs +6 -0
- package/node_modules/typebox/build/guard/unicode/unicode.d.mts +6 -0
- package/node_modules/typebox/build/guard/unicode/unicode.mjs +35 -0
- package/node_modules/typebox/build/guard/{string.mjs → unicode/unicode_segment.mjs} +16 -19
- package/node_modules/typebox/build/schema/build.mjs +2 -3
- package/node_modules/typebox/build/schema/engine/_pathing.d.mts +1 -0
- package/node_modules/typebox/build/schema/engine/_pathing.mjs +11 -0
- package/node_modules/typebox/build/schema/engine/_refine.mjs +13 -5
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/dependencies.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/dependentSchemas.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/dynamicRef.d.mts +1 -1
- package/node_modules/typebox/build/schema/engine/dynamicRef.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/format.mjs +9 -2
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +3 -2
- package/node_modules/typebox/build/schema/engine/properties.mjs +3 -2
- package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/recursiveRef.d.mts +1 -1
- package/node_modules/typebox/build/schema/engine/recursiveRef.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/ref.d.mts +1 -1
- package/node_modules/typebox/build/schema/engine/ref.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/schema.mjs +24 -24
- package/node_modules/typebox/build/schema/parse.mjs +3 -0
- package/node_modules/typebox/build/schema/pointer/pointer.d.mts +1 -6
- package/node_modules/typebox/build/schema/pointer/pointer.mjs +47 -64
- package/node_modules/typebox/build/system/settings/internal.d.mts +2 -0
- package/node_modules/typebox/build/system/settings/internal.mjs +20 -0
- package/node_modules/typebox/build/system/settings/settings.d.mts +11 -0
- package/node_modules/typebox/build/system/settings/settings.mjs +14 -0
- package/node_modules/typebox/build/type/extends/inference.d.mts +1 -1
- package/node_modules/typebox/build/type/extends/inference.mjs +15 -2
- package/node_modules/typebox/build/type/extends/tuple.d.mts +2 -1
- package/node_modules/typebox/build/type/extends/tuple.mjs +21 -1
- package/node_modules/typebox/build/value/parse/parse.mjs +10 -4
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +3 -1
- package/package.json +6 -6
- package/dist/runtime-api.js +0 -3
- 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
- /package/node_modules/typebox/build/guard/{string.d.mts → unicode/unicode_segment.d.mts} +0 -0
package/assets/icon.png
CHANGED
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { optionalPositiveIntegerSchema } from "openclaw/plugin-sdk/channel-actions";
|
|
3
|
+
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
|
|
4
4
|
import { jsonResult } from "openclaw/plugin-sdk/tool-results";
|
|
5
5
|
import { Type } from "typebox";
|
|
6
6
|
import { stat } from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { Readable, Writable } from "node:stream";
|
|
9
|
-
import { toErrorObject } from "openclaw/plugin-sdk/error-runtime";
|
|
9
|
+
import { extractErrorCode, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
|
|
10
10
|
import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
|
|
11
11
|
//#region extensions/lobster/src/lobster-runner.ts
|
|
12
12
|
const workflowExts = /* @__PURE__ */ new Set([
|
|
@@ -53,9 +53,6 @@ function normalizeEnvelope(envelope, maxStdoutBytes) {
|
|
|
53
53
|
if (Buffer.byteLength(JSON.stringify(normalized, null, 2), "utf8") > maxStdoutBytes) throw new Error("lobster runtime result exceeded maxStdoutBytes");
|
|
54
54
|
return normalized;
|
|
55
55
|
}
|
|
56
|
-
function isMissingPathError(error) {
|
|
57
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
58
|
-
}
|
|
59
56
|
async function detectWorkflowFile(candidate, cwd) {
|
|
60
57
|
const trimmed = candidate.trim();
|
|
61
58
|
if (!trimmed || trimmed.includes("|") || !workflowExts.has(path.extname(trimmed).toLowerCase())) return null;
|
|
@@ -64,7 +61,7 @@ async function detectWorkflowFile(candidate, cwd) {
|
|
|
64
61
|
if (!(await stat(resolved)).isFile()) throw new Error("Workflow path is not a file");
|
|
65
62
|
return resolved;
|
|
66
63
|
} catch (error) {
|
|
67
|
-
if (/\s/.test(trimmed) &&
|
|
64
|
+
if (/\s/.test(trimmed) && extractErrorCode(error) === "ENOENT") return null;
|
|
68
65
|
throw error;
|
|
69
66
|
}
|
|
70
67
|
}
|
|
@@ -153,257 +150,7 @@ function createEmbeddedLobsterRunner(options) {
|
|
|
153
150
|
} };
|
|
154
151
|
}
|
|
155
152
|
//#endregion
|
|
156
|
-
//#region extensions/lobster/src/lobster-taskflow.ts
|
|
157
|
-
function toJsonLike(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
158
|
-
if (value === null) return null;
|
|
159
|
-
if (typeof value === "number") return Number.isFinite(value) ? value : String(value);
|
|
160
|
-
if (typeof value === "bigint") return value.toString();
|
|
161
|
-
if (typeof value === "boolean" || typeof value === "string") return value;
|
|
162
|
-
if (typeof value !== "object") return null;
|
|
163
|
-
if (value instanceof Date) return value.toISOString();
|
|
164
|
-
if (seen.has(value)) return "[Circular]";
|
|
165
|
-
seen.add(value);
|
|
166
|
-
if (Array.isArray(value)) {
|
|
167
|
-
const jsonArray = value.map((item) => toJsonLike(item, seen));
|
|
168
|
-
seen.delete(value);
|
|
169
|
-
return jsonArray;
|
|
170
|
-
}
|
|
171
|
-
const jsonObject = {};
|
|
172
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
173
|
-
if (entry === void 0 || typeof entry === "function" || typeof entry === "symbol") continue;
|
|
174
|
-
jsonObject[key] = toJsonLike(entry, seen);
|
|
175
|
-
}
|
|
176
|
-
seen.delete(value);
|
|
177
|
-
return jsonObject;
|
|
178
|
-
}
|
|
179
|
-
function buildApprovalWaitState(envelope) {
|
|
180
|
-
const approval = envelope.requiresApproval;
|
|
181
|
-
return {
|
|
182
|
-
kind: "lobster_approval",
|
|
183
|
-
prompt: approval ? approval.prompt : "",
|
|
184
|
-
items: approval ? approval.items.map((item) => toJsonLike(item)) : [],
|
|
185
|
-
...approval?.resumeToken ? { resumeToken: approval.resumeToken } : {},
|
|
186
|
-
...approval?.approvalId ? { approvalId: approval.approvalId } : {}
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
async function executeManagedLobsterFlow(params, flow) {
|
|
190
|
-
try {
|
|
191
|
-
const envelope = await params.runner.run(params.runnerParams);
|
|
192
|
-
if (envelope.ok && envelope.status === "cancelled") try {
|
|
193
|
-
const mutation = await params.taskFlow.cancel({
|
|
194
|
-
flowId: flow.flowId,
|
|
195
|
-
cfg: params.config
|
|
196
|
-
});
|
|
197
|
-
return mutation.cancelled ? {
|
|
198
|
-
ok: true,
|
|
199
|
-
envelope,
|
|
200
|
-
flow,
|
|
201
|
-
mutation
|
|
202
|
-
} : {
|
|
203
|
-
ok: false,
|
|
204
|
-
flow,
|
|
205
|
-
mutation,
|
|
206
|
-
error: /* @__PURE__ */ new Error(`TaskFlow cancellation failed: ${mutation.reason ?? "unknown"}`)
|
|
207
|
-
};
|
|
208
|
-
} catch (error) {
|
|
209
|
-
return {
|
|
210
|
-
ok: false,
|
|
211
|
-
flow,
|
|
212
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
const flowMutation = {
|
|
216
|
-
flowId: flow.flowId,
|
|
217
|
-
expectedRevision: flow.revision
|
|
218
|
-
};
|
|
219
|
-
if (!envelope.ok) return {
|
|
220
|
-
ok: false,
|
|
221
|
-
flow,
|
|
222
|
-
mutation: await params.taskFlow.fail(flowMutation),
|
|
223
|
-
error: new Error(envelope.error.message)
|
|
224
|
-
};
|
|
225
|
-
return {
|
|
226
|
-
ok: true,
|
|
227
|
-
envelope,
|
|
228
|
-
flow,
|
|
229
|
-
mutation: envelope.status === "needs_approval" ? await params.taskFlow.setWaiting({
|
|
230
|
-
...flowMutation,
|
|
231
|
-
currentStep: params.waitingStep ?? "await_lobster_approval",
|
|
232
|
-
waitJson: buildApprovalWaitState(envelope)
|
|
233
|
-
}) : await params.taskFlow.finish(flowMutation)
|
|
234
|
-
};
|
|
235
|
-
} catch (error) {
|
|
236
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
237
|
-
try {
|
|
238
|
-
return {
|
|
239
|
-
ok: false,
|
|
240
|
-
flow,
|
|
241
|
-
mutation: await params.taskFlow.fail({
|
|
242
|
-
flowId: flow.flowId,
|
|
243
|
-
expectedRevision: flow.revision
|
|
244
|
-
}),
|
|
245
|
-
error: err
|
|
246
|
-
};
|
|
247
|
-
} catch {
|
|
248
|
-
return {
|
|
249
|
-
ok: false,
|
|
250
|
-
flow,
|
|
251
|
-
error: err
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
async function runManagedLobsterFlow(params) {
|
|
257
|
-
const createFlowParams = {
|
|
258
|
-
controllerId: params.controllerId,
|
|
259
|
-
goal: params.goal,
|
|
260
|
-
currentStep: params.currentStep ?? "run_lobster",
|
|
261
|
-
...params.stateJson !== void 0 ? { stateJson: params.stateJson } : {}
|
|
262
|
-
};
|
|
263
|
-
const flow = await params.taskFlow.tryCreateManaged(createFlowParams);
|
|
264
|
-
if (!flow) return {
|
|
265
|
-
ok: false,
|
|
266
|
-
error: /* @__PURE__ */ new Error("TaskFlow persistence failed.")
|
|
267
|
-
};
|
|
268
|
-
return await executeManagedLobsterFlow(params, flow);
|
|
269
|
-
}
|
|
270
|
-
async function resumeManagedLobsterFlow(params) {
|
|
271
|
-
const saved = await params.taskFlow.get(params.flowId);
|
|
272
|
-
if (!saved || saved.syncMode !== "managed" || saved.endedAt !== void 0 || saved.cancelRequestedAt !== void 0 || saved.status !== "waiting" && saved.status !== "blocked") return {
|
|
273
|
-
ok: false,
|
|
274
|
-
error: /* @__PURE__ */ new Error("TaskFlow has no resumable Lobster checkpoint.")
|
|
275
|
-
};
|
|
276
|
-
const wait = saved.waitJson;
|
|
277
|
-
if (!wait || typeof wait !== "object" || Array.isArray(wait) || wait.kind !== "lobster_approval") return {
|
|
278
|
-
ok: false,
|
|
279
|
-
error: /* @__PURE__ */ new Error("TaskFlow is not waiting for Lobster approval.")
|
|
280
|
-
};
|
|
281
|
-
const token = typeof wait.resumeToken === "string" ? wait.resumeToken : void 0;
|
|
282
|
-
const approvalId = typeof wait.approvalId === "string" ? wait.approvalId : void 0;
|
|
283
|
-
if (!token && !approvalId || params.runnerParams.token !== void 0 && params.runnerParams.token.trim() !== token || params.runnerParams.approvalId !== void 0 && params.runnerParams.approvalId.trim() !== approvalId) return {
|
|
284
|
-
ok: false,
|
|
285
|
-
error: /* @__PURE__ */ new Error("Lobster checkpoint does not match the selected TaskFlow.")
|
|
286
|
-
};
|
|
287
|
-
const runnerParams = {
|
|
288
|
-
...params.runnerParams,
|
|
289
|
-
...params.runnerParams.token || params.runnerParams.approvalId ? {} : token ? { token } : { approvalId }
|
|
290
|
-
};
|
|
291
|
-
if (saved.revision !== params.expectedRevision) return {
|
|
292
|
-
ok: false,
|
|
293
|
-
error: /* @__PURE__ */ new Error("TaskFlow resume failed: revision_conflict")
|
|
294
|
-
};
|
|
295
|
-
const resumed = await params.taskFlow.resume({
|
|
296
|
-
flowId: params.flowId,
|
|
297
|
-
expectedRevision: params.expectedRevision,
|
|
298
|
-
status: "running",
|
|
299
|
-
currentStep: params.currentStep ?? "resume_lobster"
|
|
300
|
-
});
|
|
301
|
-
if (!resumed.applied) return {
|
|
302
|
-
ok: false,
|
|
303
|
-
mutation: resumed,
|
|
304
|
-
error: /* @__PURE__ */ new Error(`TaskFlow resume failed: ${resumed.code}`)
|
|
305
|
-
};
|
|
306
|
-
return await executeManagedLobsterFlow({
|
|
307
|
-
...params,
|
|
308
|
-
runnerParams
|
|
309
|
-
}, resumed.flow);
|
|
310
|
-
}
|
|
311
|
-
//#endregion
|
|
312
153
|
//#region extensions/lobster/src/lobster-tool.ts
|
|
313
|
-
function readOptionalTrimmedString(value, fieldName) {
|
|
314
|
-
if (value === void 0) return;
|
|
315
|
-
if (typeof value !== "string") throw new Error(`${fieldName} must be a string`);
|
|
316
|
-
const trimmed = value.trim();
|
|
317
|
-
return trimmed ? trimmed : void 0;
|
|
318
|
-
}
|
|
319
|
-
function readOptionalNumber(value, fieldName) {
|
|
320
|
-
return readNonNegativeIntegerParam({ [fieldName]: value }, fieldName, { message: `${fieldName} must be a non-negative integer` });
|
|
321
|
-
}
|
|
322
|
-
function readOptionalBoolean(value, fieldName) {
|
|
323
|
-
if (value === void 0) return;
|
|
324
|
-
if (typeof value !== "boolean") throw new Error(`${fieldName} must be a boolean`);
|
|
325
|
-
return value;
|
|
326
|
-
}
|
|
327
|
-
function parseOptionalFlowStateJson(value) {
|
|
328
|
-
if (value === void 0) return;
|
|
329
|
-
if (typeof value !== "string") throw new Error("flowStateJson must be a JSON string");
|
|
330
|
-
const trimmed = value.trim();
|
|
331
|
-
if (!trimmed) return;
|
|
332
|
-
try {
|
|
333
|
-
return JSON.parse(trimmed);
|
|
334
|
-
} catch {
|
|
335
|
-
throw new Error("flowStateJson must be valid JSON");
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
function isEmptyJsonObject(value) {
|
|
339
|
-
return value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
340
|
-
}
|
|
341
|
-
function parseManagedFlowParams(action, params, runnerParams) {
|
|
342
|
-
if (action === "run") {
|
|
343
|
-
const controllerId = readOptionalTrimmedString(params.flowControllerId, "flowControllerId");
|
|
344
|
-
const goal = readOptionalTrimmedString(params.flowGoal, "flowGoal");
|
|
345
|
-
const currentStep = readOptionalTrimmedString(params.flowCurrentStep, "flowCurrentStep");
|
|
346
|
-
const waitingStep = readOptionalTrimmedString(params.flowWaitingStep, "flowWaitingStep");
|
|
347
|
-
const stateJson = parseOptionalFlowStateJson(params.flowStateJson);
|
|
348
|
-
const resumeFlowId = readOptionalTrimmedString(params.flowId, "flowId");
|
|
349
|
-
const resumeRevision = readOptionalNumber(params.flowExpectedRevision, "flowExpectedRevision");
|
|
350
|
-
const stateJsonSignalsRunMode = stateJson !== void 0 && !isEmptyJsonObject(stateJson);
|
|
351
|
-
if (resumeFlowId !== void 0 || resumeRevision !== void 0 && resumeRevision !== 0) throw new Error("run action does not accept flowId or flowExpectedRevision");
|
|
352
|
-
if (controllerId === void 0 && goal === void 0 && currentStep === void 0 && waitingStep === void 0 && !stateJsonSignalsRunMode) return null;
|
|
353
|
-
if (!controllerId) throw new Error("flowControllerId required when using managed TaskFlow run mode");
|
|
354
|
-
if (!goal) throw new Error("flowGoal required when using managed TaskFlow run mode");
|
|
355
|
-
return {
|
|
356
|
-
action,
|
|
357
|
-
controllerId,
|
|
358
|
-
goal,
|
|
359
|
-
...currentStep ? { currentStep } : {},
|
|
360
|
-
...waitingStep ? { waitingStep } : {},
|
|
361
|
-
...stateJson !== void 0 ? { stateJson } : {}
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
const flowId = readOptionalTrimmedString(params.flowId, "flowId");
|
|
365
|
-
const expectedRevision = readOptionalNumber(params.flowExpectedRevision, "flowExpectedRevision");
|
|
366
|
-
const currentStep = readOptionalTrimmedString(params.flowCurrentStep, "flowCurrentStep");
|
|
367
|
-
const waitingStep = readOptionalTrimmedString(params.flowWaitingStep, "flowWaitingStep");
|
|
368
|
-
const token = readOptionalTrimmedString(params.token, "token");
|
|
369
|
-
const approvalId = readOptionalTrimmedString(params.approvalId, "approvalId");
|
|
370
|
-
const approve = readOptionalBoolean(params.approve, "approve");
|
|
371
|
-
const runControllerId = readOptionalTrimmedString(params.flowControllerId, "flowControllerId");
|
|
372
|
-
const runGoal = readOptionalTrimmedString(params.flowGoal, "flowGoal");
|
|
373
|
-
const stateJson = parseOptionalFlowStateJson(params.flowStateJson);
|
|
374
|
-
const stateJsonDisallowed = stateJson !== void 0 && !isEmptyJsonObject(stateJson);
|
|
375
|
-
if (runControllerId !== void 0 || runGoal !== void 0 || stateJsonDisallowed) throw new Error("resume action does not accept flowControllerId, flowGoal, or flowStateJson");
|
|
376
|
-
if (!(flowId !== void 0 || expectedRevision !== void 0 && expectedRevision !== 0 || currentStep !== void 0 || waitingStep !== void 0)) return null;
|
|
377
|
-
if (!flowId) throw new Error("flowId required when using managed TaskFlow resume mode");
|
|
378
|
-
if (expectedRevision === void 0) throw new Error("flowExpectedRevision required when using managed TaskFlow resume mode");
|
|
379
|
-
const credentialParams = token ? { token: runnerParams.token ?? token } : approvalId ? { approvalId: runnerParams.approvalId ?? approvalId } : null;
|
|
380
|
-
if (approve === void 0) throw new Error("approve required when using managed TaskFlow resume mode");
|
|
381
|
-
return {
|
|
382
|
-
action,
|
|
383
|
-
flowId,
|
|
384
|
-
expectedRevision,
|
|
385
|
-
...currentStep ? { currentStep } : {},
|
|
386
|
-
...waitingStep ? { waitingStep } : {},
|
|
387
|
-
runnerParams: {
|
|
388
|
-
...runnerParams,
|
|
389
|
-
...credentialParams,
|
|
390
|
-
action,
|
|
391
|
-
approve
|
|
392
|
-
}
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
function resolveManagedFlowToolResult(result) {
|
|
396
|
-
if (!result.ok) throw result.error;
|
|
397
|
-
return jsonResult({
|
|
398
|
-
...result.envelope,
|
|
399
|
-
flow: result.flow,
|
|
400
|
-
mutation: result.mutation
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
function requireTaskFlowRuntime(taskFlow, action) {
|
|
404
|
-
if (!taskFlow) throw new Error(`Managed TaskFlow ${action} mode requires a bound taskFlow runtime`);
|
|
405
|
-
return taskFlow;
|
|
406
|
-
}
|
|
407
154
|
function createLobsterTool(api, options) {
|
|
408
155
|
const runner = options?.runner ?? createEmbeddedLobsterRunner();
|
|
409
156
|
return {
|
|
@@ -419,14 +166,7 @@ function createLobsterTool(api, options) {
|
|
|
419
166
|
approve: Type.Optional(Type.Boolean()),
|
|
420
167
|
cwd: Type.Optional(Type.String({ description: "Relative working directory (optional). Must stay within the gateway working directory." })),
|
|
421
168
|
timeoutMs: optionalPositiveIntegerSchema(),
|
|
422
|
-
maxStdoutBytes: optionalPositiveIntegerSchema()
|
|
423
|
-
flowControllerId: Type.Optional(Type.String()),
|
|
424
|
-
flowGoal: Type.Optional(Type.String()),
|
|
425
|
-
flowStateJson: Type.Optional(Type.String()),
|
|
426
|
-
flowId: Type.Optional(Type.String({ description: "Managed flow to resume. Its saved approval checkpoint is used when token and approvalId are omitted." })),
|
|
427
|
-
flowExpectedRevision: optionalNonNegativeIntegerSchema(),
|
|
428
|
-
flowCurrentStep: Type.Optional(Type.String()),
|
|
429
|
-
flowWaitingStep: Type.Optional(Type.String())
|
|
169
|
+
maxStdoutBytes: optionalPositiveIntegerSchema()
|
|
430
170
|
}),
|
|
431
171
|
async execute(_id, params) {
|
|
432
172
|
const action = typeof params.action === "string" ? params.action.trim() : "";
|
|
@@ -447,29 +187,6 @@ function createLobsterTool(api, options) {
|
|
|
447
187
|
timeoutMs,
|
|
448
188
|
maxStdoutBytes
|
|
449
189
|
};
|
|
450
|
-
const taskFlow = options?.taskFlow;
|
|
451
|
-
const flowParams = parseManagedFlowParams(action, params, runnerParams);
|
|
452
|
-
if (flowParams?.action === "run") return resolveManagedFlowToolResult(await runManagedLobsterFlow({
|
|
453
|
-
taskFlow: requireTaskFlowRuntime(taskFlow, "run"),
|
|
454
|
-
config: api.config,
|
|
455
|
-
runner,
|
|
456
|
-
runnerParams,
|
|
457
|
-
controllerId: flowParams.controllerId,
|
|
458
|
-
goal: flowParams.goal,
|
|
459
|
-
...flowParams.stateJson !== void 0 ? { stateJson: flowParams.stateJson } : {},
|
|
460
|
-
...flowParams.currentStep ? { currentStep: flowParams.currentStep } : {},
|
|
461
|
-
...flowParams.waitingStep ? { waitingStep: flowParams.waitingStep } : {}
|
|
462
|
-
}));
|
|
463
|
-
if (flowParams?.action === "resume") return resolveManagedFlowToolResult(await resumeManagedLobsterFlow({
|
|
464
|
-
taskFlow: requireTaskFlowRuntime(taskFlow, "resume"),
|
|
465
|
-
config: api.config,
|
|
466
|
-
runner,
|
|
467
|
-
runnerParams: flowParams.runnerParams,
|
|
468
|
-
flowId: flowParams.flowId,
|
|
469
|
-
expectedRevision: flowParams.expectedRevision,
|
|
470
|
-
...flowParams.currentStep ? { currentStep: flowParams.currentStep } : {},
|
|
471
|
-
...flowParams.waitingStep ? { waitingStep: flowParams.waitingStep } : {}
|
|
472
|
-
}));
|
|
473
190
|
const envelope = await runner.run(runnerParams);
|
|
474
191
|
if (!envelope.ok) throw new Error(envelope.error.message);
|
|
475
192
|
return jsonResult(envelope);
|
|
@@ -483,13 +200,10 @@ var lobster_default = definePluginEntry({
|
|
|
483
200
|
name: "Lobster",
|
|
484
201
|
description: "Optional local shell helper tools",
|
|
485
202
|
register(api) {
|
|
486
|
-
api.registerTool((
|
|
203
|
+
api.registerTool((ctx) => {
|
|
487
204
|
if (ctx.sandboxed) return null;
|
|
488
|
-
return createLobsterTool(api
|
|
489
|
-
|
|
490
|
-
cancel: api.runtime.tasks.managedFlows.fromToolContext(ctx).cancel
|
|
491
|
-
} : void 0 });
|
|
492
|
-
}), { optional: true });
|
|
205
|
+
return createLobsterTool(api);
|
|
206
|
+
}, { optional: true });
|
|
493
207
|
}
|
|
494
208
|
});
|
|
495
209
|
//#endregion
|
|
@@ -2,139 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
An OpenClaw-native workflow shell: typed (JSON-first) pipelines
|
|
5
|
+
An OpenClaw-native workflow shell: typed (JSON-first) pipelines and approval gates.
|
|
6
6
|
|
|
7
|
+
## Example
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
OpenClaw (or any other AI agent) can use `lobster` as a workflow engine and avoid re-planning every step — saving tokens while improving determinism and resumability.
|
|
9
|
+
A named workflow fetches a pull request with `gh`, compares it with the saved snapshot, and returns a notification only when it changes:
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo\":\"openclaw/openclaw\",\"pr\":1152}'"
|
|
14
|
-
[
|
|
15
|
-
{
|
|
16
|
-
"kind": "github.pr.monitor",
|
|
17
|
-
"repo": "openclaw/openclaw",
|
|
18
|
-
"prNumber": 1152,
|
|
19
|
-
"key": "github.pr:openclaw/openclaw#1152",
|
|
20
|
-
"changed": false,
|
|
21
|
-
"summary": {
|
|
22
|
-
"changedFields": [],
|
|
23
|
-
"changes": {}
|
|
24
|
-
},
|
|
25
|
-
"prSnapshot": {
|
|
26
|
-
"author": {
|
|
27
|
-
"id": "MDQ6VXNlcjE0MzY4NTM=",
|
|
28
|
-
"is_bot": false,
|
|
29
|
-
"login": "vignesh07",
|
|
30
|
-
"name": "Vignesh"
|
|
31
|
-
},
|
|
32
|
-
"baseRefName": "main",
|
|
33
|
-
"headRefName": "feat/lobster-plugin",
|
|
34
|
-
"isDraft": false,
|
|
35
|
-
"mergeable": "MERGEABLE",
|
|
36
|
-
"number": 1152,
|
|
37
|
-
"reviewDecision": "",
|
|
38
|
-
"state": "OPEN",
|
|
39
|
-
"title": "feat: Add optional lobster plugin tool (typed workflows, approvals/resume)",
|
|
40
|
-
"updatedAt": "2026-01-18T20:16:56Z",
|
|
41
|
-
"url": "https://github.com/openclaw/openclaw/pull/1152"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
]
|
|
11
|
+
```bash
|
|
12
|
+
node bin/lobster.js run --mode tool "workflows.run --name github.pr.monitor.notify --args-json '{\"repo\":\"openclaw/lobster\",\"pr\":167}'"
|
|
45
13
|
```
|
|
46
|
-
### And a PR that has a state change (in this case an approved PR)
|
|
47
14
|
|
|
48
|
-
|
|
49
|
-
node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo\":\"openclaw/openclaw\",\"pr\":1200}'"
|
|
50
|
-
[
|
|
51
|
-
{
|
|
52
|
-
"kind": "github.pr.monitor",
|
|
53
|
-
"repo": "openclaw/openclaw",
|
|
54
|
-
"prNumber": 1200,
|
|
55
|
-
"key": "github.pr:openclaw/openclaw#1200",
|
|
56
|
-
"changed": true,
|
|
57
|
-
"summary": {
|
|
58
|
-
"changedFields": [
|
|
59
|
-
"number",
|
|
60
|
-
"title",
|
|
61
|
-
"url",
|
|
62
|
-
"state",
|
|
63
|
-
"isDraft",
|
|
64
|
-
"mergeable",
|
|
65
|
-
"reviewDecision",
|
|
66
|
-
"updatedAt",
|
|
67
|
-
"baseRefName",
|
|
68
|
-
"headRefName"
|
|
69
|
-
],
|
|
70
|
-
"changes": {
|
|
71
|
-
"number": {
|
|
72
|
-
"from": null,
|
|
73
|
-
"to": 1200
|
|
74
|
-
},
|
|
75
|
-
"title": {
|
|
76
|
-
"from": null,
|
|
77
|
-
"to": "feat(tui): add syntax highlighting for code blocks"
|
|
78
|
-
},
|
|
79
|
-
"url": {
|
|
80
|
-
"from": null,
|
|
81
|
-
"to": "https://github.com/openclaw/openclaw/pull/1200"
|
|
82
|
-
},
|
|
83
|
-
"state": {
|
|
84
|
-
"from": null,
|
|
85
|
-
"to": "MERGED"
|
|
86
|
-
},
|
|
87
|
-
"isDraft": {
|
|
88
|
-
"from": null,
|
|
89
|
-
"to": false
|
|
90
|
-
},
|
|
91
|
-
"mergeable": {
|
|
92
|
-
"from": null,
|
|
93
|
-
"to": "UNKNOWN"
|
|
94
|
-
},
|
|
95
|
-
"reviewDecision": {
|
|
96
|
-
"from": null,
|
|
97
|
-
"to": ""
|
|
98
|
-
},
|
|
99
|
-
"updatedAt": {
|
|
100
|
-
"from": null,
|
|
101
|
-
"to": "2026-01-19T05:06:09Z"
|
|
102
|
-
},
|
|
103
|
-
"baseRefName": {
|
|
104
|
-
"from": null,
|
|
105
|
-
"to": "main"
|
|
106
|
-
},
|
|
107
|
-
"headRefName": {
|
|
108
|
-
"from": null,
|
|
109
|
-
"to": "feat/tui-syntax-highlighting"
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
"prSnapshot": {
|
|
114
|
-
"author": {
|
|
115
|
-
"id": "MDQ6VXNlcjE0MzY4NTM=",
|
|
116
|
-
"is_bot": false,
|
|
117
|
-
"login": "vignesh07",
|
|
118
|
-
"name": "Vignesh"
|
|
119
|
-
},
|
|
120
|
-
"baseRefName": "main",
|
|
121
|
-
"headRefName": "feat/tui-syntax-highlighting",
|
|
122
|
-
"isDraft": false,
|
|
123
|
-
"mergeable": "UNKNOWN",
|
|
124
|
-
"number": 1200,
|
|
125
|
-
"reviewDecision": "",
|
|
126
|
-
"state": "MERGED",
|
|
127
|
-
"title": "feat(tui): add syntax highlighting for code blocks",
|
|
128
|
-
"updatedAt": "2026-01-19T05:06:09Z",
|
|
129
|
-
"url": "https://github.com/openclaw/openclaw/pull/1200"
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
]
|
|
133
|
-
```
|
|
15
|
+
For repeatable automation, put shell commands, native pipeline steps, and explicit approval/input gates in a workflow file. OpenClaw presents the gate to the user and resumes with their decision.
|
|
134
16
|
|
|
135
17
|
## Goals
|
|
136
18
|
|
|
137
|
-
|
|
138
19
|
- Typed pipelines (objects/arrays), not text pipes.
|
|
139
20
|
- Local-first execution.
|
|
140
21
|
- No new auth surface: Lobster must not own OAuth/tokens.
|
|
@@ -142,9 +23,10 @@ node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo
|
|
|
142
23
|
|
|
143
24
|
## Quick start
|
|
144
25
|
|
|
145
|
-
From this folder:
|
|
26
|
+
Requires Node.js 22 or newer and the pnpm version pinned in `package.json`. From this folder:
|
|
146
27
|
|
|
147
|
-
- `pnpm install`
|
|
28
|
+
- `pnpm install --frozen-lockfile`
|
|
29
|
+
- `pnpm build`
|
|
148
30
|
- `pnpm test`
|
|
149
31
|
- `pnpm lint`
|
|
150
32
|
- `node ./bin/lobster.js --help`
|
|
@@ -154,7 +36,9 @@ From this folder:
|
|
|
154
36
|
### Notes
|
|
155
37
|
|
|
156
38
|
- `pnpm test` runs `tsc` and then executes tests against `dist/`.
|
|
157
|
-
- `bin/lobster.js`
|
|
39
|
+
- `bin/lobster.js` runs the compiled entrypoint in `dist/`; build after changing source files.
|
|
40
|
+
Invalid explicit workflow file paths are reported as input errors (exit code 2). In `--mode tool`, these failures use the same JSON error envelope as other parsing errors.
|
|
41
|
+
|
|
158
42
|
## Process output limits
|
|
159
43
|
|
|
160
44
|
Captured process stdout and stderr remain unlimited by default. Set
|
|
@@ -187,20 +71,16 @@ keep pipes open after overflow. Calls without an AbortSignal relay terminal
|
|
|
187
71
|
interrupts to that group and retain the host's existing signal handlers or
|
|
188
72
|
conventional interrupt exit status.
|
|
189
73
|
|
|
190
|
-
|
|
191
74
|
## Commands
|
|
192
75
|
|
|
193
76
|
- `exec`: run OS commands
|
|
194
77
|
- `exec --json <command...>`: parse subprocess stdout as JSON before the next stage
|
|
195
78
|
- `exec --stdin raw|json|jsonl`: feed pipeline input into subprocess stdin
|
|
79
|
+
- Explicit quoted empty arguments are retained by both pipeline `exec` and SDK `exec`. Invocation shims preserve argument punctuation without shell expansion.
|
|
196
80
|
- `where`, `pick`, `head`: data shaping
|
|
197
81
|
- `json`, `table`: renderers
|
|
198
82
|
- `approve`: approval gate (TTY prompt or `--emit` for OpenClaw integration)
|
|
199
83
|
|
|
200
|
-
## Next steps
|
|
201
|
-
|
|
202
|
-
- OpenClaw integration: ship as an optional OpenClaw plugin tool.
|
|
203
|
-
|
|
204
84
|
## SDK cancellation
|
|
205
85
|
|
|
206
86
|
Pass an `AbortSignal` to `new Lobster({ signal })` to cancel SDK `exec()` stages,
|
|
@@ -303,6 +183,7 @@ Commands are re-run on resume, so they must be idempotent until `requestInput` r
|
|
|
303
183
|
Use `lobster graph` to inspect workflow structure before execution.
|
|
304
184
|
|
|
305
185
|
```bash
|
|
186
|
+
lobster graph --help
|
|
306
187
|
lobster graph --file path/to/workflow.lobster
|
|
307
188
|
lobster graph --file path/to/workflow.lobster --format mermaid
|
|
308
189
|
lobster graph --file path/to/workflow.lobster --format dot
|
|
@@ -462,3 +343,9 @@ The limit counts bytes read from the response stream, including decompressed
|
|
|
462
343
|
content. A declared Content-Length over the limit is also rejected immediately.
|
|
463
344
|
Tool dispatch remains non-retryable after an overflow, as with other
|
|
464
345
|
post-dispatch errors; `llm.invoke` retains its existing retry policy.
|
|
346
|
+
|
|
347
|
+
## Development
|
|
348
|
+
|
|
349
|
+
The source is one TypeScript package. `src/core` contains the embeddable tool API, cost tracking, and LLM accounting; `src/sdk` provides pipeline composition; `src/commands` holds the command registry and standard library. Workflow loading, expressions, dry-run rendering, and execution live under `src/workflows`. `src/state` owns atomic file persistence, locks, and resume capabilities. GitHub SDK recipes and built-in workflows share transport and snapshot helpers.
|
|
350
|
+
|
|
351
|
+
Run `pnpm test`, `pnpm typecheck`, and `pnpm lint` before submitting changes. Tests compile into `dist/test` and use Node's test runner; platform-specific process tests run only where their OS primitives exist. Dependencies observe the two-day release-age policy in `pnpm-workspace.yaml`.
|