@clawops/cli 1.7.9 → 2.0.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.
Files changed (98) hide show
  1. package/README.md +504 -76
  2. package/dist/apply-KYFPWI5U.js +27 -0
  3. package/dist/automation-RMAIBNVC.js +44 -0
  4. package/dist/bedrock-XVASJX2K.js +61 -0
  5. package/dist/{bootstrap-HL4DNUNG.js → bootstrap-HNGKY6WW.js} +31 -19
  6. package/dist/bootstrap.sh.tmpl +51 -31
  7. package/dist/channels-KBUR42IU.js +53 -0
  8. package/dist/chunk-2I7KCMXZ.js +142 -0
  9. package/dist/{chunk-DQDAWNGB.js → chunk-2Z7J3ERQ.js} +1 -1
  10. package/dist/chunk-3OLUJPCB.js +36 -0
  11. package/dist/chunk-5XT5IAJK.js +69 -0
  12. package/dist/chunk-AEEMRFZV.js +24 -0
  13. package/dist/chunk-BSLIYB5Q.js +106 -0
  14. package/dist/{chunk-3XK66DKK.js → chunk-E4NJSJ54.js} +57 -93
  15. package/dist/chunk-FEK7VJAY.js +16 -0
  16. package/dist/{chunk-UKTP332Z.js → chunk-GAZPPSKU.js} +6 -7
  17. package/dist/chunk-GIEVAGEK.js +126 -0
  18. package/dist/chunk-HSPHXG6W.js +67 -0
  19. package/dist/chunk-IVWP4QUJ.js +901 -0
  20. package/dist/{chunk-PVUVI35Y.js → chunk-JW5A7E3X.js} +5 -1
  21. package/dist/{chunk-GZN7PKP4.js → chunk-KC3GC3PE.js} +328 -121
  22. package/dist/chunk-LBE6COC4.js +244 -0
  23. package/dist/chunk-LEVIW2PV.js +890 -0
  24. package/dist/chunk-LT4XMO5D.js +50 -0
  25. package/dist/chunk-MC5ATCXS.js +108 -0
  26. package/dist/chunk-N4WZLWJ4.js +16 -0
  27. package/dist/chunk-NUDIHZEH.js +55 -0
  28. package/dist/chunk-NUL5F6YV.js +56 -0
  29. package/dist/chunk-RHOWYIWT.js +52 -0
  30. package/dist/chunk-STLRHLLV.js +193 -0
  31. package/dist/chunk-V25QRZRB.js +45 -0
  32. package/dist/chunk-VOEJYRIK.js +33 -0
  33. package/dist/{providers-2OABPW2E.js → chunk-VUFMZEIM.js} +3 -2
  34. package/dist/chunk-W3OLNNJW.js +51 -0
  35. package/dist/cli-WLD3EBEM.js +11 -0
  36. package/dist/cli-auth-GD5SK7JU.js +13 -0
  37. package/dist/cli.js +1296 -695
  38. package/dist/config-validate-K2N6XLDX.js +76 -0
  39. package/dist/context-7EVC6W45.js +20 -0
  40. package/dist/docker-3PDO4TS7.js +12 -0
  41. package/dist/{firewall-XQGO7HWK.js → firewall-5VKZ4WBH.js} +32 -1
  42. package/dist/generate-I6YQ5V2T.js +29 -0
  43. package/dist/{harden-URPXLSVF.js → harden-72JMRT4E.js} +51 -11
  44. package/dist/health-5CCOXYGG.js +11 -0
  45. package/dist/index.d.ts +56 -0
  46. package/dist/known-hosts-file-I3Q3NBYQ.js +22 -0
  47. package/dist/logs-5A7YYA53.js +15 -0
  48. package/dist/{mcp-wire-7E5JQY47.js → mcp-wire-3SWBM7D6.js} +32 -16
  49. package/dist/models-AGTQWYDL.js +32 -0
  50. package/dist/network-args-PYJRJBYO.js +64 -0
  51. package/dist/{package-5AR4ZPK4.js → package-5BCMEBTE.js} +11 -4
  52. package/dist/passphrase-MJBE45N7.js +15 -0
  53. package/dist/plugins-DTST3OER.js +49 -0
  54. package/dist/{pool-QGFQRMGU.js → pool-PIIKJ4L2.js} +3 -2
  55. package/dist/preflight-EF52S3WW.js +162 -0
  56. package/dist/preflight-MWWM5JT3.js +181 -0
  57. package/dist/preflight-TFD4N5ZU.js +141 -0
  58. package/dist/privileged-JFA6LLTV.js +17 -0
  59. package/dist/providers-FW6H6OPF.js +11 -0
  60. package/dist/ready-KDNRBNSU.js +95 -0
  61. package/dist/{remote-config-QDLN4UMG.js → remote-config-3HZLUWEA.js} +4 -3
  62. package/dist/runtime-X3KW5TCP.js +44 -0
  63. package/dist/server-GBA7BJGK.js +46 -0
  64. package/dist/spec-path-KQOOIQCA.js +9 -0
  65. package/dist/ssh-VAI5DU5M.js +11 -0
  66. package/dist/ssh-key-DUO6OKOI.js +7 -0
  67. package/dist/stack-config-R4SPSUQZ.js +12 -0
  68. package/dist/upgrade-ULD4KFYC.js +88 -0
  69. package/dist/validate-YVI3BJ55.js +14 -0
  70. package/dist/{version-guard-VUCNZXQS.js → version-guard-XVYW5WFJ.js} +2 -2
  71. package/dist/{versions-2UVAT6PQ.js → versions-W5LRQCAJ.js} +2 -2
  72. package/dist/wait-WO72TRRQ.js +79 -0
  73. package/package.json +11 -4
  74. package/spec/deploy-plan.schema.json +114 -25
  75. package/spec/integrations.yaml +135 -28
  76. package/spec/mcp-tools.yaml +89 -37
  77. package/spec/models.yaml +23 -3
  78. package/spec/openclaw-2.0.config.schema.json +1 -0
  79. package/spec/openclaw-versions.yaml +116 -27
  80. package/spec/providers.schema.json +163 -28
  81. package/dist/apply-F3KY2HTB.js +0 -17
  82. package/dist/automation-RULUSJBW.js +0 -30
  83. package/dist/aws-L4RR2LSE.js +0 -254
  84. package/dist/azure-7OXQENQL.js +0 -262
  85. package/dist/chunk-35WVQY3X.js +0 -122
  86. package/dist/chunk-6X45DDGH.js +0 -42
  87. package/dist/chunk-A4QR4TXW.js +0 -120
  88. package/dist/chunk-C5X375RO.js +0 -85
  89. package/dist/chunk-FNS56UQY.js +0 -741
  90. package/dist/chunk-P5LKWRPL.js +0 -114
  91. package/dist/chunk-SEZMVVAS.js +0 -156
  92. package/dist/context-5LCUG2QR.js +0 -10
  93. package/dist/gcp-UG6AWPIZ.js +0 -193
  94. package/dist/generate-OAGYT3VH.js +0 -15
  95. package/dist/local-DXBEVZ5C.js +0 -47
  96. package/dist/server-FIZCOLHL.js +0 -32
  97. package/dist/ssh-CNJNLM3E.js +0 -8
  98. package/dist/validate-CFQ4FH2T.js +0 -10
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ compareVersions
4
+ } from "./chunk-GAZPPSKU.js";
5
+ import "./chunk-JW5A7E3X.js";
6
+ import "./chunk-EI7CQQ7D.js";
7
+
8
+ // src/openclaw/config-validate.ts
9
+ var cached;
10
+ async function getValidator() {
11
+ if (cached) return cached;
12
+ const [{ default: Ajv }, { default: addFormats }, { readFileSync }, { join }, { resolveSpecDir }] = await Promise.all([
13
+ import("ajv"),
14
+ import("ajv-formats"),
15
+ import("fs"),
16
+ import("path"),
17
+ import("./spec-path-KQOOIQCA.js")
18
+ ]);
19
+ const schema = JSON.parse(
20
+ readFileSync(join(resolveSpecDir(), "openclaw-2.0.config.schema.json"), "utf-8")
21
+ );
22
+ const ajv = new Ajv({ strict: false, allErrors: true });
23
+ addFormats(ajv);
24
+ cached = ajv.compile(schema);
25
+ return cached;
26
+ }
27
+ function isUnknownKey(e) {
28
+ return e.keyword === "additionalProperties";
29
+ }
30
+ function dotPath(instancePath) {
31
+ if (!instancePath) return "(root)";
32
+ return instancePath.slice(1).split("/").map((seg) => seg.replace(/~1/g, "/").replace(/~0/g, "~")).join(".");
33
+ }
34
+ function describe(e) {
35
+ const where = dotPath(e.instancePath);
36
+ if (isUnknownKey(e)) {
37
+ const extra = e.params.additionalProperty;
38
+ return `${where}: unknown key "${extra}"`;
39
+ }
40
+ return `${where}: ${e.message ?? "invalid"}`;
41
+ }
42
+ function deploymentContractErrors(cfg) {
43
+ const errors = [];
44
+ if (cfg === null || typeof cfg !== "object") return errors;
45
+ const gateway = cfg["gateway"];
46
+ const mode = gateway !== null && typeof gateway === "object" ? gateway["mode"] : void 0;
47
+ if (mode === void 0) {
48
+ errors.push(
49
+ 'gateway.mode: required by clawops. OpenClaw treats it as optional only because --allow-unconfigured can bypass the check; clawops does not pass that flag, so a config without gateway.mode exits 78 with "Gateway start blocked". Set it to "local".'
50
+ );
51
+ }
52
+ return errors;
53
+ }
54
+ async function validateConfig(cfg, opts = {}) {
55
+ const validate = await getValidator();
56
+ const contract = deploymentContractErrors(cfg);
57
+ if (validate(cfg)) return { errors: contract, warnings: [] };
58
+ const { openclawVersion, schemaCapturedFrom } = opts;
59
+ const runtimeIsNewer = openclawVersion !== void 0 && schemaCapturedFrom !== void 0 && compareVersions(openclawVersion, schemaCapturedFrom) > 0;
60
+ const errors = [...contract];
61
+ const warnings = [];
62
+ for (const e of validate.errors ?? []) {
63
+ const text = describe(e);
64
+ if (isUnknownKey(e) && runtimeIsNewer) {
65
+ warnings.push(
66
+ `${text} \u2014 not in the schema captured from OpenClaw ${schemaCapturedFrom}; you are deploying ${openclawVersion}, so this may be a newer setting clawops does not know about yet.`
67
+ );
68
+ } else {
69
+ errors.push(text);
70
+ }
71
+ }
72
+ return { errors, warnings };
73
+ }
74
+ export {
75
+ validateConfig
76
+ };
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ buildContext,
4
+ loadAdapterModule
5
+ } from "./chunk-5XT5IAJK.js";
6
+ import "./chunk-LEVIW2PV.js";
7
+ import "./chunk-GIEVAGEK.js";
8
+ import "./chunk-VOEJYRIK.js";
9
+ import "./chunk-EI7CQQ7D.js";
10
+ import "./chunk-FEK7VJAY.js";
11
+ import "./chunk-RHOWYIWT.js";
12
+ import "./chunk-N4WZLWJ4.js";
13
+ import "./chunk-A2I76FTA.js";
14
+ import "./chunk-CX5SL5HP.js";
15
+ import "./chunk-KGXPLI7W.js";
16
+ import "./chunk-VUFMZEIM.js";
17
+ export {
18
+ buildContext,
19
+ loadAdapterModule
20
+ };
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ containerStatus,
4
+ inspectContainer,
5
+ looksLikeStillBooting
6
+ } from "./chunk-3OLUJPCB.js";
7
+ import "./chunk-NUDIHZEH.js";
8
+ export {
9
+ containerStatus,
10
+ inspectContainer,
11
+ looksLikeStillBooting
12
+ };
@@ -32,7 +32,10 @@ function resolveIngressCidrs(accessMode, allowedCidrs, portOverride, egressResul
32
32
  }
