@clawops/cli 2.2.1 → 2.3.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 (44) hide show
  1. package/README.md +63 -4
  2. package/dist/{apply-F5SQ2ZOS.js → apply-TXPUNOGW.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-AX65JMDZ.js → chunk-7LLVIQ7G.js} +12 -9
  10. package/dist/{chunk-55UMO37O.js → chunk-CHPMOE4T.js} +1 -1
  11. package/dist/{chunk-LV7UO5CG.js → chunk-G2ROJXYJ.js} +2 -2
  12. package/dist/{chunk-GFIR2IIJ.js → chunk-GYMAC4MW.js} +47 -28
  13. package/dist/{chunk-T6OCPTTR.js → chunk-LIAXA43Z.js} +14 -8
  14. package/dist/{chunk-Y3N63PDS.js → chunk-ORBZGTNX.js} +93 -60
  15. package/dist/{chunk-OUQVKIIA.js → chunk-PNRFFYOA.js} +14 -14
  16. package/dist/{chunk-755X5OGS.js → chunk-Q56AM57B.js} +4 -1
  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-4JWTMOLQ.js → generate-XJWEQKT6.js} +9 -7
  24. package/dist/{harden-UHAOVQMZ.js → harden-7O553IOK.js} +5 -5
  25. package/dist/{init-HOPXDNWY.js → init-DYOLZX65.js} +2 -2
  26. package/dist/{mcp-wire-7MGEAMZH.js → mcp-wire-NAKI2WME.js} +21 -21
  27. package/dist/{migrate-flow-24XA4GZL.js → migrate-flow-4XQUUFMH.js} +2 -2
  28. package/dist/{package-ESYPCIES.js → package-HRMX7K3C.js} +1 -1
  29. package/dist/policy-AZGFCQNV.js +42 -0
  30. package/dist/{remote-config-OZYBMBSK.js → remote-config-V2WXIXIF.js} +1 -1
  31. package/dist/{server-Y2EDRUDL.js → server-FR5LUAOI.js} +21 -21
  32. package/dist/setup-policy-HTO2KSZR.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/dist/{chunk-UAJJEDCJ.js → chunk-AGSNTIYZ.js} +0 -0
  43. package/dist/{chunk-YPVWF7IA.js → chunk-HSGKK3RF.js} +3 -3
  44. package/dist/{ready-IJEUBFVX.js → ready-4H3NNEYQ.js} +3 -3
package/README.md CHANGED
@@ -14,7 +14,7 @@ and Cursor drive them through typed MCP tools with explicit safety controls.
14
14
 
15
15
  ---
16
16
 
17
- **What's new:** [2.0.1 and 2.0 release notes](#whats-new-in-201) below, and the full history
17
+ **What's new:** [2.3.1 release notes](#whats-new-in-231) below, and the full history
18
18
  in [CHANGELOG.md](CHANGELOG.md).
19
19
 
20
20
  ---
@@ -262,7 +262,7 @@ clawops down --yes # Destroy local-provider stack
262
262
 
263
263
  | Command | Description |
264
264
  |---|---|
265
- | `setup` | First-run wizard: guided LLM, integrations, and deploy-plan generation |
265
+ | `setup` | First-run wizard: guided LLM, integrations, spending limits, and deploy-plan generation |
266
266
  | `init` | Register a stack in `~/.clawops/config.json` without provisioning. Additive, existing stacks are kept; `--force` is needed only to overwrite one |
267
267
  | `up` | Provision or update stack (`--dry-run` for preview, `--gateway-port` for a non-default port) |
268
268
  | `down` | Destroy local-provider stack (requires `--yes`; `--dry-run` shows current outputs) |
@@ -397,7 +397,7 @@ Do not bind to a non-loopback address without additional authentication controls
397
397
  | `clawops_harden` | cli | Apply hardening modules; join or leave a tailnet (elicits confirmation) |
398
398
  | `clawops_init` | cli | Register a stack and write `~/.clawops/config.json` (no cloud resources) |
399
399
  | `clawops_workflow_deploy_app` | workflow | End-to-end deploy: plan → confirm → apply → status |
400
- | `clawops_workflow_recover` | workflow | Diagnostic workflow for an unhealthy stack |
400
+ | `clawops_workflow_recover` | workflow | Diagnose an unhealthy stack: status, failed health checks, recent logs, and which tool fixes what |
401
401
  | `clawops_task_status` | cli | Poll a long-running task |
402
402
 
