@clawops/cli 1.4.0 → 1.6.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 (46) hide show
  1. package/README.md +40 -0
  2. package/dist/{apply-5RREMN3L.js → apply-SI3JKS4Z.js} +2 -2
  3. package/dist/automation-RULUSJBW.js +0 -0
  4. package/dist/{aws-FRE2JVAZ.js → aws-D7Y6LCGK.js} +52 -90
  5. package/dist/{azure-PVC3AQVC.js → azure-JQAMNVHN.js} +15 -56
  6. package/dist/{bootstrap-G4UZ2FKH.js → bootstrap-RY4VOSDJ.js} +2 -2
  7. package/dist/chunk-3QJBNAHW.js +0 -0
  8. package/dist/chunk-6ZFIFDBJ.js +0 -0
  9. package/dist/{chunk-UAA6YOOP.js → chunk-72FF7IK2.js} +2 -2
  10. package/dist/chunk-A2I76FTA.js +0 -0
  11. package/dist/chunk-BOPSG2LI.js +0 -0
  12. package/dist/{chunk-JYQZJMD3.js → chunk-C5NDLOK2.js} +41 -33
  13. package/dist/chunk-CX5SL5HP.js +0 -0
  14. package/dist/{chunk-4U3LTLWZ.js → chunk-GJEF6UQA.js} +1 -0
  15. package/dist/chunk-KGXPLI7W.js +0 -0
  16. package/dist/{chunk-ZVOEQCNW.js → chunk-LCKD7L7X.js} +1 -1
  17. package/dist/chunk-OIGTOLB3.js +0 -0
  18. package/dist/{chunk-ZONXY3C6.js → chunk-QHGFENYR.js} +2 -2
  19. package/dist/{chunk-T5EX55GP.js → chunk-QI75OVJK.js} +3 -3
  20. package/dist/chunk-XKT42M34.js +72 -0
  21. package/dist/chunk-YTH4L2GN.js +0 -0
  22. package/dist/chunk-ZFNPM2WG.js +0 -0
  23. package/dist/cli.js +311 -54
  24. package/dist/{context-PSGEX2D7.js → context-KPKBOWOP.js} +1 -1
  25. package/dist/errors-OK47MQFD.js +0 -0
  26. package/dist/firewall-XQGO7HWK.js +51 -0
  27. package/dist/{gcp-BXDTC6EK.js → gcp-6FT2A45S.js} +14 -63
  28. package/dist/{generate-Z7DBMANX.js → generate-YV7226Q7.js} +2 -2
  29. package/dist/js-yaml-PTEEG4FO.js +0 -0
  30. package/dist/local-DXBEVZ5C.js +0 -0
  31. package/dist/mcp-apps-KY44ED3Y.js +0 -0
  32. package/dist/mcp-wire-ZWIYMLEW.js +45 -0
  33. package/dist/outputs-DJHBY7EE.js +0 -0
  34. package/dist/overlay-store-ADZPD7EU.js +0 -0
  35. package/dist/{package-WAJRGBBM.js → package-EW3FS257.js} +1 -1
  36. package/dist/{pool-FBFHATDG.js → pool-JZGKPP6K.js} +2 -2
  37. package/dist/providers-2OABPW2E.js +0 -0
  38. package/dist/remote-config-QF5TT7GU.js +0 -0
  39. package/dist/secrets-SVZWNAPK.js +0 -0
  40. package/dist/{server-IWLZJ4EU.js → server-3DEZHUWB.js} +9 -9
  41. package/dist/{ssh-IQXNME3D.js → ssh-E2AMBPZY.js} +1 -1
  42. package/dist/state-PRBIIN7I.js +0 -0
  43. package/dist/store-SDUR52Z5.js +0 -0
  44. package/dist/validate-T5M5EHSJ.js +0 -0
  45. package/package.json +26 -14
  46. package/dist/firewall-YYDOWDDP.js +0 -36
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/providers/firewall.ts
4
+ function resolveIngressCidrs(accessMode, allowedCidrs, portOverride, egressResult) {
5
+ if (portOverride.trim()) {
6
+ return portOverride.split(",").map((s) => s.trim()).filter(Boolean);
7
+ }
8
+ switch (accessMode) {
9
+ case "restricted": {
10
+ if (!allowedCidrs.trim()) return [];
11
+ return allowedCidrs.split(",").map((s) => s.trim()).filter(Boolean);
12
+ }
13
+ case "auto": {
14
+ if (!egressResult.ok) {
15
+ throw new Error(
16
+ `accessMode=auto: egress IP detection failed \u2014 ${egressResult.error}. Set allowedCidrs explicitly or use accessMode=restricted.`
17
+ );
18
+ }
19
+ const ip = egressResult.ip.trim();
20
+ if (!ip) {
21
+ throw new Error(
22
+ `accessMode=auto: egress IP detection returned an empty address. Set allowedCidrs explicitly or use accessMode=restricted.`
23
+ );
24
+ }
25
+ return [ip.includes("/") ? ip : `${ip}/32`];
26
+ }
27
+ case "open":
28
+ return ["0.0.0.0/0"];
29
+ default:
30
+ return [];
31
+ }
32
+ }
33
+ async function detectEgressIp(checkUrl) {
34
+ try {
35
+ const res = await fetch(checkUrl, { signal: AbortSignal.timeout(3e3) });
36
+ if (!res.ok) {
37
+ return { ok: false, error: `HTTP ${res.status} from ${checkUrl}` };
38
+ }
39
+ const ip = (await res.text()).trim();
40
+ if (!ip) {
41
+ return { ok: false, error: `empty response from ${checkUrl}` };
42
+ }
43
+ return { ok: true, ip };
44
+ } catch (err) {
45
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
46
+ }
47
+ }
48
+ export {
49
+ detectEgressIp,
50
+ resolveIngressCidrs
51
+ };
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ makeStartupScript
4
+ } from "./chunk-XKT42M34.js";
2
5
 
