diff --git a/.gitignore b/.gitignore index bd804cc..d3fe19a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ dist/ # Private docs clawdbot_enhancement.md +# Pack artifacts +*.tgz + # OS files .DS_Store diff --git a/README.md b/README.md index 7469fe0..4bfd828 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ # Lobster -A Moltbot-native workflow shell: typed (JSON-first) pipelines, jobs, and approval gates. +A OpenClaw-native workflow shell: typed (JSON-first) pipelines, jobs, and approval gates. ## Example of lobster at work -Moltbot or any other AI agent can use `lobster` as a workflow engine and not construct a query every time - thus saving tokens, providing room for determinism, and resumability. +OpenClaw or any other AI agent can use `lobster` as a workflow engine and not construct a query every time - thus saving tokens, providing room for determinism, and resumability. ### Watching a PR that hasn't had changes ``` -node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo\":\"moltbot/moltbot\",\"pr\":1152}'" +node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo\":\"openclaw/openclaw\",\"pr\":1152}'" [ { "kind": "github.pr.monitor", - "repo": "moltbot/moltbot", + "repo": "openclaw/openclaw", "prNumber": 1152, - "key": "github.pr:moltbot/moltbot#1152", + "key": "github.pr:openclaw/openclaw#1152", "changed": false, "summary": { "changedFields": [], @@ -36,7 +36,7 @@ node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo "state": "OPEN", "title": "feat: Add optional lobster plugin tool (typed workflows, approvals/resume)", "updatedAt": "2026-01-18T20:16:56Z", - "url": "https://github.com/moltbot/moltbot/pull/1152" + "url": "https://github.com/openclaw/openclaw/pull/1152" } } ] @@ -44,13 +44,13 @@ node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo ### And a PR that has a state change (in this case an approved PR) ``` - node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo\":\"moltbot/moltbot\",\"pr\":1200}'" + node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo\":\"openclaw/openclaw\",\"pr\":1200}'" [ { "kind": "github.pr.monitor", - "repo": "moltbot/moltbot", + "repo": "openclaw/openclaw", "prNumber": 1200, - "key": "github.pr:moltbot/moltbot#1200", + "key": "github.pr:openclaw/openclaw#1200", "changed": true, "summary": { "changedFields": [ @@ -76,7 +76,7 @@ node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo }, "url": { "from": null, - "to": "https://github.com/moltbot/moltbot/pull/1200" + "to": "https://github.com/openclaw/openclaw/pull/1200" }, "state": { "from": null, @@ -124,7 +124,7 @@ node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo "state": "MERGED", "title": "feat(tui): add syntax highlighting for code blocks", "updatedAt": "2026-01-19T05:06:09Z", - "url": "https://github.com/moltbot/moltbot/pull/1200" + "url": "https://github.com/openclaw/openclaw/pull/1200" } } ] @@ -136,7 +136,7 @@ node bin/lobster.js "workflows.run --name github.pr.monitor --args-json '{\"repo - Typed pipelines (objects/arrays), not text pipes. - Local-first execution. - No new auth surface: Lobster must not own OAuth/tokens. -- Composable macros that Moltbot can invoke in one step to save tokens. +- Composable macros that OpenClaw can invoke in one step to save tokens. ## Quick start @@ -159,11 +159,11 @@ From this folder: - `exec --stdin raw|json|jsonl`: feed pipeline input into subprocess stdin - `where`, `pick`, `head`: data shaping - `json`, `table`: renderers -- `approve`: approval gate (TTY prompt or `--emit` for Moltbot integration) +- `approve`: approval gate (TTY prompt or `--emit` for OpenClaw integration) ## Next steps -- Moltbot integration: ship as an optional Moltbot plugin tool. +- OpenClaw integration: ship as an optional OpenClaw plugin tool. ## Workflow files diff --git a/VISION.md b/VISION.md index 388b8cb..b052796 100644 --- a/VISION.md +++ b/VISION.md @@ -2,26 +2,26 @@ ## One-liner -**Lobster is safe automation for Clawdbot — workflows that ask before acting.** +**Lobster is safe automation for OpenClaw — workflows that ask before acting.** --- ## What is Lobster? -Lobster is a workflow runtime for Clawdbot. It lets you define multi-step automations that: +Lobster is a workflow runtime for OpenClaw. It lets you define multi-step automations that: - Run deterministically (no LLM re-planning each step) - Halt at checkpoints and ask for approval before side effects - Resume exactly where they left off - Remember what they've already processed -Think of it as **IFTTT/Zapier for Clawdbot, but with human checkpoints**. +Think of it as **IFTTT/Zapier for OpenClaw, but with human checkpoints**. --- ## The Problem Lobster Solves -### Today's workflow in Clawdbot +### Today's workflow in OpenClaw ``` User: "Check my email, draft replies to anything urgent, and send them" @@ -46,7 +46,7 @@ What happens: ### With Lobster ``` -Clawdbot calls: lobster.run("email.triage") +OpenClaw calls: lobster.run("email.triage") What happens: 1. Lobster fetches emails (deterministic) @@ -63,7 +63,7 @@ Tomorrow: Lobster remembers cursor, only processes new emails --- -## Why Lobster Makes Clawdbot Better +## Why Lobster Makes OpenClaw Better | Without Lobster | With Lobster | |-----------------|--------------| @@ -85,7 +85,7 @@ Workflows can persist state: "last processed email ID", "last PR SHA seen", etc. --- -## How Lobster Fits with Clawdbot +## How Lobster Fits with OpenClaw ``` ┌─────────────────────────────────────────────────┐ @@ -95,7 +95,7 @@ Workflows can persist state: "last processed email ID", "last PR SHA seen", etc. │ ▼ ┌─────────────────────────────────────────────────┐ -│ Clawdbot │ +│ OpenClaw │ │ - Understands intent │ │ - Chooses appropriate tool/workflow │ │ - Presents results and approval prompts │ @@ -105,15 +105,15 @@ Workflows can persist state: "last processed email ID", "last PR SHA seen", etc. ┌─────────────────────────────────────────────────┐ │ Lobster │ │ - Executes deterministic pipeline │ -│ - Calls Clawdbot tools (gmail, trello, etc) │ +│ - Calls OpenClaw tools (gmail, trello, etc) │ │ - Halts at approval checkpoints │ │ - Returns structured result + resume token │ └─────────────────────────────────────────────────┘ ``` -**Key insight:** Lobster doesn't replace Clawdbot. It's the execution layer that makes Clawdbot's automations safe and efficient. +**Key insight:** Lobster doesn't replace OpenClaw. It's the execution layer that makes OpenClaw's automations safe and efficient. -- **Clawdbot** = the brain (understands what you want) +- **OpenClaw** = the brain (understands what you want) - **Lobster** = the hands (executes workflows safely) - **Tools/Skills** = the capabilities (gmail, trello, github, etc.) @@ -121,7 +121,7 @@ Workflows can persist state: "last processed email ID", "last PR SHA seen", etc. ## Who Should Use Lobster? -### Average Clawdbot users (invisible benefit) +### Average OpenClaw users (invisible benefit) They don't need to know Lobster exists. They just notice: - "Set up daily email triage" works better - Automations ask before sending/posting @@ -133,7 +133,7 @@ They can: - Write new workflows for their specific needs - Share workflows with the community -### The Clawdbot ecosystem +### The OpenClaw ecosystem - Workflow recipes become a new category of shareable assets - Skills stay simple (just expose APIs) - Lobster handles the orchestration layer @@ -151,21 +151,21 @@ They can: | Temporal | But 80/20 version for personal workflows | **Best analogy for most people:** -> "Lobster is Zapier for Clawdbot, except it asks you before doing anything irreversible." +> "Lobster is Zapier for OpenClaw, except it asks you before doing anything irreversible." --- -## Why Not Build This Into Clawdbot Core? +## Why Not Build This Into OpenClaw Core? It could be. But the plugin architecture is intentional: -1. **Core stays small** — Clawdbot is already complex +1. **Core stays small** — OpenClaw is already complex 2. **Faster iteration** — Lobster can evolve without core releases 3. **Opt-in** — Not everyone needs workflow automation 4. **Community** — Easier to contribute workflows than core changes 5. **Ecosystem proof** — If plugins work for Lobster, they work for other capabilities -Pete (Clawdbot creator) explicitly built the plugin boundary to enable this pattern. Lobster is the first proof that it works. +Pete (OpenClaw creator, formerly Clawdbot) explicitly built the plugin boundary to enable this pattern. Lobster is the first proof that it works. --- @@ -221,9 +221,9 @@ Other automation tools either: - Don't integrate with your AI assistant Lobster is: -- Native to Clawdbot (uses the same tools you already have) +- Native to OpenClaw (uses the same tools you already have) - Safe by default (approvals are a language primitive) -- Invisible when you want (Clawdbot uses it automatically) +- Invisible when you want (OpenClaw uses it automatically) - Customizable when you need (write your own workflows) --- @@ -232,8 +232,8 @@ Lobster is: - **Not a terminal replacement** — You don't switch your shell to Lobster - **Not a general programming language** — It's for workflows, not apps -- **Not trying to replace Clawdbot** — It makes Clawdbot better -- **Not managing your secrets** — Clawdbot handles auth, Lobster orchestrates +- **Not trying to replace OpenClaw** — It makes OpenClaw better +- **Not managing your secrets** — OpenClaw handles auth, Lobster orchestrates --- @@ -241,9 +241,9 @@ Lobster is: | Question | Answer | |----------|--------| -| What is it? | Workflow runtime for Clawdbot | +| What is it? | Workflow runtime for OpenClaw | | One-liner? | Safe automation — workflows that ask before acting | | Why use it? | Cheaper, safer, stateful automations | | Who uses it? | Everyone (invisibly) or power users (directly) | | Why not in core? | Plugin architecture — core stays small, capabilities are extensions | -| Best analogy? | Zapier for Clawdbot, but with approval checkpoints | +| Best analogy? | Zapier for OpenClaw, but with approval checkpoints | diff --git a/bin/clawd-invoke.js b/bin/clawd-invoke.js new file mode 100755 index 0000000..d6b721f --- /dev/null +++ b/bin/clawd-invoke.js @@ -0,0 +1,130 @@ +#!/usr/bin/env node + +// Shell-accessible wrapper for the clawd.invoke pipeline command. +// Allows workflow steps (which run via /bin/sh) to call clawd.invoke +// without going through the lobster pipeline runner. +// +// Usage: +// clawd-invoke --tool web_search --action search --args-json '{"query":"test"}' +// echo '{"query":"test"}' | clawd-invoke --tool web_search --action search --each + +import { existsSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +function resolveImport(relPath) { + const distPath = join(__dirname, "../dist", relPath); + if (existsSync(distPath)) return distPath; + return join(__dirname, "..", relPath); +} + +function parseArgs(argv) { + const args = { _: [] }; + for (let i = 0; i < argv.length; i++) { + const tok = argv[i]; + if (tok === "--help" || tok === "-h") { + args._help = true; + continue; + } + if (tok.startsWith("--")) { + const eq = tok.indexOf("="); + if (eq !== -1) { + args[tok.slice(2, eq)] = tok.slice(eq + 1); + continue; + } + const key = tok.slice(2); + const next = argv[i + 1]; + if (!next || next.startsWith("--")) { + args[key] = true; + continue; + } + args[key] = next; + i++; + continue; + } + args._.push(tok); + } + return args; +} + +async function readStdin() { + if (process.stdin.isTTY) return null; + const chunks = []; + for await (const chunk of process.stdin) chunks.push(chunk); + const text = Buffer.concat(chunks).toString("utf8").trim(); + if (!text) return null; + return text; +} + +async function* stdinToItems(text) { + // Try JSONL (one object per line), then JSON array, then single JSON object + const lines = text.split("\n").filter((l) => l.trim()); + let yieldedAny = false; + + // Try each line as JSON + for (const line of lines) { + try { + yield JSON.parse(line); + yieldedAny = true; + } catch { + // not JSON, skip + } + } + + if (!yieldedAny) { + // Try full text as JSON array + try { + const parsed = JSON.parse(text); + if (Array.isArray(parsed)) { + for (const item of parsed) yield item; + return; + } + yield parsed; + return; + } catch { + // not JSON at all + } + } +} + +async function main() { + const args = parseArgs(process.argv.slice(2)); + + if (args._help) { + const mod = await import(resolveImport("src/commands/stdlib/clawd_invoke.js")); + process.stdout.write(mod.clawdInvokeCommand.help() + "\n"); + process.exit(0); + } + + const mod = await import(resolveImport("src/commands/stdlib/clawd_invoke.js")); + const command = mod.clawdInvokeCommand; + + const stdinText = await readStdin(); + + async function* emptyStream() { + // no items + } + + const input = stdinText ? stdinToItems(stdinText) : emptyStream(); + + const ctx = { + env: process.env, + stdin: process.stdin, + stdout: process.stdout, + stderr: process.stderr, + }; + + const result = await command.run({ input, args, ctx }); + + for await (const item of result.output) { + process.stdout.write(JSON.stringify(item) + "\n"); + } +} + +main().catch((err) => { + process.stderr.write(`clawd-invoke: ${err.message}\n`); + process.exitCode = 1; +}); diff --git a/bin/llm-task-invoke.js b/bin/llm-task-invoke.js new file mode 100755 index 0000000..7367054 --- /dev/null +++ b/bin/llm-task-invoke.js @@ -0,0 +1,132 @@ +#!/usr/bin/env node + +// Shell-accessible wrapper for the llm_task.invoke pipeline command. +// Allows workflow steps (which run via /bin/sh) to call llm_task.invoke +// without going through the lobster pipeline runner. +// +// Usage: +// llm-task-invoke --prompt 'Summarize this document' +// cat artifact.json | llm-task-invoke --prompt 'Score the item' +// llm-task-invoke --prompt 'Extract metadata' --output-schema '{"type":"object"}' + +import { existsSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +function resolveImport(relPath) { + const distPath = join(__dirname, "../dist", relPath); + if (existsSync(distPath)) return distPath; + return join(__dirname, "..", relPath); +} + +function parseArgs(argv) { + const args = { _: [] }; + for (let i = 0; i < argv.length; i++) { + const tok = argv[i]; + if (tok === "--help" || tok === "-h") { + args._help = true; + continue; + } + if (tok.startsWith("--")) { + const eq = tok.indexOf("="); + if (eq !== -1) { + args[tok.slice(2, eq)] = tok.slice(eq + 1); + continue; + } + const key = tok.slice(2); + const next = argv[i + 1]; + if (!next || next.startsWith("--")) { + args[key] = true; + continue; + } + args[key] = next; + i++; + continue; + } + args._.push(tok); + } + return args; +} + +async function readStdin() { + if (process.stdin.isTTY) return null; + const chunks = []; + for await (const chunk of process.stdin) chunks.push(chunk); + const text = Buffer.concat(chunks).toString("utf8").trim(); + if (!text) return null; + return text; +} + +async function* stdinToArtifacts(text) { + // Try as JSON first (array or single object) + try { + const parsed = JSON.parse(text); + if (Array.isArray(parsed)) { + for (const item of parsed) yield item; + return; + } + yield parsed; + return; + } catch { + // not valid JSON + } + + // Try JSONL (one object per line) + const lines = text.split("\n").filter((l) => l.trim()); + let yieldedAny = false; + for (const line of lines) { + try { + yield JSON.parse(line); + yieldedAny = true; + } catch { + // skip non-JSON lines + } + } + + // Plain text -> text artifact + if (!yieldedAny) { + yield { kind: "text", text }; + } +} + +async function main() { + const args = parseArgs(process.argv.slice(2)); + + if (args._help) { + const mod = await import(resolveImport("src/commands/stdlib/llm_task_invoke.js")); + process.stdout.write(mod.llmTaskInvokeCommand.help() + "\n"); + process.exit(0); + } + + const mod = await import(resolveImport("src/commands/stdlib/llm_task_invoke.js")); + const command = mod.llmTaskInvokeCommand; + + const stdinText = await readStdin(); + + async function* emptyStream() { + // no items + } + + const input = stdinText ? stdinToArtifacts(stdinText) : emptyStream(); + + const ctx = { + env: process.env, + stdin: process.stdin, + stdout: process.stdout, + stderr: process.stderr, + }; + + const result = await command.run({ input, args, ctx }); + + for await (const item of result.output) { + process.stdout.write(JSON.stringify(item) + "\n"); + } +} + +main().catch((err) => { + process.stderr.write(`llm-task-invoke: ${err.message}\n`); + process.exitCode = 1; +}); diff --git a/docs/adr/001-workflow-command-resolution.md b/docs/adr/001-workflow-command-resolution.md new file mode 100644 index 0000000..461c7dc --- /dev/null +++ b/docs/adr/001-workflow-command-resolution.md @@ -0,0 +1,92 @@ +# ADR-001: Workflow Command Resolution + +## Status + +Accepted (2026-02-14) + +## Context + +Lobster workflow files (`.workflow.yml`) define steps as shell commands: + +```yaml +steps: + - name: search + command: | + clawd.invoke --tool web_search --action search --args-json '{"query":"$QUERY"}' +``` + +The workflow runner (`workflows/file.ts`) executes each step's `command` via +`/bin/sh -lc ""`. Pipeline commands like `clawd.invoke` and +`llm_task.invoke` are registered in the TypeScript command registry and are +only available when running through the lobster pipeline runner, not as +standalone executables on PATH. + +This causes silent failures: the shell cannot find the command, the step +produces empty output, and downstream steps (e.g. `bib-build`) crash on +missing data. Error messages are often masked by `2>/dev/null || true` +patterns in workflow scripts. + +## Options Considered + +### A. Bin wrappers (chosen) + +Create standalone Node.js scripts (`bin/clawd-invoke.js`, +`bin/llm-task-invoke.js`) that import the command's `run()` function and +expose it as a shell-callable binary. Register them in `package.json` `bin` +so `npm install -g` creates symlinks in `/usr/local/bin/`. + +Tradeoffs: +- [+] Works immediately with existing workflow files +- [+] No changes to workflow runner +- [+] Each wrapper is self-contained (< 100 lines) +- [-] Duplicates arg parsing (parser.ts vs manual argv parsing) +- [-] Each new pipeline command needs a corresponding bin wrapper + +### B. Workflow runner interception + +Modify the workflow runner to detect pipeline command names in step commands +and route them through the command registry instead of spawning a shell. + +Tradeoffs: +- [+] No bin wrappers needed +- [+] Commands share the existing context (registry, state, caching) +- [-] Mixed shell/lobster commands in one line are hard to parse reliably + (e.g. `clawd.invoke --tool x | jq .result`) +- [-] Breaks the simple mental model of "steps are shell commands" +- [-] Requires changes to the workflow runner's execution model + +### C. HTTP shell wrappers in skills + +Have each skill provide its own `curl`-based wrappers that call the OpenClaw +gateway directly, bypassing the lobster command layer entirely. + +Tradeoffs: +- [+] Skill-independent, works with any deployment +- [-] Loses caching, run-state management, and schema validation from + `llm_task.invoke` +- [-] Each skill must reimplement auth, error handling, and retry logic +- [-] Tightly couples skills to the gateway HTTP API + +## Decision + +Option A (bin wrappers). The arg parsing duplication is acceptable given +that the parser is simple (`--key value` pairs) and the wrappers are thin +pass-through scripts. This provides the fastest fix with minimal risk. + +## Future Consideration + +The workflow format could support a `pipeline:` step type alongside +`command:` for native lobster command execution without the shell +intermediary: + +```yaml +steps: + - name: search + pipeline: | + clawd.invoke --tool web_search --action search | jq-filter --expr '.results' +``` + +This would use the existing pipeline runner (with its registry, context, and +streaming) for steps that don't need shell features. This approach combines +the reliability of option B with the simplicity of the current model, but +requires a workflow format version bump and runner changes. diff --git a/package.json b/package.json index 2ba5b15..ff408ef 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,12 @@ { - "name": "@clawdbot/lobster", + "name": "@openclaw/lobster", "version": "2026.1.21-1", "description": "Workflow runtime for AI agents - deterministic pipelines with approval gates", "type": "module", "bin": { - "lobster": "bin/lobster.js" + "lobster": "bin/lobster.js", + "clawd.invoke": "bin/clawd-invoke.js", + "llm_task.invoke": "bin/llm-task-invoke.js" }, "files": [ "bin", @@ -43,16 +45,16 @@ "approval", "pipeline", "lobster", - "clawdbot" + "openclaw" ], "repository": { "type": "git", - "url": "git+https://github.com/clawdbot/lobster.git" + "url": "git+https://github.com/openclaw/lobster.git" }, "bugs": { - "url": "https://github.com/clawdbot/lobster/issues" + "url": "https://github.com/openclaw/lobster/issues" }, - "homepage": "https://github.com/clawdbot/lobster#readme", + "homepage": "https://github.com/openclaw/lobster#readme", "license": "MIT", "dependencies": { "ajv": "^8.17.1", diff --git a/src/cli.ts b/src/cli.ts index 3f78b7a..128c8c5 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -449,7 +449,7 @@ function writeToolEnvelope(payload) { } function helpText() { - return `lobster — Clawdbot-native typed shell\n\n` + + return `lobster — OpenClaw-native typed shell\n\n` + `Usage:\n` + ` lobster ''\n` + ` lobster run --mode tool ''\n` + diff --git a/src/commands/commands_list.ts b/src/commands/commands_list.ts index c76ab0b..218b435 100644 --- a/src/commands/commands_list.ts +++ b/src/commands/commands_list.ts @@ -14,7 +14,7 @@ export const commandsListCommand: LobsterCommand = { `Usage:\n` + ` commands.list\n\n` + `Notes:\n` + - ` - Intended for agents (e.g. Clawdbot) to discover available pipeline stages dynamically.\n` + + ` - Intended for agents (e.g. OpenClaw) to discover available pipeline stages dynamically.\n` + ` - Output includes name/description plus optional metadata (argsSchema/examples/sideEffects) when provided by commands.\n` ); }, diff --git a/src/commands/stdlib/clawd_invoke.ts b/src/commands/stdlib/clawd_invoke.ts index 95b0378..5fd574d 100644 --- a/src/commands/stdlib/clawd_invoke.ts +++ b/src/commands/stdlib/clawd_invoke.ts @@ -1,11 +1,11 @@ export const clawdInvokeCommand = { name: 'clawd.invoke', meta: { - description: 'Call a local Clawdbot tool endpoint', + description: 'Call a local OpenClaw tool endpoint', argsSchema: { type: 'object', properties: { - url: { type: 'string', description: 'Clawdbot control URL (or CLAWD_URL)' }, + url: { type: 'string', description: 'OpenClaw control URL (or CLAWD_URL)' }, token: { type: 'string', description: 'Bearer token (or CLAWD_TOKEN)' }, tool: { type: 'string', description: 'Tool name (e.g. message, cron, github, etc.)' }, action: { type: 'string', description: 'Tool action' }, @@ -21,7 +21,7 @@ export const clawdInvokeCommand = { sideEffects: ['calls_clawd_tool'], }, help() { - return `clawd.invoke — call a local Clawdbot tool endpoint\n\n` + + return `clawd.invoke — call a local OpenClaw tool endpoint\n\n` + `Usage:\n` + ` clawd.invoke --tool message --action send --args-json '{"provider":"telegram","to":"...","message":"..."}'\n` + ` clawd.invoke --tool message --action send --args-json '{...}' --dry-run\n` + diff --git a/src/commands/stdlib/email_triage.ts b/src/commands/stdlib/email_triage.ts index 570ce4c..9517938 100644 --- a/src/commands/stdlib/email_triage.ts +++ b/src/commands/stdlib/email_triage.ts @@ -233,7 +233,7 @@ export const emailTriageCommand = { } const model = String(args.model ?? '').trim(); - // Model is optional when running under Clawdbot (llm_task.invoke will use Clawdbot defaults). + // Model is optional when running under OpenClaw (llm_task.invoke will use OpenClaw defaults). if (!ctx?.registry) throw new Error('email.triage (LLM mode) requires ctx.registry'); const llmCmd = ctx.registry.get('llm_task.invoke'); diff --git a/src/commands/stdlib/llm_task_invoke.ts b/src/commands/stdlib/llm_task_invoke.ts index 86ece36..2e314ff 100644 --- a/src/commands/stdlib/llm_task_invoke.ts +++ b/src/commands/stdlib/llm_task_invoke.ts @@ -26,7 +26,7 @@ const payloadSchema = { type: 'object', properties: { prompt: { type: 'string', minLength: 1 }, - // In direct mode, the remote likely requires model; in CLAWD mode, Clawdbot can default. + // In direct mode, the remote likely requires model; in CLAWD mode, OpenClaw can default. model: { type: 'string', minLength: 1 }, artifacts: { type: 'array', items: artifactSchema }, artifactHashes: { type: 'array', items: { type: 'string', minLength: 10 } }, @@ -146,12 +146,10 @@ type CacheEntry = { storedAt: string; }; -type Transport = 'clawd'; - export const llmTaskInvokeCommand = { name: 'llm_task.invoke', meta: { - description: 'Call Clawdbot llm-task tool with typed payloads and caching', + description: 'Call OpenClaw llm-task tool with typed payloads and caching', argsSchema: { type: 'object', properties: { @@ -160,7 +158,7 @@ export const llmTaskInvokeCommand = { description: 'Bearer token (or CLAWD_TOKEN). Optional if unauthenticated.', }, prompt: { type: 'string', description: 'Primary prompt / instructions' }, - model: { type: 'string', description: 'Model identifier (optional; Clawdbot default will be used if omitted in CLAWD mode)' }, + model: { type: 'string', description: 'Model identifier (optional; OpenClaw default will be used if omitted in CLAWD mode)' }, 'artifacts-json': { type: 'string', description: 'JSON array of artifacts to send' }, 'metadata-json': { type: 'string', description: 'JSON object of metadata to include' }, 'output-schema': { type: 'string', description: 'JSON schema LLM output must satisfy' }, @@ -179,14 +177,14 @@ export const llmTaskInvokeCommand = { }, help() { return ( - `llm_task.invoke — call Clawdbot llm-task tool with caching and schema validation\n\n` + + `llm_task.invoke — call OpenClaw llm-task tool with caching and schema validation\n\n` + `Usage:\n` + ` llm_task.invoke --prompt 'Write summary'\n` + ` llm_task.invoke --model claude-3-sonnet --prompt 'Write summary'\n` + ` cat artifacts.json | llm_task.invoke --prompt 'Score each item'\n` + ` ... | llm_task.invoke --prompt 'Plan next steps' --output-schema '{"type":"object"}'\n\n` + `Config:\n` + - ` - Requires CLAWD_URL (Clawdbot gateway).\n` + + ` - Requires CLAWD_URL (OpenClaw gateway).\n` + ` - Optional CLAWD_TOKEN for auth.\n\n` + `Features:\n` + ` - Typed payload validation before invoking tool.\n` + @@ -198,16 +196,15 @@ export const llmTaskInvokeCommand = { const env = ctx.env ?? process.env; const clawdUrl = String(env.CLAWD_URL ?? '').trim(); - const transport: Transport = 'clawd'; if (!clawdUrl) { - throw new Error('llm_task.invoke requires CLAWD_URL (run via Clawdbot gateway)'); + throw new Error('llm_task.invoke requires CLAWD_URL (run via OpenClaw gateway)'); } const prompt = extractPrompt(args); if (!prompt) throw new Error('llm_task.invoke requires --prompt or positional text'); const model = String(args.model ?? env.LLM_TASK_MODEL ?? '').trim(); - // Model is optional in Clawdbot mode (Clawdbot llm-task tool can use its default model). + // Model is optional in OpenClaw mode (OpenClaw llm-task tool can use its default model). const schemaVersion = args['schema-version'] ? String(args['schema-version']).trim() @@ -425,7 +422,7 @@ function computeCacheKey({ }) { const payload = { prompt, - // If model is omitted (Clawdbot default), keep caching stable but explicit. + // If model is omitted (OpenClaw default), keep caching stable but explicit. model: model || 'clawd-default', schemaVersion, artifactHashes, @@ -464,7 +461,7 @@ async function invokeRemoteViaClawd({ endpoint, token, payload }: { endpoint: UR throw new Error('Response was not JSON'); } - // Clawdbot tool router envelope: { ok, result, error } + // OpenClaw tool router envelope: { ok, result, error } if (parsed && typeof parsed === 'object' && !Array.isArray(parsed) && 'ok' in parsed) { if (parsed.ok !== true) { const msg = parsed?.error?.message ?? 'Unknown error'; diff --git a/src/commands/workflows/workflows_list.ts b/src/commands/workflows/workflows_list.ts index fb4983f..ca1d421 100644 --- a/src/commands/workflows/workflows_list.ts +++ b/src/commands/workflows/workflows_list.ts @@ -8,7 +8,7 @@ export const workflowsListCommand = { sideEffects: [], }, help() { - return `workflows.list — list available Lobster workflows\n\nUsage:\n workflows.list\n\nNotes:\n - Intended for Clawdbot to discover workflows dynamically.\n`; + return `workflows.list — list available Lobster workflows\n\nUsage:\n workflows.list\n\nNotes:\n - Intended for OpenClaw to discover workflows dynamically.\n`; }, async run({ input }) { // Drain input. diff --git a/src/commands/workflows/workflows_run.ts b/src/commands/workflows/workflows_run.ts index cd01b84..0b5dd6f 100644 --- a/src/commands/workflows/workflows_run.ts +++ b/src/commands/workflows/workflows_run.ts @@ -26,7 +26,7 @@ export const workflowsRunCommand = { sideEffects: [], }, help() { - return `workflows.run — run a named Lobster workflow\n\nUsage:\n workflows.run --name [--args-json '{...}']\n\nExample:\n workflows.run --name github.pr.monitor.notify --args-json '{"repo":"clawdbot/clawdbot","pr":1152}'\n`; + return `workflows.run — run a named Lobster workflow\n\nUsage:\n workflows.run --name [--args-json '{...}']\n\nExample:\n workflows.run --name github.pr.monitor.notify --args-json '{"repo":"openclaw/openclaw","pr":1152}'\n`; }, async run({ input, args, ctx }) { // Drain input. diff --git a/src/recipes/index.ts b/src/recipes/index.ts index 4156274..538bee5 100644 --- a/src/recipes/index.ts +++ b/src/recipes/index.ts @@ -1,7 +1,7 @@ /** * Recipe entrypoints. * - * Core Lobster intentionally keeps only Clawdbot-first recipes here. + * Core Lobster intentionally keeps only OpenClaw-first recipes here. */ export * from "./github/index.js"; diff --git a/src/workflows/registry.ts b/src/workflows/registry.ts index 67bf36a..cf9f186 100644 --- a/src/workflows/registry.ts +++ b/src/workflows/registry.ts @@ -5,7 +5,7 @@ export const workflowRegistry = { argsSchema: { type: 'object', properties: { - repo: { type: 'string', description: 'owner/repo (e.g. clawdbot/clawdbot)' }, + repo: { type: 'string', description: 'owner/repo (e.g. openclaw/openclaw)' }, pr: { type: 'number', description: 'Pull request number' }, key: { type: 'string', description: 'Optional state key override.' }, changesOnly: { type: 'boolean', description: 'If true, suppress output when unchanged.' }, @@ -15,7 +15,7 @@ export const workflowRegistry = { }, examples: [ { - args: { repo: 'clawdbot/clawdbot', pr: 1152 }, + args: { repo: 'openclaw/openclaw', pr: 1152 }, description: 'Monitor a PR and report when it changes.', }, ], @@ -27,7 +27,7 @@ export const workflowRegistry = { argsSchema: { type: 'object', properties: { - repo: { type: 'string', description: 'owner/repo (e.g. clawdbot/clawdbot)' }, + repo: { type: 'string', description: 'owner/repo (e.g. openclaw/openclaw)' }, pr: { type: 'number', description: 'Pull request number' }, key: { type: 'string', description: 'Optional state key override.' }, }, @@ -35,7 +35,7 @@ export const workflowRegistry = { }, examples: [ { - args: { repo: 'clawdbot/clawdbot', pr: 1152 }, + args: { repo: 'openclaw/openclaw', pr: 1152 }, description: 'Emit "PR updated" message only when changed.', }, ], diff --git a/test/email_triage.test.ts b/test/email_triage.test.ts index b6a30dc..324b1b0 100644 --- a/test/email_triage.test.ts +++ b/test/email_triage.test.ts @@ -139,7 +139,7 @@ test("email.triage --llm uses llm_task.invoke to draft replies (and can emit dra bodyLog.push(parsed); res.writeHead(200, { "content-type": "application/json" }); - // Clawdbot tool router envelope -> llm-task tool envelope + // OpenClaw tool router envelope -> llm-task tool envelope res.end( JSON.stringify({ ok: true, diff --git a/test/llm_task_invoke.test.ts b/test/llm_task_invoke.test.ts index 4a9728a..61cd82c 100644 --- a/test/llm_task_invoke.test.ts +++ b/test/llm_task_invoke.test.ts @@ -308,7 +308,7 @@ test('llm_task.invoke uses CLAWD_URL (/tools/invoke) without requiring --url/--m const parsed = JSON.parse(buf || '{}'); bodyLog.push(parsed); - // This is the Clawdbot tool router envelope. + // This is the OpenClaw tool router envelope. res.writeHead(200, { 'content-type': 'application/json' }); res.end( JSON.stringify({