@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vignesh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# 🦞 Lobster
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
An OpenClaw-native workflow shell: typed (JSON-first) pipelines, jobs, and approval gates.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Example of Lobster at work
|
|
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.
|
|
10
|
+
|
|
11
|
+
### Watching a PR that hasn't had changes
|
|
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
|
+
]
|
|
45
|
+
```
|
|
46
|
+
### And a PR that has a state change (in this case an approved PR)
|
|
47
|
+
|
|
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
|
+
```
|
|
134
|
+
|
|
135
|
+
## Goals
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
- Typed pipelines (objects/arrays), not text pipes.
|
|
139
|
+
- Local-first execution.
|
|
140
|
+
- No new auth surface: Lobster must not own OAuth/tokens.
|
|
141
|
+
- Composable macros that OpenClaw (or any agent) can invoke in one step to save tokens.
|
|
142
|
+
|
|
143
|
+
## Quick start
|
|
144
|
+
|
|
145
|
+
From this folder:
|
|
146
|
+
|
|
147
|
+
- `pnpm install`
|
|
148
|
+
- `pnpm test`
|
|
149
|
+
- `pnpm lint`
|
|
150
|
+
- `node ./bin/lobster.js --help`
|
|
151
|
+
- `node ./bin/lobster.js doctor`
|
|
152
|
+
- `node ./bin/lobster.js "exec --json --shell 'echo [1,2,3]' | where '0>=0' | json"`
|
|
153
|
+
|
|
154
|
+
### Notes
|
|
155
|
+
|
|
156
|
+
- `pnpm test` runs `tsc` and then executes tests against `dist/`.
|
|
157
|
+
- `bin/lobster.js` prefers the compiled entrypoint in `dist/` when present.
|
|
158
|
+
## Commands
|
|
159
|
+
|
|
160
|
+
- `exec`: run OS commands
|
|
161
|
+
- `exec --stdin raw|json|jsonl`: feed pipeline input into subprocess stdin
|
|
162
|
+
- `where`, `pick`, `head`: data shaping
|
|
163
|
+
- `json`, `table`: renderers
|
|
164
|
+
- `approve`: approval gate (TTY prompt or `--emit` for OpenClaw integration)
|
|
165
|
+
|
|
166
|
+
## Next steps
|
|
167
|
+
|
|
168
|
+
- OpenClaw integration: ship as an optional OpenClaw plugin tool.
|
|
169
|
+
|
|
170
|
+
## Workflow files
|
|
171
|
+
|
|
172
|
+
Lobster workflow files are meant to read like small scripts:
|
|
173
|
+
|
|
174
|
+
- `run:` or `command:` for deterministic shell/CLI steps
|
|
175
|
+
- `pipeline:` for native Lobster stages like `llm.invoke`
|
|
176
|
+
- `approval:` for hard workflow gates between steps
|
|
177
|
+
- `stdin: $step.stdout` or `stdin: $step.json` to pass data forward
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
lobster run path/to/workflow.lobster
|
|
181
|
+
lobster run --file path/to/workflow.lobster --args-json '{"tag":"family"}'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Example file:
|
|
185
|
+
|
|
186
|
+
```yaml
|
|
187
|
+
name: jacket-advice
|
|
188
|
+
args:
|
|
189
|
+
location:
|
|
190
|
+
default: Phoenix
|
|
191
|
+
steps:
|
|
192
|
+
- id: fetch
|
|
193
|
+
run: weather --json ${location}
|
|
194
|
+
|
|
195
|
+
- id: confirm
|
|
196
|
+
approval: Want jacket advice from the LLM?
|
|
197
|
+
stdin: $fetch.json
|
|
198
|
+
|
|
199
|
+
- id: advice
|
|
200
|
+
pipeline: >
|
|
201
|
+
llm.invoke --prompt "Given this weather data, should I wear a jacket?
|
|
202
|
+
Be concise and return JSON."
|
|
203
|
+
stdin: $fetch.json
|
|
204
|
+
when: $confirm.approved
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Notes:
|
|
208
|
+
|
|
209
|
+
- `run:` and `command:` are equivalent; `run:` is the preferred spelling for new files.
|
|
210
|
+
- `pipeline:` shares the same args/env/results model as shell steps, so later steps can still reference `$step.stdout` or `$step.json`.
|
|
211
|
+
- If you need a human checkpoint before an LLM call, use a dedicated `approval:` step in the workflow file rather than `approve` inside the nested pipeline.
|
|
212
|
+
- `cwd`, `env`, `stdin`, `when`, and `condition` work for both shell and pipeline steps.
|
|
213
|
+
- Use `retry`, `timeout_ms`, and `on_error` per step to control transient-failure behavior and recovery.
|
|
214
|
+
- Approval steps can optionally enforce identity constraints:
|
|
215
|
+
- `approval.required_approver` (or `requiredApprover`) requires an exact approver id.
|
|
216
|
+
- `approval.require_different_approver` (or `requireDifferentApprover`) requires approver id to differ from initiator.
|
|
217
|
+
- `approval.initiated_by` (or `initiatedBy`) sets the initiator id for comparison.
|
|
218
|
+
- `LOBSTER_APPROVAL_INITIATED_BY` can provide a default initiator id at run time.
|
|
219
|
+
- `LOBSTER_APPROVAL_APPROVED_BY` is used at resume/approval time for identity checks.
|
|
220
|
+
|
|
221
|
+
### Command-level input requests
|
|
222
|
+
|
|
223
|
+
Pipeline commands can call `ctx.requestInput({ prompt, responseSchema, defaults, subject, suspendedState })` to pause in tool mode, workflows, or the SDK and resume the same command after a structured response. CLI/tool resume tokens store only a state key; the persisted state validates the suspended request metadata before returning the submitted response to the command. SDK same-command resumes store the command frame in the configured SDK state directory.
|
|
224
|
+
|
|
225
|
+
Commands are re-run on resume, so they must be idempotent until `requestInput` returns. Array-backed command input is snapshotted with bounds for replay; lazy stream input is not buffered and requires a compact JSON `suspendedState` supplied by the command. On resume, call `ctx.requestInput.getSuspendedState()` before reading lazy input to restore that command-owned continuation state.
|
|
226
|
+
|
|
227
|
+
## Visualizing workflows
|
|
228
|
+
|
|
229
|
+
Use `lobster graph` to inspect workflow structure before execution.
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
lobster graph --file path/to/workflow.lobster
|
|
233
|
+
lobster graph --file path/to/workflow.lobster --format mermaid
|
|
234
|
+
lobster graph --file path/to/workflow.lobster --format dot
|
|
235
|
+
lobster graph --file path/to/workflow.lobster --format ascii
|
|
236
|
+
lobster graph --file path/to/workflow.lobster --args-json '{"location":"Seattle"}'
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
What gets visualized:
|
|
240
|
+
|
|
241
|
+
- each workflow step as a node (`run`, `pipeline`, `approval`, etc.)
|
|
242
|
+
- data-flow edges from `stdin: $step.stdout` / `$step.json` references
|
|
243
|
+
- conditional dependencies from `when:` / `condition:` expressions
|
|
244
|
+
- approval gates as diamond-shaped nodes in `mermaid` and `dot` output
|
|
245
|
+
|
|
246
|
+
Format notes:
|
|
247
|
+
|
|
248
|
+
- `mermaid` (default): emits `flowchart TD` text for GitHub/Markdown rendering
|
|
249
|
+
- `dot`: emits Graphviz DOT syntax
|
|
250
|
+
- `ascii`: emits a terminal-friendly node/edge list
|
|
251
|
+
|
|
252
|
+
## Calling LLMs from workflows
|
|
253
|
+
|
|
254
|
+
Use `llm.invoke` from a native `pipeline:` step for model-backed work:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
llm.invoke --prompt 'Summarize this diff'
|
|
258
|
+
llm.invoke --provider openclaw --prompt 'Summarize this diff'
|
|
259
|
+
llm.invoke --provider pi --prompt 'Summarize this diff'
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Provider resolution order:
|
|
263
|
+
|
|
264
|
+
- `--provider`
|
|
265
|
+
- `LOBSTER_LLM_PROVIDER`
|
|
266
|
+
- auto-detect from environment
|
|
267
|
+
|
|
268
|
+
Built-in providers today:
|
|
269
|
+
|
|
270
|
+
- `openclaw` via `OPENCLAW_URL` / `OPENCLAW_TOKEN`
|
|
271
|
+
- `pi` via `LOBSTER_PI_LLM_ADAPTER_URL` (typically supplied by the Pi extension)
|
|
272
|
+
- `http` via `LOBSTER_LLM_ADAPTER_URL`
|
|
273
|
+
|
|
274
|
+
Workflow `_meta.cost` and `cost_limit` use a static pricing table plus optional overrides from `LOBSTER_LLM_PRICING_JSON`, for example `{"my-model":{"input":1.0,"output":2.0}}` in USD per million tokens. Unknown or missing model IDs still record token counts with zero estimated cost, but Lobster warns on stderr so stale or missing pricing does not fail silently.
|
|
275
|
+
|
|
276
|
+
`llm_task.invoke` remains available as a backward-compatible alias for the OpenClaw provider.
|
|
277
|
+
|
|
278
|
+
### `pipeline:` vs `run:` for LLM calls
|
|
279
|
+
|
|
280
|
+
- Use `pipeline:` for `llm.invoke` and `llm_task.invoke` (they are Lobster pipeline stages, not shell executables).
|
|
281
|
+
- Use `run:` only for real binaries in your shell (for example `openclaw.invoke`).
|
|
282
|
+
|
|
283
|
+
Example (`stdin` from a prior step is passed to the LLM as artifacts):
|
|
284
|
+
|
|
285
|
+
```yaml
|
|
286
|
+
steps:
|
|
287
|
+
- id: make_words
|
|
288
|
+
run: echo "One two three four five six"
|
|
289
|
+
|
|
290
|
+
- id: count_words
|
|
291
|
+
pipeline: llm_task.invoke --prompt "How many words have been pasted below?"
|
|
292
|
+
stdin: $make_words.stdout
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Calling OpenClaw tools from workflows
|
|
296
|
+
|
|
297
|
+
Shell `run:` steps execute in your system shell, so OpenClaw tool calls there must be real executables.
|
|
298
|
+
|
|
299
|
+
If you install Lobster via npm/pnpm, it installs a small shim executable named:
|
|
300
|
+
|
|
301
|
+
- `openclaw.invoke` (preferred)
|
|
302
|
+
- `clawd.invoke` (alias)
|
|
303
|
+
|
|
304
|
+
These shims forward to the Lobster pipeline command of the same name.
|
|
305
|
+
|
|
306
|
+
### Example: invoke llm-task
|
|
307
|
+
|
|
308
|
+
Prereqs:
|
|
309
|
+
|
|
310
|
+
- `OPENCLAW_URL` points at a running OpenClaw gateway
|
|
311
|
+
- optionally `OPENCLAW_TOKEN` if auth is enabled
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
export OPENCLAW_URL=http://127.0.0.1:18789
|
|
315
|
+
# export OPENCLAW_TOKEN=...
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
In a workflow:
|
|
319
|
+
|
|
320
|
+
```yaml
|
|
321
|
+
name: hello-world
|
|
322
|
+
steps:
|
|
323
|
+
- id: greeting
|
|
324
|
+
run: >
|
|
325
|
+
openclaw.invoke --tool llm-task --action json --args-json '{"prompt":"Hello"}'
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Passing data between steps (no temp files)
|
|
329
|
+
|
|
330
|
+
Use `stdin: $stepId.stdout` to pipe output from one step into the next.
|
|
331
|
+
|
|
332
|
+
## Args and shell-safety
|
|
333
|
+
|
|
334
|
+
`${arg}` substitution is a raw string replace into the shell command text.
|
|
335
|
+
|
|
336
|
+
For anything that may contain quotes, `$`, backticks, or newlines, prefer env vars:
|
|
337
|
+
|
|
338
|
+
- every resolved workflow arg is exposed as `LOBSTER_ARG_<NAME>` (uppercased, non-alnum → `_`)
|
|
339
|
+
- the full args object is also available as `LOBSTER_ARGS_JSON`
|
|
340
|
+
|
|
341
|
+
Example:
|
|
342
|
+
|
|
343
|
+
```yaml
|
|
344
|
+
args:
|
|
345
|
+
text:
|
|
346
|
+
default: ""
|
|
347
|
+
steps:
|
|
348
|
+
- id: safe
|
|
349
|
+
env:
|
|
350
|
+
TEXT: "$LOBSTER_ARG_TEXT"
|
|
351
|
+
command: |
|
|
352
|
+
jq -n --arg text "$TEXT" '{"result": $text}'
|
|
353
|
+
```
|
package/VISION.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Lobster: Vision & Value Proposition
|
|
2
|
+
|
|
3
|
+
## One-liner
|
|
4
|
+
|
|
5
|
+
**Lobster is safe automation for OpenClaw — workflows that ask before acting.**
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What is Lobster?
|
|
10
|
+
|
|
11
|
+
Lobster is a workflow runtime for OpenClaw. It lets you define multi-step automations that:
|
|
12
|
+
|
|
13
|
+
- Run deterministically (no LLM re-planning each step)
|
|
14
|
+
- Halt at checkpoints and ask for approval before side effects
|
|
15
|
+
- Resume exactly where they left off
|
|
16
|
+
- Remember what they've already processed
|
|
17
|
+
|
|
18
|
+
Think of it as **IFTTT/Zapier for OpenClaw, but with human checkpoints**.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## The Problem Lobster Solves
|
|
23
|
+
|
|
24
|
+
### Today's workflow in OpenClaw
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
User: "Check my email, draft replies to anything urgent, and send them"
|
|
28
|
+
|
|
29
|
+
What happens:
|
|
30
|
+
1. LLM plans: "I should search emails first"
|
|
31
|
+
2. Tool call: gmail.search
|
|
32
|
+
3. LLM interprets results, plans next step
|
|
33
|
+
4. Tool call: gmail.get (for each email)
|
|
34
|
+
5. LLM drafts replies
|
|
35
|
+
6. LLM decides to send
|
|
36
|
+
7. Tool call: gmail.send
|
|
37
|
+
... repeat for each email
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Problems:**
|
|
41
|
+
- Many tool calls = many tokens = expensive
|
|
42
|
+
- LLM decides when to send = risky (what if it misunderstands?)
|
|
43
|
+
- No memory = tomorrow it re-triages the same emails
|
|
44
|
+
- Not repeatable = you can't save this as an automation
|
|
45
|
+
|
|
46
|
+
### With Lobster
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
OpenClaw calls: lobster.run("email.triage")
|
|
50
|
+
|
|
51
|
+
What happens:
|
|
52
|
+
1. Lobster fetches emails (deterministic)
|
|
53
|
+
2. Lobster classifies them (rule-based)
|
|
54
|
+
3. Lobster drafts replies (optional LLM step)
|
|
55
|
+
4. Lobster HALTS: "Send 3 drafts? [approve/reject]"
|
|
56
|
+
5. User approves
|
|
57
|
+
6. Lobster sends
|
|
58
|
+
|
|
59
|
+
Tomorrow: Lobster remembers cursor, only processes new emails
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**One call. Deterministic. Safe. Stateful.**
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Why Lobster Makes OpenClaw Better
|
|
67
|
+
|
|
68
|
+
| Without Lobster | With Lobster |
|
|
69
|
+
|-----------------|--------------|
|
|
70
|
+
| LLM orchestrates every step | Deterministic pipeline, LLM only for judgment |
|
|
71
|
+
| 10+ tool calls per workflow | 1 call to Lobster |
|
|
72
|
+
| "Don't send until I confirm" (hope it listens) | `approve` halts execution until explicit resume |
|
|
73
|
+
| Forgets what it did yesterday | Stateful — tracks cursors/checkpoints |
|
|
74
|
+
| Hard to share automations | Workflows are code — shareable, versionable |
|
|
75
|
+
|
|
76
|
+
### Token savings (real)
|
|
77
|
+
A 10-step workflow today might cost 10 tool calls × LLM planning overhead.
|
|
78
|
+
With Lobster: 1 tool call + compact structured output.
|
|
79
|
+
|
|
80
|
+
### Safety (the killer feature)
|
|
81
|
+
The `approve` primitive isn't a prompt hint — it's a **hard stop**. The workflow literally cannot continue until you resume it. No "oops, it sent 50 emails."
|
|
82
|
+
|
|
83
|
+
### Memory (underrated)
|
|
84
|
+
Workflows can persist state: "last processed email ID", "last PR SHA seen", etc. Tomorrow's run picks up where yesterday left off.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## How Lobster Fits with OpenClaw
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
┌─────────────────────────────────────────────────┐
|
|
92
|
+
│ User │
|
|
93
|
+
│ "triage my email daily" │
|
|
94
|
+
└─────────────────────┬───────────────────────────┘
|
|
95
|
+
│
|
|
96
|
+
▼
|
|
97
|
+
┌─────────────────────────────────────────────────┐
|
|
98
|
+
│ OpenClaw │
|
|
99
|
+
│ - Understands intent │
|
|
100
|
+
│ - Chooses appropriate tool/workflow │
|
|
101
|
+
│ - Presents results and approval prompts │
|
|
102
|
+
└─────────────────────┬───────────────────────────┘
|
|
103
|
+
│ lobster.run("email.triage")
|
|
104
|
+
▼
|
|
105
|
+
┌─────────────────────────────────────────────────┐
|
|
106
|
+
│ Lobster │
|
|
107
|
+
│ - Executes deterministic pipeline │
|
|
108
|
+
│ - Calls OpenClaw tools (gmail, trello, etc) │
|
|
109
|
+
│ - Halts at approval checkpoints │
|
|
110
|
+
│ - Returns structured result + resume token │
|
|
111
|
+
└─────────────────────────────────────────────────┘
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Key insight:** Lobster doesn't replace OpenClaw. It's the execution layer that makes OpenClaw's automations safe and efficient.
|
|
115
|
+
|
|
116
|
+
- **OpenClaw** = the brain (understands what you want)
|
|
117
|
+
- **Lobster** = the hands (executes workflows safely)
|
|
118
|
+
- **Tools/Skills** = the capabilities (gmail, trello, github, etc.)
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Who Should Use Lobster?
|
|
123
|
+
|
|
124
|
+
### Average OpenClaw users (invisible benefit)
|
|
125
|
+
They don't need to know Lobster exists. They just notice:
|
|
126
|
+
- "Set up daily email triage" works better
|
|
127
|
+
- Automations ask before sending/posting
|
|
128
|
+
- Things don't get re-processed every day
|
|
129
|
+
|
|
130
|
+
### Power users / tinkerers
|
|
131
|
+
They can:
|
|
132
|
+
- Customize workflows ("I want triage to also create Trello cards")
|
|
133
|
+
- Write new workflows for their specific needs
|
|
134
|
+
- Share workflows with the community
|
|
135
|
+
|
|
136
|
+
### The OpenClaw ecosystem
|
|
137
|
+
- Workflow recipes become a new category of shareable assets
|
|
138
|
+
- Skills stay simple (just expose APIs)
|
|
139
|
+
- Lobster handles the orchestration layer
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Analogies That Work
|
|
144
|
+
|
|
145
|
+
| If you know... | Lobster is like... |
|
|
146
|
+
|----------------|-------------------|
|
|
147
|
+
| IFTTT/Zapier | Multi-step version with approval checkpoints |
|
|
148
|
+
| GitHub Actions | But for personal automation, not CI/CD |
|
|
149
|
+
| AWS Step Functions | But local-first and human-in-the-loop |
|
|
150
|
+
| Unix pipes | But for JSON objects, not text bytes |
|
|
151
|
+
| Temporal | But 80/20 version for personal workflows |
|
|
152
|
+
|
|
153
|
+
**Best analogy for most people:**
|
|
154
|
+
> "Lobster is Zapier for OpenClaw, except it asks you before doing anything irreversible."
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Why Not Build This Into OpenClaw Core?
|
|
159
|
+
|
|
160
|
+
It could be. But the plugin architecture is intentional:
|
|
161
|
+
|
|
162
|
+
1. **Core stays small** — OpenClaw is already complex
|
|
163
|
+
2. **Faster iteration** — Lobster can evolve without core releases
|
|
164
|
+
3. **Opt-in** — Not everyone needs workflow automation
|
|
165
|
+
4. **Community** — Easier to contribute workflows than core changes
|
|
166
|
+
5. **Ecosystem proof** — If plugins work for Lobster, they work for other capabilities
|
|
167
|
+
|
|
168
|
+
OpenClaw explicitly provides a plugin boundary to enable this pattern. Lobster is the first proof that it works.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Example Workflows
|
|
173
|
+
|
|
174
|
+
### Email triage (flagship)
|
|
175
|
+
```
|
|
176
|
+
gog.gmail.search --query "newer_than:1d"
|
|
177
|
+
| email.normalize
|
|
178
|
+
| email.classify
|
|
179
|
+
| where bucket==needs_reply
|
|
180
|
+
| draft.reply
|
|
181
|
+
| approve --prompt "Send N replies?"
|
|
182
|
+
| gog.gmail.send
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### PR monitor
|
|
186
|
+
```
|
|
187
|
+
github.pr.get --repo org/repo --pr 123
|
|
188
|
+
| diff.last
|
|
189
|
+
| where changed==true
|
|
190
|
+
| notify --channel telegram --message "PR updated: {summary}"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Daily planning
|
|
194
|
+
```
|
|
195
|
+
calendar.today
|
|
196
|
+
| join tasks.today
|
|
197
|
+
| prioritize
|
|
198
|
+
| format.plan
|
|
199
|
+
| approve --prompt "Post to #daily?"
|
|
200
|
+
| message.send --channel slack --to "#daily"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Inbox → Trello
|
|
204
|
+
```
|
|
205
|
+
email.triage
|
|
206
|
+
| where bucket==needs_action
|
|
207
|
+
| to.trelloCard
|
|
208
|
+
| approve --prompt "Create N cards?"
|
|
209
|
+
| trello.card.create --list "Inbox"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## The USP (Unique Selling Proposition)
|
|
215
|
+
|
|
216
|
+
**"Automations that ask before acting."**
|
|
217
|
+
|
|
218
|
+
Other automation tools either:
|
|
219
|
+
- Run blindly (IFTTT, Zapier) — scary for important actions
|
|
220
|
+
- Require complex configuration for approvals
|
|
221
|
+
- Don't integrate with your AI assistant
|
|
222
|
+
|
|
223
|
+
Lobster is:
|
|
224
|
+
- Native to OpenClaw (uses the same tools you already have)
|
|
225
|
+
- Safe by default (approvals are a language primitive)
|
|
226
|
+
- Invisible when you want (OpenClaw uses it automatically)
|
|
227
|
+
- Customizable when you need (write your own workflows)
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## What Lobster Is NOT
|
|
232
|
+
|
|
233
|
+
- **Not a terminal replacement** — You don't switch your shell to Lobster
|
|
234
|
+
- **Not a general programming language** — It's for workflows, not apps
|
|
235
|
+
- **Not trying to replace OpenClaw** — It makes OpenClaw better
|
|
236
|
+
- **Not managing your secrets** — OpenClaw handles auth, Lobster orchestrates
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Summary
|
|
241
|
+
|
|
242
|
+
| Question | Answer |
|
|
243
|
+
|----------|--------|
|
|
244
|
+
| What is it? | Workflow runtime for OpenClaw |
|
|
245
|
+
| One-liner? | Safe automation — workflows that ask before acting |
|
|
246
|
+
| Why use it? | Cheaper, safer, stateful automations |
|
|
247
|
+
| Who uses it? | Everyone (invisibly) or power users (directly) |
|
|
248
|
+
| Why not in core? | Plugin architecture — core stays small, capabilities are extensions |
|
|
249
|
+
| Best analogy? | Zapier for OpenClaw, but with approval checkpoints |
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
function shellQuote(arg) {
|
|
6
|
+
if (/^[A-Za-z0-9_\-./:=@]+$/.test(arg)) return arg;
|
|
7
|
+
return `'${String(arg).replace(/'/g, `'\\''`)}'`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const argv = process.argv.slice(2);
|
|
11
|
+
const pipeline = ['clawd.invoke', ...argv.map(shellQuote)].join(' ');
|
|
12
|
+
|
|
13
|
+
const res = spawnSync('lobster', [pipeline], {
|
|
14
|
+
stdio: 'inherit',
|
|
15
|
+
env: process.env,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
process.exit(res.status ?? 1);
|
package/bin/lobster.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
async function load() {
|
|
11
|
+
const distEntry = join(__dirname, "../dist/src/cli.js");
|
|
12
|
+
if (existsSync(distEntry)) {
|
|
13
|
+
return import(pathToFileURL(distEntry).href);
|
|
14
|
+
}
|
|
15
|
+
const srcEntry = join(__dirname, "../src/cli.js");
|
|
16
|
+
return import(pathToFileURL(srcEntry).href);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const mod = await load();
|
|
20
|
+
if (typeof mod.runCli !== "function") {
|
|
21
|
+
throw new Error("lobster CLI entrypoint missing runCli()");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
await mod.runCli(process.argv.slice(2));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
function shellQuote(arg) {
|
|
6
|
+
// Conservative POSIX-ish quoting for embedding argv into a single pipeline string.
|
|
7
|
+
// Lobster's pipeline parser preserves quoted substrings.
|
|
8
|
+
if (/^[A-Za-z0-9_\-./:=@]+$/.test(arg)) return arg;
|
|
9
|
+
// single-quote, escaping embedded single quotes: ' -> '\''
|
|
10
|
+
return `'${String(arg).replace(/'/g, `'\\''`)}'`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const argv = process.argv.slice(2);
|
|
14
|
+
const pipeline = ['openclaw.invoke', ...argv.map(shellQuote)].join(' ');
|
|
15
|
+
|
|
16
|
+
const res = spawnSync('lobster', [pipeline], {
|
|
17
|
+
stdio: 'inherit',
|
|
18
|
+
env: process.env,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
process.exit(res.status ?? 1);
|