3
6
  // src/providers/gcp/index.ts
4
7
  import process2 from "process";
@@ -10,7 +13,7 @@ var gcpProgram = async () => {
10
13
  const [pulumi, gcp, { resolveIngressCidrs, detectEgressIp }] = await Promise.all([
11
14
  import("@pulumi/pulumi"),
12
15
  import("@pulumi/gcp"),
13
- import("./firewall-YYDOWDDP.js")
16
+ import("./firewall-XQGO7HWK.js")
14
17
  ]);
15
18
  const cfg = new pulumi.Config();
16
19
  const instanceType = cfg.get("instanceType") ?? "e2-standard-2";
@@ -27,14 +30,14 @@ var gcpProgram = async () => {
27
30
  'Stack config "sshPublicKey" is required for the GCP adapter. Set it with: pulumi config set --stack <name> sshPublicKey "ssh-ed25519 ..."'
28
31
  );
29
32
  }
30
- const detectedIp = accessMode === "auto" ? await detectEgressIp("https://checkip.amazonaws.com") : "";
33
+ const egressResult = accessMode === "auto" ? await detectEgressIp("https://ifconfig.me") : { ok: true, ip: "" };
31
34
  if (accessMode === "open") {
32
35
  process.stderr.write(
33
36
  "[clawops] WARNING: accessMode=open allows 0.0.0.0/0 on SSH and gateway ports. Only use this for development/sandbox stacks.\n"
34
37
  );
35
38
  }
