@clawops/cli 2.2.0 → 2.3.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 +61 -3
  2. package/dist/{apply-F5SQ2ZOS.js → apply-ODV3NOJJ.js} +9 -9
  3. package/dist/{automation-NBYQ6MFZ.js → automation-VDKLZMJI.js} +1 -1
  4. package/dist/{backup-flows-NDP5JTR3.js → backup-flows-THPX2D7S.js} +1 -1
  5. package/dist/{bootstrap-32TZXVDF.js → bootstrap-L3P5E4D7.js} +2 -2
  6. package/dist/{chunk-PASAVVNS.js → chunk-47CGHUVT.js} +1 -1
  7. package/dist/{chunk-E6BRQK67.js → chunk-4SEFKZRL.js} +1 -1
  8. package/dist/{chunk-PCSX6Y3U.js → chunk-66CUJFXK.js} +2 -2
  9. package/dist/{chunk-55UMO37O.js → chunk-CHPMOE4T.js} +1 -1
  10. package/dist/{chunk-CQOYUWR7.js → chunk-EGGXYZC6.js} +60 -59
  11. package/dist/{chunk-755X5OGS.js → chunk-FGY262WZ.js} +4 -1
  12. package/dist/{chunk-LV7UO5CG.js → chunk-G2ROJXYJ.js} +2 -2
  13. package/dist/{chunk-GFIR2IIJ.js → chunk-GYMAC4MW.js} +47 -28
  14. package/dist/{chunk-66R52R7K.js → chunk-K7OWMI63.js} +14 -8
  15. package/dist/{chunk-OUQVKIIA.js → chunk-PNRFFYOA.js} +14 -14
  16. package/dist/{chunk-AX65JMDZ.js → chunk-PRBDJSWN.js} +12 -9
  17. package/dist/{chunk-4MHDW2NB.js → chunk-QRF4OWG4.js} +1 -1
  18. package/dist/{chunk-AVERNLUY.js → chunk-SQPHNVCF.js} +6 -6
  19. package/dist/{chunk-RDUKRIRC.js → chunk-U3NRKBDW.js} +1 -1
  20. package/dist/cli.js +111 -72
  21. package/dist/{context-EHSLWKSH.js → context-4KTM4DKH.js} +5 -5
  22. package/dist/{flows-6B2I6TRW.js → flows-6YEJGHCK.js} +7 -7
  23. package/dist/{generate-4YYNW3IU.js → generate-OJ5UWTLV.js} +9 -7
  24. package/dist/{harden-UHAOVQMZ.js → harden-7O553IOK.js} +5 -5
  25. package/dist/{init-HOPXDNWY.js → init-RVQ72GZ5.js} +2 -2
  26. package/dist/{mcp-wire-BXF4DJFT.js → mcp-wire-HSGZFOG5.js} +21 -21
  27. package/dist/{migrate-flow-24XA4GZL.js → migrate-flow-4XQUUFMH.js} +2 -2
  28. package/dist/{package-HYGHRX35.js → package-UOWSHBWP.js} +1 -1
  29. package/dist/policy-4FFK7REN.js +42 -0
  30. package/dist/{remote-config-OZYBMBSK.js → remote-config-V2WXIXIF.js} +1 -1
  31. package/dist/{server-E3YA32ND.js → server-YH2JWZ2Q.js} +21 -21
  32. package/dist/setup-policy-OK5PGWP7.js +90 -0
  33. package/dist/{stack-config-ICQ5YHUL.js → stack-config-3E7KWFPI.js} +2 -2
  34. package/dist/{stack-delete-NTIGKWUF.js → stack-delete-SNPMLWMD.js} +2 -2
  35. package/dist/{state-AKZP2S4T.js → state-KG6K5QZC.js} +2 -2
  36. package/dist/{store-YOLQYXMB.js → store-BTQOOIJH.js} +1 -1
  37. package/dist/{tailscale-cutover-V42HO5UM.js → tailscale-cutover-XTMGDM5H.js} +2 -2
  38. package/dist/{validate-DZE6LFEB.js → validate-CRXX7OYF.js} +1 -1
  39. package/package.json +1 -1
  40. package/spec/errors.yaml +5 -3
  41. package/spec/mcp-tools.yaml +4 -3
  42. package/spec/openclaw-2.0.config.schema.json +1 -1
  43. package/dist/{chunk-UAJJEDCJ.js → chunk-AGSNTIYZ.js} +0 -0
  44. package/dist/{chunk-YPVWF7IA.js → chunk-HSGKK3RF.js} +3 -3
  45. package/dist/{ready-IJEUBFVX.js → ready-4H3NNEYQ.js} +3 -3
package/dist/cli.js CHANGED
@@ -1,9 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- MCP_APPS,
4
- buildMcpEntry,
5
- writeAppConfigs
6
- } from "./chunk-OIGTOLB3.js";
7
2
  import {
8
3
  AUDIT_CLEAN_MESSAGE,
9
4
  AUDIT_FIX_HINT,
@@ -24,25 +19,36 @@ import {
24
19
  runDiagnostics,
25
20
  secretsDir,
26
21
  updateGateway
27
- } from "./chunk-OUQVKIIA.js";
22
+ } from "./chunk-PNRFFYOA.js";
28
23
  import {
29
24
  GATEWAY_LOGS_PROBE,
30
25
  chooseLogSource,
31
26
  containerLogsCommand,
32
27
  gatewayLogsCommand
33
28
  } from "./chunk-SOZ62ZXW.js";
