@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.
- package/README.md +23 -1
- package/dist/{apply-HJ5GAKHW.js → apply-JARPVA5Y.js} +8 -8
- package/dist/{automation-MKCJ46I6.js → automation-NBYQ6MFZ.js} +1 -1
- package/dist/{bootstrap-NN647RCY.js → bootstrap-3PBVY7MA.js} +2 -2
- package/dist/{chunk-X56CQ6GB.js → chunk-4MHDW2NB.js} +1 -1
- package/dist/{chunk-3Z5TDXCJ.js → chunk-55UMO37O.js} +1 -1
- package/dist/chunk-755X5OGS.js +213 -0
- package/dist/{chunk-R3DTA35B.js → chunk-7T4CIT7C.js} +6 -6
- package/dist/{chunk-QDNFADJI.js → chunk-AVERNLUY.js} +6 -6
- package/dist/{chunk-GFUK6KDR.js → chunk-CPCO6UJK.js} +9 -9
- package/dist/{chunk-64PONSA5.js → chunk-GFIR2IIJ.js} +3 -1
- package/dist/{chunk-YEBFVLOE.js → chunk-PTCVFLZ3.js} +4 -4
- package/dist/{chunk-ELU3XQEA.js → chunk-RDUKRIRC.js} +1 -1
- package/dist/{chunk-OFTAXLOY.js → chunk-TYHTISI5.js} +239 -159
- package/dist/cli.js +127 -274
- package/dist/{context-7VI3R6EK.js → context-WOKSKAIA.js} +5 -5
- package/dist/{flows-RI7FGHAM.js → flows-HGJIA6EL.js} +7 -7
- package/dist/{generate-JNJ63ZYZ.js → generate-NE54BATS.js} +6 -6
- package/dist/{harden-3KKCR7BL.js → harden-RUOUA5BV.js} +2 -2
- package/dist/init-HOPXDNWY.js +13 -0
- package/dist/{mcp-wire-B57DUNOD.js → mcp-wire-FIO5DFBY.js} +11 -11
- package/dist/{package-SAUDFQLG.js → package-MF5LPITL.js} +1 -1
- package/dist/{server-RHON6QMN.js → server-OXWHBTXK.js} +11 -11
- package/dist/{stack-config-LUTS2XAD.js → stack-config-ICQ5YHUL.js} +2 -2
- package/dist/{state-AOAYLYY5.js → state-AKZP2S4T.js} +2 -2
- package/dist/{store-OVB3DAJR.js → store-YOLQYXMB.js} +1 -1
- package/dist/{tailscale-cutover-6YP2QKFM.js → tailscale-cutover-EKAQSDXP.js} +2 -2
- package/package.json +1 -1
- package/spec/mcp-tools.yaml +69 -28
- package/dist/{chunk-BELTQGHT.js → chunk-CHNSL3TC.js} +3 -3
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
monitor_default,
|
|
9
9
|
runDiagnostics
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CPCO6UJK.js";
|
|
11
11
|
import {
|
|
12
12
|
GATEWAY_LOGS_PROBE,
|
|
13
13
|
chooseLogSource,
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
execPrivilegedWithInput,
|
|
21
21
|
streamPrivileged
|
|
22
22
|
} from "./chunk-NUDIHZEH.js";
|
|
23
|
+
import "./chunk-CHNSL3TC.js";
|
|
23
24
|
import {
|
|
24
25
|
resolveSpecDir
|
|
25
26
|
} from "./chunk-JW5A7E3X.js";
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
interpretProbe,
|
|
28
29
|
probeCommand
|
|
29
30
|
} from "./chunk-LT4XMO5D.js";
|
|
30
|
-
import "./chunk-
|
|
31
|
+
import "./chunk-VUFMZEIM.js";
|
|
31
32
|
import {
|
|
32
33
|
CONFIG_FILENAME,
|
|
33
34
|
CONTAINER_UID,
|
|
@@ -58,110 +59,29 @@ import {
|
|
|
58
59
|
} from "./chunk-Q7NQY5HV.js";
|
|
59
60
|
import "./chunk-N4WZLWJ4.js";
|
|
60
61
|
import {
|
|
61
|
-
|
|
62
|
+
SUPPORTED_PROVIDERS,
|
|
63
|
+
deriveStateBucket,
|
|
64
|
+
initStack,
|
|
65
|
+
resolveScopeAccount,
|
|
66
|
+
validateStateBucket
|
|
67
|
+
} from "./chunk-755X5OGS.js";
|
|
68
|
+
import {
|
|
62
69
|
getConfigDir,
|
|
63
70
|
requireConfig,
|
|
64
71
|
setConfig
|
|
65
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-GFIR2IIJ.js";
|
|
66
73
|
import {
|
|
67
74
|
ClawopsError,
|
|
68
75
|
UsageError
|
|
69
76
|
} from "./chunk-KGXPLI7W.js";
|
|
70
|
-
import "./chunk-VUFMZEIM.js";
|
|
71
77
|
|
|
72
78
|
// src/cli/index.ts
|
|
73
79
|
import { defineCommand as defineCommand24, runMain } from "citty";
|
|
74
80
|
|
|
75
81
|
// src/cli/commands/init.ts
|
|
76
82
|
import { defineCommand } from "citty";
|
|
77
|
-
import { spawnSync } from "child_process";
|
|
78
|
-
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
79
83
|
import path from "path";
|
|
80
84
|
import process2 from "process";
|
|
81
|
-
|
|
82
|
-
// src/providers/state-bucket.ts
|
|
83
|
-
function deriveStateBucket(provider, scope) {
|
|
84
|
-
if (provider === "azure") {
|
|
85
|
-
return { ok: true, name: "clawops-state" };
|
|
86
|
-
}
|
|
87
|
-
if (!scope.account) {
|
|
88
|
-
return {
|
|
89
|
-
ok: false,
|
|
90
|
-
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"
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
if (provider === "gcp") return { ok: true, name: `clawops-state-${scope.account}` };
|
|
94
|
-
if (!scope.region) return { ok: false, needs: "a region" };
|
|
95
|
-
return { ok: true, name: `clawops-state-${scope.account}-${scope.region}` };
|
|
96
|
-
}
|
|
97
|
-
function stateUrlFor(provider, bucket) {
|
|
98
|
-
const verdict = validateStateBucket(provider, bucket);
|
|
99
|
-
if (verdict !== true) throw new Error(`Not a usable ${provider} state backend name: ${verdict}`);
|
|
100
|
-
const scheme = provider === "aws" ? "s3://" : provider === "gcp" ? "gs://" : "azblob://";
|
|
101
|
-
return provider === "azure" ? `${scheme}${bucket}` : `${scheme}${bucket}/clawops`;
|
|
102
|
-
}
|
|
103
|
-
var IPV4 = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
|
104
|
-
function validateStateBucket(provider, name) {
|
|
105
|
-
const n = name.trim();
|
|
106
|
-
if (n === "") return "Required";
|
|
107
|
-
if (n !== name) return "No leading or trailing spaces";
|
|
108
|
-
if (n.length < 3) return `Too short \u2014 ${labelFor(provider)} names are at least 3 characters`;
|
|
109
|
-
const dotted = provider === "gcp" && n.includes(".");
|
|
110
|
-
const max = dotted ? 222 : 63;
|
|
111
|
-
if (n.length > max) return `Too long \u2014 ${labelFor(provider)} names are at most ${max} characters`;
|
|
112
|
-
if (dotted && n.split(".").some((part) => part.length > 63)) {
|
|
113
|
-
return "Each dot-separated part of a Cloud Storage name is at most 63 characters";
|
|
114
|
-
}
|
|
115
|
-
if (n !== n.toLowerCase()) return "Lowercase only";
|
|
116
|
-
if (provider === "azure") {
|
|
117
|
-
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(n)) {
|
|
118
|
-
return "Letters, digits and single hyphens only, starting and ending with a letter or digit";
|
|
119
|
-
}
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
if (!/^[a-z0-9][a-z0-9._-]*[a-z0-9]$/.test(n)) {
|
|
123
|
-
return "Letters, digits, dots, hyphens and underscores only, starting and ending with a letter or digit";
|
|
124
|
-
}
|
|
125
|
-
if (n.includes("..")) return "No consecutive dots";
|
|
126
|
-
if (IPV4.test(n)) return "Cannot be formatted as an IP address";
|
|
127
|
-
if (provider === "aws") {
|
|
128
|
-
if (n.startsWith("xn--")) return 'Cannot start with "xn--"';
|
|
129
|
-
if (n.startsWith("sthree-")) return 'Cannot start with "sthree-"';
|
|
130
|
-
if (n.endsWith("-s3alias")) return 'Cannot end with "-s3alias"';
|
|
131
|
-
if (n.endsWith("--ol-s3")) return 'Cannot end with "--ol-s3"';
|
|
132
|
-
if (n.includes("_")) return "Underscores are not allowed in S3 bucket names";
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
if (n.startsWith("goog")) return 'Cannot start with "goog"';
|
|
136
|
-
if (n.includes("google")) return 'Cannot contain "google"';
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
function labelFor(provider) {
|
|
140
|
-
if (provider === "aws") return "S3 bucket";
|
|
141
|
-
if (provider === "gcp") return "Cloud Storage bucket";
|
|
142
|
-
return "blob container";
|
|
143
|
-
}
|
|
144
|
-
async function resolveScopeAccount(provider, signal) {
|
|
145
|
-
if (provider === "azure") return void 0;
|
|
146
|
-
try {
|
|
147
|
-
if (provider === "aws") {
|
|
148
|
-
const { callerAccount } = await import("./preflight-TFD4N5ZU.js");
|
|
149
|
-
return await callerAccount(signal);
|
|
150
|
-
}
|
|
151
|
-
const { resolveProjectId } = await import("./preflight-IE2YWBAC.js");
|
|
152
|
-
return resolveProjectId();
|
|
153
|
-
} catch {
|
|
154
|
-
return void 0;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// src/cli/commands/init.ts
|
|
159
|
-
var SUPPORTED_PROVIDERS = ["gcp", "aws", "azure", "local"];
|
|
160
|
-
var PROVIDER_DEFAULTS = {
|
|
161
|
-
gcp: { region: "us-central1", credEnv: "GOOGLE_APPLICATION_CREDENTIALS", stateScheme: "gs://" },
|
|
162
|
-
aws: { region: "us-east-1", credEnv: "AWS_PROFILE", stateScheme: "s3://" },
|
|
163
|
-
azure: { region: "eastus", credEnv: "AZURE_CLIENT_ID", stateScheme: "azblob://" }
|
|
164
|
-
};
|
|
165
85
|
var init_default = defineCommand({
|
|
166
86
|
meta: {
|
|
167
87
|
name: "init",
|
|
@@ -183,8 +103,6 @@ var init_default = defineCommand({
|
|
|
183
103
|
async run({ args }) {
|
|
184
104
|
const nonInteractive = Boolean(args["non-interactive"]);
|
|
185
105
|
const providerArg = typeof args.provider === "string" ? args.provider : null;
|
|
186
|
-
const stackName = typeof args.stack === "string" ? args.stack : "default";
|
|
187
|
-
const forceOverwrite = Boolean(args.force);
|
|
188
106
|
if (nonInteractive && !providerArg) {
|
|
189
107
|
throw new UsageError(
|
|
190
108
|
"--non-interactive requires --provider. Example: clawops init --provider gcp --non-interactive"
|
|
@@ -196,107 +114,42 @@ var init_default = defineCommand({
|
|
|
196
114
|
`Unsupported provider: ${provider}. Supported: ${SUPPORTED_PROVIDERS.join(", ")}`
|
|
197
115
|
);
|
|
198
116
|
}
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (!
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
info("Generating SSH key pair...");
|
|
215
|
-
const gen = spawnSync(
|
|
216
|
-
"ssh-keygen",
|
|
217
|
-
["-t", "ed25519", "-f", keyPath, "-N", "", "-C", "clawops", "-q"],
|
|
218
|
-
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
219
|
-
);
|
|
220
|
-
if (gen.error || gen.status !== 0) {
|
|
221
|
-
const reason = gen.error?.message ?? gen.stderr?.toString().trim() ?? `exit ${gen.status}`;
|
|
222
|
-
throw new UsageError(
|
|
223
|
-
`Could not generate an SSH key: ${reason}
|
|
224
|
-
Generate one yourself and point clawops at it:
|
|
225
|
-
ssh-keygen -t ed25519 -f ${keyPath} -N '' -C clawops
|
|
226
|
-
clawops init --key-path ${keyPath}`
|
|
227
|
-
);
|
|
117
|
+
const result = await initStack({
|
|
118
|
+
provider,
|
|
119
|
+
stackName: typeof args.stack === "string" ? args.stack : void 0,
|
|
120
|
+
stateUrl: typeof args.state === "string" ? args.state : void 0,
|
|
121
|
+
region: typeof args.region === "string" ? args.region : void 0,
|
|
122
|
+
force: Boolean(args.force),
|
|
123
|
+
keyPath: typeof args["key-path"] === "string" ? args["key-path"] : void 0,
|
|
124
|
+
host: typeof args.host === "string" ? args.host : void 0,
|
|
125
|
+
sshUser: typeof args["ssh-user"] === "string" ? args["ssh-user"] : void 0,
|
|
126
|
+
sshPort: typeof args["ssh-port"] === "string" ? parseInt(args["ssh-port"], 10) : void 0
|
|
127
|
+
});
|
|
128
|
+
if (!result.ok) {
|
|
129
|
+
if (result.reason.includes("already exists")) {
|
|
130
|
+
failure(result.reason.replace("Pass force", "Use --force"));
|
|
131
|
+
process2.exit(1);
|
|
228
132
|
}
|
|
229
|
-
|
|
230
|
-
} else {
|
|
231
|
-
info(`Using existing SSH key at ${keyPath}`);
|
|
133
|
+
throw new UsageError(result.reason);
|
|
232
134
|
}
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
let config;
|
|
237
|
-
if (provider === "local") {
|
|
238
|
-
const host = typeof args.host === "string" ? args.host : "";
|
|
239
|
-
if (!host) {
|
|
240
|
-
throw new UsageError("--host is required for the local provider");
|
|
241
|
-
}
|
|
242
|
-
const sshUser = typeof args["ssh-user"] === "string" ? args["ssh-user"] : "root";
|
|
243
|
-
const sshPort = typeof args["ssh-port"] === "string" ? parseInt(args["ssh-port"], 10) : 22;
|
|
244
|
-
config = merge(existing, stackName, provider, keyPath, knownHostsPath, {
|
|
245
|
-
provider,
|
|
246
|
-
stateUrl: "file://~/.clawops/state",
|
|
247
|
-
credentialsRef: { source: "file", envVars: [] },
|
|
248
|
-
localOpts: { host, sshUser, sshPort, sshKeyPath: keyPath }
|
|
249
|
-
});
|
|
250
|
-
} else {
|
|
251
|
-
const defaults = PROVIDER_DEFAULTS[provider];
|
|
252
|
-
const region = typeof args.region === "string" ? args.region : defaults.region;
|
|
253
|
-
let stateUrl;
|
|
254
|
-
if (typeof args.state === "string") {
|
|
255
|
-
stateUrl = args.state;
|
|
256
|
-
} else {
|
|
257
|
-
const account = await resolveScopeAccount(provider);
|
|
258
|
-
const derived = deriveStateBucket(provider, { account, region });
|
|
259
|
-
if (!derived.ok) {
|
|
260
|
-
throw new UsageError(
|
|
261
|
-
`Could not name a state backend for ${provider}: clawops needs ${derived.needs}.
|
|
262
|
-
Authenticate and run this again, or name the backend yourself:
|
|
263
|
-
clawops init --provider ${provider} --state ${defaults.stateScheme}your-bucket/clawops`
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
stateUrl = stateUrlFor(provider, derived.name);
|
|
267
|
-
}
|
|
268
|
-
config = merge(existing, stackName, provider, keyPath, knownHostsPath, {
|
|
269
|
-
provider,
|
|
270
|
-
stateUrl,
|
|
271
|
-
region,
|
|
272
|
-
credentialsRef: { source: "env", envVars: [defaults.credEnv] }
|
|
273
|
-
});
|
|
135
|
+
if (result.keyGenerated) success(`SSH key pair written to ${result.keyPath} (and ${result.keyPath}.pub)`);
|
|
136
|
+
else info(`Using existing SSH key at ${result.keyPath}`);
|
|
137
|
+
if (result.provider !== "local") {
|
|
274
138
|
process2.stdout.write("\n");
|
|
275
139
|
info(
|
|
276
|
-
`State backend: ${stateUrl}
|
|
277
|
-
clawops doctor --provider ${provider} checks it exists, and clawops setup offers to create it.`
|
|
140
|
+
`State backend: ${result.stateUrl}
|
|
141
|
+
clawops doctor --provider ${result.provider} checks it exists, and clawops setup offers to create it.`
|
|
278
142
|
);
|
|
279
143
|
process2.stdout.write("\n");
|
|
280
|
-
success(`Provider: ${provider} Region: ${region} Stack: ${stackName}`);
|
|
281
|
-
|
|
282
|
-
success(`Config written to ${path.join(configDir, "config.json")}`);
|
|
144
|
+
success(`Provider: ${result.provider} Region: ${result.region} Stack: ${result.stackName}`);
|
|
145
|
+
success(`Config written to ${path.join(getConfigDir(), "config.json")}`);
|
|
283
146
|
return;
|
|
284
147
|
}
|
|
285
|
-
|
|
286
|
-
success(`Config written to ${path.join(configDir, "config.json")}`);
|
|
148
|
+
success(`Config written to ${result.configPath}`);
|
|
287
149
|
process2.stdout.write("\n");
|
|
288
|
-
success(`Provider: ${provider} Stack: ${stackName}`);
|
|
150
|
+
success(`Provider: ${result.provider} Stack: ${result.stackName}`);
|
|
289
151
|
}
|
|
290
152
|
});
|
|
291
|
-
function merge(existing, stackName, provider, keyPath, knownHostsPath, stack) {
|
|
292
|
-
return {
|
|
293
|
-
...existing ?? {},
|
|
294
|
-
version: 1,
|
|
295
|
-
defaults: { stack: stackName, provider },
|
|
296
|
-
stacks: { ...existing?.stacks ?? {}, [stackName]: stack },
|
|
297
|
-
ssh: { keyPath, knownHostsPath }
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
153
|
|
|
301
154
|
// src/cli/commands/up.ts
|
|
302
155
|
import { defineCommand as defineCommand2 } from "citty";
|
|
@@ -339,7 +192,7 @@ var up_default = defineCommand2({
|
|
|
339
192
|
"publish-gateway": { type: "string", description: 'loopback (default) or all. "all" serves plaintext HTTP \u2014 put TLS in front of it' }
|
|
340
193
|
},
|
|
341
194
|
async run({ args }) {
|
|
342
|
-
const { buildContext } = await import("./context-
|
|
195
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
343
196
|
const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-XVYW5WFJ.js");
|
|
344
197
|
const ctx = buildContext(args);
|
|
345
198
|
const requestedVersion = typeof args["openclaw-version"] === "string" ? args["openclaw-version"] : await defaultOpenclawVersion();
|
|
@@ -352,7 +205,7 @@ var up_default = defineCommand2({
|
|
|
352
205
|
);
|
|
353
206
|
}
|
|
354
207
|
const { localOpts } = stackConfig;
|
|
355
|
-
const { localBootstrap } = await import("./bootstrap-
|
|
208
|
+
const { localBootstrap } = await import("./bootstrap-3PBVY7MA.js");
|
|
356
209
|
const gatewayPort = parseGatewayPort(args["gateway-port"]);
|
|
357
210
|
const abortController2 = new AbortController();
|
|
358
211
|
process3.on("SIGINT", () => abortController2.abort());
|
|
@@ -392,8 +245,8 @@ var up_default = defineCommand2({
|
|
|
392
245
|
}
|
|
393
246
|
return;
|
|
394
247
|
}
|
|
395
|
-
const { generatePlan } = await import("./generate-
|
|
396
|
-
const { applyPlan } = await import("./apply-
|
|
248
|
+
const { generatePlan } = await import("./generate-NE54BATS.js");
|
|
249
|
+
const { applyPlan } = await import("./apply-JARPVA5Y.js");
|
|
397
250
|
const { resolveNetworkFlags } = await import("./network-args-PYJRJBYO.js");
|
|
398
251
|
const { detectEgressIp } = await import("./firewall-5VKZ4WBH.js");
|
|
399
252
|
const isDryRun = Boolean(args["dry-run"]);
|
|
@@ -523,7 +376,7 @@ var down_default = defineCommand3({
|
|
|
523
376
|
"dry-run": { type: "boolean", description: "Show what would be destroyed without destroying" }
|
|
524
377
|
},
|
|
525
378
|
async run({ args }) {
|
|
526
|
-
const { buildContext } = await import("./context-
|
|
379
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
527
380
|
const ctx = buildContext(args);
|
|
528
381
|
if (args["dry-run"]) {
|
|
529
382
|
info(`Dry run \u2014 would destroy stack "${ctx.stackName}"`);
|
|
@@ -587,7 +440,7 @@ var status_default = defineCommand4({
|
|
|
587
440
|
json: { type: "boolean", description: "Emit JSON" }
|
|
588
441
|
},
|
|
589
442
|
async run({ args }) {
|
|
590
|
-
const { buildContext } = await import("./context-
|
|
443
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
591
444
|
const ctx = buildContext(args);
|
|
592
445
|
if (ctx.adapter.name === "local") {
|
|
593
446
|
const state = ctx.localState;
|
|
@@ -643,7 +496,7 @@ var status_default = defineCommand4({
|
|
|
643
496
|
// src/cli/commands/plan.ts
|
|
644
497
|
import { defineCommand as defineCommand5 } from "citty";
|
|
645
498
|
import process7 from "process";
|
|
646
|
-
import { writeFileSync
|
|
499
|
+
import { writeFileSync } from "fs";
|
|
647
500
|
import { isAbsolute } from "path";
|
|
648
501
|
function disruptionWarnings(diff) {
|
|
649
502
|
const out = [];
|
|
@@ -687,8 +540,8 @@ var plan_default = defineCommand5({
|
|
|
687
540
|
"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" }
|
|
688
541
|
},
|
|
689
542
|
async run({ args }) {
|
|
690
|
-
const { buildContext } = await import("./context-
|
|
691
|
-
const { generatePlan } = await import("./generate-
|
|
543
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
544
|
+
const { generatePlan } = await import("./generate-NE54BATS.js");
|
|
692
545
|
const { resolveNetworkFlags } = await import("./network-args-PYJRJBYO.js");
|
|
693
546
|
const { detectEgressIp } = await import("./firewall-5VKZ4WBH.js");
|
|
694
547
|
const ctx = buildContext(args);
|
|
@@ -713,7 +566,7 @@ var plan_default = defineCommand5({
|
|
|
713
566
|
let network = resolved;
|
|
714
567
|
if (args["private-only"]) {
|
|
715
568
|
const { privateOnlyNetwork, assertTailnetReachable } = await import("./private-only-DIA26I2E.js");
|
|
716
|
-
const { probeSsh } = await import("./tailscale-cutover-
|
|
569
|
+
const { probeSsh } = await import("./tailscale-cutover-EKAQSDXP.js");
|
|
717
570
|
network = privateOnlyNetwork(flags, resolved, ctx.config.stacks[ctx.stackName]?.tailscale, ctx.stackName);
|
|
718
571
|
await assertTailnetReachable(ctx, (conn) => probeSsh(conn));
|
|
719
572
|
}
|
|
@@ -741,7 +594,7 @@ var plan_default = defineCommand5({
|
|
|
741
594
|
}
|
|
742
595
|
const planJson = JSON.stringify(plan, null, 2);
|
|
743
596
|
if (outPath) {
|
|
744
|
-
|
|
597
|
+
writeFileSync(outPath, planJson + "\n", "utf-8");
|
|
745
598
|
success(`Plan written to ${outPath}`);
|
|
746
599
|
} else {
|
|
747
600
|
process7.stdout.write(planJson + "\n");
|
|
@@ -804,7 +657,7 @@ var apply_default = defineCommand6({
|
|
|
804
657
|
},
|
|
805
658
|
async run({ args }) {
|
|
806
659
|
const { validatePlan } = await import("./validate-JXKN5WD6.js");
|
|
807
|
-
const { applyPlan } = await import("./apply-
|
|
660
|
+
const { applyPlan } = await import("./apply-JARPVA5Y.js");
|
|
808
661
|
const planPath = args._?.[0];
|
|
809
662
|
if (!planPath) {
|
|
810
663
|
throw new UsageError("Usage: clawops apply <plan.json>");
|
|
@@ -930,7 +783,7 @@ var destroy_default = defineCommand7({
|
|
|
930
783
|
"dry-run": { type: "boolean", description: "Show what would be destroyed without destroying" }
|
|
931
784
|
},
|
|
932
785
|
async run({ args }) {
|
|
933
|
-
const { buildContext } = await import("./context-
|
|
786
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
934
787
|
const ctx = buildContext(args);
|
|
935
788
|
if (ctx.adapter.name === "local") {
|
|
936
789
|
throw new UsageError(
|
|
@@ -1023,7 +876,7 @@ var ssh_default = defineCommand8({
|
|
|
1023
876
|
command: { type: "string", description: "Remote command to run (instead of interactive shell)" }
|
|
1024
877
|
},
|
|
1025
878
|
async run({ args }) {
|
|
1026
|
-
const { buildContext } = await import("./context-
|
|
879
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
1027
880
|
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1028
881
|
const ctx = buildContext(args);
|
|
1029
882
|
let conn;
|
|
@@ -1110,7 +963,7 @@ var tunnel_default = defineCommand9({
|
|
|
1110
963
|
"no-open": { type: "boolean", description: "Do not open browser after tunnel is ready" }
|
|
1111
964
|
},
|
|
1112
965
|
async run({ args }) {
|
|
1113
|
-
const { buildContext } = await import("./context-
|
|
966
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
1114
967
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1115
968
|
const { acquireSession } = await import("./pool-PIIKJ4L2.js");
|
|
1116
969
|
const ctx = buildContext(args);
|
|
@@ -1179,7 +1032,7 @@ var logs_default = defineCommand10({
|
|
|
1179
1032
|
json: { type: "boolean", description: "Emit the gateway's structured JSON log lines" }
|
|
1180
1033
|
},
|
|
1181
1034
|
async run({ args }) {
|
|
1182
|
-
const { buildContext } = await import("./context-
|
|
1035
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
1183
1036
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1184
1037
|
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1185
1038
|
const ctx = buildContext(args);
|
|
@@ -1285,7 +1138,7 @@ var config_default = defineCommand11({
|
|
|
1285
1138
|
"dry-run": { type: "boolean", description: "Show what would change without writing" }
|
|
1286
1139
|
},
|
|
1287
1140
|
async run({ args }) {
|
|
1288
|
-
const { buildContext } = await import("./context-
|
|
1141
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
1289
1142
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1290
1143
|
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1291
1144
|
const [action, key, value] = args._ ?? [];
|
|
@@ -1420,7 +1273,7 @@ var agents_default = defineCommand12({
|
|
|
1420
1273
|
cursor: { type: "string", description: "Continue from a previous `logs` result cursor" }
|
|
1421
1274
|
},
|
|
1422
1275
|
async run({ args }) {
|
|
1423
|
-
const { buildContext } = await import("./context-
|
|
1276
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
1424
1277
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1425
1278
|
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1426
1279
|
const [action, name] = args._ ?? [];
|
|
@@ -1652,7 +1505,7 @@ var migrate_default = defineCommand13({
|
|
|
1652
1505
|
yes: { type: "boolean", description: "Skip the confirmation prompt" }
|
|
1653
1506
|
},
|
|
1654
1507
|
async run({ args }) {
|
|
1655
|
-
const { buildContext } = await import("./context-
|
|
1508
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
1656
1509
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1657
1510
|
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1658
1511
|
const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-XVYW5WFJ.js");
|
|
@@ -1791,7 +1644,7 @@ var gateway_default = defineCommand14({
|
|
|
1791
1644
|
json: { type: "boolean", description: "Emit JSON (for status)" }
|
|
1792
1645
|
},
|
|
1793
1646
|
async run({ args }) {
|
|
1794
|
-
const { buildContext } = await import("./context-
|
|
1647
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
1795
1648
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1796
1649
|
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1797
1650
|
const [action, versionArg] = args._ ?? [];
|
|
@@ -2023,7 +1876,7 @@ var backup_default = defineCommand15({
|
|
|
2023
1876
|
yes: { type: "boolean", description: "[restore] Skip confirmation prompt" }
|
|
2024
1877
|
},
|
|
2025
1878
|
async run({ args }) {
|
|
2026
|
-
const { buildContext } = await import("./context-
|
|
1879
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
2027
1880
|
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
2028
1881
|
const action = args.action;
|
|
2029
1882
|
if (action !== "create" && action !== "restore") {
|
|
@@ -2234,7 +2087,7 @@ var stacks_default = defineCommand16({
|
|
|
2234
2087
|
let isDeployed = false;
|
|
2235
2088
|
if (!args.force) {
|
|
2236
2089
|
try {
|
|
2237
|
-
const { buildContext } = await import("./context-
|
|
2090
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
2238
2091
|
const ctx = buildContext({ stack: name });
|
|
2239
2092
|
if (ctx.adapter.name === "local") {
|
|
2240
2093
|
isDeployed = !!ctx.localState;
|
|
@@ -2375,7 +2228,7 @@ var serveCmd = defineCommand18({
|
|
|
2375
2228
|
inspector: { type: "boolean", description: "Enable MCP inspector" }
|
|
2376
2229
|
},
|
|
2377
2230
|
async run({ args }) {
|
|
2378
|
-
const { serveMcp } = await import("./server-
|
|
2231
|
+
const { serveMcp } = await import("./server-OXWHBTXK.js");
|
|
2379
2232
|
await serveMcp({
|
|
2380
2233
|
port: args.http ? Number(args.http) : void 0,
|
|
2381
2234
|
bind: args.bind,
|
|
@@ -2453,11 +2306,11 @@ var wireCmd = defineCommand18({
|
|
|
2453
2306
|
rewire: { type: "boolean", description: "Replace an existing clawops entry" }
|
|
2454
2307
|
},
|
|
2455
2308
|
async run({ args }) {
|
|
2456
|
-
const { buildContext } = await import("./context-
|
|
2309
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
2457
2310
|
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
2458
2311
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
2459
|
-
const { wireGatewayMcp } = await import("./mcp-wire-
|
|
2460
|
-
const { MCP_HTTP_PORT } = await import("./server-
|
|
2312
|
+
const { wireGatewayMcp } = await import("./mcp-wire-FIO5DFBY.js");
|
|
2313
|
+
const { MCP_HTTP_PORT } = await import("./server-OXWHBTXK.js");
|
|
2461
2314
|
const ac = new AbortController();
|
|
2462
2315
|
process20.on("SIGINT", () => {
|
|
2463
2316
|
ac.abort();
|
|
@@ -2557,8 +2410,8 @@ import { defineCommand as defineCommand19 } from "citty";
|
|
|
2557
2410
|
import process21 from "process";
|
|
2558
2411
|
import os from "os";
|
|
2559
2412
|
import { randomBytes } from "crypto";
|
|
2560
|
-
import { readFileSync as readFileSync3, writeFileSync as
|
|
2561
|
-
import { execSync, spawnSync
|
|
2413
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync, mkdirSync } from "fs";
|
|
2414
|
+
import { execSync, spawnSync, spawn as spawn2 } from "child_process";
|
|
2562
2415
|
import path2 from "path";
|
|
2563
2416
|
var setup_default = defineCommand19({
|
|
2564
2417
|
meta: {
|
|
@@ -2617,7 +2470,7 @@ var setup_default = defineCommand19({
|
|
|
2617
2470
|
} else {
|
|
2618
2471
|
provider = "local";
|
|
2619
2472
|
info("We'll connect to your server over SSH to install and configure OpenClaw.\n");
|
|
2620
|
-
if (!
|
|
2473
|
+
if (!existsSync(localKeyPath)) {
|
|
2621
2474
|
info(`No SSH key found at ${localKeyPath}.`);
|
|
2622
2475
|
const { genKey } = await inquirer.prompt([{
|
|
2623
2476
|
type: "confirm",
|
|
@@ -2653,7 +2506,7 @@ var setup_default = defineCommand19({
|
|
|
2653
2506
|
name: "keyPath",
|
|
2654
2507
|
message: `SSH private key file: (the key file on your computer used to log in \u2014 e.g. ~/.ssh/id_ed25519)`,
|
|
2655
2508
|
default: localKeyPath,
|
|
2656
|
-
validate: (v) =>
|
|
2509
|
+
validate: (v) => existsSync(v.trim()) || `File not found: ${v.trim()}`
|
|
2657
2510
|
}
|
|
2658
2511
|
]);
|
|
2659
2512
|
localHost = localAnswers.host;
|
|
@@ -2875,10 +2728,10 @@ var setup_default = defineCommand19({
|
|
|
2875
2728
|
}
|
|
2876
2729
|
const gatewayToken = randomBytes(24).toString("hex");
|
|
2877
2730
|
const secretsDir2 = path2.join(os.homedir(), ".clawops", "secrets");
|
|
2878
|
-
|
|
2879
|
-
|
|
2731
|
+
mkdirSync(secretsDir2, { recursive: true });
|
|
2732
|
+
spawnSync("chmod", ["700", secretsDir2], { stdio: "ignore" });
|
|
2880
2733
|
const tokenPath = path2.join(secretsDir2, `GATEWAY_TOKEN_${stackAnswers.stackName}`);
|
|
2881
|
-
|
|
2734
|
+
writeFileSync2(tokenPath, gatewayToken, { encoding: "utf-8", mode: 384 });
|
|
2882
2735
|
const openclawConfigOverlay = {
|
|
2883
2736
|
models: builtModelConfig,
|
|
2884
2737
|
gateway: { auth: { mode: "token", token: gatewayToken } }
|
|
@@ -2889,7 +2742,7 @@ var setup_default = defineCommand19({
|
|
|
2889
2742
|
let outputPath;
|
|
2890
2743
|
if (provider === "local") {
|
|
2891
2744
|
outputPath = path2.join(outDir, `openclaw-${stackAnswers.stackName}.json`);
|
|
2892
|
-
|
|
2745
|
+
writeFileSync2(outputPath, JSON.stringify(openclawConfigOverlay, null, 2), "utf-8");
|
|
2893
2746
|
process21.stdout.write("\n");
|
|
2894
2747
|
success(`Config file saved to ${outputPath}`);
|
|
2895
2748
|
} else {
|
|
@@ -2935,7 +2788,7 @@ var setup_default = defineCommand19({
|
|
|
2935
2788
|
}
|
|
2936
2789
|
};
|
|
2937
2790
|
outputPath = path2.join(outDir, `clawops-${stackAnswers.stackName}-plan.json`);
|
|
2938
|
-
|
|
2791
|
+
writeFileSync2(outputPath, JSON.stringify(plan, null, 2), "utf-8");
|
|
2939
2792
|
process21.stdout.write("\n");
|
|
2940
2793
|
success(`Deployment plan saved to ${outputPath}`);
|
|
2941
2794
|
}
|
|
@@ -3040,7 +2893,7 @@ var setup_default = defineCommand19({
|
|
|
3040
2893
|
inquirer
|
|
3041
2894
|
});
|
|
3042
2895
|
} else {
|
|
3043
|
-
const { getConfigDir: getConfigDir2 } = await import("./store-
|
|
2896
|
+
const { getConfigDir: getConfigDir2 } = await import("./store-YOLQYXMB.js");
|
|
3044
2897
|
const knownHostsPath = path2.join(getConfigDir2(), "known_hosts");
|
|
3045
2898
|
process21.stdout.write("\n");
|
|
3046
2899
|
info("To deploy later, run these two commands:");
|
|
@@ -3057,7 +2910,7 @@ var setup_default = defineCommand19({
|
|
|
3057
2910
|
default: false
|
|
3058
2911
|
}]);
|
|
3059
2912
|
if (applyNow) {
|
|
3060
|
-
const { applyPlan } = await import("./apply-
|
|
2913
|
+
const { applyPlan } = await import("./apply-JARPVA5Y.js");
|
|
3061
2914
|
const plan = JSON.parse(readFileSync3(outputPath, "utf-8"));
|
|
3062
2915
|
try {
|
|
3063
2916
|
await applyPlan(plan, {
|
|
@@ -3085,13 +2938,13 @@ To deploy later: clawops apply ${outputPath}`);
|
|
|
3085
2938
|
}
|
|
3086
2939
|
});
|
|
3087
2940
|
async function runLocalDeploy(opts) {
|
|
3088
|
-
const { setConfig: setConfig2, getConfig
|
|
3089
|
-
const { localBootstrap } = await import("./bootstrap-
|
|
2941
|
+
const { setConfig: setConfig2, getConfig, getConfigDir: getConfigDir2 } = await import("./store-YOLQYXMB.js");
|
|
2942
|
+
const { localBootstrap } = await import("./bootstrap-3PBVY7MA.js");
|
|
3090
2943
|
const { connect } = await import("./ssh-VAI5DU5M.js");
|
|
3091
2944
|
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-3HZLUWEA.js");
|
|
3092
2945
|
const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
|
|
3093
2946
|
const knownHostsPath = path2.join(getConfigDir2(), "known_hosts");
|
|
3094
|
-
const existing =
|
|
2947
|
+
const existing = getConfig();
|
|
3095
2948
|
const stackEntry = {
|
|
3096
2949
|
provider: "local",
|
|
3097
2950
|
stateUrl: "file://~/.clawops/state",
|
|
@@ -3229,7 +3082,7 @@ ${cfg.loginHint}`);
|
|
|
3229
3082
|
}]);
|
|
3230
3083
|
if (runNow) {
|
|
3231
3084
|
process21.stdout.write("\n");
|
|
3232
|
-
const result =
|
|
3085
|
+
const result = spawnSync(cfg.loginCmd[0], cfg.loginCmd.slice(1), { stdio: "inherit" });
|
|
3233
3086
|
if (result.error) {
|
|
3234
3087
|
failure(`Could not launch ${cliName} \u2014 is it installed? (${result.error.message})`);
|
|
3235
3088
|
info(`Install guide: ${cliInstallUrl(provider)}`);
|
|
@@ -3273,10 +3126,10 @@ async function promptSecret(name, label, envDefault, inquirer) {
|
|
|
3273
3126
|
message: `Paste your ${label}: (input is hidden)`,
|
|
3274
3127
|
validate: (v) => v.trim() !== "" || "Value cannot be empty"
|
|
3275
3128
|
}]);
|
|
3276
|
-
|
|
3277
|
-
|
|
3129
|
+
mkdirSync(secretsDir2, { recursive: true });
|
|
3130
|
+
spawnSync("chmod", ["700", secretsDir2], { stdio: "ignore" });
|
|
3278
3131
|
const secretPath2 = path2.join(secretsDir2, name);
|
|
3279
|
-
|
|
3132
|
+
writeFileSync2(secretPath2, secretValue.trim(), { encoding: "utf-8", mode: 384 });
|
|
3280
3133
|
success(`Secret saved to ${secretPath2} (chmod 600)`);
|
|
3281
3134
|
return { name, source: "file", ref: secretPath2 };
|
|
3282
3135
|
}
|
|
@@ -3293,7 +3146,7 @@ async function promptSecret(name, label, envDefault, inquirer) {
|
|
|
3293
3146
|
type: "input",
|
|
3294
3147
|
name: "filePath",
|
|
3295
3148
|
message: `File path: (full path to the file containing your ${label})`,
|
|
3296
|
-
validate: (v) =>
|
|
3149
|
+
validate: (v) => existsSync(v.trim()) || `File not found: ${v.trim()}`
|
|
3297
3150
|
}]);
|
|
3298
3151
|
return { name, source: "file", ref: filePath };
|
|
3299
3152
|
}
|
|
@@ -3451,7 +3304,7 @@ async function startDockerAndWait(inquirer) {
|
|
|
3451
3304
|
await pollUntilDockerReady("Waiting for Docker daemon...");
|
|
3452
3305
|
} else if (choice === "systemctl") {
|
|
3453
3306
|
const spin = spinner("Running: sudo systemctl start docker...");
|
|
3454
|
-
const res =
|
|
3307
|
+
const res = spawnSync("sudo", ["systemctl", "start", "docker"], { stdio: "pipe" });
|
|
3455
3308
|
if (res.status !== 0) {
|
|
3456
3309
|
spin.fail("systemctl start docker failed.");
|
|
3457
3310
|
throw new Error("systemctl start docker failed");
|
|
@@ -3459,18 +3312,18 @@ async function startDockerAndWait(inquirer) {
|
|
|
3459
3312
|
spin.succeed("Docker service started.");
|
|
3460
3313
|
await pollUntilDockerReady("Waiting for Docker daemon to be reachable...");
|
|
3461
3314
|
} else if (choice === "desktop") {
|
|
3462
|
-
const backendRunning =
|
|
3315
|
+
const backendRunning = spawnSync("pgrep", ["-f", "com.docker.backend"], { stdio: "ignore" }).status === 0;
|
|
3463
3316
|
if (backendRunning) {
|
|
3464
3317
|
const killSpin = spinner("Docker Desktop is stuck \u2014 force-restarting...");
|
|
3465
3318
|
for (const pat of ["com.docker.backend", "com.docker.virtualization", "com.docker.build", "Docker Desktop"]) {
|
|
3466
|
-
|
|
3319
|
+
spawnSync("pkill", ["-9", "-f", pat], { stdio: "ignore" });
|
|
3467
3320
|
}
|
|
3468
3321
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
3469
3322
|
killSpin.succeed("Old Docker processes cleared.");
|
|
3470
3323
|
}
|
|
3471
3324
|
const openSpin = spinner("Launching Docker Desktop...");
|
|
3472
|
-
let opened =
|
|
3473
|
-
if (opened.status !== 0) opened =
|
|
3325
|
+
let opened = spawnSync("open", ["-a", "Docker"], { stdio: "ignore" });
|
|
3326
|
+
if (opened.status !== 0) opened = spawnSync("open", ["/Applications/Docker.app"], { stdio: "ignore" });
|
|
3474
3327
|
if (opened.status !== 0) {
|
|
3475
3328
|
openSpin.fail("Could not open Docker Desktop \u2014 is it installed in /Applications?");
|
|
3476
3329
|
throw new Error("Failed to open Docker Desktop");
|
|
@@ -3479,7 +3332,7 @@ async function startDockerAndWait(inquirer) {
|
|
|
3479
3332
|
await pollUntilDockerReady("Waiting for Docker engine to start...");
|
|
3480
3333
|
} else if (choice === "colima") {
|
|
3481
3334
|
const spin = spinner("Running: colima start...");
|
|
3482
|
-
const res =
|
|
3335
|
+
const res = spawnSync("colima", ["start"], { stdio: "pipe" });
|
|
3483
3336
|
if (res.status !== 0) {
|
|
3484
3337
|
spin.fail("colima start failed \u2014 is Colima installed? (brew install colima docker)");
|
|
3485
3338
|
throw new Error("colima start failed");
|
|
@@ -3491,12 +3344,12 @@ async function startDockerAndWait(inquirer) {
|
|
|
3491
3344
|
var LOCALHOST_ALIASES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
3492
3345
|
async function ensureAuthorizedKey(keyPath, host, inquirer) {
|
|
3493
3346
|
const pubKeyPath = `${keyPath}.pub`;
|
|
3494
|
-
if (!
|
|
3347
|
+
if (!existsSync(pubKeyPath)) return;
|
|
3495
3348
|
const pubKey = readFileSync3(pubKeyPath, "utf-8").trim();
|
|
3496
3349
|
const isLocal = LOCALHOST_ALIASES.has(host.toLowerCase());
|
|
3497
3350
|
const authorizedKeysPath = path2.join(os.homedir(), ".ssh", "authorized_keys");
|
|
3498
3351
|
if (isLocal) {
|
|
3499
|
-
const existing =
|
|
3352
|
+
const existing = existsSync(authorizedKeysPath) ? readFileSync3(authorizedKeysPath, "utf-8") : "";
|
|
3500
3353
|
if (existing.includes(pubKey)) {
|
|
3501
3354
|
success("SSH public key is already in authorized_keys.");
|
|
3502
3355
|
return;
|
|
@@ -3509,10 +3362,10 @@ async function ensureAuthorizedKey(keyPath, host, inquirer) {
|
|
|
3509
3362
|
default: true
|
|
3510
3363
|
}]);
|
|
3511
3364
|
if (addKey) {
|
|
3512
|
-
|
|
3365
|
+
mkdirSync(path2.join(os.homedir(), ".ssh"), { recursive: true });
|
|
3513
3366
|
const entry = existing.endsWith("\n") || existing === "" ? pubKey + "\n" : "\n" + pubKey + "\n";
|
|
3514
|
-
|
|
3515
|
-
|
|
3367
|
+
writeFileSync2(authorizedKeysPath, existing + entry, { encoding: "utf-8", flag: "a" });
|
|
3368
|
+
spawnSync("chmod", ["600", authorizedKeysPath], { stdio: "ignore" });
|
|
3516
3369
|
success("Public key added to ~/.ssh/authorized_keys.");
|
|
3517
3370
|
} else {
|
|
3518
3371
|
info("SSH will likely fail. Add this line to ~/.ssh/authorized_keys manually:");
|
|
@@ -3527,8 +3380,8 @@ async function ensureAuthorizedKey(keyPath, host, inquirer) {
|
|
|
3527
3380
|
}
|
|
3528
3381
|
async function generateSshKey() {
|
|
3529
3382
|
const keyPath = path2.join(os.homedir(), ".ssh", "id_ed25519");
|
|
3530
|
-
|
|
3531
|
-
const result =
|
|
3383
|
+
mkdirSync(path2.join(os.homedir(), ".ssh"), { recursive: true });
|
|
3384
|
+
const result = spawnSync("ssh-keygen", ["-t", "ed25519", "-f", keyPath, "-N", "", "-C", "clawops"], {
|
|
3532
3385
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3533
3386
|
});
|
|
3534
3387
|
if (result.status !== 0) {
|
|
@@ -3543,7 +3396,7 @@ function detectSshKey() {
|
|
|
3543
3396
|
const candidates = ["id_ed25519", "id_ecdsa", "id_rsa", "id_dsa"];
|
|
3544
3397
|
for (const name of candidates) {
|
|
3545
3398
|
const p = path2.join(os.homedir(), ".ssh", name);
|
|
3546
|
-
if (
|
|
3399
|
+
if (existsSync(p)) return p;
|
|
3547
3400
|
}
|
|
3548
3401
|
return path2.join(os.homedir(), ".ssh", "id_ed25519");
|
|
3549
3402
|
}
|
|
@@ -3560,8 +3413,8 @@ async function maybeWireGatewayMcp(opts) {
|
|
|
3560
3413
|
const { wireGateway } = await inquirerPromptWire(opts.inquirer);
|
|
3561
3414
|
if (!wireGateway) return;
|
|
3562
3415
|
const { connect } = await import("./ssh-VAI5DU5M.js");
|
|
3563
|
-
const { getConfigDir: getConfigDir2 } = await import("./store-
|
|
3564
|
-
const { wireGatewayMcp } = await import("./mcp-wire-
|
|
3416
|
+
const { getConfigDir: getConfigDir2 } = await import("./store-YOLQYXMB.js");
|
|
3417
|
+
const { wireGatewayMcp } = await import("./mcp-wire-FIO5DFBY.js");
|
|
3565
3418
|
const knownHostsPath = path2.join(getConfigDir2(), "known_hosts");
|
|
3566
3419
|
const spin = spinner("Wiring gateway MCP client...");
|
|
3567
3420
|
const session = await connect({
|
|
@@ -3680,9 +3533,9 @@ function instanceChoices(provider) {
|
|
|
3680
3533
|
return table[provider] ?? [];
|
|
3681
3534
|
}
|
|
3682
3535
|
async function maybeHarden(opts) {
|
|
3683
|
-
const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-
|
|
3684
|
-
const { getConfig
|
|
3685
|
-
const config =
|
|
3536
|
+
const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-RUOUA5BV.js");
|
|
3537
|
+
const { getConfig } = await import("./store-YOLQYXMB.js");
|
|
3538
|
+
const config = getConfig();
|
|
3686
3539
|
if (!config) return;
|
|
3687
3540
|
const availableModules = resolveModules(MODULE_CATALOG, void 0, opts.provider);
|
|
3688
3541
|
if (availableModules.length === 0) return;
|
|
@@ -3704,7 +3557,7 @@ async function maybeHarden(opts) {
|
|
|
3704
3557
|
const modules = availableModules.filter((m) => selectedIds.includes(m.id));
|
|
3705
3558
|
process21.stdout.write("\nApplying hardening modules...\n\n");
|
|
3706
3559
|
try {
|
|
3707
|
-
const { buildContext } = await import("./context-
|
|
3560
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
3708
3561
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
3709
3562
|
const ctx = buildContext({ stack: opts.stackName });
|
|
3710
3563
|
const stackObj = await ctx.getStack();
|
|
@@ -3827,8 +3680,8 @@ async function runAccountPreflight(opts) {
|
|
|
3827
3680
|
|
|
3828
3681
|
// src/cli/commands/secret.ts
|
|
3829
3682
|
import { defineCommand as defineCommand20 } from "citty";
|
|
3830
|
-
import { existsSync as
|
|
3831
|
-
import { spawnSync as
|
|
3683
|
+
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync3, unlinkSync, mkdirSync as mkdirSync2, statSync } from "fs";
|
|
3684
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
3832
3685
|
import path3 from "path";
|
|
3833
3686
|
import os2 from "os";
|
|
3834
3687
|
import process22 from "process";
|
|
@@ -3840,7 +3693,7 @@ function secretPath(name) {
|
|
|
3840
3693
|
return path3.join(secretsDir(), name);
|
|
3841
3694
|
}
|
|
3842
3695
|
function listSecretNames() {
|
|
3843
|
-
if (!
|
|
3696
|
+
if (!existsSync2(secretsDir())) return [];
|
|
3844
3697
|
return readdirSync(secretsDir()).filter((f) => !f.startsWith("."));
|
|
3845
3698
|
}
|
|
3846
3699
|
var listCmd = defineCommand20({
|
|
@@ -3904,9 +3757,9 @@ var setCmd = defineCommand20({
|
|
|
3904
3757
|
}]);
|
|
3905
3758
|
value = secretValue;
|
|
3906
3759
|
}
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3760
|
+
mkdirSync2(secretsDir(), { recursive: true });
|
|
3761
|
+
spawnSync2("chmod", ["700", secretsDir()], { stdio: "ignore" });
|
|
3762
|
+
writeFileSync3(secretPath(name), value.trim(), { encoding: "utf-8", mode: 384 });
|
|
3910
3763
|
success(`Secret "${name}" saved to ${secretPath(name)} (chmod 600)`);
|
|
3911
3764
|
info("Run `clawops secret rotate <name>` to propagate it to a running stack.");
|
|
3912
3765
|
}
|
|
@@ -3923,7 +3776,7 @@ var deleteCmd = defineCommand20({
|
|
|
3923
3776
|
process22.exit(2);
|
|
3924
3777
|
}
|
|
3925
3778
|
const p = secretPath(name);
|
|
3926
|
-
if (!
|
|
3779
|
+
if (!existsSync2(p)) {
|
|
3927
3780
|
failure(`Secret "${name}" not found at ${p}`);
|
|
3928
3781
|
process22.exit(1);
|
|
3929
3782
|
}
|
|
@@ -3975,12 +3828,12 @@ var rotateCmd = defineCommand20({
|
|
|
3975
3828
|
}]);
|
|
3976
3829
|
value = secretValue;
|
|
3977
3830
|
}
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3831
|
+
mkdirSync2(secretsDir(), { recursive: true });
|
|
3832
|
+
spawnSync2("chmod", ["700", secretsDir()], { stdio: "ignore" });
|
|
3833
|
+
writeFileSync3(secretPath(name), value.trim(), { encoding: "utf-8", mode: 384 });
|
|
3981
3834
|
success(`Secret "${name}" updated.`);
|
|
3982
|
-
const { getConfig
|
|
3983
|
-
const cfg =
|
|
3835
|
+
const { getConfig } = await import("./store-YOLQYXMB.js");
|
|
3836
|
+
const cfg = getConfig();
|
|
3984
3837
|
const targetStack = args.stack ?? cfg?.defaults?.stack;
|
|
3985
3838
|
if (!targetStack) {
|
|
3986
3839
|
warn("No stack specified and no default stack in config \u2014 skipping re-apply.");
|
|
@@ -3996,11 +3849,11 @@ var rotateCmd = defineCommand20({
|
|
|
3996
3849
|
}
|
|
3997
3850
|
info(`Re-applying config overlay to stack "${targetStack}"\u2026`);
|
|
3998
3851
|
try {
|
|
3999
|
-
const { buildContext } = await import("./context-
|
|
3852
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
4000
3853
|
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-3HZLUWEA.js");
|
|
4001
3854
|
const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
|
|
4002
3855
|
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
4003
|
-
const { localStateToConnectionInfo } = await import("./state-
|
|
3856
|
+
const { localStateToConnectionInfo } = await import("./state-AKZP2S4T.js");
|
|
4004
3857
|
const ctx = buildContext({ stack: targetStack });
|
|
4005
3858
|
let conn;
|
|
4006
3859
|
if (ctx.adapter.name === "local") {
|
|
@@ -4058,7 +3911,7 @@ var auditCmd = defineCommand20({
|
|
|
4058
3911
|
for (const s of overlay.secrets) {
|
|
4059
3912
|
if (s.source === "file") {
|
|
4060
3913
|
const ref = s.ref ?? secretPath(s.name);
|
|
4061
|
-
if (!
|
|
3914
|
+
if (!existsSync2(ref)) {
|
|
4062
3915
|
issues.push({ kind: "missing-file", stack: overlay.stackName, secret: s.name, detail: `File not found: ${ref}` });
|
|
4063
3916
|
}
|
|
4064
3917
|
} else if (s.source === "env") {
|
|
@@ -4183,7 +4036,7 @@ import { accessSync, constants } from "fs";
|
|
|
4183
4036
|
import { execSync as execSync2 } from "child_process";
|
|
4184
4037
|
var ISSUES_URL = `${REPO_URL}/issues/new`;
|
|
4185
4038
|
var BODY_CAP = 2e3;
|
|
4186
|
-
var VERSION = true ? "2.1.
|
|
4039
|
+
var VERSION = true ? "2.1.3" : "dev";
|
|
4187
4040
|
async function collectContext() {
|
|
4188
4041
|
const nodeVersion = process23.version;
|
|
4189
4042
|
const os3 = `${process23.platform} ${process23.arch}`;
|
|
@@ -4191,8 +4044,8 @@ async function collectContext() {
|
|
|
4191
4044
|
let stackCount = 0;
|
|
4192
4045
|
let sshKey = "unknown";
|
|
4193
4046
|
try {
|
|
4194
|
-
const { getConfig
|
|
4195
|
-
const config =
|
|
4047
|
+
const { getConfig } = await import("./store-YOLQYXMB.js");
|
|
4048
|
+
const config = getConfig();
|
|
4196
4049
|
if (config) {
|
|
4197
4050
|
const stacks = Object.values(config.stacks);
|
|
4198
4051
|
stackCount = stacks.length;
|
|
@@ -4302,9 +4155,9 @@ var harden_default = defineCommand23({
|
|
|
4302
4155
|
"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" }
|
|
4303
4156
|
},
|
|
4304
4157
|
async run({ args }) {
|
|
4305
|
-
const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-
|
|
4306
|
-
const { buildContext } = await import("./context-
|
|
4307
|
-
const { getConfig
|
|
4158
|
+
const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-RUOUA5BV.js");
|
|
4159
|
+
const { buildContext } = await import("./context-WOKSKAIA.js");
|
|
4160
|
+
const { getConfig } = await import("./store-YOLQYXMB.js");
|
|
4308
4161
|
if (args.list) {
|
|
4309
4162
|
process24.stdout.write("\nAvailable hardening modules:\n\n");
|
|
4310
4163
|
for (const mod of MODULE_CATALOG) {
|
|
@@ -4318,7 +4171,7 @@ var harden_default = defineCommand23({
|
|
|
4318
4171
|
process24.stdout.write("\n");
|
|
4319
4172
|
return;
|
|
4320
4173
|
}
|
|
4321
|
-
const config =
|
|
4174
|
+
const config = getConfig();
|
|
4322
4175
|
if (!config) {
|
|
4323
4176
|
failure("No clawops config found. Run `clawops init` first.");
|
|
4324
4177
|
process24.exit(1);
|
|
@@ -4326,7 +4179,7 @@ var harden_default = defineCommand23({
|
|
|
4326
4179
|
const ctx = buildContext({ stack: args.stack });
|
|
4327
4180
|
const provider = ctx.adapter.name;
|
|
4328
4181
|
if (args["tailscale-revert"]) {
|
|
4329
|
-
const { revertTailnet } = await import("./flows-
|
|
4182
|
+
const { revertTailnet } = await import("./flows-HGJIA6EL.js");
|
|
4330
4183
|
const override = config.stacks[ctx.stackName]?.tailscale;
|
|
4331
4184
|
if (override && args.yes !== true) {
|
|
4332
4185
|
const { createInterface: createInterface3 } = await import("readline/promises");
|
|
@@ -4349,7 +4202,7 @@ var harden_default = defineCommand23({
|
|
|
4349
4202
|
else success(outcome.message);
|
|
4350
4203
|
return;
|
|
4351
4204
|
}
|
|
4352
|
-
const { makeTailscaleModule } = await import("./harden-
|
|
4205
|
+
const { makeTailscaleModule } = await import("./harden-RUOUA5BV.js");
|
|
4353
4206
|
const forStack = makeTailscaleModule(ctx.stackName);
|
|
4354
4207
|
const resolved = resolveModules(MODULE_CATALOG, args.options, provider).map(
|
|
4355
4208
|
(m) => m.id === "tailscale" ? forStack : m
|
|
@@ -4433,7 +4286,7 @@ var harden_default = defineCommand23({
|
|
|
4433
4286
|
success("Hardening complete.");
|
|
4434
4287
|
}
|
|
4435
4288
|
if (args.tailscale && !args["dry-run"]) {
|
|
4436
|
-
const { cutOverToTailnet } = await import("./flows-
|
|
4289
|
+
const { cutOverToTailnet } = await import("./flows-HGJIA6EL.js");
|
|
4437
4290
|
process24.stdout.write("\nVerifying the tailnet address before using it...\n");
|
|
4438
4291
|
const outcome = await cutOverToTailnet(ctx, conn, config);
|
|
4439
4292
|
if (!outcome.ok) {
|
|
@@ -4470,7 +4323,7 @@ function resolveArgv(argv2, stdinIsTty) {
|
|
|
4470
4323
|
}
|
|
4471
4324
|
|
|
4472
4325
|
// src/cli/index.ts
|
|
4473
|
-
var CLI_VERSION = true ? "2.1.
|
|
4326
|
+
var CLI_VERSION = true ? "2.1.3" : "dev";
|
|
4474
4327
|
var main = defineCommand24({
|
|
4475
4328
|
meta: {
|
|
4476
4329
|
name: "clawops",
|