36
- const sshIngressCidrs = resolveIngressCidrs(accessMode, allowedCidrs, sshCidrs, detectedIp);
37
- const gatewayIngressCidrs = resolveIngressCidrs(accessMode, allowedCidrs, gatewayCidrs, detectedIp);
39
+ const sshIngressCidrs = resolveIngressCidrs(accessMode, allowedCidrs, sshCidrs, egressResult);
40
+ const gatewayIngressCidrs = resolveIngressCidrs(accessMode, allowedCidrs, gatewayCidrs, egressResult);
38
41
  const network = new gcp.compute.Network("clawops-network", {
39
42
  autoCreateSubnetworks: false,
40
43
  description: "clawops managed network"
@@ -42,7 +45,7 @@ var gcpProgram = async () => {
42
45
  const subnet = new gcp.compute.Subnetwork("clawops-subnet", {
43
46
  ipCidrRange: "10.0.0.0/24",
44
47
  region,
45
- network: network.id
48
+ network: network.selfLink
46
49
  });
47
50
  if (sshIngressCidrs.length > 0) {
48
51
  new gcp.compute.Firewall("clawops-firewall-ssh", {
@@ -73,8 +76,8 @@ var gcpProgram = async () => {
73
76
  },
74
77
  networkInterfaces: [
75
78
  {
76
- network: network.id,
77
- subnetwork: subnet.id,
79
+ network: network.selfLink,
80
+ subnetwork: subnet.selfLink,
78
81
  accessConfigs: [
79
82
  {
80
83
  natIp: address.address,
@@ -86,7 +89,7 @@ var gcpProgram = async () => {
86
89
  metadata: {
87
90
  // GCP guest agent reads 'ssh-keys' and populates /home/<user>/.ssh/authorized_keys
88
91
  "ssh-keys": `clawops:${sshPublicKey}`,
89
- "startup-script": makeStartupScript(openclawVersion)
92
+ "startup-script": makeStartupScript({ openclawVersion, os: "debian" })
90
93
  },
91
94
  serviceAccount: {
92
95
  scopes: ["https://www.googleapis.com/auth/cloud-platform"]
@@ -103,58 +106,6 @@ var gcpProgram = async () => {
103
106
  provisionedAt: (/* @__PURE__ */ new Date()).toISOString()
104
107
  };
105
108
  };
106
- function makeStartupScript(openclawVersion) {
107
- return `#!/bin/bash
108
- set -euo pipefail
109
-
110
- # Create clawops user with SSH access
111
- id -u clawops &>/dev/null || useradd -m -s /bin/bash clawops
112
- mkdir -p /home/clawops/.ssh
113
- chmod 700 /home/clawops/.ssh
114
-
115
- # Install Docker if not present
116
- if ! command -v docker &>/dev/null; then
117
- apt-get update -q
118
- apt-get install -y -q ca-certificates curl gnupg lsb-release
119
- install -m 0755 -d /etc/apt/keyrings
120
- curl -fsSL https://download.docker.com/linux/debian/gpg \\
121
- | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
122
- chmod a+r /etc/apt/keyrings/docker.gpg
123
- echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \\
124
- https://download.docker.com/linux/debian $(lsb_release -cs) stable" \\
125
- > /etc/apt/sources.list.d/docker.list
126
- apt-get update -q
127
- apt-get install -y -q docker-ce docker-ce-cli containerd.io
128
- systemctl enable --now docker
129
- fi
130
-
131
- usermod -aG docker clawops
132
-
133
- # Pull OpenClaw image
134
- OPENCLAW_VERSION="${openclawVersion}"
135
- docker pull ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION}
136
-
137
- # Create default openclaw.json if not present
138
- OPENCLAW_CONFIG=/home/clawops/openclaw.json
139
- if [ ! -f "\${OPENCLAW_CONFIG}" ]; then
140
- cat > "\${OPENCLAW_CONFIG}" <<'OPENCLAWJSON'
141
- {"meta":{"lastTouchedVersion":"2026.4"},"gateway":{"port":18789,"auth":{"mode":"token"}},"models":{},"channels":{}}
142
- OPENCLAWJSON
143
- chown clawops:clawops "\${OPENCLAW_CONFIG}"
144
- fi
145
-
146
- # Start OpenClaw container
147
- docker stop openclaw 2>/dev/null || true
148
- docker rm openclaw 2>/dev/null || true
149
- docker run -d \\
150
- --name openclaw \\
151
- --restart unless-stopped \\
152
- -p ${GATEWAY_PORT}:${GATEWAY_PORT} \\
153
- -v "\${OPENCLAW_CONFIG}":/app/config.json:ro \\
154
- ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION} \\
155
- node openclaw.mjs gateway run --allow-unconfigured
156
- `;
157
- }
158
109
 
159
110
  // src/providers/gcp/index.ts
160
111
  var INSTANCE_TYPE_MAP = {
@@ -172,9 +123,9 @@ var gcpAdapter = {
172
123
  },
173
124
  getConnectionInfo(outputs) {
174
125
  return {
175
- host: String(outputs["sshHost"]),
176
- port: Number(outputs["sshPort"]),
177
- user: String(outputs["sshUser"]),
126
+ host: String(outputs["sshHost"] ?? ""),
127
+ port: Number(outputs["sshPort"] ?? 22),
128
+ user: String(outputs["sshUser"] ?? "clawops"),
178
129
  privateKeyPath: String(outputs["privateKeyPath"] ?? ""),
179
130
  knownHostsPath: String(outputs["knownHostsPath"] ?? "")
180
131
  };
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  generatePlan,
4
4
  planId
5
- } from "./chunk-UAA6YOOP.js";
5
+ } from "./chunk-72FF7IK2.js";
6
6
  import "./chunk-YTH4L2GN.js";
7
- import "./chunk-T5EX55GP.js";
7
+ import "./chunk-QI75OVJK.js";
8
8
  import "./chunk-A2I76FTA.js";
9
9
  import "./chunk-CX5SL5HP.js";
10
10
  import "./chunk-KGXPLI7W.js";
File without changes
File without changes
File without changes
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ atomicWriteConfig,
4
+ deepMerge,
5
+ readRemoteConfig,
6
+ restartGateway
7
+ } from "./chunk-ZFNPM2WG.js";
8
+
9
+ // src/cli/mcp-wire.ts
10
+ var GATEWAY_MCP_MIN_VERSION = "2026.4";
11
+ var GATEWAY_MCP_ENTRY = {
12
+ command: "clawops",
13
+ args: ["mcp", "serve"],
14
+ transport: "stdio"
15
+ };
16
+ function versionAtLeast(version, min) {
17
+ const [vy = 0, vm = 0] = version.split(".").map(Number);
18
+ const [my = 0, mm = 0] = min.split(".").map(Number);
19
+ if (vy !== my) return vy > my;
20
+ return vm >= mm;
21
+ }
22
+ async function wireGatewayMcp(session, signal, opts = {}) {
23
+ const cfg = await readRemoteConfig(session, signal);
24
+ const version = cfg["meta"]?.["lastTouchedVersion"];
25
+ if (version && !versionAtLeast(version, GATEWAY_MCP_MIN_VERSION) && !opts.force) {
26
+ return { status: "version-blocked", version };
27
+ }
28
+ const mcpClients = cfg["gateway"]?.["mcpClients"];
29
+ const rewired = !!mcpClients?.["clawops"];
30
+ const updated = deepMerge(cfg, {
31
+ gateway: {
32
+ mcpClients: {
33
+ clawops: GATEWAY_MCP_ENTRY
34
+ }
35
+ }
36
+ });
37
+ await atomicWriteConfig(session, updated, signal);
38
+ await restartGateway(session, signal);
39
+ return { status: "wired", rewired };
40
+ }
41
+ export {
42
+ GATEWAY_MCP_ENTRY,
43
+ GATEWAY_MCP_MIN_VERSION,
44
+ wireGatewayMcp
45
+ };
File without changes
File without changes
@@ -2,7 +2,7 @@
2
2
 
3
3
  // package.json
4
4
  var name = "@clawops/cli";
5
- var version = "1.4.0";
5
+ var version = "1.6.0";
6
6
  var description = "Deploy and manage self-hosted OpenClaw instances across clouds";
7
7
  var type = "module";
8
8
  var bin = {
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  acquireSession,
4
4
  drainPool
5
- } from "./chunk-ZVOEQCNW.js";
6
- import "./chunk-4U3LTLWZ.js";
5
+ } from "./chunk-LCKD7L7X.js";
6
+ import "./chunk-GJEF6UQA.js";
7
7
  import "./chunk-KGXPLI7W.js";
8
8
  export {
9
9
  acquireSession,
File without changes
File without changes
File without changes
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-3QJBNAHW.js";
5
5
  import {
6
6
  generatePlan
7
- } from "./chunk-UAA6YOOP.js";
7
+ } from "./chunk-72FF7IK2.js";
8
8
  import {
9
9
  applyPlan
10
- } from "./chunk-ZONXY3C6.js";
10
+ } from "./chunk-QHGFENYR.js";
11
11
  import {
12
12
  validatePlan
13
13
  } from "./chunk-YTH4L2GN.js";
@@ -23,15 +23,15 @@ import {
23
23
  handleConfigValidate,
24
24
  okText,
25
25
  resolveConn
26
- } from "./chunk-JYQZJMD3.js";
26
+ } from "./chunk-C5NDLOK2.js";
27
27
  import {
28
28
  buildContext
29
- } from "./chunk-T5EX55GP.js";
29
+ } from "./chunk-QI75OVJK.js";
30
30
  import {
31
31
  acquireSession,
32
32
  drainPool
33
- } from "./chunk-ZVOEQCNW.js";
34
- import "./chunk-4U3LTLWZ.js";
33
+ } from "./chunk-LCKD7L7X.js";
34
+ import "./chunk-GJEF6UQA.js";
35
35
  import "./chunk-ZFNPM2WG.js";
36
36
  import "./chunk-A2I76FTA.js";
37
37
  import {
@@ -630,7 +630,7 @@ async function handleUp(input, server) {
630
630
  );
631
631
  }
632
632
  const { localOpts } = stackConfig;
633
- const { localBootstrap } = await import("./bootstrap-G4UZ2FKH.js");
633
+ const { localBootstrap } = await import("./bootstrap-RY4VOSDJ.js");
634
634
  const ac = new AbortController();
635
635
  const state = await localBootstrap({
636
636
  host: localOpts.host,
@@ -853,7 +853,7 @@ async function handleTaskStatus(input, _server) {
853
853
 
854
854
  // src/mcp/tools/cli/monitor.ts
855
855
  async function handleMonitor(input, _server) {
856
- const { acquireSession: acquireSession2, drainPool: drainPool2 } = await import("./pool-FBFHATDG.js");
856
+ const { acquireSession: acquireSession2, drainPool: drainPool2 } = await import("./pool-JZGKPP6K.js");
857
857
  const ctx = buildContext({ stack: input.stackName });
858
858
  const tailLines = input.tailLines ?? 5;
859
859
  let conn;
@@ -1305,7 +1305,7 @@ If none of the above resolves the issue:
1305
1305
 
1306
1306
  // src/mcp/server.ts
1307
1307
  async function serveMcp(opts) {
1308
- const { version } = await import("./package-WAJRGBBM.js");
1308
+ const { version } = await import("./package-EW3FS257.js");
1309
1309
  const server = new McpServer({ name: "clawops", version });
1310
1310
  registerTools(server, opts);
1311
1311
  registerResources(server);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  connect
4
- } from "./chunk-4U3LTLWZ.js";
4
+ } from "./chunk-GJEF6UQA.js";
5
5
  import "./chunk-KGXPLI7W.js";
6
6
  export {
7
7
  connect
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawops/cli",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Deploy and manage self-hosted OpenClaw instances across clouds",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,6 +24,19 @@
24
24
  "engines": {
25
25
  "node": ">=22"
26
26
  },
27
+ "scripts": {
28
+ "dev": "tsx src/cli/index.ts",
29
+ "build": "tsup",
30
+ "test": "vitest run",
31
+ "test:pulumi": "vitest run tests/providers/aws/program.test.ts tests/providers/azure/program.test.ts",
32
+ "test:changed": "vitest --changed",
33
+ "test:integration": "vitest run --config vitest.integration.config.ts",
34
+ "typecheck": "NODE_OPTIONS=--max-old-space-size=4096 tsc --noEmit",
35
+ "lint": "eslint --max-warnings=0 src tests scripts",
36
+ "gen:schemas": "tsx scripts/gen-schemas.ts",
37
+ "changeset": "changeset",
38
+ "release": "pnpm build && changeset publish"
39
+ },
27
40
  "dependencies": {
28
41
  "@modelcontextprotocol/sdk": "^1.0.0",
29
42
  "@pulumi/aws": "^6.0.0",
@@ -60,17 +73,16 @@
60
73
  "typescript-eslint": "^8.0.0",
61
74
  "vitest": "^2.0.0"
62
75
  },
63
- "scripts": {
64
- "dev": "tsx src/cli/index.ts",
65
- "build": "tsup",
66
- "test": "vitest run",
67
- "test:pulumi": "vitest run tests/providers/aws/program.test.ts tests/providers/azure/program.test.ts",
68
- "test:changed": "vitest --changed",
69
- "test:integration": "vitest run --config vitest.integration.config.ts",
70
- "typecheck": "NODE_OPTIONS=--max-old-space-size=4096 tsc --noEmit",
71
- "lint": "eslint --max-warnings=0 src tests scripts",
72
- "gen:schemas": "tsx scripts/gen-schemas.ts",
73
- "changeset": "changeset",
74
- "release": "pnpm build && changeset publish"
76
+ "pnpm": {
77
+ "overrides": {
78
+ "@pulumi/pulumi": "^3.0.0"
79
+ },
80
+ "onlyBuiltDependencies": [
81
+ "@pulumi/command",
82
+ "cpu-features",
83
+ "esbuild",
84
+ "protobufjs",
85
+ "ssh2"
86
+ ]
75
87
  }
76
- }
88
+ }
@@ -1,36 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/providers/firewall.ts
4
- function resolveIngressCidrs(accessMode, allowedCidrs, portOverride, detectedIp) {
5
- if (portOverride.trim()) {
6
- return portOverride.split(",").map((s) => s.trim()).filter(Boolean);
7
- }
8
- switch (accessMode) {
9
- case "restricted": {
10
- if (!allowedCidrs.trim()) return [];
11
- return allowedCidrs.split(",").map((s) => s.trim()).filter(Boolean);
12
- }
13
- case "auto": {
14
- if (!detectedIp.trim()) return [];
15
- const ip = detectedIp.trim();
16
- return [ip.includes("/") ? ip : `${ip}/32`];
17
- }
18
- case "open":
19
- return ["0.0.0.0/0"];
20
- default:
21
- return [];
22
- }
23
- }
24
- async function detectEgressIp(checkUrl) {
25
- try {
26
- const res = await fetch(checkUrl, { signal: AbortSignal.timeout(5e3) });
27
- if (!res.ok) return "";
28
- return (await res.text()).trim();
29
- } catch {
30
- return "";
31
- }
32
- }
33
- export {
34
- detectEgressIp,
35
- resolveIngressCidrs
36
- };