34
- import "./chunk-6ZFIFDBJ.js";
29
+ import {
30
+ MCP_APPS,
31
+ buildMcpEntry,
32
+ writeAppConfigs
33
+ } from "./chunk-OIGTOLB3.js";
35
34
  import "./chunk-Z3XLWZWF.js";
35
+ import "./chunk-6ZFIFDBJ.js";
36
+ import {
37
+ SUPPORTED_PROVIDERS,
38
+ deriveStateBucket,
39
+ initStack,
40
+ resolveScopeAccount,
41
+ validateStateBucket
42
+ } from "./chunk-FGY262WZ.js";
43
+ import "./chunk-LT4XMO5D.js";
36
44
  import {
37
45
  execPrivileged,
38
46
  streamPrivileged
39
47
  } from "./chunk-WPZIJONN.js";
40
- import "./chunk-YPVWF7IA.js";
41
48
  import {
42
49
  resolveSpecDir
43
50
  } from "./chunk-JW5A7E3X.js";
44
- import "./chunk-LT4XMO5D.js";
45
- import "./chunk-VUFMZEIM.js";
51
+ import "./chunk-HSGKK3RF.js";
46
52
  import {
47
53
  PUBLISH_INSPECT_CMD,
48
54
  STATE_DIR_HOST_LINUX,
@@ -70,21 +76,15 @@ import {
70
76
  warn
71
77
  } from "./chunk-Q7NQY5HV.js";
72
78
  import "./chunk-N4WZLWJ4.js";
73
- import {
74
- SUPPORTED_PROVIDERS,
75
- deriveStateBucket,
76
- initStack,
77
- resolveScopeAccount,
78
- validateStateBucket
79
- } from "./chunk-755X5OGS.js";
80
79
  import {
81
80
  getConfigDir,
82
81
  requireConfig
83
- } from "./chunk-GFIR2IIJ.js";
82
+ } from "./chunk-GYMAC4MW.js";
84
83
  import {
85
84
  ClawopsError,
86
85
  UsageError
87
86
  } from "./chunk-KGXPLI7W.js";
87
+ import "./chunk-VUFMZEIM.js";
88
88
 
89
89
  // src/cli/index.ts
90
90
  import { defineCommand as defineCommand24, runMain } from "citty";
