@clawops/cli 2.1.1 → 2.1.3

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 (30) hide show
  1. package/README.md +23 -1
  2. package/dist/{apply-HJ5GAKHW.js → apply-JARPVA5Y.js} +8 -8
  3. package/dist/{automation-MKCJ46I6.js → automation-NBYQ6MFZ.js} +1 -1
  4. package/dist/{bootstrap-NN647RCY.js → bootstrap-3PBVY7MA.js} +2 -2
  5. package/dist/{chunk-X56CQ6GB.js → chunk-4MHDW2NB.js} +1 -1
  6. package/dist/{chunk-3Z5TDXCJ.js → chunk-55UMO37O.js} +1 -1
  7. package/dist/chunk-755X5OGS.js +213 -0
  8. package/dist/{chunk-R3DTA35B.js → chunk-7T4CIT7C.js} +6 -6
  9. package/dist/{chunk-QDNFADJI.js → chunk-AVERNLUY.js} +6 -6
  10. package/dist/{chunk-GFUK6KDR.js → chunk-CPCO6UJK.js} +9 -9
  11. package/dist/{chunk-64PONSA5.js → chunk-GFIR2IIJ.js} +3 -1
  12. package/dist/{chunk-YEBFVLOE.js → chunk-PTCVFLZ3.js} +4 -4
  13. package/dist/{chunk-ELU3XQEA.js → chunk-RDUKRIRC.js} +1 -1
  14. package/dist/{chunk-OFTAXLOY.js → chunk-TYHTISI5.js} +239 -159
  15. package/dist/cli.js +127 -274
  16. package/dist/{context-7VI3R6EK.js → context-WOKSKAIA.js} +5 -5
  17. package/dist/{flows-RI7FGHAM.js → flows-HGJIA6EL.js} +7 -7
  18. package/dist/{generate-JNJ63ZYZ.js → generate-NE54BATS.js} +6 -6
  19. package/dist/{harden-3KKCR7BL.js → harden-RUOUA5BV.js} +2 -2
  20. package/dist/init-HOPXDNWY.js +13 -0
  21. package/dist/{mcp-wire-B57DUNOD.js → mcp-wire-FIO5DFBY.js} +11 -11
  22. package/dist/{package-SAUDFQLG.js → package-MF5LPITL.js} +1 -1
  23. package/dist/{server-RHON6QMN.js → server-OXWHBTXK.js} +11 -11
  24. package/dist/{stack-config-LUTS2XAD.js → stack-config-ICQ5YHUL.js} +2 -2
  25. package/dist/{state-AOAYLYY5.js → state-AKZP2S4T.js} +2 -2
  26. package/dist/{store-OVB3DAJR.js → store-YOLQYXMB.js} +1 -1
  27. package/dist/{tailscale-cutover-6YP2QKFM.js → tailscale-cutover-EKAQSDXP.js} +2 -2
  28. package/package.json +1 -1
  29. package/spec/mcp-tools.yaml +69 -28
  30. package/dist/{chunk-BELTQGHT.js → chunk-CHNSL3TC.js} +3 -3
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@clawops/cli)](https://www.npmjs.com/package/@clawops/cli)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@clawops/cli)](https://www.npmjs.com/package/@clawops/cli)
5
+ [![clawops MCP server – quality and maintenance score on Glama](https://glama.ai/mcp/servers/dfridkin/clawops/badges/score.svg)](https://glama.ai/mcp/servers/dfridkin/clawops)
5
6
 
6
7
  MCP-native infrastructure ops for OpenClaw, with read-only mode, destructive-action confirmation, and audit logs built in.
7
8
 
@@ -362,6 +363,7 @@ Do not bind to a non-loopback address without additional authentication controls
362
363
  | `clawops_config_validate` | cli | Validate the deployed config against the OpenClaw schema |
363
364
  | `clawops_gateway_restart` | cli | Restart the gateway (elicits confirmation) |
364
365
  | `clawops_harden` | cli | Apply hardening modules; join or leave a tailnet (elicits confirmation) |
366
+ | `clawops_init` | cli | Register a stack and write `~/.clawops/config.json` (no cloud resources) |
365
367
  | `clawops_workflow_deploy_app` | workflow | End-to-end deploy: plan → confirm → apply → status |
366
368
  | `clawops_workflow_recover` | workflow | Diagnostic workflow for an unhealthy stack |
367
369
  | `clawops_task_status` | cli | Poll a long-running task |
@@ -542,7 +544,7 @@ pnpm dev doctor # verify toolchain
542
544
  ```bash
543
545
  pnpm dev # run CLI from src/ via tsx
544
546
  pnpm build # tsup → dist/
545
- pnpm test # vitest (1855 tests, ~13s)
547
+ pnpm test # vitest (1897 tests, ~13s)
546
548
  pnpm test:changed # vitest --changed (fast edit loop)
547
549
  pnpm test:integration # Docker-based SSH integration tests
548
550
  pnpm typecheck # tsc --noEmit
@@ -603,6 +605,7 @@ Use `pnpm changeset` to record a release note before merging a `feat` or `fix`.
603
605
  ## What's new in 2.1
604
606
 
605
607
  Private networking, hardening on every cloud, and two fixes to commands that could not start.
608
+ 2.1.1 follows with the fixes below it.
606
609
 
607
610
  ### Reach a stack over your tailnet
608
611
 
@@ -645,6 +648,25 @@ Private networking, hardening on every cloud, and two fixes to commands that cou
645
648
  - The published package carries its license, keywords and issue tracker, so it is findable on npm
646
649
  and its listing is complete.
647
650
 
651
+ ### 2.1.1
652
+
653
+ - **`clawops` started with no command and a pipe on stdin serves MCP** instead of printing help.
654
+ That is how every MCP client starts a server, and how directories that infer a run command
655
+ start one; several were getting the help text and reporting the server as broken. Typed at a
656
+ terminal, `clawops` still prints help, and so does `clawops | less`.
657
+ - The MCP config example is `npx -y @clawops/cli mcp serve`, which runs as written. It used to
658
+ say `/path/to/clawops`, which nothing could run and no directory could copy.
659
+ - The description is inside the MCP registry's 100-character limit. The one 2.1.0 shipped was
660
+ 110, and the registry refused it with a 422 after npm had already published — so 2.1.0 reached
661
+ npm and not the registry.
662
+ - `clawops harden` says a stack is not deployed, or that its state could not be read, instead of
663
+ passing along `code: -2` and a subprocess dump.
664
+ - The MCP registry backfill registers the version that was released rather than the one the
665
+ release tooling is preparing, so an entry that falls behind can actually be repaired.
666
+ - The Docker image builds. It had never been built, and did not: `npm pack --pack-destination`
667
+ does not create its destination. `pnpm verify:docker` builds it and speaks MCP to the running
668
+ container, both through its entrypoint and as a bare binary, in CI.
669
+
648
670
  ---
649
671
 
650
672
  ## What's new in 2.0.1
@@ -1,27 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  applyPlan
4
- } from "./chunk-R3DTA35B.js";
4
+ } from "./chunk-7T4CIT7C.js";
5
5
  import "./chunk-BOPSG2LI.js";
6
6
  import "./chunk-6ZFIFDBJ.js";
7
7
  import "./chunk-2I7KCMXZ.js";
8
- import "./chunk-3Z5TDXCJ.js";
9
- import "./chunk-AEEMRFZV.js";
10
8
  import "./chunk-XFN3YDZE.js";
11
- import "./chunk-QDNFADJI.js";
9
+ import "./chunk-55UMO37O.js";
10
+ import "./chunk-AEEMRFZV.js";
11
+ import "./chunk-AVERNLUY.js";
12
12
  import "./chunk-NUDIHZEH.js";
13
+ import "./chunk-CHNSL3TC.js";
13
14
  import "./chunk-JW5A7E3X.js";
14
- import "./chunk-BELTQGHT.js";
15
+ import "./chunk-VUFMZEIM.js";
15
16
  import "./chunk-GIEVAGEK.js";
16
17
  import "./chunk-VOEJYRIK.js";
17
18
  import "./chunk-EI7CQQ7D.js";
18
19
  import "./chunk-FEK7VJAY.js";
19
20
  import "./chunk-RHOWYIWT.js";
20
21
  import "./chunk-N4WZLWJ4.js";
21
- import "./chunk-ELU3XQEA.js";
22
- import "./chunk-64PONSA5.js";
22
+ import "./chunk-RDUKRIRC.js";
23
+ import "./chunk-GFIR2IIJ.js";
23
24
  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-64PONSA5.js";
10
+ } from "./chunk-GFIR2IIJ.js";
11
11
  import "./chunk-KGXPLI7W.js";
12
12
 
13
13
  // src/pulumi/automation.ts
@@ -14,8 +14,8 @@ import {
14
14
  import "./chunk-EI7CQQ7D.js";
15
15
  import {
16
16
  writeLocalState
17
- } from "./chunk-ELU3XQEA.js";
18
- import "./chunk-64PONSA5.js";
17
+ } from "./chunk-RDUKRIRC.js";
18
+ import "./chunk-GFIR2IIJ.js";
19
19
  import {
20
20
  ProviderError
21
21
  } from "./chunk-KGXPLI7W.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getConfigDir
4
- } from "./chunk-64PONSA5.js";
4
+ } from "./chunk-GFIR2IIJ.js";
5
5
 
6
6
  // src/config/secrets.ts
7
7
  import { readFileSync, existsSync } from "fs";
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VOEJYRIK.js";
8
8
  import {
9
9
  getConfig
10
- } from "./chunk-64PONSA5.js";
10
+ } from "./chunk-GFIR2IIJ.js";
11
11
  import {
12
12
  UsageError
13
13
  } from "./chunk-KGXPLI7W.js";
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getConfig,
4
+ getConfigDir,
5
+ setConfig
6
+ } from "./chunk-GFIR2IIJ.js";
7
+
8
+ // src/config/init.ts
9
+ import { spawnSync } from "child_process";
10
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
11
+ import path from "path";
12
+
13
+ // src/providers/state-bucket.ts
14
+ function deriveStateBucket(provider, scope) {
15
+ if (provider === "azure") {
16
+ return { ok: true, name: "clawops-state" };
17
+ }
18
+ if (!scope.account) {
19
+ return {
20
+ ok: false,
21
+ needs: provider === "aws" ? "an AWS account id \u2014 set AWS_PROFILE, or run `aws sso login --profile <name>`" : "a GCP project \u2014 run `gcloud config set project <id>`, or set GOOGLE_CLOUD_PROJECT"
22
+ };
23
+ }
24
+ if (provider === "gcp") return { ok: true, name: `clawops-state-${scope.account}` };
25
+ if (!scope.region) return { ok: false, needs: "a region" };
26
+ return { ok: true, name: `clawops-state-${scope.account}-${scope.region}` };
27
+ }
28
+ function stateUrlFor(provider, bucket) {
29
+ const verdict = validateStateBucket(provider, bucket);
30
+ if (verdict !== true) throw new Error(`Not a usable ${provider} state backend name: ${verdict}`);
31
+ const scheme = provider === "aws" ? "s3://" : provider === "gcp" ? "gs://" : "azblob://";
32
+ return provider === "azure" ? `${scheme}${bucket}` : `${scheme}${bucket}/clawops`;
33
+ }
34
+ var IPV4 = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
35
+ function validateStateBucket(provider, name) {
36
+ const n = name.trim();
37
+ if (n === "") return "Required";
38
+ if (n !== name) return "No leading or trailing spaces";
39
+ if (n.length < 3) return `Too short \u2014 ${labelFor(provider)} names are at least 3 characters`;
40
+ const dotted = provider === "gcp" && n.includes(".");
41
+ const max = dotted ? 222 : 63;
42
+ if (n.length > max) return `Too long \u2014 ${labelFor(provider)} names are at most ${max} characters`;
43
+ if (dotted && n.split(".").some((part) => part.length > 63)) {
44
+ return "Each dot-separated part of a Cloud Storage name is at most 63 characters";
45
+ }
46
+ if (n !== n.toLowerCase()) return "Lowercase only";
47
+ if (provider === "azure") {
48
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(n)) {
49
+ return "Letters, digits and single hyphens only, starting and ending with a letter or digit";
50
+ }
51
+ return true;
52
+ }
53
+ if (!/^[a-z0-9][a-z0-9._-]*[a-z0-9]$/.test(n)) {
54
+ return "Letters, digits, dots, hyphens and underscores only, starting and ending with a letter or digit";
55
+ }
56
+ if (n.includes("..")) return "No consecutive dots";
57
+ if (IPV4.test(n)) return "Cannot be formatted as an IP address";
58
+ if (provider === "aws") {
59
+ if (n.startsWith("xn--")) return 'Cannot start with "xn--"';
60
+ if (n.startsWith("sthree-")) return 'Cannot start with "sthree-"';
61
+ if (n.endsWith("-s3alias")) return 'Cannot end with "-s3alias"';
62
+ if (n.endsWith("--ol-s3")) return 'Cannot end with "--ol-s3"';
63
+ if (n.includes("_")) return "Underscores are not allowed in S3 bucket names";
64
+ return true;
65
+ }
66
+ if (n.startsWith("goog")) return 'Cannot start with "goog"';
67
+ if (n.includes("google")) return 'Cannot contain "google"';
68
+ return true;
69
+ }
70
+ function labelFor(provider) {
71
+ if (provider === "aws") return "S3 bucket";
72
+ if (provider === "gcp") return "Cloud Storage bucket";
73
+ return "blob container";
74
+ }
75
+ async function resolveScopeAccount(provider, signal) {
76
+ if (provider === "azure") return void 0;
77
+ try {
78
+ if (provider === "aws") {
79
+ const { callerAccount } = await import("./preflight-TFD4N5ZU.js");
80
+ return await callerAccount(signal);
81
+ }
82
+ const { resolveProjectId } = await import("./preflight-IE2YWBAC.js");
83
+ return resolveProjectId();
84
+ } catch {
85
+ return void 0;
86
+ }
87
+ }
88
+
89
+ // src/config/init.ts
90
+ var SUPPORTED_PROVIDERS = ["gcp", "aws", "azure", "local"];
91
+ var PROVIDER_DEFAULTS = {
92
+ gcp: { region: "us-central1", credEnv: "GOOGLE_APPLICATION_CREDENTIALS", stateScheme: "gs://" },
93
+ aws: { region: "us-east-1", credEnv: "AWS_PROFILE", stateScheme: "s3://" },
94
+ azure: { region: "eastus", credEnv: "AZURE_CLIENT_ID", stateScheme: "azblob://" }
95
+ };
96
+ async function initStack(opts) {
97
+ const provider = opts.provider;
98
+ if (!SUPPORTED_PROVIDERS.includes(provider)) {
99
+ return {
100
+ ok: false,
101
+ reason: `Unsupported provider: ${provider}. Supported: ${SUPPORTED_PROVIDERS.join(", ")}`
102
+ };
103
+ }
104
+ const stackName = opts.stackName ?? "default";
105
+ const existing = getConfig();
106
+ if (existing?.stacks[stackName] && !opts.force) {
107
+ return {
108
+ ok: false,
109
+ reason: `Stack "${stackName}" already exists in ${path.join(getConfigDir(), "config.json")} (${existing.stacks[stackName].stateUrl}). Pass force to overwrite its settings, or use a different stack name to add another one.`
110
+ };
111
+ }
112
+ const configDir = getConfigDir();
113
+ mkdirSync(configDir, { recursive: true });
114
+ const keyPath = opts.keyPath ?? path.join(configDir, "id_ed25519");
115
+ const knownHostsPath = path.join(configDir, "known_hosts");
116
+ let keyGenerated = false;
117
+ if (!existsSync(keyPath)) {
118
+ if (opts.keyPath) return { ok: false, reason: `SSH key not found at ${keyPath}` };
119
+ const gen = spawnSync(
120
+ "ssh-keygen",
121
+ ["-t", "ed25519", "-f", keyPath, "-N", "", "-C", "clawops", "-q"],
122
+ { stdio: ["ignore", "pipe", "pipe"] }
123
+ );
124
+ if (gen.error || gen.status !== 0) {
125
+ const reason = gen.error?.message ?? gen.stderr?.toString().trim() ?? `exit ${gen.status}`;
126
+ return {
127
+ ok: false,
128
+ reason: `Could not generate an SSH key: ${reason}
129
+ Generate one yourself and point clawops at it:
130
+ ssh-keygen -t ed25519 -f ${keyPath} -N '' -C clawops
131
+ clawops init --key-path ${keyPath}`
132
+ };
133
+ }
134
+ keyGenerated = true;
135
+ }
136
+ if (!existsSync(knownHostsPath)) writeFileSync(knownHostsPath, "", "utf-8");
137
+ if (provider === "local") {
138
+ if (!opts.host) return { ok: false, reason: "host is required for the local provider" };
139
+ const config = merge(existing, stackName, provider, keyPath, knownHostsPath, {
140
+ provider,
141
+ stateUrl: "file://~/.clawops/state",
142
+ credentialsRef: { source: "file", envVars: [] },
143
+ localOpts: {
144
+ host: opts.host,
145
+ sshUser: opts.sshUser ?? "root",
146
+ sshPort: opts.sshPort ?? 22,
147
+ sshKeyPath: keyPath
148
+ }
149
+ });
150
+ setConfig(config);
151
+ return {
152
+ ok: true,
153
+ configPath: path.join(configDir, "config.json"),
154
+ stackName,
155
+ provider,
156
+ stateUrl: "file://~/.clawops/state",
157
+ keyPath,
158
+ keyGenerated
159
+ };
160
+ }
161
+ const defaults = PROVIDER_DEFAULTS[provider];
162
+ const region = opts.region ?? defaults.region;
163
+ let stateUrl;
164
+ if (opts.stateUrl) {
165
+ stateUrl = opts.stateUrl;
166
+ } else {
167
+ const account = await resolveScopeAccount(provider);
168
+ const derived = deriveStateBucket(provider, { account, region });
169
+ if (!derived.ok) {
170
+ return {
171
+ ok: false,
172
+ reason: `Could not name a state backend for ${provider}: clawops needs ${derived.needs}. Authenticate first, or name the backend yourself, e.g. ${defaults.stateScheme}your-bucket/clawops`
173
+ };
174
+ }
175
+ stateUrl = stateUrlFor(provider, derived.name);
176
+ }
177
+ setConfig(
178
+ merge(existing, stackName, provider, keyPath, knownHostsPath, {
179
+ provider,
180
+ stateUrl,
181
+ region,
182
+ credentialsRef: { source: "env", envVars: [defaults.credEnv] }
183
+ })
184
+ );
185
+ return {
186
+ ok: true,
187
+ configPath: path.join(configDir, "config.json"),
188
+ stackName,
189
+ provider,
190
+ stateUrl,
191
+ region,
192
+ keyPath,
193
+ keyGenerated
194
+ };
195
+ }
196
+ function merge(existing, stackName, provider, keyPath, knownHostsPath, stack) {
197
+ return {
198
+ ...existing ?? {},
199
+ version: 1,
200
+ defaults: { stack: stackName, provider },
201
+ stacks: { ...existing?.stacks ?? {}, [stackName]: stack },
202
+ ssh: { keyPath, knownHostsPath }
203
+ };
204
+ }
205
+
206
+ export {
207
+ deriveStateBucket,
208
+ validateStateBucket,
209
+ resolveScopeAccount,
210
+ SUPPORTED_PROVIDERS,
211
+ PROVIDER_DEFAULTS,
212
+ initStack
213
+ };
@@ -11,22 +11,22 @@ import {
11
11
  readRemoteConfig,
12
12
  restartGateway
13
13
  } from "./chunk-2I7KCMXZ.js";
14
- import {
15
- writeStackConfig
16
- } from "./chunk-3Z5TDXCJ.js";
17
14
  import {
18
15
  validatePlan
19
16
  } from "./chunk-XFN3YDZE.js";
17
+ import {
18
+ writeStackConfig
19
+ } from "./chunk-55UMO37O.js";
20
20
  import {
21
21
  buildContext
22
- } from "./chunk-QDNFADJI.js";
22
+ } from "./chunk-AVERNLUY.js";
23
23
  import {
24
24
  GATEWAY_PORT
25
25
  } from "./chunk-VOEJYRIK.js";
26
26
  import {
27
27
  getConfig,
28
28
  setConfig
29
- } from "./chunk-64PONSA5.js";
29
+ } from "./chunk-GFIR2IIJ.js";
30
30
  import {
31
31
  UsageError
32
32
  } from "./chunk-KGXPLI7W.js";
@@ -54,7 +54,7 @@ ${validation.errors.join("\n")}`
54
54
  });
55
55
  const stack = await ctx.getStack();
56
56
  const { guardPrivateOnlyApply } = await import("./private-only-DIA26I2E.js");
57
- const { probeSsh } = await import("./tailscale-cutover-6YP2QKFM.js");
57
+ const { probeSsh } = await import("./tailscale-cutover-EKAQSDXP.js");
58
58
  await guardPrivateOnlyApply(plan, ctx, opts?.probeTailnet ?? ((conn2) => probeSsh(conn2, opts?.signal)));
59
59
  await writeStackConfig(stack, plan);
60
60
  if (plan.metadata.stackVersion !== void 0) {
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ getProvider
4
+ } from "./chunk-VUFMZEIM.js";
2
5
  import {
3
6
  readLocalState
4
- } from "./chunk-ELU3XQEA.js";
7
+ } from "./chunk-RDUKRIRC.js";
5
8
  import {
6
9
  getConfigDir,
7
10
  requireConfig
8
- } from "./chunk-64PONSA5.js";
11
+ } from "./chunk-GFIR2IIJ.js";
9
12
  import {
10
13
  UsageError
11
14
  } from "./chunk-KGXPLI7W.js";