33
33
  async function detectEgressIp(checkUrl) {
34
34
  try {
35
- const res = await fetch(checkUrl, { signal: AbortSignal.timeout(3e3) });
35
+ const res = await fetch(checkUrl, {
36
+ signal: AbortSignal.timeout(3e3),
37
+ headers: { accept: "text/plain" }
38
+ });
36
39
  if (!res.ok) {
37
40
  return { ok: false, error: `HTTP ${res.status} from ${checkUrl}` };
38
41
  }
@@ -40,12 +43,40 @@ async function detectEgressIp(checkUrl) {
40
43
  if (!ip) {
41
44
  return { ok: false, error: `empty response from ${checkUrl}` };
42
45
  }
46
+ if (!isIpAddress(ip)) {
47
+ return {
48
+ ok: false,
49
+ error: `${checkUrl} did not return an IP address (got ${summarise(ip)})`
50
+ };
51
+ }
43
52
  return { ok: true, ip };
44
53
  } catch (err) {
45
54
  return { ok: false, error: err instanceof Error ? err.message : String(err) };
46
55
  }
47
56
  }
57
+ function isIpAddress(value) {
58
+ const v = value.trim();
59
+ if (/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.test(v)) {
60
+ return v.split(".").every((o) => Number(o) <= 255);
61
+ }
62
+ return /^[0-9a-fA-F:]+$/.test(v) && v.includes(":");
63
+ }
64
+ function summarise(body) {
65
+ const oneLine = body.replace(/\s+/g, " ").trim();
66
+ return oneLine.length > 40 ? `${oneLine.slice(0, 40)}\u2026` : oneLine;
67
+ }
68
+ function resolveGatewayIngressCidrs(publishGateway, accessMode, allowedCidrs, portOverride, egressResult) {
69
+ if (publishGateway !== "all") return [];
70
+ return resolveIngressCidrs(accessMode, allowedCidrs, portOverride, egressResult);
71
+ }
72
+ function resolveGatewayPort(raw, fallback) {
73
+ const port = Number(raw);
74
+ return Number.isInteger(port) && port > 0 && port < 65536 ? port : fallback;
75
+ }
48
76
  export {
49
77
  detectEgressIp,
78
+ isIpAddress,
79
+ resolveGatewayIngressCidrs,
80
+ resolveGatewayPort,
50
81
  resolveIngressCidrs
51
82
  };
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ INSTANCE_ALIASES,
4
+ generatePlan,
5
+ isInstanceAlias,
6
+ parseDiffForTest,
7
+ planId
8
+ } from "./chunk-STLRHLLV.js";
9
+ import "./chunk-BSLIYB5Q.js";
10
+ import "./chunk-5XT5IAJK.js";
11
+ import "./chunk-JW5A7E3X.js";
12
+ import "./chunk-LEVIW2PV.js";
13
+ import "./chunk-GIEVAGEK.js";
14
+ import "./chunk-VOEJYRIK.js";
15
+ import "./chunk-EI7CQQ7D.js";
16
+ import "./chunk-FEK7VJAY.js";
17
+ import "./chunk-RHOWYIWT.js";
18
+ import "./chunk-N4WZLWJ4.js";
19
+ import "./chunk-A2I76FTA.js";
20
+ import "./chunk-CX5SL5HP.js";
21
+ import "./chunk-KGXPLI7W.js";
22
+ import "./chunk-VUFMZEIM.js";
23
+ export {
24
+ INSTANCE_ALIASES,
25
+ generatePlan,
26
+ isInstanceAlias,
27
+ parseDiffForTest,
28
+ planId
29
+ };
@@ -2,8 +2,18 @@
2
2
  import {
3
3
  acquireSession,
4
4
  drainPool
5
- } from "./chunk-DQDAWNGB.js";
6
- import "./chunk-3XK66DKK.js";
5
+ } from "./chunk-2Z7J3ERQ.js";
6
+ import "./chunk-E4NJSJ54.js";
7
+ import "./chunk-MC5ATCXS.js";
8
+ import {
9
+ PUBLISH_INSPECT_CMD,
10
+ publishForRestart,
11
+ publishedGatewayPort
12
+ } from "./chunk-GIEVAGEK.js";
13
+ import {
14
+ GATEWAY_PORT
15
+ } from "./chunk-VOEJYRIK.js";
16
+ import "./chunk-EI7CQQ7D.js";
7
17
  import "./chunk-KGXPLI7W.js";