@@ -203,7 +203,7 @@ var up_default = defineCommand2({
203
203
  "publish-gateway": { type: "string", description: 'loopback (default) or all. "all" serves plaintext HTTP \u2014 put TLS in front of it' }
204
204
  },
205
205
  async run({ args }) {
206
- const { buildContext } = await import("./context-EHSLWKSH.js");
206
+ const { buildContext } = await import("./context-4KTM4DKH.js");
207
207
  const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-XVYW5WFJ.js");
208
208
  const ctx = buildContext(args);
209
209
  const requestedVersion = typeof args["openclaw-version"] === "string" ? args["openclaw-version"] : await defaultOpenclawVersion();
@@ -216,7 +216,7 @@ var up_default = defineCommand2({
216
216
  );
217
217
  }
218
218
  const { localOpts } = stackConfig;
219
- const { localBootstrap } = await import("./bootstrap-32TZXVDF.js");
219
+ const { localBootstrap } = await import("./bootstrap-L3P5E4D7.js");
220
220
  const gatewayPort = parseGatewayPort(args["gateway-port"]);
221
221
  const abortController2 = new AbortController();
222
222
  process3.on("SIGINT", () => abortController2.abort());
@@ -256,8 +256,8 @@ var up_default = defineCommand2({
256
256
  }
257
257
  return;
258
258
  }
259
- const { generatePlan } = await import("./generate-4YYNW3IU.js");
260
- const { applyPlan } = await import("./apply-F5SQ2ZOS.js");
259
+ const { generatePlan } = await import("./generate-OJ5UWTLV.js");
260
+ const { applyPlan } = await import("./apply-ODV3NOJJ.js");
261
261
  const { resolveNetworkFlags } = await import("./network-args-PYJRJBYO.js");
262
262
  const { detectEgressIp } = await import("./firewall-5VKZ4WBH.js");
263
263
  const isDryRun = Boolean(args["dry-run"]);
@@ -339,7 +339,7 @@ function strArg(value) {
339
339
  async function applyLocalConfigOverlay(opts) {
340
340
  const { connect } = await import("./ssh-HOM7GF3M.js");
341
341
  const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
342
- const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-OZYBMBSK.js");
342
+ const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-V2WXIXIF.js");
343
343
  let overlay;
344
344
  try {
345
345
  overlay = JSON.parse(readFileSync(opts.configPath, "utf-8"));
@@ -387,7 +387,7 @@ var down_default = defineCommand3({
387
387
  "dry-run": { type: "boolean", description: "Show what would be destroyed without destroying" }
388
388
  },
389
389
  async run({ args }) {
390
- const { buildContext } = await import("./context-EHSLWKSH.js");
390
+ const { buildContext } = await import("./context-4KTM4DKH.js");
391
391
  const ctx = buildContext(args);
392
392
  if (args["dry-run"]) {
393
393
  info(`Dry run \u2014 would destroy stack "${ctx.stackName}"`);
@@ -451,7 +451,7 @@ var status_default = defineCommand4({
451
451
  json: { type: "boolean", description: "Emit JSON" }
452
452
  },
453
453
  async run({ args }) {
454
- const { buildContext } = await import("./context-EHSLWKSH.js");
454
+ const { buildContext } = await import("./context-4KTM4DKH.js");
455
455
  const ctx = buildContext(args);
456
456
  if (ctx.adapter.name === "local") {
457
457
  const state = ctx.localState;
@@ -551,8 +551,8 @@ var plan_default = defineCommand5({
551
551
  "private-only": { type: "boolean", description: "Close public SSH and gateway access; reach the stack over its tailnet. Needs `clawops harden --tailscale` first, and checks the tailnet answers before planning" }
552
552
  },
553
553
  async run({ args }) {
554
- const { buildContext } = await import("./context-EHSLWKSH.js");
555
- const { generatePlan } = await import("./generate-4YYNW3IU.js");
554
+ const { buildContext } = await import("./context-4KTM4DKH.js");
555
+ const { generatePlan } = await import("./generate-OJ5UWTLV.js");
556
556
  const { resolveNetworkFlags } = await import("./network-args-PYJRJBYO.js");
557
557
  const { detectEgressIp } = await import("./firewall-5VKZ4WBH.js");
558
558
  const ctx = buildContext(args);
@@ -577,7 +577,7 @@ var plan_default = defineCommand5({
577
577
  let network = resolved;
578
578
  if (args["private-only"]) {
579
579
  const { privateOnlyNetwork, assertTailnetReachable } = await import("./private-only-DIA26I2E.js");
580
- const { probeSsh } = await import("./tailscale-cutover-V42HO5UM.js");
580
+ const { probeSsh } = await import("./tailscale-cutover-XTMGDM5H.js");
581
581
  network = privateOnlyNetwork(flags, resolved, ctx.config.stacks[ctx.stackName]?.tailscale, ctx.stackName);
582
582
  await assertTailnetReachable(ctx, (conn) => probeSsh(conn));
583
583
  }
@@ -667,8 +667,8 @@ var apply_default = defineCommand6({
667
667
  "dry-run": { type: "boolean", description: "Validate plan and show diff without applying" }
668
668
  },
669
669
  async run({ args }) {
670
- const { validatePlan } = await import("./validate-DZE6LFEB.js");
671
- const { applyPlan } = await import("./apply-F5SQ2ZOS.js");
670
+ const { validatePlan } = await import("./validate-CRXX7OYF.js");
671
+ const { applyPlan } = await import("./apply-ODV3NOJJ.js");
672
672
  const planPath = args._?.[0];
673
673
  if (!planPath) {
674
674
  throw new UsageError("Usage: clawops apply <plan.json>");
@@ -794,7 +794,7 @@ var destroy_default = defineCommand7({
794
794
  "dry-run": { type: "boolean", description: "Show what would be destroyed without destroying" }
795
795
  },
796
796
  async run({ args }) {
797
- const { buildContext } = await import("./context-EHSLWKSH.js");
797
+ const { buildContext } = await import("./context-4KTM4DKH.js");
798
798
  const ctx = buildContext(args);
799
799
  if (ctx.adapter.name === "local") {
800
800
  throw new UsageError(
@@ -887,7 +887,7 @@ var ssh_default = defineCommand8({
887
887
  command: { type: "string", description: "Remote command to run (instead of interactive shell)" }
888
888
  },
889
889
  async run({ args }) {
890
- const { buildContext } = await import("./context-EHSLWKSH.js");
890
+ const { buildContext } = await import("./context-4KTM4DKH.js");
891
891
  const { acquireSession, drainPool } = await import("./pool-NT4XBSUT.js");
892
892
  const ctx = buildContext(args);
893
893
  let conn;
@@ -974,7 +974,7 @@ var tunnel_default = defineCommand9({
974
974
  "no-open": { type: "boolean", description: "Do not open browser after tunnel is ready" }
975
975
  },
976
976
  async run({ args }) {
977
- const { buildContext } = await import("./context-EHSLWKSH.js");
977
+ const { buildContext } = await import("./context-4KTM4DKH.js");
978
978
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
979
979
  const { acquireSession } = await import("./pool-NT4XBSUT.js");
980
980
  const ctx = buildContext(args);
@@ -1043,7 +1043,7 @@ var logs_default = defineCommand10({
1043
1043
  json: { type: "boolean", description: "Emit the gateway's structured JSON log lines" }
1044
1044
  },
1045
1045
  async run({ args }) {
1046
- const { buildContext } = await import("./context-EHSLWKSH.js");
1046
+ const { buildContext } = await import("./context-4KTM4DKH.js");
1047
1047
  const { acquireSession, drainPool } = await import("./pool-NT4XBSUT.js");
1048
1048
  const ctx = buildContext(args);
1049
1049
  const conn = await connFor(ctx);
@@ -1155,7 +1155,7 @@ var config_default = defineCommand11({
1155
1155
  "dry-run": { type: "boolean", description: "Show what would change without writing" }
1156
1156
  },
1157
1157
  async run({ args }) {
1158
- const { buildContext } = await import("./context-EHSLWKSH.js");
1158
+ const { buildContext } = await import("./context-4KTM4DKH.js");
1159
1159
  const { acquireSession, drainPool } = await import("./pool-NT4XBSUT.js");
1160
1160
  const [action, key, value] = args._ ?? [];
1161
1161
  if (!action || !["get", "set", "unset", "validate"].includes(action)) {
@@ -1235,7 +1235,7 @@ var config_default = defineCommand11({
1235
1235
  process13.stdout.write(JSON.stringify(cfg, null, 2) + "\n");
1236
1236
  return;
1237
1237
  }
1238
- const { atomicWriteConfig } = await import("./remote-config-OZYBMBSK.js");
1238
+ const { atomicWriteConfig } = await import("./remote-config-V2WXIXIF.js");
1239
1239
  try {
1240
1240
  await atomicWriteConfig(session, cfg, abortController.signal);
1241
1241
  } catch (err) {
@@ -1280,7 +1280,7 @@ var agents_default = defineCommand12({
1280
1280
  cursor: { type: "string", description: "Continue from a previous `logs` result cursor" }
1281
1281
  },
1282
1282
  async run({ args }) {
1283
- const { buildContext } = await import("./context-EHSLWKSH.js");
1283
+ const { buildContext } = await import("./context-4KTM4DKH.js");
1284
1284
  const { acquireSession, drainPool } = await import("./pool-NT4XBSUT.js");
1285
1285
  const [action, name] = args._ ?? [];
1286
1286
  if (action === "restart") {
@@ -1408,7 +1408,7 @@ var migrate_default = defineCommand13({
1408
1408
  yes: { type: "boolean", description: "Skip the confirmation prompt" }
1409
1409
  },
1410
1410
  async run({ args }) {
1411
- const { prepareMigration, runMigration, migrationQuestion } = await import("./migrate-flow-24XA4GZL.js");
1411
+ const { prepareMigration, runMigration, migrationQuestion } = await import("./migrate-flow-4XQUUFMH.js");
1412
1412
  const target = await prepareMigration({
1413
1413
  stack: typeof args.stack === "string" ? args.stack : void 0,
1414
1414
  openclawVersion: typeof args["openclaw-version"] === "string" ? args["openclaw-version"] : void 0
@@ -1461,7 +1461,7 @@ var gateway_default = defineCommand14({
1461
1461
  json: { type: "boolean", description: "Emit JSON (for status)" }
1462
1462
  },
1463
1463
  async run({ args }) {
1464
- const { buildContext } = await import("./context-EHSLWKSH.js");
1464
+ const { buildContext } = await import("./context-4KTM4DKH.js");
1465
1465
  const { acquireSession, drainPool } = await import("./pool-NT4XBSUT.js");
1466
1466
  const [action, versionArg] = args._ ?? [];
1467
1467
  if (!action || !["status", "restart", "update"].includes(action)) {
@@ -1571,7 +1571,7 @@ var backup_default = defineCommand15({
1571
1571
  activate: { type: "boolean", description: "[restore] Put the restored state into service: stop the gateway, swap it in, restart, and roll back if it does not come up" }
1572
1572
  },
1573
1573
  async run({ args }) {
1574
- const { buildContext } = await import("./context-EHSLWKSH.js");
1574
+ const { buildContext } = await import("./context-4KTM4DKH.js");
1575
1575
  const { acquireSession, drainPool } = await import("./pool-NT4XBSUT.js");
1576
1576
  const action = args.action;
1577
1577
  if (action !== "create" && action !== "restore") {
@@ -1622,7 +1622,7 @@ var backup_default = defineCommand15({
1622
1622
  signal: abortController.signal
1623
1623
  });
1624
1624
  try {
1625
- const flows = await import("./backup-flows-NDP5JTR3.js");
1625
+ const flows = await import("./backup-flows-THPX2D7S.js");
1626
1626
  if (action === "create") {
1627
1627
  const outPath = typeof args.out === "string" ? args.out : flows.defaultBackupFilename();
1628
1628
  info(`Writing backup to ${outPath}...`);
@@ -1769,7 +1769,7 @@ var stacks_default = defineCommand16({
1769
1769
  failure("Usage: clawops stacks delete <name>");
1770
1770
  process18.exit(2);
1771
1771
  }
1772
- const { checkStackDelete, deleteStackFromConfig, forgetNotice } = await import("./stack-delete-NTIGKWUF.js");
1772
+ const { checkStackDelete, deleteStackFromConfig, forgetNotice } = await import("./stack-delete-SNPMLWMD.js");
1773
1773
  const check = await checkStackDelete(name, { force: !!args.force });
1774
1774
  if (!check.ok) {
1775
1775
  if (check.refusal === "deployed") {
@@ -1887,7 +1887,7 @@ var serveCmd = defineCommand18({
1887
1887
  inspector: { type: "boolean", description: "Enable MCP inspector" }
1888
1888
  },
1889
1889
  async run({ args }) {
1890
- const { serveMcp } = await import("./server-E3YA32ND.js");
1890
+ const { serveMcp } = await import("./server-YH2JWZ2Q.js");
1891
1891
  await serveMcp({
1892
1892
  port: args.http ? Number(args.http) : void 0,
1893
1893
  bind: args.bind,
@@ -1965,11 +1965,11 @@ var wireCmd = defineCommand18({
1965
1965
  rewire: { type: "boolean", description: "Replace an existing clawops entry" }
1966
1966
  },
1967
1967
  async run({ args }) {
1968
- const { buildContext } = await import("./context-EHSLWKSH.js");
1968
+ const { buildContext } = await import("./context-4KTM4DKH.js");
1969
1969
  const { acquireSession, drainPool } = await import("./pool-NT4XBSUT.js");
1970
1970
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
1971
- const { wireGatewayMcp } = await import("./mcp-wire-BXF4DJFT.js");
1972
- const { MCP_HTTP_PORT } = await import("./server-E3YA32ND.js");
1971
+ const { wireGatewayMcp } = await import("./mcp-wire-HSGZFOG5.js");
1972
+ const { MCP_HTTP_PORT } = await import("./server-YH2JWZ2Q.js");
1973
1973
  const ac = new AbortController();
1974
1974
  process20.on("SIGINT", () => {
1975
1975
  ac.abort();
@@ -2180,6 +2180,18 @@ var setup_default = defineCommand19({
2180
2180
  }]);
2181
2181
  localSudoPassword = sudoPass;
2182
2182
  }
2183
+ const { promptSpendingPolicy, allowedChoices, savePolicy } = await import("./setup-policy-OK5PGWP7.js");
2184
+ const { getConfig: readConfig } = await import("./store-BTQOOIJH.js");
2185
+ const configBefore = readConfig();
2186
+ const pendingPolicy = await promptSpendingPolicy({
2187
+ provider: provider === "local" ? void 0 : provider,
2188
+ choices: provider === "local" ? [] : instanceChoices(provider),
2189
+ current: configBefore?.policy,
2190
+ stackCount: Object.keys(configBefore?.stacks ?? {}).length,
2191
+ inquirer
2192
+ });
2193
+ let policySaved = pendingPolicy ? await savePolicy(pendingPolicy) : true;
2194
+ const sizeChoices = provider === "local" ? [] : await allowedChoices(provider, instanceChoices(provider), pendingPolicy ?? configBefore?.policy);
2183
2195
  process21.stdout.write("\n");
2184
2196
  const sshCidrDefault = provider === "local" ? void 0 : await detectSshCidrDefault();
2185
2197
  const scopeAccount = provider === "local" ? void 0 : await resolveScopeAccount(provider);
@@ -2209,7 +2221,7 @@ var setup_default = defineCommand19({
2209
2221
  type: "list",
2210
2222
  name: "instanceSize",
2211
2223
  message: "Server size: (bigger = faster, but costs more per month)",
2212
- choices: instanceChoices(provider)
2224
+ choices: sizeChoices
2213
2225
  },
2214
2226
  stateBucketQuestion(provider, scopeAccount),
2215
2227
  {
@@ -2237,6 +2249,12 @@ var setup_default = defineCommand19({
2237
2249
  default: "latest"
2238
2250
  }
2239
2251
  ]);
2252
+ {
2253
+ const { stackCapRefusal } = await import("./policy-4FFK7REN.js");
2254
+ const { getConfig } = await import("./store-BTQOOIJH.js");
2255
+ const overCap = stackCapRefusal(getConfig(), stackAnswers.stackName);
2256
+ if (overCap) throw new UsageError(overCap);
2257
+ }
2240
2258
  if (provider !== "local") {
2241
2259
  await runAccountPreflight({
2242
2260
  provider,
@@ -2412,6 +2430,11 @@ var setup_default = defineCommand19({
2412
2430
  failure(`Cannot read SSH public key at ${stackAnswers.sshKeyPath ?? ""}`);
2413
2431
  process21.exit(1);
2414
2432
  }
2433
+ const { nativeInstanceType } = await import("./generate-OJ5UWTLV.js");
2434
+ const instanceType = await nativeInstanceType(provider, stackAnswers.instanceSize);
2435
+ const { enforceInstanceType } = await import("./policy-4FFK7REN.js");
2436
+ const { getConfig: currentConfig } = await import("./store-BTQOOIJH.js");
2437
+ await enforceInstanceType(currentConfig(), provider, instanceType);
2415
2438
  const plan = {
2416
2439
  apiVersion: "clawops.dev/v1",
2417
2440
  kind: "DeployPlan",
@@ -2424,7 +2447,7 @@ var setup_default = defineCommand19({
2424
2447
  provider,
2425
2448
  region: stackAnswers.region,
2426
2449
  stackName: stackAnswers.stackName,
2427
- instanceType: stackAnswers.instanceSize,
2450
+ instanceType,
2428
2451
  openclaw: {
2429
2452
  version: stackAnswers.openclawVersion,
2430
2453
  config: openclawConfigOverlay,
@@ -2513,7 +2536,19 @@ var setup_default = defineCommand19({
2513
2536
  info("After deployment, run this command on the server (or locally if Ollama is local):");
2514
2537
  info(` ollama pull ${selectedModel.id}${selectedModel.pullSuffix}`);
2515
2538
  }
2516
- if (dryRun) return;
2539
+ const finishPolicy = async () => {
2540
+ if (!pendingPolicy || policySaved) return;
2541
+ policySaved = await savePolicy(pendingPolicy);
2542
+ if (!policySaved) {
2543
+ warn(
2544
+ "Spending limits were not saved: there is no ~/.clawops/config.json yet. Run `clawops setup` again after your first deployment to set them."
2545
+ );
2546
+ }
2547
+ };
2548
+ if (dryRun) {
2549
+ await finishPolicy();
2550
+ return;
2551
+ }
2517
2552
  if (provider === "local") {
2518
2553
  const { deployNow } = await inquirer.prompt([{
2519
2554
  type: "confirm",
@@ -2552,7 +2587,7 @@ var setup_default = defineCommand19({
2552
2587
  inquirer
2553
2588
  });
2554
2589
  } else {
2555
- const { getConfigDir: getConfigDir2 } = await import("./store-YOLQYXMB.js");
2590
+ const { getConfigDir: getConfigDir2 } = await import("./store-BTQOOIJH.js");
2556
2591
  const knownHostsPath = path2.join(getConfigDir2(), "known_hosts");
2557
2592
  process21.stdout.write("\n");
2558
2593
  info("To deploy later, run these two commands:");
@@ -2569,7 +2604,7 @@ var setup_default = defineCommand19({
2569
2604
  default: false
2570
2605
  }]);
2571
2606
  if (applyNow) {
2572
- const { applyPlan } = await import("./apply-F5SQ2ZOS.js");
2607
+ const { applyPlan } = await import("./apply-ODV3NOJJ.js");
2573
2608
  const plan = JSON.parse(readFileSync3(outputPath, "utf-8"));
2574
2609
  try {
2575
2610
  await applyPlan(plan, {
@@ -2594,16 +2629,20 @@ var setup_default = defineCommand19({
2594
2629
  To deploy later: clawops apply ${outputPath}`);
2595
2630
  }
2596
2631
  }
2632
+ await finishPolicy();
2597
2633
  }
2598
2634
  });
2599
2635
  async function runLocalDeploy(opts) {
2600
- const { setConfig, getConfig, getConfigDir: getConfigDir2 } = await import("./store-YOLQYXMB.js");
2601
- const { localBootstrap } = await import("./bootstrap-32TZXVDF.js");
2636
+ const { setConfig, getConfig, getConfigDir: getConfigDir2 } = await import("./store-BTQOOIJH.js");
2637
+ const { localBootstrap } = await import("./bootstrap-L3P5E4D7.js");
2602
2638
  const { connect } = await import("./ssh-HOM7GF3M.js");
2603
- const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-OZYBMBSK.js");
2639
+ const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-V2WXIXIF.js");
2604
2640
  const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
2605
2641
  const knownHostsPath = path2.join(getConfigDir2(), "known_hosts");
2606
2642
  const existing = getConfig();
2643
+ const { stackCapRefusal } = await import("./policy-4FFK7REN.js");
2644
+ const overCap = stackCapRefusal(existing, opts.stackName);
2645
+ if (overCap) throw new UsageError(overCap);
2607
2646
  const stackEntry = {
2608
2647
  provider: "local",
2609
2648
  stateUrl: "file://~/.clawops/state",
@@ -3072,8 +3111,8 @@ async function maybeWireGatewayMcp(opts) {
3072
3111
  const { wireGateway } = await inquirerPromptWire(opts.inquirer);
3073
3112
  if (!wireGateway) return;
3074
3113
  const { connect } = await import("./ssh-HOM7GF3M.js");
3075
- const { getConfigDir: getConfigDir2 } = await import("./store-YOLQYXMB.js");
3076
- const { wireGatewayMcp } = await import("./mcp-wire-BXF4DJFT.js");
3114
+ const { getConfigDir: getConfigDir2 } = await import("./store-BTQOOIJH.js");
3115
+ const { wireGatewayMcp } = await import("./mcp-wire-HSGZFOG5.js");
3077
3116
  const knownHostsPath = path2.join(getConfigDir2(), "known_hosts");
3078
3117
  const spin = spinner("Wiring gateway MCP client...");
3079
3118
  const session = await connect({
@@ -3192,8 +3231,8 @@ function instanceChoices(provider) {
3192
3231
  return table[provider] ?? [];
3193
3232
  }
3194
3233
  async function maybeHarden(opts) {
3195
- const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-UHAOVQMZ.js");
3196
- const { getConfig } = await import("./store-YOLQYXMB.js");
3234
+ const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-7O553IOK.js");
3235
+ const { getConfig } = await import("./store-BTQOOIJH.js");
3197
3236
  const config = getConfig();
3198
3237
  if (!config) return;
3199
3238
  const availableModules = resolveModules(MODULE_CATALOG, void 0, opts.provider);
@@ -3216,7 +3255,7 @@ async function maybeHarden(opts) {
3216
3255
  const modules = availableModules.filter((m) => selectedIds.includes(m.id));
3217
3256
  process21.stdout.write("\nApplying hardening modules...\n\n");
3218
3257
  try {
3219
- const { buildContext } = await import("./context-EHSLWKSH.js");
3258
+ const { buildContext } = await import("./context-4KTM4DKH.js");
3220
3259
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
3221
3260
  const ctx = buildContext({ stack: opts.stackName });
3222
3261
  const stackObj = await ctx.getStack();
@@ -3470,7 +3509,7 @@ var rotateCmd = defineCommand20({
3470
3509
  spawnSync2("chmod", ["700", secretsDir()], { stdio: "ignore" });
3471
3510
  writeFileSync3(target, value.trim(), { encoding: "utf-8", mode: 384 });
3472
3511
  success(`Secret "${name}" updated.`);
3473
- const { getConfig } = await import("./store-YOLQYXMB.js");
3512
+ const { getConfig } = await import("./store-BTQOOIJH.js");
3474
3513
  const cfg = getConfig();
3475
3514
  const targetStack = args.stack ?? cfg?.defaults?.stack;
3476
3515
  if (!targetStack) {
@@ -3487,11 +3526,11 @@ var rotateCmd = defineCommand20({
3487
3526
  }
3488
3527
  info(`Re-applying config overlay to stack "${targetStack}"\u2026`);
3489
3528
  try {
3490
- const { buildContext } = await import("./context-EHSLWKSH.js");
3491
- const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-OZYBMBSK.js");
3529
+ const { buildContext } = await import("./context-4KTM4DKH.js");
3530
+ const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-V2WXIXIF.js");
3492
3531
  const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
3493
3532
  const { acquireSession, drainPool } = await import("./pool-NT4XBSUT.js");
3494
- const { localStateToConnectionInfo } = await import("./state-AKZP2S4T.js");
3533
+ const { localStateToConnectionInfo } = await import("./state-KG6K5QZC.js");
3495
3534
  const ctx = buildContext({ stack: targetStack });
3496
3535
  let conn;
3497
3536
  if (ctx.adapter.name === "local") {
@@ -3641,7 +3680,7 @@ import { accessSync, constants } from "fs";
3641
3680
  import { execSync as execSync2 } from "child_process";
3642
3681
  var ISSUES_URL = `${REPO_URL}/issues/new`;
3643
3682
  var BODY_CAP = 2e3;
3644
- var VERSION = true ? "2.2.0" : "dev";
3683
+ var VERSION = true ? "2.3.0" : "dev";
3645
3684
  async function collectContext() {
3646
3685
  const nodeVersion = process23.version;
3647
3686
  const os2 = `${process23.platform} ${process23.arch}`;
@@ -3649,7 +3688,7 @@ async function collectContext() {
3649
3688
  let stackCount = 0;
3650
3689
  let sshKey = "unknown";
3651
3690
  try {
3652
- const { getConfig } = await import("./store-YOLQYXMB.js");
3691
+ const { getConfig } = await import("./store-BTQOOIJH.js");
3653
3692
  const config = getConfig();
3654
3693
  if (config) {
3655
3694
  const stacks = Object.values(config.stacks);
@@ -3760,9 +3799,9 @@ var harden_default = defineCommand23({
3760
3799
  "tailscale-revert": { type: "boolean", description: "Take the host off its tailnet and go back to its public address. A private-only stack must be reopened with a plan first" }
3761
3800
  },
3762
3801
  async run({ args }) {
3763
- const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-UHAOVQMZ.js");
3764
- const { buildContext } = await import("./context-EHSLWKSH.js");
3765
- const { getConfig } = await import("./store-YOLQYXMB.js");
3802
+ const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-7O553IOK.js");
3803
+ const { buildContext } = await import("./context-4KTM4DKH.js");
3804
+ const { getConfig } = await import("./store-BTQOOIJH.js");
3766
3805
  if (args.list) {
3767
3806
  process24.stdout.write("\nAvailable hardening modules:\n\n");
3768
3807
  for (const mod of MODULE_CATALOG) {
@@ -3784,7 +3823,7 @@ var harden_default = defineCommand23({
3784
3823
  const ctx = buildContext({ stack: args.stack });
3785
3824
  const provider = ctx.adapter.name;
3786
3825
  if (args["tailscale-revert"]) {
3787
- const { revertTailnet } = await import("./flows-6B2I6TRW.js");
3826
+ const { revertTailnet } = await import("./flows-6YEJGHCK.js");
3788
3827
  const override = config.stacks[ctx.stackName]?.tailscale;
3789
3828
  if (override && args.yes !== true) {
3790
3829
  const { createInterface: createInterface3 } = await import("readline/promises");
@@ -3807,7 +3846,7 @@ var harden_default = defineCommand23({
3807
3846
  else success(outcome.message);
3808
3847
  return;
3809
3848
  }
3810
- const { makeTailscaleModule } = await import("./harden-UHAOVQMZ.js");
3849
+ const { makeTailscaleModule } = await import("./harden-7O553IOK.js");
3811
3850
  const forStack = makeTailscaleModule(ctx.stackName);
3812
3851
  const resolved = resolveModules(MODULE_CATALOG, args.options, provider).map(
3813
3852
  (m) => m.id === "tailscale" ? forStack : m
@@ -3891,7 +3930,7 @@ var harden_default = defineCommand23({
3891
3930
  success("Hardening complete.");
3892
3931
  }
3893
3932
  if (args.tailscale && !args["dry-run"]) {
3894
- const { cutOverToTailnet } = await import("./flows-6B2I6TRW.js");
3933
+ const { cutOverToTailnet } = await import("./flows-6YEJGHCK.js");
3895
3934
  process24.stdout.write("\nVerifying the tailnet address before using it...\n");
3896
3935
  const outcome = await cutOverToTailnet(ctx, conn, config);
3897
3936
  if (!outcome.ok) {
@@ -3928,7 +3967,7 @@ function resolveArgv(argv2, stdinIsTty) {
3928
3967
  }
3929
3968
 
3930
3969
  // src/cli/index.ts
3931
- var CLI_VERSION = true ? "2.2.0" : "dev";
3970
+ var CLI_VERSION = true ? "2.3.0" : "dev";
3932
3971
  var main = defineCommand24({
3933
3972
  meta: {
3934
3973
  name: "clawops",
@@ -2,18 +2,18 @@
2
2
  import {
3
3
  buildContext,
4
4
  loadAdapterModule
5
- } from "./chunk-AVERNLUY.js";
6
- import "./chunk-YPVWF7IA.js";
7
- import "./chunk-VUFMZEIM.js";
5
+ } from "./chunk-SQPHNVCF.js";
6
+ import "./chunk-HSGKK3RF.js";
8
7
  import "./chunk-N5ZYUTZS.js";
9
8
  import "./chunk-VOEJYRIK.js";
10
9
  import "./chunk-EI7CQQ7D.js";
11
10
  import "./chunk-FEK7VJAY.js";
12
11
  import "./chunk-RHOWYIWT.js";
13
12
  import "./chunk-N4WZLWJ4.js";
14
- import "./chunk-RDUKRIRC.js";
15
- import "./chunk-GFIR2IIJ.js";
13
+ import "./chunk-U3NRKBDW.js";
14
+ import "./chunk-GYMAC4MW.js";
16
15
  import "./chunk-KGXPLI7W.js";
16
+ import "./chunk-VUFMZEIM.js";
17
17
  export {
18
18
  buildContext,
19
19
  loadAdapterModule
@@ -2,9 +2,9 @@
2
2
 
3
3
  // src/harden/flows.ts
4
4
  async function cutOverToTailnet(ctx, conn, config) {
5
- const { withRemoteExec } = await import("./harden-UHAOVQMZ.js");
6
- const { verifyTailnetAddress, probeSsh } = await import("./tailscale-cutover-V42HO5UM.js");
7
- const { setConfig } = await import("./store-YOLQYXMB.js");
5
+ const { withRemoteExec } = await import("./harden-7O553IOK.js");
6
+ const { verifyTailnetAddress, probeSsh } = await import("./tailscale-cutover-XTMGDM5H.js");
7
+ const { setConfig } = await import("./store-BTQOOIJH.js");
8
8
  const result = await withRemoteExec(
9
9
  conn,
10
10
  void 0,
@@ -49,11 +49,11 @@ async function revertTailnet(ctx, config) {
49
49
  message: `Stack "${ctx.stackName}" is not using a tailnet address; nothing to revert.`
50
50
  };
51
51
  }
52
- const { buildContext } = await import("./context-EHSLWKSH.js");
52
+ const { buildContext } = await import("./context-4KTM4DKH.js");
53
53
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
54
- const { probeSsh, leaveTailnet } = await import("./tailscale-cutover-V42HO5UM.js");
55
- const { withRemoteExec } = await import("./harden-UHAOVQMZ.js");
56
- const { getConfig, setConfig } = await import("./store-YOLQYXMB.js");
54
+ const { probeSsh, leaveTailnet } = await import("./tailscale-cutover-XTMGDM5H.js");
55
+ const { withRemoteExec } = await import("./harden-7O553IOK.js");
56
+ const { getConfig, setConfig } = await import("./store-BTQOOIJH.js");
57
57
  const { forgetHost } = await import("./known-hosts-file-I3Q3NBYQ.js");
58
58
  const direct = buildContext({ stack: ctx.stackName, ignoreTailnet: true });
59
59
  const outputMap = await (await direct.getStack()).outputs();
@@ -3,28 +3,30 @@ import {
3
3
  INSTANCE_ALIASES,
4
4
  generatePlan,
5
5
  isInstanceAlias,
6
+ nativeInstanceType,
6
7
  parseDiffForTest,
7
8
  planId
8
- } from "./chunk-66R52R7K.js";
9
+ } from "./chunk-K7OWMI63.js";
9
10
  import "./chunk-POYS3EVQ.js";
10
- import "./chunk-PASAVVNS.js";
11
- import "./chunk-AVERNLUY.js";
12
- import "./chunk-YPVWF7IA.js";
11
+ import "./chunk-47CGHUVT.js";
12
+ import "./chunk-SQPHNVCF.js";
13
13
  import "./chunk-JW5A7E3X.js";
14
- import "./chunk-VUFMZEIM.js";
14
+ import "./chunk-HSGKK3RF.js";
15
15
  import "./chunk-N5ZYUTZS.js";
16
16
  import "./chunk-VOEJYRIK.js";
17
17
  import "./chunk-EI7CQQ7D.js";
18
18
  import "./chunk-FEK7VJAY.js";
19
19
  import "./chunk-RHOWYIWT.js";
20
20
  import "./chunk-N4WZLWJ4.js";
21
- import "./chunk-RDUKRIRC.js";
22
- import "./chunk-GFIR2IIJ.js";
21
+ import "./chunk-U3NRKBDW.js";
22
+ import "./chunk-GYMAC4MW.js";
23
23
  import "./chunk-KGXPLI7W.js";
24
+ import "./chunk-VUFMZEIM.js";
24
25
  export {
25
26
  INSTANCE_ALIASES,
26
27
  generatePlan,
27
28
  isInstanceAlias,
29
+ nativeInstanceType,
28
30
  parseDiffForTest,
29
31
  planId
30
32
  };
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- makeTailscaleModule,
4
- tailscaleModule
5
- } from "./chunk-4MHDW2NB.js";
6
2
  import {
7
3
  acquireSession,
8
4
  drainPool
9
5
  } from "./chunk-TYNMOYVM.js";
6
+ import {
7
+ makeTailscaleModule,
8
+ tailscaleModule
9
+ } from "./chunk-QRF4OWG4.js";
10
10
  import "./chunk-4PY6PN4G.js";
11
11
  import "./chunk-MC5ATCXS.js";
12
12
  import {
@@ -28,7 +28,7 @@ import {
28
28
  import {
29
29
  resolveProjectId
30
30
  } from "./chunk-TC2VYPLH.js";
31
- import "./chunk-GFIR2IIJ.js";
31
+ import "./chunk-GYMAC4MW.js";
32
32
  import "./chunk-KGXPLI7W.js";
33
33
 
34
34
  // src/harden/types.ts
@@ -3,8 +3,8 @@ import {
3
3
  PROVIDER_DEFAULTS,
4
4
  SUPPORTED_PROVIDERS,
5
5
  initStack
6
- } from "./chunk-755X5OGS.js";
7
- import "./chunk-GFIR2IIJ.js";
6
+ } from "./chunk-FGY262WZ.js";
7
+ import "./chunk-GYMAC4MW.js";
8
8
  import "./chunk-KGXPLI7W.js";
9
9
  export {
10
10
  PROVIDER_DEFAULTS,