@clawops/cli 1.3.0 → 1.5.0

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.
Files changed (45) hide show
  1. package/README.md +136 -0
  2. package/dist/{apply-DLNE2JYZ.js → apply-5RREMN3L.js} +3 -3
  3. package/dist/automation-RULUSJBW.js +0 -0
  4. package/dist/{aws-KQBWY43W.js → aws-FRE2JVAZ.js} +9 -4
  5. package/dist/{azure-4EJFVJZL.js → azure-PVC3AQVC.js} +11 -4
  6. package/dist/bootstrap-G4UZ2FKH.js +0 -0
  7. package/dist/chunk-3QJBNAHW.js +0 -0
  8. package/dist/chunk-4U3LTLWZ.js +0 -0
  9. package/dist/chunk-6ZFIFDBJ.js +0 -0
  10. package/dist/chunk-A2I76FTA.js +0 -0
  11. package/dist/chunk-BOPSG2LI.js +0 -0
  12. package/dist/chunk-CX5SL5HP.js +0 -0
  13. package/dist/{chunk-ZDPBBYYV.js → chunk-IVX62LFO.js} +2 -2
  14. package/dist/chunk-KGXPLI7W.js +0 -0
  15. package/dist/chunk-OIGTOLB3.js +0 -0
  16. package/dist/chunk-R5S6IXBE.js +771 -0
  17. package/dist/{chunk-JDN6PLH2.js → chunk-T5EX55GP.js} +3 -3
  18. package/dist/chunk-YTH4L2GN.js +0 -0
  19. package/dist/{chunk-UDNZUSKA.js → chunk-ZFNPM2WG.js} +1 -1
  20. package/dist/{chunk-JCUZU5BH.js → chunk-ZONXY3C6.js} +2 -2
  21. package/dist/chunk-ZVOEQCNW.js +0 -0
  22. package/dist/cli.js +207 -53
  23. package/dist/{context-M2RMDHX6.js → context-PSGEX2D7.js} +1 -1
  24. package/dist/errors-OK47MQFD.js +0 -0
  25. package/dist/firewall-YYDOWDDP.js +0 -0
  26. package/dist/{gcp-SKURG3L6.js → gcp-BXDTC6EK.js} +60 -17
  27. package/dist/{generate-U7PJ5LRG.js → generate-2QGKYR42.js} +2 -2
  28. package/dist/js-yaml-PTEEG4FO.js +0 -0
  29. package/dist/local-DXBEVZ5C.js +0 -0
  30. package/dist/mcp-apps-KY44ED3Y.js +0 -0
  31. package/dist/mcp-wire-ZWIYMLEW.js +45 -0
  32. package/dist/outputs-DJHBY7EE.js +0 -0
  33. package/dist/overlay-store-ADZPD7EU.js +0 -0
  34. package/dist/{package-AFUTH62F.js → package-QLDA65A3.js} +2 -1
  35. package/dist/pool-FBFHATDG.js +0 -0
  36. package/dist/providers-2OABPW2E.js +0 -0
  37. package/dist/{remote-config-JQ77SRC2.js → remote-config-QF5TT7GU.js} +1 -1
  38. package/dist/secrets-SVZWNAPK.js +0 -0
  39. package/dist/{server-I62UES5V.js → server-LOL2NUJ2.js} +92 -19
  40. package/dist/ssh-IQXNME3D.js +0 -0
  41. package/dist/state-PRBIIN7I.js +0 -0
  42. package/dist/store-SDUR52Z5.js +0 -0
  43. package/dist/validate-T5M5EHSJ.js +0 -0
  44. package/package.json +27 -14
  45. package/dist/chunk-LVIIYY27.js +0 -266
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # clawops
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@clawops/cli)](https://www.npmjs.com/package/@clawops/cli)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@clawops/cli)](https://www.npmjs.com/package/@clawops/cli)
5
+
3
6
  MCP-native infrastructure ops for OpenClaw — with read-only mode, destructive-action confirmation, and audit logs built in.
4
7
 
5
8
  **clawops** is a CLI and [MCP](https://modelcontextprotocol.io/) server for deploying and operating
@@ -9,6 +12,52 @@ and Cursor drive them through typed MCP tools with explicit safety controls.
9
12
 
10
13
  ---
11
14
 
15
+ ## What's new in v1.5.0
16
+
17
+ **`clawops mcp wire`** — wire the OpenClaw gateway's own AI as an MCP client of clawops.
18
+ Once wired, in-conversation commands like "check if my stack is healthy" or "show me the last 20
19
+ log lines" invoke the real `clawops` CLI rather than having the gateway AI guess.
20
+
21
+ ```bash
22
+ clawops mcp wire # wire the default stack
23
+ clawops mcp wire --stack prod # target a named stack
24
+ clawops mcp wire --force # bypass gateway version check
25
+ ```
26
+
27
+ Version-gated: requires OpenClaw ≥ 2026.4 on the gateway side. If the version is older, the
28
+ command surfaces a clear upgrade prompt and exits cleanly.
29
+
30
+ The `clawops setup` wizard now also asks at the end of a successful deploy:
31
+ *"Should the OpenClaw gateway's AI also be able to manage this stack?"* — accepting wires the
32
+ client automatically over the same SSH session.
33
+
34
+ ---
35
+
36
+ ## What's new in v1.4.0
37
+
38
+ **`clawops monitor`** — live dashboard for any deployed stack. Shows gateway health, container
39
+ status, CPU/memory, disk usage, and a rolling log tail. Refreshes on an interval with `[r]`,
40
+ toggles logs with `[l]`, and quits with `[q]`.
41
+
42
+ Run without `--stack` to get an **interactive stack picker** first — probes all registered stacks
43
+ in parallel, shows only running ones by default, and lets you toggle to a full list (`[a]`) where
44
+ not-deployed stacks can be deleted from the registry with `[d]`. Press `[s]` inside the dashboard
45
+ to go back to the picker.
46
+
47
+ ```bash
48
+ clawops monitor # interactive stack selection → dashboard
49
+ clawops monitor --stack prod # jump straight to a named stack
50
+ clawops monitor --stack prod | cat # one-shot snapshot for CI
51
+ ```
52
+
53
+ **`clawops_monitor` MCP tool** — same snapshot as a single structured JSON call, so Claude Code
54
+ and Cursor can check stack health without opening a terminal.
55
+
56
+ **`clawops stacks delete` guard** — now blocks deletion of a still-deployed stack and requires
57
+ `--force` to remove from the registry without tearing down cloud resources first.
58
+
59
+ ---
60
+
12
61
  ## Who this is for
13
62
 
14
63
  - **OpenClaw users** who want the simplest path to self-hosting across cloud or local VMs, with
@@ -222,8 +271,11 @@ clawops down --yes # Destroy local-provider stack
222
271
  | `backup` | Create or restore an OpenClaw state backup |
223
272
  | `stacks` | List named stacks and their state |
224
273
  | `doctor` | Check Node version, config, SSH key, provider credentials, and Pulumi home |
274
+ | `secret` | Manage secrets: `list`, `set`, `delete`, `rotate`, `audit` |
275
+ | `monitor` | Live dashboard: gateway health, container stats, log tail, stack picker |
225
276
  | `mcp serve` | Start the embedded MCP server (stdio or HTTP) |
226
277
  | `mcp install` | Interactively wire clawops into AI editors |
278
+ | `mcp wire` | Wire the gateway's AI as an MCP client of clawops |
227
279
  | `help` | List all commands and global flags |
228
280
 
229
281
  Full flag reference: `clawops <command> --help`
@@ -272,6 +324,18 @@ The wizard resolves the absolute binary path automatically so app launchers can
272
324
  without inheriting your shell's `PATH`. See [Connect an AI editor](#connect-an-ai-editor) above
273
325
  for manual config paths.
274
326
 
327
+ ### Wire the gateway AI
328
+
329
+ The OpenClaw gateway runs its own AI agent. Once wired, that agent can call clawops directly
330
+ instead of guessing at infrastructure state:
331
+
332
+ ```bash
333
+ clawops mcp wire --stack prod # write MCP client entry into gateway config + restart
334
+ ```
335
+
336
+ Requires OpenClaw ≥ 2026.4 on the gateway. The `clawops setup` wizard offers this step
337
+ automatically after a successful deploy.
338
+
275
339
  ### Stdio mode (Claude Code / Cursor / VS Code)
276
340
 
277
341
  Start the server manually or confirm your config is correct:
@@ -393,6 +457,78 @@ Key design decisions:
393
457
 
394
458
  See [`docs/architecture.md`](docs/architecture.md) for a full narrative, and [`docs/decisions/`](docs/decisions/) for ADRs.
395
459
 
460
+ ### Cloud provider stacks
461
+
462
+ Each cloud provider is an inline Pulumi program that creates the resources below. All three share the same outputs (`publicIp`, `gatewayUrl`, `sshHost`, `sshPort`, `sshUser`) consumed by the SSH and config-overlay layers.
463
+
464
+ #### AWS
465
+
466
+ ```mermaid
467
+ flowchart LR
468
+ subgraph NET["Networking"]
469
+ VPC["VPC (10.0.0.0/16)"]
470
+ IGW[Internet Gateway]
471
+ SUBNET["Subnet (10.0.1.0/24)"]
472
+ RT[Route Table]
473
+ SG["Security Group (ports 22, 18789)"]
474
+ end
475
+ subgraph IAM["IAM"]
476
+ ROLE[IAM Role]
477
+ SSM[SSM Policy Attachment]
478
+ BED["Bedrock Policy Attachment (optional)"]
479
+ IP[Instance Profile]
480
+ end
481
+ subgraph COMPUTE["Compute"]
482
+ KP[EC2 Key Pair]
483
+ EC2["EC2 Instance (Ubuntu 22.04, IMDSv2)"]
484
+ EIP[Elastic IP]
485
+ end
486
+ ```
487
+
488
+ [Detailed diagram →](docs/providers/aws.md#stack-diagram)
489
+
490
+ #### GCP
491
+
492
+ ```mermaid
493
+ flowchart LR
494
+ subgraph NET["Networking"]
495
+ NW[VPC Network]
496
+ SN["Subnetwork (10.0.0.0/24)"]
497
+ FW1["Firewall: SSH port 22 (conditional)"]
498
+ FW2["Firewall: Gateway port 18789 (conditional)"]
499
+ ADDR[Static External IP]
500
+ end
501
+ subgraph COMPUTE["Compute"]
502
+ VM["Compute Instance (Debian 12, 20 GB)"]
503
+ end
504
+ ```
505
+
506
+ [Detailed diagram →](docs/providers/gcp.md#stack-diagram)
507
+
508
+ #### Azure
509
+
510
+ ```mermaid
511
+ flowchart LR
512
+ RG[Resource Group]
513
+ subgraph NET["Networking"]
514
+ VNET["Virtual Network (10.0.0.0/16)"]
515
+ SUBNET["Subnet (10.0.1.0/24)"]
516
+ NSG["Network Security Group (ports 22, 18789)"]
517
+ PIP["Public IP Address (Static)"]
518
+ NIC[Network Interface]
519
+ end
520
+ subgraph COMPUTE["Compute"]
521
+ VM["VM (Ubuntu 22.04, managed identity)"]
522
+ end
523
+ subgraph KV["Key Vault (optional)"]
524
+ VAULT["Key Vault (RBAC, name max 24 chars)"]
525
+ RA["Role Assignment (Secrets User)"]
526
+ SECRET["Secret: gateway-token"]
527
+ end
528
+ ```
529
+
530
+ [Detailed diagram →](docs/providers/azure.md#stack-diagram)
531
+
396
532
  ---
397
533
 
398
534
  ## Development
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  applyPlan
4
- } from "./chunk-JCUZU5BH.js";
4
+ } from "./chunk-ZONXY3C6.js";
5
5
  import "./chunk-YTH4L2GN.js";
6
6
  import "./chunk-6ZFIFDBJ.js";
7
7
  import "./chunk-BOPSG2LI.js";
8
- import "./chunk-JDN6PLH2.js";
9
- import "./chunk-UDNZUSKA.js";
8
+ import "./chunk-T5EX55GP.js";
9
+ import "./chunk-ZFNPM2WG.js";
10
10
  import "./chunk-A2I76FTA.js";
11
11
  import "./chunk-CX5SL5HP.js";
12
12
  import "./chunk-KGXPLI7W.js";
File without changes
@@ -142,6 +142,12 @@ var awsProgram = async () => {
142
142
  iamInstanceProfile: instanceProfile.name,
143
143
  keyName: keyPair.keyName,
144
144
  userData: makeStartupScript(openclawVersion, bedrockEnabled),
145
+ // IMDSv2 with hopLimit=2 so Docker containers on this host can reach IMDS
146
+ // and obtain the instance role credentials (required for Bedrock access).
147
+ metadataOptions: {
148
+ httpTokens: "required",
149
+ httpPutResponseHopLimit: 2
150
+ },
145
151
  tags: { Name: "clawops" }
146
152
  });
147
153
  const eip = new aws.ec2.Eip("clawops-eip", {
@@ -164,8 +170,7 @@ var awsProgram = async () => {
164
170
  };
165
171
  };
166
172
  function makeStartupScript(openclawVersion, bedrockEnabled) {
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" : "";
173
+ const bedrockEnvFlag = bedrockEnabled ? " -e AWS_DEFAULT_REGION=$(curl -sf http://169.254.169.254/latest/meta-data/placement/region || echo us-east-1) \\\n" : "";
169
174
  return `#!/bin/bash
170
175
  set -euo pipefail
171
176
 
@@ -175,7 +180,7 @@ mkdir -p /home/clawops/.ssh
175
180
  chmod 700 /home/clawops/.ssh
176
181
  chown clawops:clawops /home/clawops/.ssh
177
182
 
178
- # Install Docker if not present (Ubuntu/Debian only; AWS AMI is ubuntu-22.04)
183
+ # Install Docker if not present (AWS AMI is ubuntu-22.04)
179
184
  if ! command -v docker &>/dev/null; then
180
185
  export DEBIAN_FRONTEND=noninteractive
181
186
  apt-get update -q
@@ -193,7 +198,7 @@ if ! command -v docker &>/dev/null; then
193
198
  fi
194
199
 
195
200
  usermod -aG docker clawops
196
- ${bedrockEnvSetup}
201
+
197
202
  # Pull OpenClaw image
198
203
  OPENCLAW_VERSION="${openclawVersion}"
199
204
  docker pull ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION}
@@ -4,12 +4,14 @@
4
4
  import process2 from "process";
5
5
 
6
6
  // src/providers/azure/program.ts
7
+ import { createHash } from "crypto";
7
8
  var GATEWAY_PORT = 18789;
8
9
  var SSH_PORT = 22;
9
10
  var azureProgram = async () => {
10
- const [pulumi, azure, { resolveIngressCidrs, detectEgressIp }] = await Promise.all([
11
+ const [pulumi, azure, random, { resolveIngressCidrs, detectEgressIp }] = await Promise.all([
11
12
  import("@pulumi/pulumi"),
12
13
  import("@pulumi/azure-native"),
14
+ import("@pulumi/random"),
13
15
  import("./firewall-YYDOWDDP.js")
14
16
  ]);
15
17
  const cfg = new pulumi.Config();
@@ -138,8 +140,9 @@ var azureProgram = async () => {
138
140
  }
139
141
  });
140
142
  if (keyVaultEnabled) {
141
- const rawKvName = `clawops-${stackName}-kv`;
142
- const kvName = rawKvName.length > 24 ? rawKvName.slice(0, 24) : rawKvName;
143
+ const safeName = stackName.replace(/[^a-zA-Z0-9-]/g, "-");
144
+ const rawKvName = `clawops-${safeName}-kv`;
145
+ const kvName = rawKvName.length > 24 ? `cl-${createHash("sha256").update(stackName).digest("hex").slice(0, 20)}` : rawKvName;
143
146
  const kv = new azure.keyvault.Vault("clawops-kv", {
144
147
  resourceGroupName: rg.name,
145
148
  location: rg.location,
@@ -157,11 +160,15 @@ var azureProgram = async () => {
157
160
  principalId: pulumi.output(vm.identity).apply((i) => i?.principalId ?? ""),
158
161
  principalType: "ServicePrincipal"
159
162
  });
163
+ const gatewayToken = new random.RandomPassword("clawops-kv-gateway-token", {
164
+ length: 32,
165
+ special: false
166
+ });
160
167
  new azure.keyvault.Secret("clawops-gateway-token", {
161
168
  resourceGroupName: rg.name,
162
169
  vaultName: kv.name,
163
170
  secretName: "gateway-token",
164
- properties: { value: "CHANGEME" }
171
+ properties: { value: gatewayToken.result }
165
172
  });
166
173
  }
167
174
  const resolvedIp = pulumi.output(publicIp.ipAddress).apply((ip) => ip ?? "");
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-YTH4L2GN.js";
5
5
  import {
6
6
  buildContext
7
- } from "./chunk-JDN6PLH2.js";
7
+ } from "./chunk-T5EX55GP.js";
8
8
  import {
9
9
  getConfig
10
10
  } from "./chunk-CX5SL5HP.js";
@@ -45,7 +45,7 @@ async function generatePlan(intent, _opts) {
45
45
  "plan/apply is not supported for the local provider. Use `clawops up` directly."
46
46
  );
47
47
  }
48
- const { version } = await import("./package-AFUTH62F.js");
48
+ const { version } = await import("./package-QLDA65A3.js");
49
49
  const config = getConfig();
50
50
  const instanceType = intent.instanceType ?? "small";
51
51
  const openclawVersion = intent.openclawVersion ?? "latest";
File without changes
File without changes