8
18
 
9
19
  // src/harden/types.ts
@@ -154,7 +164,11 @@ var sshModule = {
154
164
 
155
165
  // src/harden/modules/ufw.ts
156
166
  var SENTINEL2 = `${SENTINEL_DIR}/ufw.applied`;
157
- var GATEWAY_PORT = 18789;
167
+ async function publishedGateway(exec) {
168
+ const { stdout } = await exec(`${PUBLISH_INSPECT_CMD} 2>/dev/null || true`);
169
+ if (publishForRestart(stdout) !== "all") return { exposed: false };
170
+ return { exposed: true, port: publishedGatewayPort(stdout) };
171
+ }
158
172
  function makeUfwModule(sshPort = 22) {
159
173
  return {
160
174
  id: "ufw",
@@ -176,14 +190,24 @@ function makeUfwModule(sshPort = 22) {
176
190
  return { status: "missing", detail: "UFW installed but not active" };
177
191
  },
178
192
  async apply(exec) {
193
+ const gateway = await publishedGateway(exec);
194
+ const rules = [`ufw allow ${sshPort}/tcp comment "clawops SSH"`];
195
+ let gatewayNote;
196
+ if (!gateway.exposed) {
197
+ gatewayNote = "gateway port not opened (published on 127.0.0.1 \u2014 reach it with `clawops tunnel`)";
198
+ } else if (gateway.port === void 0) {
199
+ gatewayNote = "gateway published on every interface but its port could not be read \u2014 no rule added; run `clawops doctor --stack <name>` and open it yourself if that is intended";
200
+ } else {
201
+ rules.push(`ufw allow ${gateway.port}/tcp comment "OpenClaw gateway"`);
202
+ gatewayNote = `allow ${gateway.port}/tcp`;
203
+ }
179
204
  const script = [
180
205
  `mkdir -p ${SENTINEL_DIR}`,
181
206
  "apt-get install -y -q ufw",
182
207
  "ufw --force reset",
183
208
  "ufw default deny incoming",
184
209
  "ufw default allow outgoing",
185
- `ufw allow ${sshPort}/tcp comment "clawops SSH"`,
186
- `ufw allow ${GATEWAY_PORT}/tcp comment "OpenClaw gateway"`,
210
+ ...rules,
187
211
  "ufw --force enable",
188
212
  `touch ${SENTINEL2}`
189
213
  ].join(" && ");
@@ -191,7 +215,10 @@ function makeUfwModule(sshPort = 22) {
191
215
  if (code !== 0) {
192
216
  throw new Error(`UFW setup failed (exit ${code}): ${stderr.slice(0, 200)}`);
193
217
  }
194
- return { changed: true, detail: `UFW enabled: deny-all in, allow ${sshPort}/tcp + ${GATEWAY_PORT}/tcp` };
218
+ return {
219
+ changed: true,
220
+ detail: `UFW enabled: deny-all in, allow ${sshPort}/tcp; ${gatewayNote}`
221
+ };
195
222
  }
196
223
  };
197
224
  }
@@ -473,7 +500,14 @@ var sysctlModule = {
473
500
  };
474
501
 
475
502
  // src/harden/modules/aws-sg-audit.ts
476
- var EXPECTED_OPEN_PORTS = /* @__PURE__ */ new Set([22, 18789]);
503
+ var WORLD = /* @__PURE__ */ new Set(["0.0.0.0/0"]);
504
+ var WORLD_V6 = /* @__PURE__ */ new Set(["::/0"]);
505
+ var NAMED_PORTS = { 22: "SSH", [GATEWAY_PORT]: "the OpenClaw gateway" };
506
+ function describe(fromPort, toPort) {
507
+ const range = fromPort === toPort ? `port ${fromPort}` : `ports ${fromPort}-${toPort}`;
508
+ const named = NAMED_PORTS[fromPort];
509
+ return named && fromPort === toPort ? `${range} (${named})` : range;
510
+ }
477
511
  var awsSgAuditModule = {
478
512
  id: "aws-sg-audit",
479
513
  label: "AWS SG audit (check-only)",
@@ -492,19 +526,25 @@ var awsSgAuditModule = {
492
526
  for (const rule of sg.IpPermissions ?? []) {
493
527
  const fromPort = rule.FromPort ?? 0;
494
528
  const toPort = rule.ToPort ?? 65535;
529
+ const where = describe(fromPort, toPort);
495
530
  for (const range of rule.IpRanges ?? []) {
496
- if (range.CidrIp === "0.0.0.0/0" && !EXPECTED_OPEN_PORTS.has(fromPort)) {
497
- openRules.push(`port ${fromPort}\u2013${toPort} open to 0.0.0.0/0 in ${sg.GroupId}`);
531
+ if (range.CidrIp && WORLD.has(range.CidrIp)) {
532
+ openRules.push(`${where} open to ${range.CidrIp} in ${sg.GroupId}`);
533
+ }
534
+ }
535
+ for (const range of rule.Ipv6Ranges ?? []) {
536
+ if (range.CidrIpv6 && WORLD_V6.has(range.CidrIpv6)) {
537
+ openRules.push(`${where} open to ${range.CidrIpv6} in ${sg.GroupId}`);
498
538
  }
499
539
  }
500
540
  }
501
541
  }
502
542
  if (openRules.length === 0) {
503
- return { status: "applied", detail: "No unexpected open ingress rules found" };
543
+ return { status: "applied", detail: "No ingress rule admits the whole internet" };
504
544
  }
505
545
  return {
506
546
  status: "drifted",
507
- detail: `Open ingress rules detected: ${openRules.join("; ")}`
547
+ detail: `Ingress open to the internet: ${openRules.join("; ")}`
508
548
  };
509
549
  } catch (err) {
510
550
  return {
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ HEALTH_PATHS,
4
+ interpretProbe,
5
+ probeCommand
6
+ } from "./chunk-LT4XMO5D.js";
7
+ export {
8
+ HEALTH_PATHS,
9
+ interpretProbe,
10
+ probeCommand
11
+ };
package/dist/index.d.ts CHANGED
@@ -47,6 +47,62 @@ interface ProviderAdapter {
47
47
  stateBackendUrl(bucket: string): string;
48
48
  /** Validate provider-specific config (env vars, profiles) at startup. */
49
49
  validateConfig(): Promise<ValidationResult>;
50
+ /**
51
+ * Account-level setup this provider needs before anything can be provisioned.
52
+ *
53
+ * Not infrastructure: enabling an API or creating a state bucket happens outside the
54
+ * Pulumi program, because the state backend has to exist before Pulumi can run at all.
55
+ * Optional — a provider with nothing to check omits it.
56
+ */
57
+ preflight?(opts: PreflightOpts): Promise<PreflightCheck[]>;
58
+ }
59
+ interface PreflightOpts {
60
+ /** Where the stack will be deployed, when that changes what must be enabled. */
61
+ region?: string;
62
+ /** State backend bucket or container, when the check covers it. */
63
+ bucket?: string;
64
+ /**
65
+ * The machine size about to be deployed, when the caller knows it.
66
+ *
67
+ * Azure offers SKU families per subscription, so "is this size available here" can only be
68
+ * answered about a specific size. Without this the check falls back to the provider's
69
+ * default alias, which is right for a default deploy and wrong for anyone passing
70
+ * --instance-type: a deployment using an available size would be reported as broken.
71
+ */
72
+ instanceType?: string;
73
+ signal?: AbortSignal;
74
+ }
75
+ interface PreflightCheck {
76
+ /** Stable kebab-case id, e.g. "compute-api-enabled". */
77
+ id: string;
78
+ /** What is being checked, in the operator's terms. */
79
+ label: string;
80
+ ok: boolean;
81
+ /**
82
+ * clawops could not put the question — a denied read permission, an API that would not
83
+ * answer. Neither a pass nor a failure: the operator's account may be perfectly set up, and
84
+ * saying so would be a claim clawops cannot make.
85
+ *
86
+ * Dropping the check instead is worse. An operator whose credentials lack a read permission
87
+ * then sees one fewer line than everyone else and never learns why. The detail field carries
88
+ * the reason, and doctor renders these as warnings rather than failing the report over a
89
+ * question it could not ask.
90
+ */
91
+ unknown?: boolean;
92
+ /** Why it failed, and what it means — shown when ok is false. */
93
+ detail?: string;
94
+ /**
95
+ * Applies the fix. Absent when clawops cannot fix it: a missing permission or an unpaid
96
+ * billing account is not clawops's to resolve, and pretending otherwise wastes a prompt.
97
+ */
98
+ fix?: () => Promise<void>;
99
+ /**
100
+ * What the fix changes in the operator's cloud account.
101
+ *
102
+ * Shown in the prompt, and required alongside fix(): consent to 'fix it' is not consent to
103
+ * something unnamed.
104
+ */
105
+ mutates?: string;
50
106
  }
51
107
 
52
108
  export type { BaseStackOutputs, ConnectionInfo, CredentialSource, InstanceAlias, ProviderAdapter, ProviderName, PulumiFn, StackOutputs, StateBackendScheme, ValidationResult };
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ withoutHost
4
+ } from "./chunk-MC5ATCXS.js";
5
+
6
+ // src/transport/known-hosts-file.ts
7
+ import { readFileSync, writeFileSync } from "fs";
8
+ function forgetHost(knownHostsPath, host, port) {
9
+ let content;
10
+ try {
11
+ content = readFileSync(knownHostsPath, "utf-8");
12
+ } catch {
13
+ return false;
14
+ }
15
+ const next = withoutHost(content, host, port);
16
+ if (next === content) return false;
17
+ writeFileSync(knownHostsPath, next, "utf-8");
18
+ return true;
19
+ }
20
+ export {
21
+ forgetHost
22
+ };
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ GATEWAY_LOGS_PROBE,
4
+ agentAuditCommand,
5
+ chooseLogSource,
6
+ containerLogsCommand,
7
+ gatewayLogsCommand
8
+ } from "./chunk-NUL5F6YV.js";
9
+ export {
10
+ GATEWAY_LOGS_PROBE,
11
+ agentAuditCommand,
12
+ chooseLogSource,
13
+ containerLogsCommand,
14
+ gatewayLogsCommand
15
+ };
@@ -1,24 +1,40 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MCP_HTTP_PORT
4
- } from "./chunk-GZN7PKP4.js";
5
- import "./chunk-35WVQY3X.js";
6
- import "./chunk-6ZFIFDBJ.js";
4
+ } from "./chunk-KC3GC3PE.js";
5
+ import "./chunk-LBE6COC4.js";
7
6
  import "./chunk-BOPSG2LI.js";