403
403
  Tools in the `read` toolset are also available in `--read-only` mode; the table's Toolset column shows the primary toolset. All other toolsets require full mode.
@@ -442,6 +442,24 @@ Config lives at `~/.clawops/config.json` (override with `$CLAWOPS_HOME`).
442
442
  | Azure | `AZURE_CLIENT_ID` / `AZURE_CLIENT_SECRET` or `az login` |
443
443
  | Local | SSH host + key configured in `stacks[name].localOpts` |
444
444
 
445
+ ### Spending limits
446
+
447
+ An optional `policy` block limits what clawops will deploy from this machine, whichever tool asks:
448
+
449
+ ```json
450
+ "policy": {
451
+ "allowedInstanceTypes": { "aws": ["micro", "small"] },
452
+ "maxStacks": 3
453
+ }
454
+ ```
455
+
456
+ `plan`, `apply` and `up` refuse a server size outside `allowedInstanceTypes` for that provider
457
+ (a clawops size or the provider's own type; `small` and `t3.small` are the same on AWS), and
458
+ `init` refuses a new stack beyond `maxStacks`. Every MCP tool that deploys is held to the same
459
+ limits, in the same words, and no tool can change them. `clawops setup` asks whether to set
460
+ them, then offers only the sizes you allowed. The limit lives here rather than in the plan
461
+ because a plan is what an agent writes.
462
+
445
463
  ---
446
464
 
447
465
  ## Known limitations
@@ -576,7 +594,7 @@ pnpm dev doctor # verify toolchain
576
594
  ```bash
577
595
  pnpm dev # run CLI from src/ via tsx
578
596
  pnpm build # tsup → dist/
579
- pnpm test # vitest (2091 tests, ~13s)
597
+ pnpm test # vitest (2122 tests, ~13s)
580
598
  pnpm test:changed # vitest --changed (fast edit loop)
581
599
  pnpm test:integration # Docker-based SSH integration tests
582
600
  pnpm test:e2e:local # builds, then bootstrap, the host-touching MCP tools and a 1.x migration, in systemd containers
@@ -635,6 +653,47 @@ Use `pnpm changeset` to record a release note before merging a `feat` or `fix`.
635
653
 
636
654
  ---
637
655
 
656
+ ## What's new in 2.3.1
657
+
658
+ A security fix. **Upgrade if you set gateway config through MCP.**
659
+
660
+ - **Before:** `clawops_config_set` wrote the value you set to the audit log
661
+ (`~/.clawops/mcp-audit.log`) in clear and redacted the config path instead. Setting
662
+ `models.providers.openai.apiKey` through an agent therefore logged the API key. The
663
+ confirmation prompt showed the value too.
664
+ - **Now:** the value is redacted whenever its path names a secret, and secret fields inside a
665
+ JSON object value are redacted one by one. Paths are logged as written. See
666
+ [redaction](docs/security/redaction.md#config-paths-and-values).
667
+ - **Existing logs are not rewritten.** If you used `clawops_config_set` for a secret before
668
+ 2.3.1, check your audit log and rotate that secret.
669
+
670
+ ## What's new in 2.3
671
+
672
+ Spending limits that an agent cannot lift, and a run of fixes from the backlog.
673
+
674
+ ### Spending limits
675
+
676
+ - An optional `policy` block in `~/.clawops/config.json` limits the server sizes clawops may
677
+ deploy on each provider and the number of deployments it may manage. `plan`, `apply` and `up`
678
+ refuse a size outside the list; `init` refuses a stack beyond the cap.
679
+ - Every MCP tool that deploys is held to the same limits, in the same words, and no tool can
680
+ change them. Confirmation is something an agent can answer for itself; this is not.
681
+ - `clawops setup` asks whether to set them, then offers only the sizes you allowed. See
682
+ [Spending limits](#spending-limits).
683
+
684
+ ### Fixes
685
+
686
+ - **The setup wizard's plan asked the cloud for a machine called `small`.** It wrote the clawops
687
+ size where the provider's own type belongs. It now maps it, as `clawops plan` does.
688
+ - **`clawops_workflow_recover` runs the stack's health checks, and recommends only real tools.** It
689
+ ran two of the four steps it described and finished by suggesting `clawops refresh`, which does
690
+ not exist.
691
+ - **The MCP audit log redacts credential arguments.** `GOOGLE_APPLICATION_CREDENTIALS` was named
692
+ sensitive in `spec/errors.yaml` and logged in the clear. A test now fails when the spec and the
693
+ code disagree on an error class, its exit code, or a redaction pattern (#185).
694
+
695
+ ---
696
+
638
697
  ## What's new in 2.2
639
698
 
640
699
  Recovery is a flag, agents can reach every command they need, and SSH failures say what failed.
@@ -1,27 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  applyPlan
4
- } from "./chunk-AX65JMDZ.js";
4
+ } from "./chunk-7LLVIQ7G.js";
5
5
  import "./chunk-BOPSG2LI.js";
6
- import "./chunk-UAJJEDCJ.js";
7
- import "./chunk-PASAVVNS.js";
8
- import "./chunk-55UMO37O.js";
6
+ import "./chunk-CHPMOE4T.js";
9
7
  import "./chunk-AEEMRFZV.js";
10
- import "./chunk-AVERNLUY.js";
8
+ import "./chunk-AGSNTIYZ.js";
9
+ import "./chunk-47CGHUVT.js";
10
+ import "./chunk-SQPHNVCF.js";
11
11
  import "./chunk-6ZFIFDBJ.js";
12
12
  import "./chunk-WPZIJONN.js";
13
- import "./chunk-YPVWF7IA.js";
14
13
  import "./chunk-JW5A7E3X.js";
15
- import "./chunk-VUFMZEIM.js";
14
+ import "./chunk-HSGKK3RF.js";
16
15
  import "./chunk-N5ZYUTZS.js";
17
16
  import "./chunk-VOEJYRIK.js";
18
17
  import "./chunk-EI7CQQ7D.js";
19
18
  import "./chunk-FEK7VJAY.js";
20
19
  import "./chunk-RHOWYIWT.js";
21
20
  import "./chunk-N4WZLWJ4.js";
22
- import "./chunk-RDUKRIRC.js";
23
- import "./chunk-GFIR2IIJ.js";
21
+ import "./chunk-U3NRKBDW.js";
22
+ import "./chunk-GYMAC4MW.js";
24
23
  import "./chunk-KGXPLI7W.js";
24
+ import "./chunk-VUFMZEIM.js";
25
25
  export {
26
26
  applyPlan
27
27
  };
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-V25QRZRB.js";
8
8
  import {
9
9
  getConfigDir
10
- } from "./chunk-GFIR2IIJ.js";
10
+ } from "./chunk-GYMAC4MW.js";
11
11
  import "./chunk-KGXPLI7W.js";
12
12
 
13
13
  // src/pulumi/automation.ts
@@ -10,7 +10,7 @@ import {
10
10
  defaultBackupFilename,
11
11
  nothingChanged,
12
12
  stageRestore
13
- } from "./chunk-PCSX6Y3U.js";
13
+ } from "./chunk-66CUJFXK.js";
14
14
  import "./chunk-WPZIJONN.js";
15
15
  import "./chunk-N5ZYUTZS.js";
16
16
  import "./chunk-VOEJYRIK.js";
@@ -14,8 +14,8 @@ import {
14
14
  import "./chunk-EI7CQQ7D.js";
15
15
  import {
16
16
  writeLocalState
17
- } from "./chunk-RDUKRIRC.js";
18
- import "./chunk-GFIR2IIJ.js";
17
+ } from "./chunk-U3NRKBDW.js";
18
+ import "./chunk-GYMAC4MW.js";
19
19
  import {
20
20
  ProviderError
21
21
  } from "./chunk-KGXPLI7W.js";
@@ -44,7 +44,7 @@ async function validatePlanConfig(plan) {
44
44
  ]);
45
45
  const { loadVersionSpec } = await import("./versions-W5LRQCAJ.js");
46
46
  const spec = loadVersionSpec(yaml);
47
- const { deepMerge } = await import("./remote-config-OZYBMBSK.js");
47
+ const { deepMerge } = await import("./remote-config-V2WXIXIF.js");
48
48
  const merged = deepMerge(
49
49
  { gateway: { mode: "local" } },
50
50
  overlay
@@ -136,7 +136,7 @@ var SYNTHESISED_CONFIG = JSON.stringify({
136
136
  channels: {}
137
137
  });
138
138
  async function prepareMigration(args) {
139
- const { buildContext } = await import("./context-EHSLWKSH.js");
139
+ const { buildContext } = await import("./context-4KTM4DKH.js");
140
140
  const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-XVYW5WFJ.js");
141
141
  const version = args.openclawVersion ? await guardOpenclawVersion(args.openclawVersion) : await defaultOpenclawVersion();
142
142
  const ctx = buildContext({ stack: args.stack });
@@ -230,8 +230,8 @@ async function stageRestore(session, opts) {
230
230
  async function activateStaged(session, staged, opts = {}) {
231
231
  const { signal } = opts;
232
232
  const hostExec = (command) => execPrivileged(session, command, signal);
233
- const { restartGateway } = await import("./remote-config-OZYBMBSK.js");
234
- const { waitForGateway } = await import("./ready-IJEUBFVX.js");
233
+ const { restartGateway } = await import("./remote-config-V2WXIXIF.js");
234
+ const { waitForGateway } = await import("./ready-4H3NNEYQ.js");
235
235
  opts.onPhase?.("activating");
236
236
  const chown = await hostExec(`chown -R ${CONTAINER_UID}:${CONTAINER_UID} ${staged.statePath}`);
237
237
  if (chown.code !== 0) {
@@ -2,21 +2,21 @@
2
2
  import {
3
3
  resolveSecrets
4
4
  } from "./chunk-BOPSG2LI.js";
5
+ import {
6
+ writeStackConfig
7
+ } from "./chunk-CHPMOE4T.js";
5
8
  import {
6
9
  atomicWriteConfig,
7
10
  deepMerge,
8
11
  readRemoteConfig,
9
12
  restartGateway
10
- } from "./chunk-UAJJEDCJ.js";
13
+ } from "./chunk-AGSNTIYZ.js";
11
14
  import {
12
15
  validatePlan
13
- } from "./chunk-PASAVVNS.js";
14
- import {
15
- writeStackConfig
16
- } from "./chunk-55UMO37O.js";
16
+ } from "./chunk-47CGHUVT.js";
17
17
  import {
18
18
  buildContext
19
- } from "./chunk-AVERNLUY.js";
19
+ } from "./chunk-SQPHNVCF.js";
20
20
  import {
21
21
  saveOverlay
22
22
  } from "./chunk-6ZFIFDBJ.js";
@@ -26,7 +26,7 @@ import {
26
26
  import {
27
27
  getConfig,
28
28
  setConfig
29
- } from "./chunk-GFIR2IIJ.js";
29
+ } from "./chunk-GYMAC4MW.js";
30
30
  import {
31
31
  UsageError
32
32
  } from "./chunk-KGXPLI7W.js";
@@ -48,13 +48,16 @@ ${validation.errors.join("\n")}`
48
48
  }
