@clawops/cli 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -12
- package/dist/apply-KYFPWI5U.js +27 -0
- package/dist/automation-RMAIBNVC.js +44 -0
- package/dist/bedrock-XVASJX2K.js +61 -0
- package/dist/{bootstrap-DMRPWJGN.js → bootstrap-HNGKY6WW.js} +11 -8
- package/dist/{chunk-N44K3LHE.js → chunk-2I7KCMXZ.js} +6 -4
- package/dist/{chunk-3WYA2QDM.js → chunk-2Z7J3ERQ.js} +1 -1
- package/dist/chunk-3OLUJPCB.js +36 -0
- package/dist/chunk-5XT5IAJK.js +69 -0
- package/dist/chunk-AEEMRFZV.js +24 -0
- package/dist/{chunk-YR3O2I3S.js → chunk-BSLIYB5Q.js} +6 -1
- package/dist/{chunk-AHAAEXWC.js → chunk-E4NJSJ54.js} +16 -93
- package/dist/chunk-FEK7VJAY.js +16 -0
- package/dist/{chunk-6TIYVHPX.js → chunk-GIEVAGEK.js} +7 -33
- package/dist/chunk-HSPHXG6W.js +67 -0
- package/dist/{chunk-GYV4KNW7.js → chunk-IVWP4QUJ.js} +133 -40
- package/dist/{chunk-AX74VXTV.js → chunk-KC3GC3PE.js} +58 -49
- package/dist/{chunk-UFRO4H5W.js → chunk-LBE6COC4.js} +56 -40
- package/dist/chunk-LEVIW2PV.js +890 -0
- package/dist/chunk-MC5ATCXS.js +108 -0
- package/dist/chunk-N4WZLWJ4.js +16 -0
- package/dist/{chunk-SBLQH2TJ.js → chunk-NUL5F6YV.js} +1 -1
- package/dist/chunk-RHOWYIWT.js +52 -0
- package/dist/{chunk-WHJMKDV7.js → chunk-STLRHLLV.js} +65 -12
- package/dist/chunk-V25QRZRB.js +45 -0
- package/dist/chunk-VOEJYRIK.js +33 -0
- package/dist/{providers-2OABPW2E.js → chunk-VUFMZEIM.js} +3 -2
- package/dist/chunk-W3OLNNJW.js +51 -0
- package/dist/cli-WLD3EBEM.js +11 -0
- package/dist/cli-auth-GD5SK7JU.js +13 -0
- package/dist/cli.js +518 -206
- package/dist/context-7EVC6W45.js +20 -0
- package/dist/docker-3PDO4TS7.js +12 -0
- package/dist/{firewall-T3NTPZJD.js → firewall-5VKZ4WBH.js} +22 -1
- package/dist/generate-I6YQ5V2T.js +29 -0
- package/dist/{harden-PCDK3UQ5.js → harden-72JMRT4E.js} +8 -5
- package/dist/index.d.ts +56 -0
- package/dist/known-hosts-file-I3Q3NBYQ.js +22 -0
- package/dist/{logs-P7YCKVF4.js → logs-5A7YYA53.js} +1 -1
- package/dist/{mcp-wire-BFEY2Y6H.js → mcp-wire-3SWBM7D6.js} +25 -15
- package/dist/models-AGTQWYDL.js +32 -0
- package/dist/network-args-PYJRJBYO.js +64 -0
- package/dist/{package-3HUW57XH.js → package-5BCMEBTE.js} +10 -3
- package/dist/passphrase-MJBE45N7.js +15 -0
- package/dist/{pool-EX4ZTA5P.js → pool-PIIKJ4L2.js} +3 -2
- package/dist/preflight-EF52S3WW.js +162 -0
- package/dist/preflight-MWWM5JT3.js +181 -0
- package/dist/preflight-TFD4N5ZU.js +141 -0
- package/dist/providers-FW6H6OPF.js +11 -0
- package/dist/ready-KDNRBNSU.js +95 -0
- package/dist/{remote-config-Z6IIZR3D.js → remote-config-3HZLUWEA.js} +3 -2
- package/dist/{runtime-LZ7NA53V.js → runtime-X3KW5TCP.js} +4 -2
- package/dist/server-GBA7BJGK.js +46 -0
- package/dist/ssh-VAI5DU5M.js +11 -0
- package/dist/ssh-key-DUO6OKOI.js +7 -0
- package/dist/stack-config-R4SPSUQZ.js +12 -0
- package/dist/{validate-BOQRWX4F.js → validate-YVI3BJ55.js} +1 -1
- package/dist/wait-WO72TRRQ.js +79 -0
- package/package.json +10 -3
- package/spec/mcp-tools.yaml +21 -2
- package/spec/models.yaml +14 -3
- package/spec/providers.schema.json +163 -28
- package/dist/apply-AM5HERE7.js +0 -19
- package/dist/automation-RULUSJBW.js +0 -30
- package/dist/aws-AMON7DA7.js +0 -265
- package/dist/azure-T4G6OBJQ.js +0 -273
- package/dist/chunk-CKSPROD5.js +0 -129
- package/dist/chunk-LU2V3LXA.js +0 -114
- package/dist/context-KHKWJJMC.js +0 -10
- package/dist/gcp-2JBTD7ZM.js +0 -204
- package/dist/generate-2XV2GSP2.js +0 -15
- package/dist/local-DXBEVZ5C.js +0 -47
- package/dist/server-A5AB67GQ.js +0 -36
- package/dist/ssh-B3NZAU4I.js +0 -8
- package/dist/{version-guard-GPSTCPT7.js → version-guard-XVYW5WFJ.js} +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
MCP_APPS,
|
|
4
|
-
buildMcpEntry,
|
|
5
|
-
writeAppConfigs
|
|
6
|
-
} from "./chunk-OIGTOLB3.js";
|
|
7
2
|
import {
|
|
8
3
|
monitor_default,
|
|
9
4
|
runDiagnostics
|
|
10
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-IVWP4QUJ.js";
|
|
11
6
|
import {
|
|
12
7
|
GATEWAY_LOGS_PROBE,
|
|
13
8
|
chooseLogSource,
|
|
14
9
|
containerLogsCommand,
|
|
15
10
|
gatewayLogsCommand
|
|
16
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NUL5F6YV.js";
|
|
17
12
|
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
MCP_APPS,
|
|
14
|
+
buildMcpEntry,
|
|
15
|
+
writeAppConfigs
|
|
16
|
+
} from "./chunk-OIGTOLB3.js";
|
|
17
|
+
import "./chunk-3OLUJPCB.js";
|
|
21
18
|
import {
|
|
22
19
|
execPrivileged,
|
|
23
20
|
execPrivilegedWithInput,
|
|
@@ -26,6 +23,29 @@ import {
|
|
|
26
23
|
import {
|
|
27
24
|
resolveSpecDir
|
|
28
25
|
} from "./chunk-JW5A7E3X.js";
|
|
26
|
+
import {
|
|
27
|
+
interpretProbe,
|
|
28
|
+
probeCommand
|
|
29
|
+
} from "./chunk-LT4XMO5D.js";
|
|
30
|
+
import "./chunk-LEVIW2PV.js";
|
|
31
|
+
import {
|
|
32
|
+
CONFIG_FILENAME,
|
|
33
|
+
CONTAINER_UID,
|
|
34
|
+
PUBLISH_INSPECT_CMD,
|
|
35
|
+
STATE_DIR_HOST_LINUX,
|
|
36
|
+
configPathForOS,
|
|
37
|
+
gatewayRunCommand,
|
|
38
|
+
publishForRestart
|
|
39
|
+
} from "./chunk-GIEVAGEK.js";
|
|
40
|
+
import {
|
|
41
|
+
GATEWAY_PORT,
|
|
42
|
+
IMAGE_INSPECT_CMD,
|
|
43
|
+
imageForRestart,
|
|
44
|
+
versionOf
|
|
45
|
+
} from "./chunk-VOEJYRIK.js";
|
|
46
|
+
import "./chunk-EI7CQQ7D.js";
|
|
47
|
+
import "./chunk-FEK7VJAY.js";
|
|
48
|
+
import "./chunk-RHOWYIWT.js";
|
|
29
49
|
import {
|
|
30
50
|
REPO_URL,
|
|
31
51
|
chalk,
|
|
@@ -36,6 +56,7 @@ import {
|
|
|
36
56
|
success,
|
|
37
57
|
warn
|
|
38
58
|
} from "./chunk-Q7NQY5HV.js";
|
|
59
|
+
import "./chunk-N4WZLWJ4.js";
|
|
39
60
|
import {
|
|
40
61
|
getConfig,
|
|
41
62
|
getConfigDir,
|
|
@@ -46,30 +67,95 @@ import {
|
|
|
46
67
|
ClawopsError,
|
|
47
68
|
UsageError
|
|
48
69
|
} from "./chunk-KGXPLI7W.js";
|
|
49
|
-
import
|
|
50
|
-
CONFIG_FILENAME,
|
|
51
|
-
CONTAINER_UID,
|
|
52
|
-
GATEWAY_PORT,
|
|
53
|
-
IMAGE_INSPECT_CMD,
|
|
54
|
-
PUBLISH_INSPECT_CMD,
|
|
55
|
-
STATE_DIR_HOST_LINUX,
|
|
56
|
-
configPathForOS,
|
|
57
|
-
gatewayRunCommand,
|
|
58
|
-
imageForRestart,
|
|
59
|
-
publishForRestart,
|
|
60
|
-
versionOf
|
|
61
|
-
} from "./chunk-6TIYVHPX.js";
|
|
62
|
-
import "./chunk-EI7CQQ7D.js";
|
|
70
|
+
import "./chunk-VUFMZEIM.js";
|
|
63
71
|
|
|
64
72
|
// src/cli/index.ts
|
|
65
73
|
import { defineCommand as defineCommand24, runMain } from "citty";
|
|
66
74
|
|
|
67
75
|
// src/cli/commands/init.ts
|
|
68
76
|
import { defineCommand } from "citty";
|
|
69
|
-
import {
|
|
77
|
+
import { spawnSync } from "child_process";
|
|
70
78
|
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
71
79
|
import path from "path";
|
|
72
80
|
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-EF52S3WW.js");
|
|
152
|
+
return resolveProjectId();
|
|
153
|
+
} catch {
|
|
154
|
+
return void 0;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/cli/commands/init.ts
|
|
73
159
|
var SUPPORTED_PROVIDERS = ["gcp", "aws", "azure", "local"];
|
|
74
160
|
var PROVIDER_DEFAULTS = {
|
|
75
161
|
gcp: { region: "us-central1", credEnv: "GOOGLE_APPLICATION_CREDENTIALS", stateScheme: "gs://" },
|
|
@@ -111,9 +197,9 @@ var init_default = defineCommand({
|
|
|
111
197
|
);
|
|
112
198
|
}
|
|
113
199
|
const existing = getConfig();
|
|
114
|
-
if (existing && !forceOverwrite
|
|
200
|
+
if (existing?.stacks[stackName] && !forceOverwrite) {
|
|
115
201
|
failure(
|
|
116
|
-
`
|
|
202
|
+
`Stack "${stackName}" already exists in ${path.join(getConfigDir(), "config.json")} (${existing.stacks[stackName].stateUrl}). Use --force to overwrite its settings, or pass a different --stack name to add another one.`
|
|
117
203
|
);
|
|
118
204
|
process2.exit(1);
|
|
119
205
|
}
|
|
@@ -126,12 +212,21 @@ var init_default = defineCommand({
|
|
|
126
212
|
throw new UsageError(`SSH key not found at ${keyPath}`);
|
|
127
213
|
}
|
|
128
214
|
info("Generating SSH key pair...");
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
+
);
|
|
228
|
+
}
|
|
229
|
+
success(`SSH key pair written to ${keyPath} (and ${keyPath}.pub)`);
|
|
135
230
|
} else {
|
|
136
231
|
info(`Using existing SSH key at ${keyPath}`);
|
|
137
232
|
}
|
|
@@ -146,43 +241,41 @@ var init_default = defineCommand({
|
|
|
146
241
|
}
|
|
147
242
|
const sshUser = typeof args["ssh-user"] === "string" ? args["ssh-user"] : "root";
|
|
148
243
|
const sshPort = typeof args["ssh-port"] === "string" ? parseInt(args["ssh-port"], 10) : 22;
|
|
149
|
-
config = {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
stateUrl: "file://~/.clawops/state",
|
|
156
|
-
credentialsRef: { source: "file", envVars: [] },
|
|
157
|
-
localOpts: { host, sshUser, sshPort, sshKeyPath: keyPath }
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
ssh: { keyPath, knownHostsPath }
|
|
161
|
-
};
|
|
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
|
+
});
|
|
162
250
|
} else {
|
|
163
251
|
const defaults = PROVIDER_DEFAULTS[provider];
|
|
164
252
|
const region = typeof args.region === "string" ? args.region : defaults.region;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (stateUrl.includes("CHANGEME")) {
|
|
180
|
-
process2.stdout.write("\n");
|
|
181
|
-
info(
|
|
182
|
-
`Update stateUrl in the config to a real state backend before running \`clawops up\`.
|
|
183
|
-
Example: clawops init --provider ${provider} --state ${defaults.stateScheme}your-bucket/clawops`
|
|
184
|
-
);
|
|
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);
|
|
185
267
|
}
|
|
268
|
+
config = merge(existing, stackName, provider, keyPath, knownHostsPath, {
|
|
269
|
+
provider,
|
|
270
|
+
stateUrl,
|
|
271
|
+
region,
|
|
272
|
+
credentialsRef: { source: "env", envVars: [defaults.credEnv] }
|
|
273
|
+
});
|
|
274
|
+
process2.stdout.write("\n");
|
|
275
|
+
info(
|
|
276
|
+
`State backend: ${stateUrl}
|
|
277
|
+
clawops doctor --provider ${provider} checks it exists, and clawops setup offers to create it.`
|
|
278
|
+
);
|
|
186
279
|
process2.stdout.write("\n");
|
|
187
280
|
success(`Provider: ${provider} Region: ${region} Stack: ${stackName}`);
|
|
188
281
|
setConfig(config);
|
|
@@ -195,6 +288,15 @@ var init_default = defineCommand({
|
|
|
195
288
|
success(`Provider: ${provider} Stack: ${stackName}`);
|
|
196
289
|
}
|
|
197
290
|
});
|
|
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
|
+
}
|
|
198
300
|
|
|
199
301
|
// src/cli/commands/up.ts
|
|
200
302
|
import { defineCommand as defineCommand2 } from "citty";
|
|
@@ -217,7 +319,6 @@ function renderTable(headers, rows) {
|
|
|
217
319
|
}
|
|
218
320
|
|
|
219
321
|
// src/cli/commands/up.ts
|
|
220
|
-
var VALID_INSTANCE_TYPES = ["micro", "small", "medium", "large", "gpu"];
|
|
221
322
|
var up_default = defineCommand2({
|
|
222
323
|
meta: {
|
|
223
324
|
name: "up",
|
|
@@ -226,17 +327,20 @@ var up_default = defineCommand2({
|
|
|
226
327
|
args: {
|
|
227
328
|
provider: { type: "string", description: "Cloud provider (gcp|aws|azure|local)" },
|
|
228
329
|
region: { type: "string", description: "Cloud region" },
|
|
229
|
-
"instance-type": { type: "string", description: "Instance size alias (micro|small|medium|large|gpu)" },
|
|
330
|
+
"instance-type": { type: "string", description: "Instance size: a clawops alias (micro|small|medium|large|gpu) or a type your cloud names itself, e.g. t3.small" },
|
|
230
331
|
"dry-run": { type: "boolean", description: "Preview without applying" },
|
|
231
332
|
"no-wait": { type: "boolean", description: "Return immediately without waiting for healthy state" },
|
|
232
333
|
"openclaw-version": { type: "string", description: "OpenClaw release (e.g. 2026.7.1-2). Moving tags are resolved and range-checked" },
|
|
233
334
|
stack: { type: "string", description: "Target stack name" },
|
|
234
335
|
config: { type: "string", description: "Path to openclaw config overlay JSON (local provider only)" },
|
|
235
|
-
"gateway-port": { type: "string", description: `Host port to publish the gateway on (default ${GATEWAY_PORT}
|
|
336
|
+
"gateway-port": { type: "string", description: `Host port to publish the gateway on (default ${GATEWAY_PORT})` },
|
|
337
|
+
"ssh-cidr": { type: "string", description: "CIDR(s) allowed to reach SSH, comma-separated, or 'auto' for this machine's IP. Omitted = none, and nothing will be able to connect" },
|
|
338
|
+
"gateway-cidr": { type: "string", description: "CIDR(s) allowed to reach the gateway port, or 'auto'. Requires --publish-gateway all" },
|
|
339
|
+
"publish-gateway": { type: "string", description: 'loopback (default) or all. "all" serves plaintext HTTP \u2014 put TLS in front of it' }
|
|
236
340
|
},
|
|
237
341
|
async run({ args }) {
|
|
238
|
-
const { buildContext } = await import("./context-
|
|
239
|
-
const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-
|
|
342
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
343
|
+
const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-XVYW5WFJ.js");
|
|
240
344
|
const ctx = buildContext(args);
|
|
241
345
|
const requestedVersion = typeof args["openclaw-version"] === "string" ? args["openclaw-version"] : await defaultOpenclawVersion();
|
|
242
346
|
const openclawVersion = await guardOpenclawVersion(requestedVersion);
|
|
@@ -248,11 +352,11 @@ var up_default = defineCommand2({
|
|
|
248
352
|
);
|
|
249
353
|
}
|
|
250
354
|
const { localOpts } = stackConfig;
|
|
251
|
-
const { localBootstrap } = await import("./bootstrap-
|
|
355
|
+
const { localBootstrap } = await import("./bootstrap-HNGKY6WW.js");
|
|
252
356
|
const gatewayPort = parseGatewayPort(args["gateway-port"]);
|
|
253
|
-
const
|
|
254
|
-
process3.on("SIGINT", () =>
|
|
255
|
-
process3.on("SIGTERM", () =>
|
|
357
|
+
const abortController2 = new AbortController();
|
|
358
|
+
process3.on("SIGINT", () => abortController2.abort());
|
|
359
|
+
process3.on("SIGTERM", () => abortController2.abort());
|
|
256
360
|
const spin2 = spinner(`Bootstrapping local host "${localOpts.host}"...`);
|
|
257
361
|
try {
|
|
258
362
|
const state = await localBootstrap({
|
|
@@ -265,7 +369,7 @@ var up_default = defineCommand2({
|
|
|
265
369
|
stackName: ctx.stackName,
|
|
266
370
|
gatewayPort,
|
|
267
371
|
noWait: Boolean(args["no-wait"]),
|
|
268
|
-
signal:
|
|
372
|
+
signal: abortController2.signal
|
|
269
373
|
});
|
|
270
374
|
spin2.succeed(`Host "${localOpts.host}" bootstrapped`);
|
|
271
375
|
info(`Gateway URL: ${state.gatewayUrl}`);
|
|
@@ -278,7 +382,7 @@ var up_default = defineCommand2({
|
|
|
278
382
|
user: localOpts.sshUser,
|
|
279
383
|
privateKeyPath: localOpts.sshKeyPath,
|
|
280
384
|
knownHostsPath: ctx.config.ssh.knownHostsPath,
|
|
281
|
-
signal:
|
|
385
|
+
signal: abortController2.signal
|
|
282
386
|
});
|
|
283
387
|
info("Config overlay applied and gateway restarted.");
|
|
284
388
|
}
|
|
@@ -288,64 +392,90 @@ var up_default = defineCommand2({
|
|
|
288
392
|
}
|
|
289
393
|
return;
|
|
290
394
|
}
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
);
|
|
296
|
-
}
|
|
395
|
+
const { generatePlan } = await import("./generate-I6YQ5V2T.js");
|
|
396
|
+
const { applyPlan } = await import("./apply-KYFPWI5U.js");
|
|
397
|
+
const { resolveNetworkFlags } = await import("./network-args-PYJRJBYO.js");
|
|
398
|
+
const { detectEgressIp } = await import("./firewall-5VKZ4WBH.js");
|
|
297
399
|
const isDryRun = Boolean(args["dry-run"]);
|
|
298
400
|
const validation = await ctx.adapter.validateConfig();
|
|
299
401
|
if (!validation.ok) {
|
|
300
402
|
for (const e of validation.errors) failure(e);
|
|
301
403
|
process3.exit(3);
|
|
302
404
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const instanceType = ctx.adapter.normalizeInstanceType(
|
|
306
|
-
instanceAlias
|
|
307
|
-
);
|
|
308
|
-
await stack.setConfig("region", { value: region });
|
|
309
|
-
await stack.setConfig("instanceType", { value: instanceType });
|
|
310
|
-
await stack.setConfig("openclawVersion", { value: openclawVersion });
|
|
311
|
-
if (isDryRun) {
|
|
312
|
-
info("Previewing changes (--dry-run)...");
|
|
313
|
-
const preview = await stack.preview({ onOutput: (out) => process3.stdout.write(out) });
|
|
314
|
-
process3.stdout.write("\n");
|
|
315
|
-
if (preview.changeSummary) {
|
|
316
|
-
const rows = Object.entries(preview.changeSummary).filter(([, count]) => count > 0).map(([op, count]) => [op, String(count)]);
|
|
317
|
-
if (rows.length > 0) {
|
|
318
|
-
process3.stdout.write(renderTable(["Operation", "Count"], rows) + "\n");
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
success("Preview complete (no resources changed)");
|
|
322
|
-
return;
|
|
405
|
+
if (typeof args.config === "string") {
|
|
406
|
+
info("--config applies to local stacks only; put the overlay in the plan for a cloud stack.");
|
|
323
407
|
}
|
|
408
|
+
const abortController = new AbortController();
|
|
409
|
+
process3.on("SIGINT", () => abortController.abort());
|
|
410
|
+
process3.on("SIGTERM", () => abortController.abort());
|
|
411
|
+
const network = await resolveNetworkFlags(
|
|
412
|
+
{
|
|
413
|
+
sshCidr: strArg(args["ssh-cidr"]),
|
|
414
|
+
gatewayCidr: strArg(args["gateway-cidr"]),
|
|
415
|
+
publishGateway: strArg(args["publish-gateway"])
|
|
416
|
+
},
|
|
417
|
+
{ detectEgressIp: () => detectEgressIp("https://ifconfig.me/ip") }
|
|
418
|
+
);
|
|
419
|
+
const cloudGatewayPort = strArg(args["gateway-port"]);
|
|
420
|
+
if (cloudGatewayPort) network.gatewayPort = parseGatewayPort(cloudGatewayPort);
|
|
324
421
|
const spin = spinner(`Deploying stack "${ctx.stackName}"...`);
|
|
325
422
|
try {
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
423
|
+
const plan = await generatePlan(
|
|
424
|
+
{
|
|
425
|
+
stackName: ctx.stackName,
|
|
426
|
+
provider: ctx.adapter.name,
|
|
427
|
+
...strArg(args.region) ? { region: strArg(args.region) } : {},
|
|
428
|
+
...strArg(args["instance-type"]) ? { instanceType: strArg(args["instance-type"]) } : {},
|
|
429
|
+
openclawVersion,
|
|
430
|
+
network
|
|
431
|
+
},
|
|
432
|
+
{ signal: abortController.signal }
|
|
433
|
+
);
|
|
434
|
+
if (isDryRun) {
|
|
435
|
+
spin.stop();
|
|
436
|
+
info("Previewing changes (--dry-run)...");
|
|
437
|
+
const diff = plan.diff;
|
|
438
|
+
if (diff && diff.totalChanges > 0) {
|
|
439
|
+
const rows = [
|
|
440
|
+
["create", String(diff.create.length)],
|
|
441
|
+
["update", String(diff.update.length)],
|
|
442
|
+
["delete", String(diff.delete.length)]
|
|
443
|
+
].filter(([, count]) => count !== "0");
|
|
444
|
+
process3.stdout.write(renderTable(["Operation", "Count"], rows) + "\n");
|
|
329
445
|
}
|
|
446
|
+
success("Preview complete (no resources changed)");
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const result = await applyPlan(plan, {
|
|
450
|
+
onOutput: (line2) => {
|
|
451
|
+
spin.text = line2.trim() || spin.text;
|
|
452
|
+
},
|
|
453
|
+
onProgress: (line2) => {
|
|
454
|
+
const text = line2.trim();
|
|
455
|
+
if (!text) return;
|
|
456
|
+
spin.text = text;
|
|
457
|
+
if (!process3.stderr.isTTY) process3.stderr.write(`${text}
|
|
458
|
+
`);
|
|
459
|
+
},
|
|
460
|
+
signal: abortController.signal,
|
|
461
|
+
skipReadiness: Boolean(args["no-wait"])
|
|
330
462
|
});
|
|
331
463
|
spin.succeed(`Stack "${ctx.stackName}" deployed`);
|
|
332
|
-
|
|
333
|
-
if (outputs["
|
|
334
|
-
info(`Public IP: ${outputs["publicIp"].value}`);
|
|
335
|
-
}
|
|
336
|
-
if (outputs["gatewayUrl"]) {
|
|
337
|
-
info(`Gateway URL: ${outputs["gatewayUrl"].value}`);
|
|
338
|
-
}
|
|
464
|
+
if (result.outputs["publicIp"]) info(`Public IP: ${String(result.outputs["publicIp"])}`);
|
|
465
|
+
if (result.outputs["gatewayUrl"]) info(`Gateway URL: ${String(result.outputs["gatewayUrl"])}`);
|
|
339
466
|
} catch (err) {
|
|
340
467
|
spin.fail("Deployment failed");
|
|
341
468
|
throw err;
|
|
342
469
|
}
|
|
343
470
|
}
|
|
344
471
|
});
|
|
472
|
+
function strArg(value) {
|
|
473
|
+
return typeof value === "string" ? value : void 0;
|
|
474
|
+
}
|
|
345
475
|
async function applyLocalConfigOverlay(opts) {
|
|
346
|
-
const { connect } = await import("./ssh-
|
|
476
|
+
const { connect } = await import("./ssh-VAI5DU5M.js");
|
|
347
477
|
const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
|
|
348
|
-
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-
|
|
478
|
+
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-3HZLUWEA.js");
|
|
349
479
|
let overlay;
|
|
350
480
|
try {
|
|
351
481
|
overlay = JSON.parse(readFileSync(opts.configPath, "utf-8"));
|
|
@@ -393,7 +523,7 @@ var down_default = defineCommand3({
|
|
|
393
523
|
"dry-run": { type: "boolean", description: "Show what would be destroyed without destroying" }
|
|
394
524
|
},
|
|
395
525
|
async run({ args }) {
|
|
396
|
-
const { buildContext } = await import("./context-
|
|
526
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
397
527
|
const ctx = buildContext(args);
|
|
398
528
|
if (args["dry-run"]) {
|
|
399
529
|
info(`Dry run \u2014 would destroy stack "${ctx.stackName}"`);
|
|
@@ -457,7 +587,7 @@ var status_default = defineCommand4({
|
|
|
457
587
|
json: { type: "boolean", description: "Emit JSON" }
|
|
458
588
|
},
|
|
459
589
|
async run({ args }) {
|
|
460
|
-
const { buildContext } = await import("./context-
|
|
590
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
461
591
|
const ctx = buildContext(args);
|
|
462
592
|
if (ctx.adapter.name === "local") {
|
|
463
593
|
const state = ctx.localState;
|
|
@@ -524,13 +654,18 @@ var plan_default = defineCommand5({
|
|
|
524
654
|
provider: { type: "string", description: "Cloud provider (aws|gcp|azure)" },
|
|
525
655
|
stack: { type: "string", description: "Target stack name" },
|
|
526
656
|
region: { type: "string", description: "Cloud region" },
|
|
527
|
-
"instance-type": { type: "string", description: "Instance size alias (micro|small|medium|large|gpu)" },
|
|
657
|
+
"instance-type": { type: "string", description: "Instance size: a clawops alias (micro|small|medium|large|gpu) or a type your cloud names itself, e.g. t3.small" },
|
|
528
658
|
"openclaw-version": { type: "string", description: "semver or 'stable'/'dev'" },
|
|
659
|
+
"ssh-cidr": { type: "string", description: "CIDR(s) allowed to reach SSH, comma-separated, or 'auto' for this machine's IP. Omitted = none, and nothing will be able to connect" },
|
|
660
|
+
"gateway-cidr": { type: "string", description: "CIDR(s) allowed to reach the gateway port, comma-separated, or 'auto'. Requires --publish-gateway all" },
|
|
661
|
+
"publish-gateway": { type: "string", description: 'loopback (default) or all. "all" serves plaintext HTTP \u2014 put TLS in front of it' },
|
|
529
662
|
out: { type: "string", description: "Write plan JSON to this absolute path (default: stdout)" }
|
|
530
663
|
},
|
|
531
664
|
async run({ args }) {
|
|
532
|
-
const { buildContext } = await import("./context-
|
|
533
|
-
const { generatePlan } = await import("./generate-
|
|
665
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
666
|
+
const { generatePlan } = await import("./generate-I6YQ5V2T.js");
|
|
667
|
+
const { resolveNetworkFlags } = await import("./network-args-PYJRJBYO.js");
|
|
668
|
+
const { detectEgressIp } = await import("./firewall-5VKZ4WBH.js");
|
|
534
669
|
const ctx = buildContext(args);
|
|
535
670
|
if (ctx.adapter.name === "local") {
|
|
536
671
|
throw new UsageError(
|
|
@@ -542,6 +677,14 @@ var plan_default = defineCommand5({
|
|
|
542
677
|
if (outPath && !isAbsolute(outPath)) {
|
|
543
678
|
throw new UsageError("--out path must be absolute (R7). Use an absolute path like /tmp/plan.json.");
|
|
544
679
|
}
|
|
680
|
+
const network = await resolveNetworkFlags(
|
|
681
|
+
{
|
|
682
|
+
sshCidr: strArg2(args["ssh-cidr"]),
|
|
683
|
+
gatewayCidr: strArg2(args["gateway-cidr"]),
|
|
684
|
+
publishGateway: strArg2(args["publish-gateway"])
|
|
685
|
+
},
|
|
686
|
+
{ detectEgressIp: () => detectEgressIp("https://ifconfig.me/ip") }
|
|
687
|
+
);
|
|
545
688
|
const abortController = new AbortController();
|
|
546
689
|
process7.on("SIGINT", () => abortController.abort());
|
|
547
690
|
process7.on("SIGTERM", () => abortController.abort());
|
|
@@ -554,7 +697,8 @@ var plan_default = defineCommand5({
|
|
|
554
697
|
provider,
|
|
555
698
|
region: typeof args.region === "string" ? args.region : void 0,
|
|
556
699
|
instanceType: typeof args["instance-type"] === "string" ? args["instance-type"] : void 0,
|
|
557
|
-
openclawVersion: typeof args["openclaw-version"] === "string" ? args["openclaw-version"] : void 0
|
|
700
|
+
openclawVersion: typeof args["openclaw-version"] === "string" ? args["openclaw-version"] : void 0,
|
|
701
|
+
network
|
|
558
702
|
},
|
|
559
703
|
{ signal: abortController.signal }
|
|
560
704
|
);
|
|
@@ -604,6 +748,9 @@ Changes: ${create.length} to create, ${update.length} to update, ${del.length} t
|
|
|
604
748
|
}
|
|
605
749
|
}
|
|
606
750
|
});
|
|
751
|
+
function strArg2(value) {
|
|
752
|
+
return typeof value === "string" ? value : void 0;
|
|
753
|
+
}
|
|
607
754
|
|
|
608
755
|
// src/cli/commands/apply.ts
|
|
609
756
|
import { defineCommand as defineCommand6 } from "citty";
|
|
@@ -621,8 +768,8 @@ var apply_default = defineCommand6({
|
|
|
621
768
|
"dry-run": { type: "boolean", description: "Validate plan and show diff without applying" }
|
|
622
769
|
},
|
|
623
770
|
async run({ args }) {
|
|
624
|
-
const { validatePlan } = await import("./validate-
|
|
625
|
-
const { applyPlan } = await import("./apply-
|
|
771
|
+
const { validatePlan } = await import("./validate-YVI3BJ55.js");
|
|
772
|
+
const { applyPlan } = await import("./apply-KYFPWI5U.js");
|
|
626
773
|
const planPath = args._?.[0];
|
|
627
774
|
if (!planPath) {
|
|
628
775
|
throw new UsageError("Usage: clawops apply <plan.json>");
|
|
@@ -691,6 +838,16 @@ ${validation.errors.join("\n")}`);
|
|
|
691
838
|
onOutput: (line2) => {
|
|
692
839
|
spin.text = line2.trim() || spin.text;
|
|
693
840
|
},
|
|
841
|
+
// A spinner renders nothing without a TTY, so a scripted or CI deploy sat silent
|
|
842
|
+
// through the minutes an image pull takes. These lines are rare — one per half
|
|
843
|
+
// minute at most — so they are cheap to print outright where nothing else will.
|
|
844
|
+
onProgress: (line2) => {
|
|
845
|
+
const text = line2.trim();
|
|
846
|
+
if (!text) return;
|
|
847
|
+
spin.text = text;
|
|
848
|
+
if (!process8.stderr.isTTY) process8.stderr.write(`${text}
|
|
849
|
+
`);
|
|
850
|
+
},
|
|
694
851
|
signal: abortController.signal,
|
|
695
852
|
confirmDrift: args.yes ? void 0 : async () => {
|
|
696
853
|
spin.stop();
|
|
@@ -738,7 +895,7 @@ var destroy_default = defineCommand7({
|
|
|
738
895
|
"dry-run": { type: "boolean", description: "Show what would be destroyed without destroying" }
|
|
739
896
|
},
|
|
740
897
|
async run({ args }) {
|
|
741
|
-
const { buildContext } = await import("./context-
|
|
898
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
742
899
|
const ctx = buildContext(args);
|
|
743
900
|
if (ctx.adapter.name === "local") {
|
|
744
901
|
throw new UsageError(
|
|
@@ -776,6 +933,7 @@ Pass --yes to proceed with destruction.
|
|
|
776
933
|
const abortController = new AbortController();
|
|
777
934
|
process9.on("SIGINT", () => abortController.abort());
|
|
778
935
|
process9.on("SIGTERM", () => abortController.abort());
|
|
936
|
+
const doomedHost = await hostOf(stack, ctx);
|
|
779
937
|
const spin = spinner(`Destroying stack "${ctx.stackName}"\u2026`);
|
|
780
938
|
try {
|
|
781
939
|
await stack.destroy({
|
|
@@ -789,8 +947,35 @@ Pass --yes to proceed with destruction.
|
|
|
789
947
|
spin.fail("Destroy failed");
|
|
790
948
|
throw err;
|
|
791
949
|
}
|
|
950
|
+
if (doomedHost) {
|
|
951
|
+
const { forgetHost } = await import("./known-hosts-file-I3Q3NBYQ.js");
|
|
952
|
+
const removed = forgetHost(
|
|
953
|
+
expandHome(ctx.config.ssh.knownHostsPath),
|
|
954
|
+
doomedHost.host,
|
|
955
|
+
doomedHost.port
|
|
956
|
+
);
|
|
957
|
+
if (removed) info(`Forgot the host key for ${doomedHost.host} \u2014 its instance is gone.`);
|
|
958
|
+
}
|
|
792
959
|
}
|
|
793
960
|
});
|
|
961
|
+
async function hostOf(stack, ctx) {
|
|
962
|
+
try {
|
|
963
|
+
const outputMap = await stack.outputs();
|
|
964
|
+
const raw = Object.fromEntries(Object.entries(outputMap).map(([k, v]) => [k, v.value]));
|
|
965
|
+
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
966
|
+
const conn = ctx.adapter.getConnectionInfo({
|
|
967
|
+
...extractBaseOutputs(raw),
|
|
968
|
+
privateKeyPath: ctx.config.ssh.keyPath,
|
|
969
|
+
knownHostsPath: ctx.config.ssh.knownHostsPath
|
|
970
|
+
});
|
|
971
|
+
return conn.host ? { host: conn.host, port: conn.port } : void 0;
|
|
972
|
+
} catch {
|
|
973
|
+
return void 0;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
function expandHome(p) {
|
|
977
|
+
return p.replace(/^~/, process9.env["HOME"] ?? "~");
|
|
978
|
+
}
|
|
794
979
|
|
|
795
980
|
// src/cli/commands/ssh.ts
|
|
796
981
|
import { defineCommand as defineCommand8 } from "citty";
|
|
@@ -805,8 +990,8 @@ var ssh_default = defineCommand8({
|
|
|
805
990
|
command: { type: "string", description: "Remote command to run (instead of interactive shell)" }
|
|
806
991
|
},
|
|
807
992
|
async run({ args }) {
|
|
808
|
-
const { buildContext } = await import("./context-
|
|
809
|
-
const { acquireSession, drainPool } = await import("./pool-
|
|
993
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
994
|
+
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
810
995
|
const ctx = buildContext(args);
|
|
811
996
|
let conn;
|
|
812
997
|
if (ctx.adapter.name === "local") {
|
|
@@ -892,9 +1077,9 @@ var tunnel_default = defineCommand9({
|
|
|
892
1077
|
"no-open": { type: "boolean", description: "Do not open browser after tunnel is ready" }
|
|
893
1078
|
},
|
|
894
1079
|
async run({ args }) {
|
|
895
|
-
const { buildContext } = await import("./context-
|
|
1080
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
896
1081
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
897
|
-
const { acquireSession } = await import("./pool-
|
|
1082
|
+
const { acquireSession } = await import("./pool-PIIKJ4L2.js");
|
|
898
1083
|
const ctx = buildContext(args);
|
|
899
1084
|
const stack = await ctx.getStack();
|
|
900
1085
|
const outputMap = await stack.outputs();
|
|
@@ -961,9 +1146,9 @@ var logs_default = defineCommand10({
|
|
|
961
1146
|
json: { type: "boolean", description: "Emit the gateway's structured JSON log lines" }
|
|
962
1147
|
},
|
|
963
1148
|
async run({ args }) {
|
|
964
|
-
const { buildContext } = await import("./context-
|
|
1149
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
965
1150
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
966
|
-
const { acquireSession, drainPool } = await import("./pool-
|
|
1151
|
+
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
967
1152
|
const ctx = buildContext(args);
|
|
968
1153
|
const stack = await ctx.getStack();
|
|
969
1154
|
const outputMap = await stack.outputs();
|
|
@@ -997,8 +1182,8 @@ var logs_default = defineCommand10({
|
|
|
997
1182
|
});
|
|
998
1183
|
spin.stop();
|
|
999
1184
|
try {
|
|
1000
|
-
const probe = since ? {
|
|
1001
|
-
const choice = chooseLogSource({ since, gatewayReachable: probe.
|
|
1185
|
+
const probe = since ? { code: 1, stderr: "" } : await execPrivileged(session, GATEWAY_LOGS_PROBE, abortController.signal);
|
|
1186
|
+
const choice = chooseLogSource({ since, gatewayReachable: probe.code === 0 });
|
|
1002
1187
|
const opts = { tail: tailLines, follow, since, json: Boolean(args.json) };
|
|
1003
1188
|
const command = choice.source === "gateway" ? gatewayLogsCommand(opts) : containerLogsCommand(opts);
|
|
1004
1189
|
info(`Logs: ${choice.source} \u2014 ${choice.reason}`);
|
|
@@ -1067,9 +1252,9 @@ var config_default = defineCommand11({
|
|
|
1067
1252
|
"dry-run": { type: "boolean", description: "Show what would change without writing" }
|
|
1068
1253
|
},
|
|
1069
1254
|
async run({ args }) {
|
|
1070
|
-
const { buildContext } = await import("./context-
|
|
1255
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
1071
1256
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1072
|
-
const { acquireSession, drainPool } = await import("./pool-
|
|
1257
|
+
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1073
1258
|
const [action, key, value] = args._ ?? [];
|
|
1074
1259
|
if (!action || !["get", "set", "unset", "validate"].includes(action)) {
|
|
1075
1260
|
failure("Usage: clawops config <get [key] | set key value | unset key | validate>");
|
|
@@ -1157,7 +1342,7 @@ var config_default = defineCommand11({
|
|
|
1157
1342
|
process13.stdout.write(JSON.stringify(cfg, null, 2) + "\n");
|
|
1158
1343
|
return;
|
|
1159
1344
|
}
|
|
1160
|
-
const { atomicWriteConfig } = await import("./remote-config-
|
|
1345
|
+
const { atomicWriteConfig } = await import("./remote-config-3HZLUWEA.js");
|
|
1161
1346
|
try {
|
|
1162
1347
|
await atomicWriteConfig(session, cfg, abortController.signal);
|
|
1163
1348
|
} catch (err) {
|
|
@@ -1202,9 +1387,9 @@ var agents_default = defineCommand12({
|
|
|
1202
1387
|
cursor: { type: "string", description: "Continue from a previous `logs` result cursor" }
|
|
1203
1388
|
},
|
|
1204
1389
|
async run({ args }) {
|
|
1205
|
-
const { buildContext } = await import("./context-
|
|
1390
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
1206
1391
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1207
|
-
const { acquireSession, drainPool } = await import("./pool-
|
|
1392
|
+
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1208
1393
|
const [action, name] = args._ ?? [];
|
|
1209
1394
|
if (action === "restart") {
|
|
1210
1395
|
failure(
|
|
@@ -1277,7 +1462,7 @@ var agents_default = defineCommand12({
|
|
|
1277
1462
|
);
|
|
1278
1463
|
}
|
|
1279
1464
|
} else {
|
|
1280
|
-
const { agentAuditCommand } = await import("./logs-
|
|
1465
|
+
const { agentAuditCommand } = await import("./logs-5A7YYA53.js");
|
|
1281
1466
|
const limit = typeof args.limit === "string" ? parseInt(args.limit, 10) : 50;
|
|
1282
1467
|
const result = await execPrivileged(
|
|
1283
1468
|
session,
|
|
@@ -1434,10 +1619,10 @@ var migrate_default = defineCommand13({
|
|
|
1434
1619
|
yes: { type: "boolean", description: "Skip the confirmation prompt" }
|
|
1435
1620
|
},
|
|
1436
1621
|
async run({ args }) {
|
|
1437
|
-
const { buildContext } = await import("./context-
|
|
1622
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
1438
1623
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1439
|
-
const { acquireSession, drainPool } = await import("./pool-
|
|
1440
|
-
const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-
|
|
1624
|
+
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1625
|
+
const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-XVYW5WFJ.js");
|
|
1441
1626
|
const requested = typeof args["openclaw-version"] === "string" ? args["openclaw-version"] : void 0;
|
|
1442
1627
|
const target = requested ? await guardOpenclawVersion(requested) : await defaultOpenclawVersion();
|
|
1443
1628
|
const targetImage = `ghcr.io/openclaw/openclaw:${target}`;
|
|
@@ -1573,9 +1758,9 @@ var gateway_default = defineCommand14({
|
|
|
1573
1758
|
json: { type: "boolean", description: "Emit JSON (for status)" }
|
|
1574
1759
|
},
|
|
1575
1760
|
async run({ args }) {
|
|
1576
|
-
const { buildContext } = await import("./context-
|
|
1761
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
1577
1762
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1578
|
-
const { acquireSession, drainPool } = await import("./pool-
|
|
1763
|
+
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1579
1764
|
const [action, versionArg] = args._ ?? [];
|
|
1580
1765
|
if (!action || !["status", "restart", "update"].includes(action)) {
|
|
1581
1766
|
failure("Usage: clawops gateway <status | restart | update [version]>");
|
|
@@ -1606,12 +1791,24 @@ var gateway_default = defineCommand14({
|
|
|
1606
1791
|
});
|
|
1607
1792
|
try {
|
|
1608
1793
|
if (action === "status") {
|
|
1609
|
-
const
|
|
1610
|
-
const
|
|
1794
|
+
const { inspectContainer } = await import("./docker-3PDO4TS7.js");
|
|
1795
|
+
const inspected = await inspectContainer(
|
|
1796
|
+
session,
|
|
1797
|
+
"openclaw",
|
|
1798
|
+
'{"status":"{{.State.Status}}","started":"{{.State.StartedAt}}","image":"{{.Config.Image}}"}',
|
|
1799
|
+
abortController.signal
|
|
1800
|
+
);
|
|
1611
1801
|
let status = { status: "unknown", started: "", image: "" };
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1802
|
+
if (inspected.kind === "ok") {
|
|
1803
|
+
try {
|
|
1804
|
+
status = JSON.parse(inspected.value);
|
|
1805
|
+
} catch {
|
|
1806
|
+
}
|
|
1807
|
+
} else if (inspected.kind === "missing") {
|
|
1808
|
+
status = { status: "not running", started: "", image: "" };
|
|
1809
|
+
} else {
|
|
1810
|
+
failure(`Could not ask docker about the gateway: ${inspected.detail}`);
|
|
1811
|
+
process16.exit(1);
|
|
1615
1812
|
}
|
|
1616
1813
|
if (args.json) {
|
|
1617
1814
|
printJson(jsonOk(status));
|
|
@@ -1650,7 +1847,7 @@ var gateway_default = defineCommand14({
|
|
|
1650
1847
|
}
|
|
1651
1848
|
success(`Gateway restarted (${version}).`);
|
|
1652
1849
|
} else {
|
|
1653
|
-
const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-
|
|
1850
|
+
const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-XVYW5WFJ.js");
|
|
1654
1851
|
const requested = versionArg ?? args.channel;
|
|
1655
1852
|
const version = requested ? await guardOpenclawVersion(requested) : await defaultOpenclawVersion();
|
|
1656
1853
|
const spin = spinner(`Updating gateway to ${version}...`);
|
|
@@ -1793,8 +1990,8 @@ var backup_default = defineCommand15({
|
|
|
1793
1990
|
yes: { type: "boolean", description: "[restore] Skip confirmation prompt" }
|
|
1794
1991
|
},
|
|
1795
1992
|
async run({ args }) {
|
|
1796
|
-
const { buildContext } = await import("./context-
|
|
1797
|
-
const { acquireSession, drainPool } = await import("./pool-
|
|
1993
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
1994
|
+
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
1798
1995
|
const action = args.action;
|
|
1799
1996
|
if (action !== "create" && action !== "restore") {
|
|
1800
1997
|
throw new UsageError(`Unknown action: ${action}. Use "create" or "restore"`);
|
|
@@ -2004,7 +2201,7 @@ var stacks_default = defineCommand16({
|
|
|
2004
2201
|
let isDeployed = false;
|
|
2005
2202
|
if (!args.force) {
|
|
2006
2203
|
try {
|
|
2007
|
-
const { buildContext } = await import("./context-
|
|
2204
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
2008
2205
|
const ctx = buildContext({ stack: name });
|
|
2009
2206
|
if (ctx.adapter.name === "local") {
|
|
2010
2207
|
isDeployed = !!ctx.localState;
|
|
@@ -2072,6 +2269,8 @@ var doctor_default = defineCommand17({
|
|
|
2072
2269
|
},
|
|
2073
2270
|
args: {
|
|
2074
2271
|
stack: { type: "string", description: "Stack name to include remote health checks" },
|
|
2272
|
+
provider: { type: "string", description: "Check this provider's credentials and account setup, with or without a stack" },
|
|
2273
|
+
"instance-type": { type: "string", description: "Ask account checks about this machine size rather than the provider default" },
|
|
2075
2274
|
json: { type: "boolean", description: "Emit the report as JSON" }
|
|
2076
2275
|
},
|
|
2077
2276
|
async run({ args }) {
|
|
@@ -2081,7 +2280,12 @@ var doctor_default = defineCommand17({
|
|
|
2081
2280
|
process19.on("SIGTERM", abort);
|
|
2082
2281
|
let report;
|
|
2083
2282
|
try {
|
|
2084
|
-
report = await runDiagnostics({
|
|
2283
|
+
report = await runDiagnostics({
|
|
2284
|
+
stack: args.stack,
|
|
2285
|
+
provider: typeof args.provider === "string" ? args.provider : void 0,
|
|
2286
|
+
instanceType: typeof args["instance-type"] === "string" ? args["instance-type"] : void 0,
|
|
2287
|
+
signal: ac.signal
|
|
2288
|
+
});
|
|
2085
2289
|
} finally {
|
|
2086
2290
|
process19.off("SIGINT", abort);
|
|
2087
2291
|
process19.off("SIGTERM", abort);
|
|
@@ -2114,7 +2318,7 @@ ${section.title}
|
|
|
2114
2318
|
process19.stdout.write("\n");
|
|
2115
2319
|
}
|
|
2116
2320
|
function line(check) {
|
|
2117
|
-
const label = check.detail ? `${check.name.padEnd(NAME_COLUMN)}${check.detail}` : check.name;
|
|
2321
|
+
const label = check.detail ? `${check.name.padEnd(NAME_COLUMN)}${check.name.length >= NAME_COLUMN ? " " : ""}${check.detail}` : check.name;
|
|
2118
2322
|
const emit = { pass: success, fail: failure, warn, info }[check.status];
|
|
2119
2323
|
emit(label);
|
|
2120
2324
|
if (check.remedy) info(` ${check.remedy}`);
|
|
@@ -2138,7 +2342,7 @@ var serveCmd = defineCommand18({
|
|
|
2138
2342
|
inspector: { type: "boolean", description: "Enable MCP inspector" }
|
|
2139
2343
|
},
|
|
2140
2344
|
async run({ args }) {
|
|
2141
|
-
const { serveMcp } = await import("./server-
|
|
2345
|
+
const { serveMcp } = await import("./server-GBA7BJGK.js");
|
|
2142
2346
|
await serveMcp({
|
|
2143
2347
|
port: args.http ? Number(args.http) : void 0,
|
|
2144
2348
|
bind: args.bind,
|
|
@@ -2216,11 +2420,11 @@ var wireCmd = defineCommand18({
|
|
|
2216
2420
|
rewire: { type: "boolean", description: "Replace an existing clawops entry" }
|
|
2217
2421
|
},
|
|
2218
2422
|
async run({ args }) {
|
|
2219
|
-
const { buildContext } = await import("./context-
|
|
2220
|
-
const { acquireSession, drainPool } = await import("./pool-
|
|
2423
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
2424
|
+
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
2221
2425
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
2222
|
-
const { wireGatewayMcp } = await import("./mcp-wire-
|
|
2223
|
-
const { MCP_HTTP_PORT } = await import("./server-
|
|
2426
|
+
const { wireGatewayMcp } = await import("./mcp-wire-3SWBM7D6.js");
|
|
2427
|
+
const { MCP_HTTP_PORT } = await import("./server-GBA7BJGK.js");
|
|
2224
2428
|
const ac = new AbortController();
|
|
2225
2429
|
process20.on("SIGINT", () => {
|
|
2226
2430
|
ac.abort();
|
|
@@ -2321,7 +2525,7 @@ import process21 from "process";
|
|
|
2321
2525
|
import os from "os";
|
|
2322
2526
|
import { randomBytes } from "crypto";
|
|
2323
2527
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
2324
|
-
import { execSync, spawnSync, spawn as spawn2 } from "child_process";
|
|
2528
|
+
import { execSync, spawnSync as spawnSync2, spawn as spawn2 } from "child_process";
|
|
2325
2529
|
import path2 from "path";
|
|
2326
2530
|
var setup_default = defineCommand19({
|
|
2327
2531
|
meta: {
|
|
@@ -2433,6 +2637,14 @@ var setup_default = defineCommand19({
|
|
|
2433
2637
|
}
|
|
2434
2638
|
process21.stdout.write("\n");
|
|
2435
2639
|
const sshCidrDefault = provider === "local" ? void 0 : await detectSshCidrDefault();
|
|
2640
|
+
const scopeAccount = provider === "local" ? void 0 : await resolveScopeAccount(provider);
|
|
2641
|
+
if (provider !== "local" && provider !== "azure" && !scopeAccount) {
|
|
2642
|
+
const needed = deriveStateBucket(provider, {});
|
|
2643
|
+
if (!needed.ok) {
|
|
2644
|
+
warn(`clawops cannot suggest a name for the state backend: it needs ${needed.needs}.`);
|
|
2645
|
+
info("Type the name of a bucket you already have, or quit, authenticate, and start again.\n");
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2436
2648
|
const stackAnswers = await inquirer.prompt([
|
|
2437
2649
|
{
|
|
2438
2650
|
type: "input",
|
|
@@ -2454,12 +2666,7 @@ var setup_default = defineCommand19({
|
|
|
2454
2666
|
message: "Server size: (bigger = faster, but costs more per month)",
|
|
2455
2667
|
choices: instanceChoices(provider)
|
|
2456
2668
|
},
|
|
2457
|
-
|
|
2458
|
-
type: "input",
|
|
2459
|
-
name: "stateBucket",
|
|
2460
|
-
message: `${stateLabel(provider)} bucket name: (a storage bucket that tracks what's deployed \u2014 create one first if you haven't)`,
|
|
2461
|
-
validate: (v) => v.trim() !== "" || "Required \u2014 create a bucket in your cloud console first"
|
|
2462
|
-
},
|
|
2669
|
+
stateBucketQuestion(provider, scopeAccount),
|
|
2463
2670
|
{
|
|
2464
2671
|
type: "input",
|
|
2465
2672
|
name: "sshKeyPath",
|
|
@@ -2485,6 +2692,18 @@ var setup_default = defineCommand19({
|
|
|
2485
2692
|
default: "latest"
|
|
2486
2693
|
}
|
|
2487
2694
|
]);
|
|
2695
|
+
if (provider !== "local") {
|
|
2696
|
+
await runAccountPreflight({
|
|
2697
|
+
provider,
|
|
2698
|
+
region: typeof stackAnswers.region === "string" ? stackAnswers.region : void 0,
|
|
2699
|
+
bucket: stackAnswers.stateBucket,
|
|
2700
|
+
// The size was chosen three questions ago. Azure offers SKU families per subscription,
|
|
2701
|
+
// so asking about the provider default here would clear a size the operator is not
|
|
2702
|
+
// deploying and say nothing about the one they are.
|
|
2703
|
+
instanceType: typeof stackAnswers.instanceSize === "string" ? stackAnswers.instanceSize : void 0,
|
|
2704
|
+
inquirer
|
|
2705
|
+
});
|
|
2706
|
+
}
|
|
2488
2707
|
if (provider !== "local" && admitsInternet(stackAnswers.sshCidr)) {
|
|
2489
2708
|
warn(
|
|
2490
2709
|
"SSH will be reachable from anywhere on the internet. `clawops harden` reports this as a finding; narrowing it to your own IP is the usual fix."
|
|
@@ -2502,6 +2721,8 @@ var setup_default = defineCommand19({
|
|
|
2502
2721
|
const modelProvider = catalogs.models.find((p) => p.id === modelProviderId);
|
|
2503
2722
|
const modelConfig = {};
|
|
2504
2723
|
const secrets = [];
|
|
2724
|
+
let resolvedModelId;
|
|
2725
|
+
let bedrockRegion;
|
|
2505
2726
|
const { selectedModelId } = await inquirer.prompt([{
|
|
2506
2727
|
type: "list",
|
|
2507
2728
|
name: "selectedModelId",
|
|
@@ -2521,7 +2742,22 @@ var setup_default = defineCommand19({
|
|
|
2521
2742
|
} else if (modelProvider.credentialSource === "aws-profile") {
|
|
2522
2743
|
process21.stdout.write("\n");
|
|
2523
2744
|
info(modelProvider.iamNote ?? "Bedrock uses your AWS server's IAM role \u2014 no API key needed.");
|
|
2524
|
-
|
|
2745
|
+
bedrockRegion = typeof stackAnswers.region === "string" && stackAnswers.region ? stackAnswers.region : defaultRegion("aws");
|
|
2746
|
+
const { fetchInferenceProfiles, resolveInferenceProfile } = await import("./bedrock-XVASJX2K.js");
|
|
2747
|
+
const listed = await fetchInferenceProfiles(bedrockRegion);
|
|
2748
|
+
if (!listed.ok) {
|
|
2749
|
+
warn(listed.error);
|
|
2750
|
+
info("Continuing with the foundation-model id; Bedrock will refuse it until this is fixed.");
|
|
2751
|
+
} else {
|
|
2752
|
+
const wanted = selectedModel.modelId ?? selectedModel.id;
|
|
2753
|
+
const resolution = resolveInferenceProfile(wanted, listed.profiles, bedrockRegion);
|
|
2754
|
+
if (!resolution.ok) {
|
|
2755
|
+
const { UsageError: UsageError2 } = await import("./errors-OK47MQFD.js");
|
|
2756
|
+
throw new UsageError2(resolution.error);
|
|
2757
|
+
}
|
|
2758
|
+
resolvedModelId = resolution.profileId;
|
|
2759
|
+
info(`Using inference profile ${resolution.profileId} (${resolution.why}).`);
|
|
2760
|
+
}
|
|
2525
2761
|
} else if (modelProvider.id === "ollama") {
|
|
2526
2762
|
const { baseUrl } = await inquirer.prompt([{
|
|
2527
2763
|
type: "input",
|
|
@@ -2535,11 +2771,15 @@ var setup_default = defineCommand19({
|
|
|
2535
2771
|
}
|
|
2536
2772
|
modelConfig["baseUrl"] = rewritten;
|
|
2537
2773
|
}
|
|
2538
|
-
const
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2774
|
+
const { buildModelsBlock } = await import("./models-AGTQWYDL.js");
|
|
2775
|
+
const builtModelConfig = buildModelsBlock({
|
|
2776
|
+
provider: modelProvider,
|
|
2777
|
+
model: { id: selectedModel.id, modelId: selectedModel.modelId, displayName: selectedModel.displayName },
|
|
2778
|
+
resolvedModelId,
|
|
2779
|
+
apiKeyRef: typeof modelConfig["apiKey"] === "string" ? modelConfig["apiKey"] : void 0,
|
|
2780
|
+
baseUrl: typeof modelConfig["baseUrl"] === "string" ? modelConfig["baseUrl"] : void 0,
|
|
2781
|
+
region: modelProvider.credentialSource === "aws-profile" ? bedrockRegion : void 0
|
|
2782
|
+
});
|
|
2543
2783
|
process21.stdout.write("\n");
|
|
2544
2784
|
info("Use \u2191\u2193 to move, Space to select/deselect, Enter to confirm.");
|
|
2545
2785
|
const { selectedIntegrationIds } = await inquirer.prompt([{
|
|
@@ -2603,7 +2843,7 @@ var setup_default = defineCommand19({
|
|
|
2603
2843
|
const gatewayToken = randomBytes(24).toString("hex");
|
|
2604
2844
|
const secretsDir2 = path2.join(os.homedir(), ".clawops", "secrets");
|
|
2605
2845
|
mkdirSync2(secretsDir2, { recursive: true });
|
|
2606
|
-
|
|
2846
|
+
spawnSync2("chmod", ["700", secretsDir2], { stdio: "ignore" });
|
|
2607
2847
|
const tokenPath = path2.join(secretsDir2, `GATEWAY_TOKEN_${stackAnswers.stackName}`);
|
|
2608
2848
|
writeFileSync3(tokenPath, gatewayToken, { encoding: "utf-8", mode: 384 });
|
|
2609
2849
|
const openclawConfigOverlay = {
|
|
@@ -2784,7 +3024,7 @@ var setup_default = defineCommand19({
|
|
|
2784
3024
|
default: false
|
|
2785
3025
|
}]);
|
|
2786
3026
|
if (applyNow) {
|
|
2787
|
-
const { applyPlan } = await import("./apply-
|
|
3027
|
+
const { applyPlan } = await import("./apply-KYFPWI5U.js");
|
|
2788
3028
|
const plan = JSON.parse(readFileSync3(outputPath, "utf-8"));
|
|
2789
3029
|
try {
|
|
2790
3030
|
await applyPlan(plan, {
|
|
@@ -2813,9 +3053,9 @@ To deploy later: clawops apply ${outputPath}`);
|
|
|
2813
3053
|
});
|
|
2814
3054
|
async function runLocalDeploy(opts) {
|
|
2815
3055
|
const { setConfig: setConfig2, getConfig: getConfig2, getConfigDir: getConfigDir2 } = await import("./store-SDUR52Z5.js");
|
|
2816
|
-
const { localBootstrap } = await import("./bootstrap-
|
|
2817
|
-
const { connect } = await import("./ssh-
|
|
2818
|
-
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-
|
|
3056
|
+
const { localBootstrap } = await import("./bootstrap-HNGKY6WW.js");
|
|
3057
|
+
const { connect } = await import("./ssh-VAI5DU5M.js");
|
|
3058
|
+
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-3HZLUWEA.js");
|
|
2819
3059
|
const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
|
|
2820
3060
|
const knownHostsPath = path2.join(getConfigDir2(), "known_hosts");
|
|
2821
3061
|
const existing = getConfig2();
|
|
@@ -2896,7 +3136,7 @@ async function runLocalDeploy(opts) {
|
|
|
2896
3136
|
spin2.fail("Configuration failed");
|
|
2897
3137
|
throw err;
|
|
2898
3138
|
}
|
|
2899
|
-
const { drainPool } = await import("./pool-
|
|
3139
|
+
const { drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
2900
3140
|
drainPool();
|
|
2901
3141
|
const tokenPath = path2.join(os.homedir(), ".clawops", "secrets", `GATEWAY_TOKEN_${opts.stackName}`);
|
|
2902
3142
|
const dashboardUrl = `${state.gatewayUrl}?token=${opts.gatewayToken}`;
|
|
@@ -2956,7 +3196,7 @@ ${cfg.loginHint}`);
|
|
|
2956
3196
|
}]);
|
|
2957
3197
|
if (runNow) {
|
|
2958
3198
|
process21.stdout.write("\n");
|
|
2959
|
-
const result =
|
|
3199
|
+
const result = spawnSync2(cfg.loginCmd[0], cfg.loginCmd.slice(1), { stdio: "inherit" });
|
|
2960
3200
|
if (result.error) {
|
|
2961
3201
|
failure(`Could not launch ${cliName} \u2014 is it installed? (${result.error.message})`);
|
|
2962
3202
|
info(`Install guide: ${cliInstallUrl(provider)}`);
|
|
@@ -3001,7 +3241,7 @@ async function promptSecret(name, label, envDefault, inquirer) {
|
|
|
3001
3241
|
validate: (v) => v.trim() !== "" || "Value cannot be empty"
|
|
3002
3242
|
}]);
|
|
3003
3243
|
mkdirSync2(secretsDir2, { recursive: true });
|
|
3004
|
-
|
|
3244
|
+
spawnSync2("chmod", ["700", secretsDir2], { stdio: "ignore" });
|
|
3005
3245
|
const secretPath2 = path2.join(secretsDir2, name);
|
|
3006
3246
|
writeFileSync3(secretPath2, secretValue.trim(), { encoding: "utf-8", mode: 384 });
|
|
3007
3247
|
success(`Secret saved to ${secretPath2} (chmod 600)`);
|
|
@@ -3034,7 +3274,7 @@ var DOCKER_CHECK_CMD = [
|
|
|
3034
3274
|
"echo NOT_RUNNING"
|
|
3035
3275
|
].join("; ");
|
|
3036
3276
|
async function checkDockerPreflight(opts) {
|
|
3037
|
-
const { acquireSession } = await import("./pool-
|
|
3277
|
+
const { acquireSession } = await import("./pool-PIIKJ4L2.js");
|
|
3038
3278
|
const { ProviderError } = await import("./errors-OK47MQFD.js");
|
|
3039
3279
|
const spin = spinner("Checking Docker on target host...");
|
|
3040
3280
|
const timeoutSignal = AbortSignal.timeout(DOCKER_PREFLIGHT_TIMEOUT_MS);
|
|
@@ -3081,7 +3321,7 @@ async function checkDockerPreflight(opts) {
|
|
|
3081
3321
|
var DOCKER_POLL_MS = 3e3;
|
|
3082
3322
|
var DOCKER_TIMEOUT_MS = 9e4;
|
|
3083
3323
|
async function startRemoteDocker(opts, spin) {
|
|
3084
|
-
const { acquireSession } = await import("./pool-
|
|
3324
|
+
const { acquireSession } = await import("./pool-PIIKJ4L2.js");
|
|
3085
3325
|
const { ProviderError } = await import("./errors-OK47MQFD.js");
|
|
3086
3326
|
spin.text = "Starting Docker on the remote server...";
|
|
3087
3327
|
const { session, release } = await acquireSession({
|
|
@@ -3178,7 +3418,7 @@ async function startDockerAndWait(inquirer) {
|
|
|
3178
3418
|
await pollUntilDockerReady("Waiting for Docker daemon...");
|
|
3179
3419
|
} else if (choice === "systemctl") {
|
|
3180
3420
|
const spin = spinner("Running: sudo systemctl start docker...");
|
|
3181
|
-
const res =
|
|
3421
|
+
const res = spawnSync2("sudo", ["systemctl", "start", "docker"], { stdio: "pipe" });
|
|
3182
3422
|
if (res.status !== 0) {
|
|
3183
3423
|
spin.fail("systemctl start docker failed.");
|
|
3184
3424
|
throw new Error("systemctl start docker failed");
|
|
@@ -3186,18 +3426,18 @@ async function startDockerAndWait(inquirer) {
|
|
|
3186
3426
|
spin.succeed("Docker service started.");
|
|
3187
3427
|
await pollUntilDockerReady("Waiting for Docker daemon to be reachable...");
|
|
3188
3428
|
} else if (choice === "desktop") {
|
|
3189
|
-
const backendRunning =
|
|
3429
|
+
const backendRunning = spawnSync2("pgrep", ["-f", "com.docker.backend"], { stdio: "ignore" }).status === 0;
|
|
3190
3430
|
if (backendRunning) {
|
|
3191
3431
|
const killSpin = spinner("Docker Desktop is stuck \u2014 force-restarting...");
|
|
3192
3432
|
for (const pat of ["com.docker.backend", "com.docker.virtualization", "com.docker.build", "Docker Desktop"]) {
|
|
3193
|
-
|
|
3433
|
+
spawnSync2("pkill", ["-9", "-f", pat], { stdio: "ignore" });
|
|
3194
3434
|
}
|
|
3195
3435
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
3196
3436
|
killSpin.succeed("Old Docker processes cleared.");
|
|
3197
3437
|
}
|
|
3198
3438
|
const openSpin = spinner("Launching Docker Desktop...");
|
|
3199
|
-
let opened =
|
|
3200
|
-
if (opened.status !== 0) opened =
|
|
3439
|
+
let opened = spawnSync2("open", ["-a", "Docker"], { stdio: "ignore" });
|
|
3440
|
+
if (opened.status !== 0) opened = spawnSync2("open", ["/Applications/Docker.app"], { stdio: "ignore" });
|
|
3201
3441
|
if (opened.status !== 0) {
|
|
3202
3442
|
openSpin.fail("Could not open Docker Desktop \u2014 is it installed in /Applications?");
|
|
3203
3443
|
throw new Error("Failed to open Docker Desktop");
|
|
@@ -3206,7 +3446,7 @@ async function startDockerAndWait(inquirer) {
|
|
|
3206
3446
|
await pollUntilDockerReady("Waiting for Docker engine to start...");
|
|
3207
3447
|
} else if (choice === "colima") {
|
|
3208
3448
|
const spin = spinner("Running: colima start...");
|
|
3209
|
-
const res =
|
|
3449
|
+
const res = spawnSync2("colima", ["start"], { stdio: "pipe" });
|
|
3210
3450
|
if (res.status !== 0) {
|
|
3211
3451
|
spin.fail("colima start failed \u2014 is Colima installed? (brew install colima docker)");
|
|
3212
3452
|
throw new Error("colima start failed");
|
|
@@ -3239,7 +3479,7 @@ async function ensureAuthorizedKey(keyPath, host, inquirer) {
|
|
|
3239
3479
|
mkdirSync2(path2.join(os.homedir(), ".ssh"), { recursive: true });
|
|
3240
3480
|
const entry = existing.endsWith("\n") || existing === "" ? pubKey + "\n" : "\n" + pubKey + "\n";
|
|
3241
3481
|
writeFileSync3(authorizedKeysPath, existing + entry, { encoding: "utf-8", flag: "a" });
|
|
3242
|
-
|
|
3482
|
+
spawnSync2("chmod", ["600", authorizedKeysPath], { stdio: "ignore" });
|
|
3243
3483
|
success("Public key added to ~/.ssh/authorized_keys.");
|
|
3244
3484
|
} else {
|
|
3245
3485
|
info("SSH will likely fail. Add this line to ~/.ssh/authorized_keys manually:");
|
|
@@ -3255,7 +3495,7 @@ async function ensureAuthorizedKey(keyPath, host, inquirer) {
|
|
|
3255
3495
|
async function generateSshKey() {
|
|
3256
3496
|
const keyPath = path2.join(os.homedir(), ".ssh", "id_ed25519");
|
|
3257
3497
|
mkdirSync2(path2.join(os.homedir(), ".ssh"), { recursive: true });
|
|
3258
|
-
const result =
|
|
3498
|
+
const result = spawnSync2("ssh-keygen", ["-t", "ed25519", "-f", keyPath, "-N", "", "-C", "clawops"], {
|
|
3259
3499
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3260
3500
|
});
|
|
3261
3501
|
if (result.status !== 0) {
|
|
@@ -3286,9 +3526,9 @@ async function maybeWireGatewayMcp(opts) {
|
|
|
3286
3526
|
process21.stdout.write("\n");
|
|
3287
3527
|
const { wireGateway } = await inquirerPromptWire(opts.inquirer);
|
|
3288
3528
|
if (!wireGateway) return;
|
|
3289
|
-
const { connect } = await import("./ssh-
|
|
3529
|
+
const { connect } = await import("./ssh-VAI5DU5M.js");
|
|
3290
3530
|
const { getConfigDir: getConfigDir2 } = await import("./store-SDUR52Z5.js");
|
|
3291
|
-
const { wireGatewayMcp } = await import("./mcp-wire-
|
|
3531
|
+
const { wireGatewayMcp } = await import("./mcp-wire-3SWBM7D6.js");
|
|
3292
3532
|
const knownHostsPath = path2.join(getConfigDir2(), "known_hosts");
|
|
3293
3533
|
const spin = spinner("Wiring gateway MCP client...");
|
|
3294
3534
|
const session = await connect({
|
|
@@ -3362,6 +3602,21 @@ function defaultRegion(provider) {
|
|
|
3362
3602
|
if (provider === "azure") return "eastus";
|
|
3363
3603
|
return "";
|
|
3364
3604
|
}
|
|
3605
|
+
function stateBucketQuestion(provider, account) {
|
|
3606
|
+
return {
|
|
3607
|
+
type: "input",
|
|
3608
|
+
name: "stateBucket",
|
|
3609
|
+
message: `${stateLabel(provider)} name: (where clawops records what it deployed \u2014 press Enter to accept)`,
|
|
3610
|
+
default: (answers) => {
|
|
3611
|
+
const derived = deriveStateBucket(provider, {
|
|
3612
|
+
account,
|
|
3613
|
+
region: answers.region ?? defaultRegion(provider)
|
|
3614
|
+
});
|
|
3615
|
+
return derived.ok ? derived.name : void 0;
|
|
3616
|
+
},
|
|
3617
|
+
validate: (v) => validateStateBucket(provider, v)
|
|
3618
|
+
};
|
|
3619
|
+
}
|
|
3365
3620
|
function stateLabel(provider) {
|
|
3366
3621
|
if (provider === "aws") return "S3";
|
|
3367
3622
|
if (provider === "gcp") return "GCS";
|
|
@@ -3392,7 +3647,7 @@ function instanceChoices(provider) {
|
|
|
3392
3647
|
return table[provider] ?? [];
|
|
3393
3648
|
}
|
|
3394
3649
|
async function maybeHarden(opts) {
|
|
3395
|
-
const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-
|
|
3650
|
+
const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-72JMRT4E.js");
|
|
3396
3651
|
const { getConfig: getConfig2 } = await import("./store-SDUR52Z5.js");
|
|
3397
3652
|
const config = getConfig2();
|
|
3398
3653
|
if (!config) return;
|
|
@@ -3416,7 +3671,7 @@ async function maybeHarden(opts) {
|
|
|
3416
3671
|
const modules = availableModules.filter((m) => selectedIds.includes(m.id));
|
|
3417
3672
|
process21.stdout.write("\nApplying hardening modules...\n\n");
|
|
3418
3673
|
try {
|
|
3419
|
-
const { buildContext } = await import("./context-
|
|
3674
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
3420
3675
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
3421
3676
|
const ctx = buildContext({ stack: opts.stackName });
|
|
3422
3677
|
const stackObj = await ctx.getStack();
|
|
@@ -3454,8 +3709,8 @@ async function maybeHarden(opts) {
|
|
|
3454
3709
|
}
|
|
3455
3710
|
}
|
|
3456
3711
|
async function detectSshCidrDefault() {
|
|
3457
|
-
const { detectEgressIp } = await import("./firewall-
|
|
3458
|
-
const result = await detectEgressIp("https://ifconfig.me");
|
|
3712
|
+
const { detectEgressIp } = await import("./firewall-5VKZ4WBH.js");
|
|
3713
|
+
const result = await detectEgressIp("https://ifconfig.me/ip");
|
|
3459
3714
|
if (!result.ok) return void 0;
|
|
3460
3715
|
const ip = result.ip.trim();
|
|
3461
3716
|
return ip === "" ? void 0 : ip.includes("/") ? ip : `${ip}/32`;
|
|
@@ -3479,11 +3734,68 @@ function wizardChannels(integrations) {
|
|
|
3479
3734
|
function startChannelConfig(integ) {
|
|
3480
3735
|
return { ...integ.defaults ?? {} };
|
|
3481
3736
|
}
|
|
3737
|
+
async function runAccountPreflight(opts) {
|
|
3738
|
+
const { getProvider } = await import("./providers-FW6H6OPF.js");
|
|
3739
|
+
let checks;
|
|
3740
|
+
try {
|
|
3741
|
+
const adapter = getProvider(opts.provider);
|
|
3742
|
+
if (!adapter.preflight) return;
|
|
3743
|
+
checks = await adapter.preflight({
|
|
3744
|
+
region: opts.region,
|
|
3745
|
+
bucket: opts.bucket,
|
|
3746
|
+
instanceType: opts.instanceType
|
|
3747
|
+
});
|
|
3748
|
+
} catch (err) {
|
|
3749
|
+
warn(`Could not check the ${opts.provider} account: ${err instanceof Error ? err.message : String(err)}`);
|
|
3750
|
+
return;
|
|
3751
|
+
}
|
|
3752
|
+
const unanswered = checks.filter((c) => c.unknown);
|
|
3753
|
+
const failed = checks.filter((c) => !c.ok && !c.unknown);
|
|
3754
|
+
if (unanswered.length > 0) {
|
|
3755
|
+
process21.stdout.write("\n");
|
|
3756
|
+
for (const check of unanswered) {
|
|
3757
|
+
warn(`Could not check: ${check.label}`);
|
|
3758
|
+
if (check.detail) info(` ${check.detail}`);
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
if (failed.length === 0) {
|
|
3762
|
+
success(
|
|
3763
|
+
unanswered.length > 0 ? `${opts.provider} account is ready, as far as clawops could tell.` : `${opts.provider} account is ready.`
|
|
3764
|
+
);
|
|
3765
|
+
process21.stdout.write("\n");
|
|
3766
|
+
return;
|
|
3767
|
+
}
|
|
3768
|
+
process21.stdout.write("\n");
|
|
3769
|
+
for (const check of failed) {
|
|
3770
|
+
warn(check.label);
|
|
3771
|
+
if (check.detail) info(` ${check.detail}`);
|
|
3772
|
+
if (!check.fix) {
|
|
3773
|
+
continue;
|
|
3774
|
+
}
|
|
3775
|
+
const { apply } = await opts.inquirer.prompt([{
|
|
3776
|
+
type: "confirm",
|
|
3777
|
+
name: "apply",
|
|
3778
|
+
message: `Fix this now? ${check.mutates}`,
|
|
3779
|
+
default: true
|
|
3780
|
+
}]);
|
|
3781
|
+
if (!apply) {
|
|
3782
|
+
info(" Left as is. The deploy will fail until this is resolved.");
|
|
3783
|
+
continue;
|
|
3784
|
+
}
|
|
3785
|
+
try {
|
|
3786
|
+
await check.fix();
|
|
3787
|
+
success(` Done \u2014 ${check.mutates}`);
|
|
3788
|
+
} catch (err) {
|
|
3789
|
+
failure(` Could not fix it: ${err instanceof Error ? err.message : String(err)}`);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
process21.stdout.write("\n");
|
|
3793
|
+
}
|
|
3482
3794
|
|
|
3483
3795
|
// src/cli/commands/secret.ts
|
|
3484
3796
|
import { defineCommand as defineCommand20 } from "citty";
|
|
3485
3797
|
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync4, unlinkSync, mkdirSync as mkdirSync3, statSync } from "fs";
|
|
3486
|
-
import { spawnSync as
|
|
3798
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
3487
3799
|
import path3 from "path";
|
|
3488
3800
|
import os2 from "os";
|
|
3489
3801
|
import process22 from "process";
|
|
@@ -3560,7 +3872,7 @@ var setCmd = defineCommand20({
|
|
|
3560
3872
|
value = secretValue;
|
|
3561
3873
|
}
|
|
3562
3874
|
mkdirSync3(secretsDir(), { recursive: true });
|
|
3563
|
-
|
|
3875
|
+
spawnSync3("chmod", ["700", secretsDir()], { stdio: "ignore" });
|
|
3564
3876
|
writeFileSync4(secretPath(name), value.trim(), { encoding: "utf-8", mode: 384 });
|
|
3565
3877
|
success(`Secret "${name}" saved to ${secretPath(name)} (chmod 600)`);
|
|
3566
3878
|
info("Run `clawops secret rotate <name>` to propagate it to a running stack.");
|
|
@@ -3631,7 +3943,7 @@ var rotateCmd = defineCommand20({
|
|
|
3631
3943
|
value = secretValue;
|
|
3632
3944
|
}
|
|
3633
3945
|
mkdirSync3(secretsDir(), { recursive: true });
|
|
3634
|
-
|
|
3946
|
+
spawnSync3("chmod", ["700", secretsDir()], { stdio: "ignore" });
|
|
3635
3947
|
writeFileSync4(secretPath(name), value.trim(), { encoding: "utf-8", mode: 384 });
|
|
3636
3948
|
success(`Secret "${name}" updated.`);
|
|
3637
3949
|
const { getConfig: getConfig2 } = await import("./store-SDUR52Z5.js");
|
|
@@ -3651,10 +3963,10 @@ var rotateCmd = defineCommand20({
|
|
|
3651
3963
|
}
|
|
3652
3964
|
info(`Re-applying config overlay to stack "${targetStack}"\u2026`);
|
|
3653
3965
|
try {
|
|
3654
|
-
const { buildContext } = await import("./context-
|
|
3655
|
-
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-
|
|
3966
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
3967
|
+
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-3HZLUWEA.js");
|
|
3656
3968
|
const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
|
|
3657
|
-
const { acquireSession, drainPool } = await import("./pool-
|
|
3969
|
+
const { acquireSession, drainPool } = await import("./pool-PIIKJ4L2.js");
|
|
3658
3970
|
const { localStateToConnectionInfo } = await import("./state-PRBIIN7I.js");
|
|
3659
3971
|
const ctx = buildContext({ stack: targetStack });
|
|
3660
3972
|
let conn;
|
|
@@ -3838,7 +4150,7 @@ import { accessSync, constants } from "fs";
|
|
|
3838
4150
|
import { execSync as execSync2 } from "child_process";
|
|
3839
4151
|
var ISSUES_URL = `${REPO_URL}/issues/new`;
|
|
3840
4152
|
var BODY_CAP = 2e3;
|
|
3841
|
-
var VERSION = true ? "2.0.
|
|
4153
|
+
var VERSION = true ? "2.0.1" : "dev";
|
|
3842
4154
|
async function collectContext() {
|
|
3843
4155
|
const nodeVersion = process23.version;
|
|
3844
4156
|
const os3 = `${process23.platform} ${process23.arch}`;
|
|
@@ -3955,8 +4267,8 @@ var harden_default = defineCommand23({
|
|
|
3955
4267
|
json: { type: "boolean", description: "Emit structured JSON output" }
|
|
3956
4268
|
},
|
|
3957
4269
|
async run({ args }) {
|
|
3958
|
-
const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-
|
|
3959
|
-
const { buildContext } = await import("./context-
|
|
4270
|
+
const { MODULE_CATALOG, resolveModules, runHardening, formatHardenSummary } = await import("./harden-72JMRT4E.js");
|
|
4271
|
+
const { buildContext } = await import("./context-7EVC6W45.js");
|
|
3960
4272
|
const { getConfig: getConfig2 } = await import("./store-SDUR52Z5.js");
|
|
3961
4273
|
if (args.list) {
|
|
3962
4274
|
process24.stdout.write("\nAvailable hardening modules:\n\n");
|
|
@@ -4078,7 +4390,7 @@ function handleError(err) {
|
|
|
4078
4390
|
}
|
|
4079
4391
|
|
|
4080
4392
|
// src/cli/index.ts
|
|
4081
|
-
var CLI_VERSION = true ? "2.0.
|
|
4393
|
+
var CLI_VERSION = true ? "2.0.1" : "dev";
|
|
4082
4394
|
var main = defineCommand24({
|
|
4083
4395
|
meta: {
|
|
4084
4396
|
name: "clawops",
|