7
+ import "./chunk-6ZFIFDBJ.js";
8
8
  import "./chunk-3QJBNAHW.js";
9
- import "./chunk-A4QR4TXW.js";
10
- import "./chunk-6X45DDGH.js";
11
- import "./chunk-FNS56UQY.js";
12
- import "./chunk-DQDAWNGB.js";
13
- import "./chunk-SEZMVVAS.js";
14
- import "./chunk-P5LKWRPL.js";
9
+ import "./chunk-2I7KCMXZ.js";
10
+ import "./chunk-HSPHXG6W.js";
11
+ import "./chunk-AEEMRFZV.js";
12
+ import "./chunk-STLRHLLV.js";
13
+ import "./chunk-BSLIYB5Q.js";
14
+ import "./chunk-2Z7J3ERQ.js";
15
+ import "./chunk-E4NJSJ54.js";
16
+ import "./chunk-MC5ATCXS.js";
17
+ import "./chunk-5XT5IAJK.js";
18
+ import "./chunk-IVWP4QUJ.js";
19
+ import "./chunk-NUL5F6YV.js";
20
+ import "./chunk-3OLUJPCB.js";
21
+ import {
22
+ execPrivileged
23
+ } from "./chunk-NUDIHZEH.js";
24
+ import "./chunk-JW5A7E3X.js";
25
+ import "./chunk-LT4XMO5D.js";
26
+ import "./chunk-LEVIW2PV.js";
27
+ import "./chunk-GIEVAGEK.js";
28
+ import "./chunk-VOEJYRIK.js";
15
29
  import "./chunk-EI7CQQ7D.js";