49
49
  const { guardOpenclawVersion } = await import("./version-guard-XVYW5WFJ.js");
50
50
  await guardOpenclawVersion(plan.spec.openclaw.version);
51
+ const { enforceInstanceType } = await import("./policy-AZGFCQNV.js");
52
+ const { getConfig: getConfig2 } = await import("./store-BTQOOIJH.js");
53
+ await enforceInstanceType(getConfig2(), plan.spec.provider, plan.spec.instanceType);
51
54
  const ctx = buildContext({
52
55
  stack: plan.spec.stackName,
53
56
  provider: plan.spec.provider
54
57
  });
55
58
  const stack = await ctx.getStack();
56
59
  const { guardPrivateOnlyApply } = await import("./private-only-DIA26I2E.js");
57
- const { probeSsh } = await import("./tailscale-cutover-V42HO5UM.js");
60
+ const { probeSsh } = await import("./tailscale-cutover-XTMGDM5H.js");
58
61
  await guardPrivateOnlyApply(plan, ctx, opts?.probeTailnet ?? ((conn2) => probeSsh(conn2, opts?.signal)));
59
62
  await writeStackConfig(stack, plan);
60
63
  if (plan.metadata.stackVersion !== void 0) {
@@ -93,7 +96,7 @@ The diff you reviewed may no longer reflect what will be applied.
93
96
  signal: opts?.signal,
94
97
  onProgress: (line) => reportProgress(opts, line)
95
98
  });
