@clawops/cli 1.1.0 → 1.2.1
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/README.md +115 -44
- package/dist/apply-KBF7OPWL.js +14 -0
- package/dist/{automation-JG2YBEL7.js → automation-RULUSJBW.js} +2 -2
- package/dist/{aws-A3323GNM.js → aws-KQBWY43W.js} +12 -10
- package/dist/{azure-LNWBK2ZI.js → azure-4EJFVJZL.js} +4 -3
- package/dist/{bootstrap-7ZS2GLFJ.js → bootstrap-UYCOEJQX.js} +48 -12
- package/dist/{chunk-LT24GUUO.js → chunk-3QJBNAHW.js} +1 -1
- package/dist/{chunk-BA6MDFZK.js → chunk-4U3LTLWZ.js} +2 -83
- package/dist/{chunk-PERSDQMT.js → chunk-ACYJBSLJ.js} +7 -7
- package/dist/chunk-BOPSG2LI.js +77 -0
- package/dist/{chunk-PRYLTCS4.js → chunk-BRPU7AQC.js} +1 -1
- package/dist/{chunk-ALSUDYA7.js → chunk-CX5SL5HP.js} +1 -1
- package/dist/{chunk-ZSE4QRKE.js → chunk-KGXPLI7W.js} +19 -1
- package/dist/chunk-LU63NZD3.js +116 -0
- package/dist/chunk-OIGTOLB3.js +99 -0
- package/dist/chunk-QJ6ERXHN.js +266 -0
- package/dist/{chunk-D4UAHAKI.js → chunk-SSB6SGPQ.js} +10 -6
- package/dist/chunk-UDNZUSKA.js +97 -0
- package/dist/chunk-ZVOEQCNW.js +92 -0
- package/dist/cli.js +1340 -115
- package/dist/context-VUAYRAJY.js +10 -0
- package/dist/errors-OK47MQFD.js +21 -0
- package/dist/{gcp-OHWCTFLL.js → gcp-SKURG3L6.js} +4 -3
- package/dist/generate-PZPLG3ZM.js +14 -0
- package/dist/js-yaml-PTEEG4FO.js +2647 -0
- package/dist/mcp-apps-KY44ED3Y.js +11 -0
- package/dist/{outputs-6DAVEEAZ.js → outputs-DJHBY7EE.js} +2 -2
- package/dist/{package-FA6UOG2I.js → package-SE3M4NJA.js} +1 -1
- package/dist/{pool-FAVU7WCW.js → pool-FBFHATDG.js} +3 -2
- package/dist/remote-config-JQ77SRC2.js +21 -0
- package/dist/secrets-SVZWNAPK.js +7 -0
- package/dist/{server-V5ZFL76Y.js → server-4DNHLGEG.js} +64 -150
- package/dist/ssh-IQXNME3D.js +8 -0
- package/dist/{store-ARJ2EO6L.js → store-SDUR52Z5.js} +2 -2
- package/package.json +1 -1
- package/dist/apply-DPS23GLL.js +0 -12
- package/dist/chunk-UCIVU24B.js +0 -56
- package/dist/context-T52JWL3P.js +0 -10
- package/dist/generate-55MITRXU.js +0 -14
package/README.md
CHANGED
|
@@ -45,78 +45,140 @@ and Cursor drive them through typed MCP tools with explicit safety controls.
|
|
|
45
45
|
|
|
46
46
|
---
|
|
47
47
|
|
|
48
|
-
## Quick Start
|
|
48
|
+
## Quick Start
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
```bash
|
|
51
|
+
npm install -g @clawops/cli
|
|
52
|
+
clawops setup
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`clawops setup` is an interactive wizard that gets OpenClaw running in about 2 minutes. It
|
|
56
|
+
handles everything in one flow — no config files to write by hand, no commands to memorize.
|
|
57
|
+
|
|
58
|
+
### What the wizard does
|
|
59
|
+
|
|
60
|
+
**Step 1 — Choose a deployment target**
|
|
61
|
+
|
|
62
|
+
Pick an existing server you can SSH into (Linux or macOS), or a new cloud VM on AWS, GCP, or
|
|
63
|
+
Azure. Cloud deployments walk you through authenticating with the provider CLI if you aren't
|
|
64
|
+
already signed in.
|
|
65
|
+
|
|
66
|
+
**Step 2 — Pick an LLM provider**
|
|
67
|
+
|
|
68
|
+
Choose from Anthropic, OpenAI, Amazon Bedrock, Ollama, or others. The wizard prompts for your
|
|
69
|
+
API key and saves it locally (in `~/.clawops/secrets/`, chmod 600) — it is never sent anywhere
|
|
70
|
+
except to OpenClaw on the target host when the config is applied.
|
|
71
|
+
|
|
72
|
+
**Step 3 — Add chat integrations (optional)**
|
|
73
|
+
|
|
74
|
+
Select any combination of Discord, Telegram, Slack, WhatsApp, or Teams. The wizard collects each
|
|
75
|
+
integration's bot token the same way as the API key — paste it in, reference an env var, or point
|
|
76
|
+
to a file.
|
|
77
|
+
|
|
78
|
+
**Step 4 — Wire your AI editor**
|
|
79
|
+
|
|
80
|
+
Select which AI apps should have access to clawops — Claude Desktop, Claude Code, Cursor,
|
|
81
|
+
Windsurf, VS Code, and Zed are all supported. The wizard writes an MCP server entry into each
|
|
82
|
+
app's config file using the absolute binary path so the app can launch it independently.
|
|
51
83
|
|
|
52
|
-
**
|
|
84
|
+
**Step 5 — Deploy**
|
|
85
|
+
|
|
86
|
+
The wizard bootstraps OpenClaw on the target host over SSH (installs Docker, pulls the image,
|
|
87
|
+
starts the container), applies your LLM and integration config, generates a gateway auth token,
|
|
88
|
+
and prints a direct dashboard URL:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
✔ All done! OpenClaw is running.
|
|
92
|
+
ℹ Open dashboard: http://192.168.1.50:18789?token=<your-token>
|
|
93
|
+
ℹ Token saved to ~/.clawops/secrets/GATEWAY_TOKEN_my-stack
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Prerequisites:** Node.js ≥ 22, an SSH key, and either an SSH-reachable Linux/macOS host or a
|
|
97
|
+
cloud account with CLI credentials configured (`aws configure`, `gcloud auth login`, or `az login`).
|
|
98
|
+
|
|
99
|
+
For a full narrated walkthrough with example output, see [`docs/demo-script.md`](docs/demo-script.md).
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### Manual setup — existing server
|
|
104
|
+
|
|
105
|
+
If you prefer step-by-step control, or are adding clawops to an already-running deployment:
|
|
53
106
|
|
|
54
107
|
```bash
|
|
55
108
|
npm install -g @clawops/cli
|
|
56
109
|
|
|
57
|
-
#
|
|
58
|
-
clawops doctor
|
|
110
|
+
clawops doctor # verify environment
|
|
59
111
|
|
|
60
|
-
# Configure clawops for a local host
|
|
61
112
|
clawops init --provider local --host 192.168.1.50 --user ubuntu --key-path ~/.ssh/id_ed25519
|
|
62
|
-
|
|
63
|
-
# Bootstrap OpenClaw on the host (installs Docker + OpenClaw over SSH)
|
|
64
|
-
clawops up
|
|
65
|
-
|
|
66
|
-
# Verify it's running
|
|
113
|
+
clawops up # installs Docker + OpenClaw over SSH
|
|
67
114
|
clawops status
|
|
68
|
-
|
|
69
|
-
# Start the MCP server for Claude Code
|
|
70
|
-
clawops mcp serve
|
|
71
115
|
```
|
|
72
116
|
|
|
73
|
-
See [`docs/examples/local-vm.md`](docs/examples/local-vm.md) for
|
|
74
|
-
|
|
117
|
+
See [`docs/examples/local-vm.md`](docs/examples/local-vm.md) for SSH prerequisites, firewall
|
|
118
|
+
setup, and troubleshooting.
|
|
75
119
|
|
|
76
|
-
|
|
120
|
+
### Manual setup — cloud (AWS)
|
|
77
121
|
|
|
78
122
|
```bash
|
|
79
123
|
npm install -g @clawops/cli
|
|
80
|
-
# or: npx @clawops/cli
|
|
81
124
|
|
|
82
125
|
# Requires AWS credentials in your environment (AWS_PROFILE or ~/.aws/credentials)
|
|
83
126
|
clawops init --provider aws
|
|
84
127
|
|
|
85
|
-
# Edit ~/.clawops/config.json — set stateUrl to your S3 bucket
|
|
86
|
-
# "stateUrl": "s3://my-clawops-state"
|
|
128
|
+
# Edit ~/.clawops/config.json — set stateUrl to your S3 bucket
|
|
87
129
|
|
|
88
|
-
# Generate a deploy plan (runs pulumi preview internally)
|
|
89
130
|
clawops plan --provider aws --stack default --out /tmp/plan.json
|
|
90
|
-
|
|
91
|
-
# Review the plan, then apply
|
|
92
131
|
clawops apply /tmp/plan.json
|
|
93
132
|
```
|
|
94
133
|
|
|
95
134
|
---
|
|
96
135
|
|
|
97
|
-
## Connect
|
|
136
|
+
## Connect an AI editor
|
|
137
|
+
|
|
138
|
+
The `setup` wizard handles this automatically (Step 4). To wire or re-wire editors at any time:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
clawops mcp install
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
This opens the same interactive checkbox used in the wizard — select Claude Desktop, Claude Code,
|
|
145
|
+
Cursor, Windsurf, VS Code, or Zed and clawops writes the MCP entry into each app's config using
|
|
146
|
+
the correct absolute binary path.
|
|
98
147
|
|
|
99
|
-
|
|
148
|
+
To add the entry manually instead, paste this into your editor's MCP config:
|
|
100
149
|
|
|
101
150
|
```json
|
|
102
151
|
{
|
|
103
152
|
"mcpServers": {
|
|
104
153
|
"clawops": {
|
|
105
|
-
"command": "clawops",
|
|
154
|
+
"command": "/path/to/clawops",
|
|
106
155
|
"args": ["mcp", "serve", "--read-only"]
|
|
107
156
|
}
|
|
108
157
|
}
|
|
109
158
|
}
|
|
110
159
|
```
|
|
111
160
|
|
|
161
|
+
Replace `/path/to/clawops` with the output of `which clawops`. Config file locations:
|
|
162
|
+
|
|
163
|
+
| App | Path |
|
|
164
|
+
|---|---|
|
|
165
|
+
| Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
|
|
166
|
+
| Claude Desktop (Linux) | `~/.config/Claude/claude_desktop_config.json` |
|
|
167
|
+
| Claude Code | `~/.claude.json` |
|
|
168
|
+
| Cursor | `~/.cursor/mcp.json` |
|
|
169
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
|
|
170
|
+
| VS Code (macOS) | `~/Library/Application Support/Code/User/mcp.json` |
|
|
171
|
+
| VS Code (Linux) | `~/.config/Code/User/mcp.json` |
|
|
172
|
+
| Zed | `~/.config/zed/settings.json` (key: `context_servers`) |
|
|
173
|
+
|
|
112
174
|
**Start with `--read-only`** — it enables status, logs, config reads, and diagnostics while
|
|
113
|
-
blocking
|
|
114
|
-
|
|
175
|
+
blocking mutations. Remove it only after reviewing
|
|
176
|
+
[`docs/security/mcp-safety.md`](docs/security/mcp-safety.md).
|
|
115
177
|
|
|
116
178
|
Destructive tools (`clawops_destroy`, `clawops_up`, `clawops_config_set`, etc.) require explicit
|
|
117
|
-
confirmation
|
|
179
|
+
confirmation before executing — they will never run silently.
|
|
118
180
|
|
|
119
|
-
For
|
|
181
|
+
For HTTP mode setup see [`docs/mcp/`](docs/mcp/).
|
|
120
182
|
|
|
121
183
|
---
|
|
122
184
|
|
|
@@ -143,7 +205,8 @@ clawops down --yes # Destroy local-provider stack
|
|
|
143
205
|
|
|
144
206
|
| Command | Description |
|
|
145
207
|
|---|---|
|
|
146
|
-
| `
|
|
208
|
+
| `setup` | First-run wizard — guided LLM, integrations, and deploy-plan generation |
|
|
209
|
+
| `init` | Register a stack in `~/.clawops/config.json` without provisioning |
|
|
147
210
|
| `up` | Provision or update stack (`--dry-run` for preview) |
|
|
148
211
|
| `down` | Destroy local-provider stack (requires `--yes`; `--dry-run` shows current outputs) |
|
|
149
212
|
| `destroy` | Destroy cloud-provider stack with confirmation prompt (`--dry-run` shows current outputs) |
|
|
@@ -159,7 +222,9 @@ clawops down --yes # Destroy local-provider stack
|
|
|
159
222
|
| `backup` | Create or restore an OpenClaw state backup |
|
|
160
223
|
| `stacks` | List named stacks and their state |
|
|
161
224
|
| `doctor` | Check Node version, config, SSH key, provider credentials, and Pulumi home |
|
|
162
|
-
| `mcp` | Start the embedded MCP server (
|
|
225
|
+
| `mcp serve` | Start the embedded MCP server (stdio or HTTP) |
|
|
226
|
+
| `mcp install` | Interactively wire clawops into AI editors |
|
|
227
|
+
| `help` | List all commands and global flags |
|
|
163
228
|
|
|
164
229
|
Full flag reference: `clawops <command> --help`
|
|
165
230
|
|
|
@@ -197,23 +262,29 @@ See [`docs/plan-apply.md`](docs/plan-apply.md) for full semantics, drift guidanc
|
|
|
197
262
|
clawops ships an embedded [MCP](https://modelcontextprotocol.io/) server. Claude Code, Cursor, and
|
|
198
263
|
any MCP-compatible agent can drive deployments without leaving the chat interface.
|
|
199
264
|
|
|
200
|
-
###
|
|
265
|
+
### Wire your editor
|
|
201
266
|
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
267
|
+
```bash
|
|
268
|
+
clawops mcp install # interactive checkbox — writes config for selected apps
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The wizard resolves the absolute binary path automatically so app launchers can find `clawops`
|
|
272
|
+
without inheriting your shell's `PATH`. See [Connect an AI editor](#connect-an-ai-editor) above
|
|
273
|
+
for manual config paths.
|
|
274
|
+
|
|
275
|
+
### Stdio mode (Claude Code / Cursor / VS Code)
|
|
276
|
+
|
|
277
|
+
Start the server manually or confirm your config is correct:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
clawops mcp serve --read-only # safe for first evaluation
|
|
281
|
+
clawops mcp serve # full mode — enables provisioning, config write, ssh exec
|
|
211
282
|
```
|
|
212
283
|
|
|
213
284
|
### HTTP mode (remote / multi-client)
|
|
214
285
|
|
|
215
286
|
```bash
|
|
216
|
-
clawops mcp serve --http
|
|
287
|
+
clawops mcp serve --http 3333 --bind 127.0.0.1
|
|
217
288
|
# MCP HTTP server listening on 127.0.0.1:3333
|
|
218
289
|
```
|
|
219
290
|
|
|
@@ -341,7 +412,7 @@ pnpm dev doctor # verify toolchain
|
|
|
341
412
|
```bash
|
|
342
413
|
pnpm dev # run CLI from src/ via tsx
|
|
343
414
|
pnpm build # tsup → dist/
|
|
344
|
-
pnpm test # vitest (
|
|
415
|
+
pnpm test # vitest (493 tests, ~3s)
|
|
345
416
|
pnpm test:changed # vitest --changed (fast edit loop)
|
|
346
417
|
pnpm test:integration # Docker-based SSH integration tests
|
|
347
418
|
pnpm typecheck # tsc --noEmit
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
applyPlan
|
|
4
|
+
} from "./chunk-LU63NZD3.js";
|
|
5
|
+
import "./chunk-YTH4L2GN.js";
|
|
6
|
+
import "./chunk-BOPSG2LI.js";
|
|
7
|
+
import "./chunk-ACYJBSLJ.js";
|
|
8
|
+
import "./chunk-BRPU7AQC.js";
|
|
9
|
+
import "./chunk-UDNZUSKA.js";
|
|
10
|
+
import "./chunk-CX5SL5HP.js";
|
|
11
|
+
import "./chunk-KGXPLI7W.js";
|
|
12
|
+
export {
|
|
13
|
+
applyPlan
|
|
14
|
+
};
|
|
@@ -15,7 +15,7 @@ var awsProgram = async () => {
|
|
|
15
15
|
const cfg = new pulumi.Config();
|
|
16
16
|
const instanceType = cfg.get("instanceType") ?? "t3.small";
|
|
17
17
|
const region = cfg.get("region") ?? "us-east-1";
|
|
18
|
-
const openclawVersion = cfg.get("openclawVersion") ?? "
|
|
18
|
+
const openclawVersion = cfg.get("openclawVersion") ?? "latest";
|
|
19
19
|
const accessMode = cfg.get("accessMode") ?? "restricted";
|
|
20
20
|
const allowedCidrs = cfg.get("allowedCidrs") ?? "";
|
|
21
21
|
const sshCidrs = cfg.get("sshCidrs") ?? "";
|
|
@@ -114,7 +114,8 @@ var awsProgram = async () => {
|
|
|
114
114
|
if (bedrockEnabled) {
|
|
115
115
|
new aws.iam.RolePolicyAttachment("clawops-bedrock", {
|
|
116
116
|
role: role.name,
|
|
117
|
-
|
|
117
|
+
// FullAccess required for bedrock:InvokeModel — ReadOnly only covers describe/list.
|
|
118
|
+
policyArn: "arn:aws:iam::aws:policy/AmazonBedrockFullAccess"
|
|
118
119
|
});
|
|
119
120
|
}
|
|
120
121
|
const instanceProfile = new aws.iam.InstanceProfile("clawops-profile", {
|
|
@@ -163,10 +164,8 @@ var awsProgram = async () => {
|
|
|
163
164
|
};
|
|
164
165
|
};
|
|
165
166
|
function makeStartupScript(openclawVersion, bedrockEnabled) {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
echo "AWS_PROFILE=default" > /etc/openclaw.env
|
|
169
|
-
` : "";
|
|
167
|
+
const bedrockEnvSetup = bedrockEnabled ? '\n# Write AWS_PROFILE so the container can use the instance role for Bedrock\necho "AWS_PROFILE=default" > /etc/openclaw.env\n' : "";
|
|
168
|
+
const bedrockEnvFlag = bedrockEnabled ? " --env-file /etc/openclaw.env \\\\\n" : "";
|
|
170
169
|
return `#!/bin/bash
|
|
171
170
|
set -euo pipefail
|
|
172
171
|
|
|
@@ -176,8 +175,9 @@ mkdir -p /home/clawops/.ssh
|
|
|
176
175
|
chmod 700 /home/clawops/.ssh
|
|
177
176
|
chown clawops:clawops /home/clawops/.ssh
|
|
178
177
|
|
|
179
|
-
# Install Docker if not present
|
|
178
|
+
# Install Docker if not present (Ubuntu/Debian only; AWS AMI is ubuntu-22.04)
|
|
180
179
|
if ! command -v docker &>/dev/null; then
|
|
180
|
+
export DEBIAN_FRONTEND=noninteractive
|
|
181
181
|
apt-get update -q
|
|
182
182
|
apt-get install -y -q ca-certificates curl gnupg lsb-release
|
|
183
183
|
install -m 0755 -d /etc/apt/keyrings
|
|
@@ -193,16 +193,17 @@ if ! command -v docker &>/dev/null; then
|
|
|
193
193
|
fi
|
|
194
194
|
|
|
195
195
|
usermod -aG docker clawops
|
|
196
|
-
${
|
|
196
|
+
${bedrockEnvSetup}
|
|
197
197
|
# Pull OpenClaw image
|
|
198
198
|
OPENCLAW_VERSION="${openclawVersion}"
|
|
199
199
|
docker pull ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION}
|
|
200
200
|
|
|
201
201
|
# Create default openclaw.json if not present
|
|
202
|
+
# apply.ts will overwrite this with the plan's config overlay post-provisioning.
|
|
202
203
|
OPENCLAW_CONFIG=/home/clawops/openclaw.json
|
|
203
204
|
if [ ! -f "\${OPENCLAW_CONFIG}" ]; then
|
|
204
205
|
cat > "\${OPENCLAW_CONFIG}" <<'OPENCLAWJSON'
|
|
205
|
-
{"
|
|
206
|
+
{"meta":{"lastTouchedVersion":"2026.4"},"gateway":{"port":18789,"auth":{"mode":"token"}},"models":{},"channels":{}}
|
|
206
207
|
OPENCLAWJSON
|
|
207
208
|
chown clawops:clawops "\${OPENCLAW_CONFIG}"
|
|
208
209
|
fi
|
|
@@ -215,7 +216,8 @@ docker run -d \\
|
|
|
215
216
|
--restart unless-stopped \\
|
|
216
217
|
-p ${GATEWAY_PORT}:${GATEWAY_PORT} \\
|
|
217
218
|
-v "\${OPENCLAW_CONFIG}":/app/config.json:ro \\
|
|
218
|
-
ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION}
|
|
219
|
+
${bedrockEnvFlag} ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION} \\
|
|
220
|
+
node openclaw.mjs gateway run --allow-unconfigured
|
|
219
221
|
`;
|
|
220
222
|
}
|
|
221
223
|
|
|
@@ -16,7 +16,7 @@ var azureProgram = async () => {
|
|
|
16
16
|
const stackName = pulumi.getStack();
|
|
17
17
|
const instanceType = cfg.get("instanceType") ?? "Standard_B2s";
|
|
18
18
|
const location = cfg.get("region") ?? "eastus";
|
|
19
|
-
const openclawVersion = cfg.get("openclawVersion") ?? "
|
|
19
|
+
const openclawVersion = cfg.get("openclawVersion") ?? "latest";
|
|
20
20
|
const accessMode = cfg.get("accessMode") ?? "restricted";
|
|
21
21
|
const allowedCidrs = cfg.get("allowedCidrs") ?? "";
|
|
22
22
|
const sshCidrs = cfg.get("sshCidrs") ?? "";
|
|
@@ -206,7 +206,7 @@ docker pull ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION}
|
|
|
206
206
|
OPENCLAW_CONFIG=/home/clawops/openclaw.json
|
|
207
207
|
if [ ! -f "\${OPENCLAW_CONFIG}" ]; then
|
|
208
208
|
cat > "\${OPENCLAW_CONFIG}" <<'OPENCLAWJSON'
|
|
209
|
-
{"
|
|
209
|
+
{"meta":{"lastTouchedVersion":"2026.4"},"gateway":{"port":18789,"auth":{"mode":"token"}},"models":{},"channels":{}}
|
|
210
210
|
OPENCLAWJSON
|
|
211
211
|
chown clawops:clawops "\${OPENCLAW_CONFIG}"
|
|
212
212
|
fi
|
|
@@ -219,7 +219,8 @@ docker run -d \\
|
|
|
219
219
|
--restart unless-stopped \\
|
|
220
220
|
-p 18789:18789 \\
|
|
221
221
|
-v "\${OPENCLAW_CONFIG}":/app/config.json:ro \\
|
|
222
|
-
ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION}
|
|
222
|
+
ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION} \\
|
|
223
|
+
node openclaw.mjs gateway run --allow-unconfigured
|
|
223
224
|
`;
|
|
224
225
|
}
|
|
225
226
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
acquireSession
|
|
4
|
-
} from "./chunk-BA6MDFZK.js";
|
|
5
2
|
import {
|
|
6
3
|
writeLocalState
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import
|
|
4
|
+
} from "./chunk-BRPU7AQC.js";
|
|
5
|
+
import {
|
|
6
|
+
acquireSession
|
|
7
|
+
} from "./chunk-ZVOEQCNW.js";
|
|
8
|
+
import "./chunk-4U3LTLWZ.js";
|
|
9
|
+
import "./chunk-CX5SL5HP.js";
|
|
9
10
|
import {
|
|
10
11
|
ProviderError
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-KGXPLI7W.js";
|
|
12
13
|
|
|
13
14
|
// src/providers/local/bootstrap.ts
|
|
14
15
|
import { readFileSync } from "fs";
|
|
@@ -27,7 +28,8 @@ function renderScript(openclawVersion) {
|
|
|
27
28
|
async function localBootstrap(opts) {
|
|
28
29
|
const script = renderScript(opts.openclawVersion);
|
|
29
30
|
const b64 = Buffer.from(script, "utf-8").toString("base64");
|
|
30
|
-
const command = `
|
|
31
|
+
const command = opts.sudoPassword ? `printf '%s
|
|
32
|
+
' ${shellQuote(opts.sudoPassword)} | sudo -S -p '' bash -c "echo '${b64}' | base64 -d | bash"` : `echo '${b64}' | base64 -d | sudo bash`;
|
|
31
33
|
const { session, release } = await acquireSession({
|
|
32
34
|
host: opts.host,
|
|
33
35
|
port: opts.port,
|
|
@@ -37,12 +39,16 @@ async function localBootstrap(opts) {
|
|
|
37
39
|
signal: opts.signal
|
|
38
40
|
});
|
|
39
41
|
try {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
if (opts.onOutput) {
|
|
43
|
+
await execStreaming(session, command, opts.onOutput, opts.signal);
|
|
44
|
+
} else {
|
|
45
|
+
const result = await session.exec(command, opts.signal);
|
|
46
|
+
if (result.code !== 0) {
|
|
47
|
+
throw new ProviderError(
|
|
48
|
+
`Bootstrap script failed (exit ${result.code}):
|
|
44
49
|
${result.stderr || result.stdout}`
|
|
45
|
-
|
|
50
|
+
);
|
|
51
|
+
}
|
|
46
52
|
}
|
|
47
53
|
} finally {
|
|
48
54
|
release();
|
|
@@ -86,6 +92,36 @@ async function waitForGateway(host, port, signal) {
|
|
|
86
92
|
function sleep(ms) {
|
|
87
93
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
88
94
|
}
|
|
95
|
+
function shellQuote(s) {
|
|
96
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
97
|
+
}
|
|
98
|
+
async function execStreaming(session, command, onOutput, signal) {
|
|
99
|
+
const ch = await session.stream(command, signal);
|
|
100
|
+
let leftover = "";
|
|
101
|
+
let stderr = "";
|
|
102
|
+
ch.on("data", (chunk) => {
|
|
103
|
+
const text = leftover + chunk.toString("utf-8");
|
|
104
|
+
const lines = text.split("\n");
|
|
105
|
+
leftover = lines.pop() ?? "";
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
if (line.trim()) onOutput(line);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
ch.stderr.on("data", (chunk) => {
|
|
111
|
+
stderr += chunk.toString("utf-8");
|
|
112
|
+
});
|
|
113
|
+
const exitCode = await new Promise((resolve, reject) => {
|
|
114
|
+
ch.on("close", (code) => {
|
|
115
|
+
if (leftover.trim()) onOutput(leftover);
|
|
116
|
+
resolve(code ?? 0);
|
|
117
|
+
});
|
|
118
|
+
ch.on("error", reject);
|
|
119
|
+
});
|
|
120
|
+
if (exitCode !== 0) {
|
|
121
|
+
throw new ProviderError(`Bootstrap script failed (exit ${exitCode}):
|
|
122
|
+
${stderr}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
89
125
|
export {
|
|
90
126
|
localBootstrap
|
|
91
127
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
NetworkError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KGXPLI7W.js";
|
|
5
5
|
|
|
6
6
|
// src/transport/ssh.ts
|
|
7
7
|
import { readFileSync, appendFileSync, mkdirSync } from "fs";
|
|
@@ -172,87 +172,6 @@ function verifyHostKey(host, port, keyHash, knownHostsPath) {
|
|
|
172
172
|
return true;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
// src/transport/pool.ts
|
|
176
|
-
var IDLE_TTL_MS = 5 * 60 * 1e3;
|
|
177
|
-
var MAX_PER_HOST = 4;
|
|
178
|
-
var CLEANUP_INTERVAL_MS = 3e4;
|
|
179
|
-
var pool = /* @__PURE__ */ new Map();
|
|
180
|
-
var cleanupTimer = null;
|
|
181
|
-
function poolKey(opts) {
|
|
182
|
-
return `${opts.user}@${opts.host}:${opts.port}`;
|
|
183
|
-
}
|
|
184
|
-
function startCleanupTimer() {
|
|
185
|
-
if (cleanupTimer) return;
|
|
186
|
-
cleanupTimer = setInterval(() => {
|
|
187
|
-
const now = Date.now();
|
|
188
|
-
for (const [key, entries] of pool) {
|
|
189
|
-
const active = entries.filter((e) => {
|
|
190
|
-
if (!e.inUse && now - e.lastUsed > IDLE_TTL_MS) {
|
|
191
|
-
e.session.close();
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
return true;
|
|
195
|
-
});
|
|
196
|
-
if (active.length === 0) {
|
|
197
|
-
pool.delete(key);
|
|
198
|
-
} else {
|
|
199
|
-
pool.set(key, active);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
if (pool.size === 0 && cleanupTimer) {
|
|
203
|
-
clearInterval(cleanupTimer);
|
|
204
|
-
cleanupTimer = null;
|
|
205
|
-
}
|
|
206
|
-
}, CLEANUP_INTERVAL_MS);
|
|
207
|
-
cleanupTimer.unref();
|
|
208
|
-
}
|
|
209
|
-
async function acquireSession(opts) {
|
|
210
|
-
const key = poolKey(opts);
|
|
211
|
-
const entries = pool.get(key) ?? [];
|
|
212
|
-
const idle = entries.find((e) => !e.inUse);
|
|
213
|
-
if (idle) {
|
|
214
|
-
idle.inUse = true;
|
|
215
|
-
idle.lastUsed = Date.now();
|
|
216
|
-
return {
|
|
217
|
-
session: idle.session,
|
|
218
|
-
release() {
|
|
219
|
-
idle.inUse = false;
|
|
220
|
-
idle.lastUsed = Date.now();
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
if (entries.length >= MAX_PER_HOST) {
|
|
225
|
-
throw new NetworkError(
|
|
226
|
-
`SSH connection pool exhausted for ${key} (max ${MAX_PER_HOST} concurrent connections).`
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
const session = await connect(opts);
|
|
230
|
-
const entry = { session, hostKey: key, lastUsed: Date.now(), inUse: true };
|
|
231
|
-
entries.push(entry);
|
|
232
|
-
pool.set(key, entries);
|
|
233
|
-
startCleanupTimer();
|
|
234
|
-
return {
|
|
235
|
-
session,
|
|
236
|
-
release() {
|
|
237
|
-
entry.inUse = false;
|
|
238
|
-
entry.lastUsed = Date.now();
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
function drainPool() {
|
|
243
|
-
for (const entries of pool.values()) {
|
|
244
|
-
for (const e of entries) {
|
|
245
|
-
e.session.close();
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
pool.clear();
|
|
249
|
-
if (cleanupTimer) {
|
|
250
|
-
clearInterval(cleanupTimer);
|
|
251
|
-
cleanupTimer = null;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
175
|
export {
|
|
256
|
-
|
|
257
|
-
drainPool
|
|
176
|
+
connect
|
|
258
177
|
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
readLocalState
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BRPU7AQC.js";
|
|
5
5
|
import {
|
|
6
6
|
getConfigDir,
|
|
7
7
|
requireConfig
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CX5SL5HP.js";
|
|
9
9
|
import {
|
|
10
10
|
UsageError
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KGXPLI7W.js";
|
|
12
12
|
|
|
13
13
|
// src/cli/context.ts
|
|
14
14
|
function buildContext(args) {
|
|
@@ -36,7 +36,7 @@ function buildContext(args) {
|
|
|
36
36
|
`Stack "${stackName}" not found in config. Run \`clawops init\` or use \`--stack\` to specify a different stack.`
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
|
-
const { getOrCreateStack } = await import("./automation-
|
|
39
|
+
const { getOrCreateStack } = await import("./automation-RULUSJBW.js");
|
|
40
40
|
stackCache = await getOrCreateStack({
|
|
41
41
|
stack: stackName,
|
|
42
42
|
stateUrl: stackConfig.stateUrl,
|
|
@@ -56,17 +56,17 @@ function makeProviderProxy(name) {
|
|
|
56
56
|
if (resolved) return resolved;
|
|
57
57
|
switch (name) {
|
|
58
58
|
case "gcp": {
|
|
59
|
-
const mod = await import("./gcp-
|
|
59
|
+
const mod = await import("./gcp-SKURG3L6.js");
|
|
60
60
|
resolved = mod.default;
|
|
61
61
|
return resolved;
|
|
62
62
|
}
|
|
63
63
|
case "aws": {
|
|
64
|
-
const mod = await import("./aws-
|
|
64
|
+
const mod = await import("./aws-KQBWY43W.js");
|
|
65
65
|
resolved = mod.default;
|
|
66
66
|
return resolved;
|
|
67
67
|
}
|
|
68
68
|
case "azure": {
|
|
69
|
-
const mod = await import("./azure-
|
|
69
|
+
const mod = await import("./azure-4EJFVJZL.js");
|
|
70
70
|
resolved = mod.default;
|
|
71
71
|
return resolved;
|
|
72
72
|
}
|