12
- import {
13
- getProvider
14
- } from "./chunk-VUFMZEIM.js";
15
15
 
16
16
  // src/cli/context.ts
17
17
  function buildContext(args) {
@@ -41,7 +41,7 @@ function buildContext(args) {
41
41
  `Stack "${stackName}" not found in config. Run \`clawops init\` or use \`--stack\` to specify a different stack.`
42
42
  );
43
43
  }
44
- const { getOrCreateStack } = await import("./automation-MKCJ46I6.js");
44
+ const { getOrCreateStack } = await import("./automation-NBYQ6MFZ.js");
45
45
  stackCache = await getOrCreateStack({
46
46
  stack: stackName,
47
47
  stateUrl: stackConfig.stateUrl,
@@ -146,8 +146,8 @@ function renderSnapshot(snap, opts) {
146
146
  return lines.join("\n");
147
147
  }
148
148
  async function probeEntries() {
149
- const { buildContext } = await import("./context-7VI3R6EK.js");
150
- const { getConfig } = await import("./store-OVB3DAJR.js");
149
+ const { buildContext } = await import("./context-WOKSKAIA.js");
150
+ const { getConfig } = await import("./store-YOLQYXMB.js");
151
151
  const config = getConfig();
152
152
  if (!config) return [];
153
153
  return Promise.all(
@@ -215,7 +215,7 @@ function renderMenu(entries, selectedIdx, showAll, noColor, confirmDelete) {
215
215
  return lines.join("\n");
216
216
  }
217
217
  async function deleteFromRegistry(name) {
218
- const { requireConfig, setConfig } = await import("./store-OVB3DAJR.js");
218
+ const { requireConfig, setConfig } = await import("./store-YOLQYXMB.js");
219
219
  const config = requireConfig();
220
220
  const newStacks = { ...config.stacks };
221
221
  delete newStacks[name];
@@ -373,7 +373,7 @@ var monitor_default = defineCommand({
373
373
  const tailLines = Math.max(1, parseInt(String(args.tail ?? "10"), 10) || 10);
374
374
  const isTTY = Boolean(process.stdout.isTTY);
375
375
  const noColor = Boolean(args["no-color"]) || !isTTY;
376
- const { buildContext } = await import("./context-7VI3R6EK.js");
376
+ const { buildContext } = await import("./context-WOKSKAIA.js");
377
377
  const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
378
378
  const ac = new AbortController();
379
379
  process.on("SIGINT", () => ac.abort());
@@ -508,7 +508,7 @@ import { accessSync, mkdirSync, readFileSync, constants } from "fs";
508
508
  import ssh2 from "ssh2";
509
509
  import path from "path";
510
510
  async function runDiagnostics(opts = {}, deps = {}) {
511
- const { getConfig, getConfigDir } = await import("./store-OVB3DAJR.js");
511
+ const { getConfig, getConfigDir } = await import("./store-YOLQYXMB.js");
512
512
  const config = getConfig();
513
513
  const sections = [];
514
514
  sections.push({ title: "Runtime", checks: await runtimeChecks(getConfigDir()) });
@@ -808,8 +808,8 @@ async function deployedVersionCheck(image, deployedVersion) {
808
808
  }
809
809
  async function hardeningChecks(conn, stack, signal) {
810
810
  try {
811
- const { MODULE_CATALOG, resolveModules, withRemoteExec } = await import("./harden-3KKCR7BL.js");
812
- const { buildContext } = await import("./context-7VI3R6EK.js");
811
+ const { MODULE_CATALOG, resolveModules, withRemoteExec } = await import("./harden-RUOUA5BV.js");
812
+ const { buildContext } = await import("./context-WOKSKAIA.js");
813
813
  const provider = buildContext({ stack }).adapter.name;
814
814
  const modules = resolveModules(MODULE_CATALOG, void 0, provider);
815
815
  const checks = [];
@@ -831,8 +831,8 @@ async function hardeningChecks(conn, stack, signal) {
831
831
  }
832
832
  }
833
833
  async function defaultOpenSession(stack, signal) {
834
- const { getConfig } = await import("./store-OVB3DAJR.js");
835
- const { buildContext } = await import("./context-7VI3R6EK.js");
834
+ const { getConfig } = await import("./store-YOLQYXMB.js");
835
+ const { buildContext } = await import("./context-WOKSKAIA.js");
836
836
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
837
837
  const { acquireSession } = await import("./pool-PIIKJ4L2.js");
838
838
  const config = getConfig();
@@ -76,7 +76,9 @@ function getConfig() {
76
76
  function requireConfig() {
77
77
  const cfg = getConfig();
78
78
  if (!cfg) {
79
- throw new UsageError("No config found. Run `clawops init` first.");
79
+ throw new UsageError(
80
+ `No clawops config at ${getConfigPath()}. Create one with \`clawops init\` in a terminal, or by calling the clawops_init tool. Either way it registers a stack \u2014 a provider and a state backend \u2014 and generates an SSH key; nothing is provisioned and nothing is charged.`
81
+ );
80
82
  }
81
83
  return cfg;
82
84
  }
@@ -5,10 +5,10 @@ import {
5
5
  import {
6
6
  buildContext,
7
7
  loadAdapterModule
8
- } from "./chunk-QDNFADJI.js";
8
+ } from "./chunk-AVERNLUY.js";
9
9
  import {
10
10
  getConfig
11
- } from "./chunk-64PONSA5.js";
11
+ } from "./chunk-GFIR2IIJ.js";
12
12
  import {
13
13
  StateError,
14
14
  UsageError
@@ -71,7 +71,7 @@ async function generatePlan(intent, _opts) {
71
71
  "plan/apply is not supported for the local provider. Use `clawops up` directly."
72
72
  );
73
73
  }
74
- const { version } = await import("./package-SAUDFQLG.js");
74
+ const { version } = await import("./package-MF5LPITL.js");
75
75
  const config = getConfig();
76
76
  const instanceType = await resolveInstanceType(intent);
77
77
  const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-XVYW5WFJ.js");
@@ -123,7 +123,7 @@ A plan is only as good as the state it was computed against, so this is not a pl
123
123
  );
124
124
  }
125
125
  try {
126
- const { writeStackConfig } = await import("./stack-config-LUTS2XAD.js");
126
+ const { writeStackConfig } = await import("./stack-config-ICQ5YHUL.js");
127
127
  await writeStackConfig(stack, plan);
128
128
  const outputLines = [];
129
129
  const preview = await stack.preview({
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getConfigDir
4
- } from "./chunk-64PONSA5.js";
4
+ } from "./chunk-GFIR2IIJ.js";
5
5
 
6
6
  // src/providers/local/state.ts
7
7
  import { readFileSync, writeFileSync, mkdirSync, renameSync } from "fs";