96
- const { waitForGateway } = await import("./ready-IJEUBFVX.js");
99
+ const { waitForGateway } = await import("./ready-4H3NNEYQ.js");
97
100
  try {
98
101
  await waitForGateway(readySession, {
99
102
  signal: opts?.signal,
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VOEJYRIK.js";
8
8
  import {
9
9
  getConfig
10
- } from "./chunk-GFIR2IIJ.js";
10
+ } from "./chunk-GYMAC4MW.js";
11
11
  import {
12
12
  UsageError
13
13
  } from "./chunk-KGXPLI7W.js";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  requireConfig,
4
4
  setConfig
5
- } from "./chunk-GFIR2IIJ.js";
5
+ } from "./chunk-GYMAC4MW.js";
6
6
 
7
7
  // src/config/stack-delete.ts
8
8
  function forgetNotice(name) {
@@ -47,7 +47,7 @@ async function checkStackDelete(name, opts) {
47
47
  return { ok: true, name, isDefault, warnings };
48
48
  }
49
49
  async function isStackDeployed(name) {
50
- const { buildContext } = await import("./context-EHSLWKSH.js");
50
+ const { buildContext } = await import("./context-4KTM4DKH.js");
51
51
  const ctx = buildContext({ stack: name });
52
52
  if (ctx.adapter.name === "local") return !!ctx.localState;
53
53
  const stack = await ctx.getStack();
@@ -10,46 +10,64 @@ import { randomUUID } from "crypto";
10
10
  import os from "os";
11
11
  import path from "path";
12
12
  import process from "process";
13
+ import { z as z2 } from "zod";
14
+
15
+ // src/config/policy-schema.ts
13
16
  import { z } from "zod";
14
- var CredentialsRefSchema = z.object({
15
- source: z.enum(["env", "cli-profile", "file", "instance-metadata"]),
16
- envVars: z.array(z.string()).optional(),
17
- profileName: z.string().optional()
17
+ var PolicySchema = z.object({
18
+ /**
19
+ * Per provider, the instance types clawops may deploy. A clawops size (`micro`, `small`,
20
+ * `medium`, `large`) or a provider-native type; sizes are compared after the provider maps
21
+ * them, so `small` and `t3.small` are the same entry on AWS. A provider with no entry is
22
+ * unrestricted.
23
+ */
24
+ allowedInstanceTypes: z.record(z.array(z.string()).min(1)).optional(),
25
+ /** How many stacks this config may hold. Absent = no cap. */
26
+ maxStacks: z.number().int().positive().optional()
27
+ });
28
+
29
+ // src/config/store.ts
30
+ var CredentialsRefSchema = z2.object({
31
+ source: z2.enum(["env", "cli-profile", "file", "instance-metadata"]),
32
+ envVars: z2.array(z2.string()).optional(),
33
+ profileName: z2.string().optional()
18
34
  });
19
- var LocalOptsSchema = z.object({
20
- host: z.string(),
21
- sshUser: z.string(),
22
- sshPort: z.number(),
23
- sshKeyPath: z.string()
35
+ var LocalOptsSchema = z2.object({
36
+ host: z2.string(),
37
+ sshUser: z2.string(),
38
+ sshPort: z2.number(),
39
+ sshKeyPath: z2.string()
24
40
  });
25
- var TailscaleOverrideSchema = z.object({
26
- ip: z.string(),
27
- hostname: z.string().optional(),
41
+ var TailscaleOverrideSchema = z2.object({
42
+ ip: z2.string(),
43
+ hostname: z2.string().optional(),
28
44
  /** When the address was verified, ISO 8601. */
29
- verifiedAt: z.string(),
45
+ verifiedAt: z2.string(),
30
46
  /** Set once --private-only has closed the public ports, so revert knows to reopen them. */
31
- privateOnly: z.boolean().optional()
47
+ privateOnly: z2.boolean().optional()
32
48
  });
33
- var StackConfigSchema = z.object({
34
- provider: z.string(),
35
- stateUrl: z.string(),
36
- region: z.string().optional(),
49
+ var StackConfigSchema = z2.object({
50
+ provider: z2.string(),
51
+ stateUrl: z2.string(),
52
+ region: z2.string().optional(),
37
53
  credentialsRef: CredentialsRefSchema,
38
54
  localOpts: LocalOptsSchema.optional(),
39
55
  tailscale: TailscaleOverrideSchema.optional()
40
56
  });
41
- var ClawopsConfigSchema = z.object({
42
- version: z.literal(1),
43
- defaults: z.object({
44
- stack: z.string(),
45
- provider: z.string()
57
+ var ClawopsConfigSchema = z2.object({
58
+ version: z2.literal(1),
59
+ defaults: z2.object({
60
+ stack: z2.string(),
61
+ provider: z2.string()
46
62
  }),
47
- stacks: z.record(StackConfigSchema),
48
- ssh: z.object({
49
- keyPath: z.string(),
50
- knownHostsPath: z.string()
63
+ stacks: z2.record(StackConfigSchema),
64
+ ssh: z2.object({
65
+ keyPath: z2.string(),
66
+ knownHostsPath: z2.string()
51
67
  }),
52
- mcp: z.object({ auditLogPath: z.string() }).optional()
68
+ mcp: z2.object({ auditLogPath: z2.string() }).optional(),
69
+ /** The operator's spending policy; see ./policy.ts. */
70
+ policy: PolicySchema.optional()
53
71
  });
54
72
  function getConfigDir() {
55
73
  return process.env["CLAWOPS_HOME"] ?? path.join(os.homedir(), ".clawops");
@@ -92,6 +110,7 @@ function setConfig(config) {
92
110
  }
93
111
 
94
112
  export {
113
+ PolicySchema,
95
114
  getConfigDir,
96
115
  getConfigPath,
97
116
  getConfig,
@@ -4,14 +4,14 @@ import {
4
4
  } from "./chunk-POYS3EVQ.js";
5
5
  import {
6
6
  validatePlan
7
- } from "./chunk-PASAVVNS.js";
7
+ } from "./chunk-47CGHUVT.js";
8
8
  import {
9
9
  buildContext,
10
10
  loadAdapterModule
11
- } from "./chunk-AVERNLUY.js";
11
+ } from "./chunk-SQPHNVCF.js";
12
12
  import {
13
13
  getConfig
14
- } from "./chunk-GFIR2IIJ.js";
14
+ } from "./chunk-GYMAC4MW.js";
15
15
  import {
16
16
  StateError,
17
17
  UsageError
@@ -62,9 +62,12 @@ async function resolveInstanceType(intent) {
62
62
  `[clawops] note: "${requested}" is not a clawops size (${INSTANCE_ALIASES.join(", ")}), so it is passed to ${intent.provider} as written.
63
63
  `
64
64
  );
65
- return requested;
66
65
  }
67
- const adapter = await loadAdapterModule(intent.provider);
66
+ return nativeInstanceType(intent.provider, requested);
67
+ }
68
+ async function nativeInstanceType(provider, requested) {
69
+ if (!isInstanceAlias(requested)) return requested;
70
+ const adapter = await loadAdapterModule(provider);
68
71
  return adapter.normalizeInstanceType(requested);
69
72
  }
70
73
  var parseDiffForTest = parseDiff;
@@ -74,9 +77,11 @@ async function generatePlan(intent, _opts) {
74
77
  "plan/apply is not supported for the local provider. Use `clawops up` directly."
75
78
  );
76
79
  }
77
- const { version } = await import("./package-ESYPCIES.js");
80
+ const { version } = await import("./package-HRMX7K3C.js");
78
81
  const config = getConfig();
79
82
  const instanceType = await resolveInstanceType(intent);
83
+ const { enforceInstanceType } = await import("./policy-AZGFCQNV.js");
84
+ await enforceInstanceType(config, intent.provider, instanceType);
80
85
  const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-XVYW5WFJ.js");
81
86
  const openclawVersion = await guardOpenclawVersion(
82
87
  intent.openclawVersion ?? await defaultOpenclawVersion()
@@ -126,7 +131,7 @@ A plan is only as good as the state it was computed against, so this is not a pl
126
131
  );
127
132
  }
128
133
  try {
129
- const { writeStackConfig } = await import("./stack-config-ICQ5YHUL.js");
134
+ const { writeStackConfig } = await import("./stack-config-3E7KWFPI.js");
130
135
  await writeStackConfig(stack, plan);
131
136
  const outputLines = [];
132
137
  const preview = await stack.preview({
@@ -154,7 +159,7 @@ A plan is only as good as the state it was computed against, so this is not a pl
154
159
  throw new Error(`Generated plan failed schema validation:
155
160
  ${validation.errors.join("\n")}`);
156
161
  }
157
- const { validatePlanConfig, validatePlanNetwork } = await import("./validate-DZE6LFEB.js");
162
+ const { validatePlanConfig, validatePlanNetwork } = await import("./validate-CRXX7OYF.js");
158
163
  const cfgCheck = await validatePlanConfig(plan);
159
164
  for (const w of cfgCheck.warnings) process.stderr.write(`[clawops] warning: ${w}
160
165
  `);
@@ -186,6 +191,7 @@ function expandHome(p) {
186
191
  export {
187
192
  INSTANCE_ALIASES,
188
193
  isInstanceAlias,
194
+ nativeInstanceType,
189
195
  parseDiffForTest,
190
196
  generatePlan,
191
197
  planId