16
- import "./chunk-PVUVI35Y.js";
17
- import "./chunk-3XK66DKK.js";
30
+ import "./chunk-FEK7VJAY.js";
31
+ import "./chunk-RHOWYIWT.js";
18
32
  import "./chunk-Q7NQY5HV.js";
33
+ import "./chunk-N4WZLWJ4.js";
19
34
  import "./chunk-A2I76FTA.js";
20
35
  import "./chunk-CX5SL5HP.js";
21
36
  import "./chunk-KGXPLI7W.js";
37
+ import "./chunk-VUFMZEIM.js";
22
38
 
23
39
  // src/cli/mcp-wire.ts
24
40
  var GATEWAY_MCP_NAME = "clawops";
@@ -30,17 +46,17 @@ function shellQuote(value) {
30
46
  }
31
47
  var OC = "docker exec openclaw openclaw mcp";
32
48
  async function supportsMcpAdd(session, signal) {
33
- const help = await session.exec(`${OC} add --help`, signal);
49
+ const help = await execPrivileged(session, `${OC} add --help`, signal);
34
50
  return help.code === 0;
35
51
  }
36
52
  async function wireGatewayMcp(session, signal, opts = {}) {
37
53
  const url = opts.url ?? defaultGatewayMcpUrl();
38
54
  if (!await supportsMcpAdd(session, signal)) return { status: "unsupported", url };
39
- const existing = await session.exec(`${OC} show ${GATEWAY_MCP_NAME}`, signal);
55
+ const existing = await execPrivileged(session, `${OC} show ${GATEWAY_MCP_NAME}`, signal);
40
56
  const alreadyWired = existing.code === 0;
41
57
  if (alreadyWired && !opts.rewire) return { status: "exists", url };
42
58
  if (alreadyWired) {
43
- const removed = await session.exec(`${OC} unset ${GATEWAY_MCP_NAME}`, signal);
59
+ const removed = await execPrivileged(session, `${OC} unset ${GATEWAY_MCP_NAME}`, signal);
44
60
  if (removed.code !== 0) {
45
61
  return {
46
62
  status: "probe-failed",
@@ -54,7 +70,7 @@ async function wireGatewayMcp(session, signal, opts = {}) {
54
70
  `--url ${shellQuote(url)}`,
55
71
  ...opts.token ? [`--header ${shellQuote(`Authorization=Bearer ${opts.token}`)}`] : []
56
72
  ].join(" ");
57
- const added = await session.exec(`${OC} add ${GATEWAY_MCP_NAME} ${flags}`, signal);
73
+ const added = await execPrivileged(session, `${OC} add ${GATEWAY_MCP_NAME} ${flags}`, signal);
58
74
  if (added.code !== 0) {
59
75
  return {
60
76
  status: "probe-failed",
@@ -62,7 +78,7 @@ async function wireGatewayMcp(session, signal, opts = {}) {
62
78
  url
63
79
  };
64
80
  }
65
- await session.exec(`${OC} reload`, signal);
81
+ await execPrivileged(session, `${OC} reload`, signal);
66
82
  return { status: "wired", rewired: alreadyWired, url };
67
83
  }
68
84
  export {
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/openclaw/models.ts
4
+ function providerKeyOf(provider) {
5
+ const fromPath = provider.configPath?.split(".").pop();
6
+ return fromPath && fromPath.length > 0 ? fromPath : provider.id;
7
+ }
8
+ function buildModelsBlock(opts) {
9
+ const { provider, model, apiKeyRef, baseUrl, region } = opts;
10
+ const key = providerKeyOf(provider);
11
+ const modelId = opts.resolvedModelId ?? model.modelId ?? model.id;
12
+ const entry = {};
13
+ if (provider.api) entry["api"] = provider.api;
14
+ if (region) entry["region"] = region;
15
+ if (apiKeyRef) entry["apiKey"] = apiKeyRef;
16
+ if (baseUrl) entry["baseUrl"] = baseUrl;
17
+ if (provider.credentialSource === "aws-profile") {
18
+ entry["auth"] = "aws-sdk";
19
+ }
20
+ entry["models"] = [
21
+ {
22
+ id: modelId,
23
+ name: model.displayName,
24
+ ...provider.api ? { api: provider.api } : {}
25
+ }
26
+ ];
27
+ return { providers: { [key]: entry } };
28
+ }
29
+ export {
30
+ buildModelsBlock,
31
+ providerKeyOf
32
+ };
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ UsageError
4
+ } from "./chunk-KGXPLI7W.js";
5
+
6
+ // src/plan/network-args.ts
7
+ function isCidr(value) {
8
+ const v = value.trim();
9
+ if (v.includes(":")) return true;
10
+ return /^(\d{1,3}\.){3}\d{1,3}\/(3[0-2]|[12]?\d)$/.test(v);
11
+ }
12
+ function parseCidrList(flag, value) {
13
+ const parts = value.split(",").map((s) => s.trim()).filter((s) => s !== "");
14
+ for (const p of parts) {
15
+ if (!isCidr(p)) {
16
+ throw new UsageError(
17
+ `${flag}: "${p}" is not a CIDR. Use an address and a prefix, e.g. 203.0.113.4/32.`
18
+ );
19
+ }
20
+ }
21
+ return parts;
22
+ }
23
+ function parsePublishGateway(value) {
24
+ const v = value.trim();
25
+ if (v === "loopback" || v === "all") return v;
26
+ throw new UsageError(`--publish-gateway: expected "loopback" or "all", got "${v}".`);
27
+ }
28
+ async function resolveNetworkFlags(flags, deps) {
29
+ const resolved = { allowedSshCidrs: [], allowedGatewayCidrs: [] };
30
+ for (const [flag, raw, key] of [
31
+ ["--ssh-cidr", flags.sshCidr, "allowedSshCidrs"],
32
+ ["--gateway-cidr", flags.gatewayCidr, "allowedGatewayCidrs"]
33
+ ]) {
34
+ if (raw === void 0) continue;
35
+ resolved[key] = raw.trim() === "auto" ? [await autoCidr(flag, deps.detectEgressIp)] : parseCidrList(flag, raw);
36
+ }
37
+ if (flags.publishGateway !== void 0) {
38
+ resolved.publishGateway = parsePublishGateway(flags.publishGateway);
39
+ }
40
+ return resolved;
41
+ }
42
+ async function autoCidr(flag, detect) {
43
+ const result = await detect();
44
+ if (!result.ok || result.ip.trim() === "") {
45
+ const why = result.ok ? "the lookup returned an empty address" : result.error;
46
+ throw new UsageError(
47
+ `${flag} auto: could not detect this machine's public IP \u2014 ${why}. Pass the CIDR explicitly, e.g. 203.0.113.4/32.`
48
+ );
49
+ }
50
+ const ip = result.ip.trim();
51
+ const cidr = ip.includes("/") ? ip : `${ip}/32`;
52
+ if (!isCidr(cidr)) {
53
+ throw new UsageError(
54
+ `${flag} auto: the detected address "${ip}" is not usable as a CIDR. Pass the CIDR explicitly, e.g. 203.0.113.4/32.`
55
+ );
56
+ }
57
+ return cidr;
58
+ }
59
+ export {
60
+ isCidr,
61
+ parseCidrList,
62
+ parsePublishGateway,
63
+ resolveNetworkFlags
64
+ };
@@ -2,7 +2,7 @@
2
2
 
3
3
  // package.json
4
4
  var name = "@clawops/cli";
5
- var version = "1.7.9";
5
+ var version = "2.0.1";
6
6
  var description = "Deploy and manage self-hosted OpenClaw instances across clouds";
7
7
  var type = "module";
8
8
  var bin = {
@@ -35,19 +35,24 @@ var scripts = {
35
35
  test: "vitest run",
36
36
  "test:pulumi": "vitest run tests/providers/aws/program.test.ts tests/providers/azure/program.test.ts",
37
37
  "test:changed": "vitest --changed",
38
- "test:mutation": "node scripts/dev/mutation-check.mjs",
39
38
  "test:integration": "vitest run --config vitest.integration.config.ts",
39
+ "test:cloud": "bash scripts/e2e/cloud.sh",
40
40
  typecheck: "NODE_OPTIONS=--max-old-space-size=4096 tsc --noEmit",
41
41
  lint: "eslint --max-warnings=0 src tests scripts",
42
42
  "gen:schemas": "tsx scripts/gen-schemas.ts",
43
43
  changeset: "changeset",
44
- release: "pnpm build && changeset publish"
44
+ release: "pnpm build && changeset publish",
45
+ "test:mutation": "node scripts/dev/mutation-check.mjs",
46
+ graph: "node scripts/dev/graph.mjs"
45
47
  };
46
48
  var dependencies = {
49
+ "@aws-sdk/client-bedrock": "^3.1063.0",
47
50
  "@aws-sdk/client-cloudwatch-logs": "^3.1063.0",
48
51
  "@aws-sdk/client-ec2": "^3.1063.0",
49
52
  "@aws-sdk/client-guardduty": "^3.1063.0",
50
53
  "@aws-sdk/client-iam": "^3.1063.0",
54
+ "@aws-sdk/client-s3": "3.1132.0",
55
+ "@aws-sdk/client-sts": "3.1132.0",
51
56
  "@modelcontextprotocol/sdk": "^1.0.0",
52
57
  "@pulumi/aws": "^6.0.0",
53
58
  "@pulumi/azure-native": "^2.0.0",
@@ -61,6 +66,7 @@ var dependencies = {
61
66
  chalk: "^5.0.0",
62
67
  citty: "^0.1.0",
63
68
  conf: "^12.0.0",
69
+ "google-auth-library": "^11.0.2",
64
70
  inquirer: "^9.0.0",
65
71
  ora: "^8.0.0",
66
72
  ssh2: "^1.0.0",
@@ -85,7 +91,8 @@ var devDependencies = {
85
91
  };
86
92
  var pnpm = {
87
93
  overrides: {
88
- "@pulumi/pulumi": "^3.0.0"
94
+ "@pulumi/pulumi": "^3.0.0",
95
+ "@smithy/types": "4.14.3"
89
96
  },
90
97
  onlyBuiltDependencies: [
91
98
  "@pulumi/command",