@clawops/cli 1.2.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -1
- package/dist/{apply-KBF7OPWL.js → apply-5RREMN3L.js} +5 -4
- package/dist/{aws-KQBWY43W.js → aws-FRE2JVAZ.js} +9 -4
- package/dist/{azure-4EJFVJZL.js → azure-PVC3AQVC.js} +11 -4
- package/dist/{bootstrap-UYCOEJQX.js → bootstrap-G4UZ2FKH.js} +3 -3
- package/dist/chunk-6ZFIFDBJ.js +41 -0
- package/dist/{chunk-BRPU7AQC.js → chunk-A2I76FTA.js} +13 -1
- package/dist/chunk-JYQZJMD3.js +764 -0
- package/dist/{chunk-ACYJBSLJ.js → chunk-T5EX55GP.js} +4 -4
- package/dist/{chunk-SSB6SGPQ.js → chunk-UAA6YOOP.js} +2 -2
- package/dist/{chunk-UDNZUSKA.js → chunk-ZFNPM2WG.js} +1 -1
- package/dist/{chunk-LU63NZD3.js → chunk-ZONXY3C6.js} +6 -2
- package/dist/cli.js +369 -79
- package/dist/{context-VUAYRAJY.js → context-PSGEX2D7.js} +2 -2
- package/dist/{gcp-SKURG3L6.js → gcp-BXDTC6EK.js} +60 -17
- package/dist/{generate-PZPLG3ZM.js → generate-Z7DBMANX.js} +3 -3
- package/dist/overlay-store-ADZPD7EU.js +11 -0
- package/dist/{package-SE3M4NJA.js → package-WAJRGBBM.js} +2 -1
- package/dist/{remote-config-JQ77SRC2.js → remote-config-QF5TT7GU.js} +1 -1
- package/dist/{server-4DNHLGEG.js → server-IWLZJ4EU.js} +98 -24
- package/dist/state-PRBIIN7I.js +17 -0
- package/package.json +2 -1
- package/dist/chunk-QJ6ERXHN.js +0 -266
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
validateOpenclawConfig
|
|
4
|
-
} from "./chunk-QJ6ERXHN.js";
|
|
5
2
|
import {
|
|
6
3
|
MCP_APPS,
|
|
7
4
|
buildMcpEntry,
|
|
8
5
|
writeAppConfigs
|
|
9
6
|
} from "./chunk-OIGTOLB3.js";
|
|
10
|
-
import
|
|
11
|
-
|
|
7
|
+
import {
|
|
8
|
+
chalk,
|
|
9
|
+
failure,
|
|
10
|
+
info,
|
|
11
|
+
monitor_default,
|
|
12
|
+
spinner,
|
|
13
|
+
success,
|
|
14
|
+
validateOpenclawConfig,
|
|
15
|
+
warn
|
|
16
|
+
} from "./chunk-JYQZJMD3.js";
|
|
17
|
+
import "./chunk-T5EX55GP.js";
|
|
12
18
|
import "./chunk-ZVOEQCNW.js";
|
|
13
19
|
import "./chunk-4U3LTLWZ.js";
|
|
14
|
-
import "./chunk-
|
|
20
|
+
import "./chunk-ZFNPM2WG.js";
|
|
21
|
+
import "./chunk-A2I76FTA.js";
|
|
15
22
|
import {
|
|
16
23
|
getConfig,
|
|
17
24
|
getConfigDir,
|
|
@@ -24,7 +31,7 @@ import {
|
|
|
24
31
|
} from "./chunk-KGXPLI7W.js";
|
|
25
32
|
|
|
26
33
|
// src/cli/index.ts
|
|
27
|
-
import { defineCommand as
|
|
34
|
+
import { defineCommand as defineCommand21, runMain } from "citty";
|
|
28
35
|
|
|
29
36
|
// src/cli/commands/init.ts
|
|
30
37
|
import { defineCommand } from "citty";
|
|
@@ -32,27 +39,6 @@ import { generateKeyPairSync } from "crypto";
|
|
|
32
39
|
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
33
40
|
import path from "path";
|
|
34
41
|
import process2 from "process";
|
|
35
|
-
|
|
36
|
-
// src/output/human.ts
|
|
37
|
-
import chalk from "chalk";
|
|
38
|
-
import ora from "ora";
|
|
39
|
-
function success(msg) {
|
|
40
|
-
console.log(chalk.green("\u2713") + " " + msg);
|
|
41
|
-
}
|
|
42
|
-
function failure(msg) {
|
|
43
|
-
console.error(chalk.red("\u2717") + " " + msg);
|
|
44
|
-
}
|
|
45
|
-
function warn(msg) {
|
|
46
|
-
console.warn(chalk.yellow("\u26A0") + " " + msg);
|
|
47
|
-
}
|
|
48
|
-
function info(msg) {
|
|
49
|
-
console.log(chalk.blue("\u2139") + " " + msg);
|
|
50
|
-
}
|
|
51
|
-
function spinner(text) {
|
|
52
|
-
return ora(text).start();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// src/cli/commands/init.ts
|
|
56
42
|
var SUPPORTED_PROVIDERS = ["gcp", "aws", "azure", "local"];
|
|
57
43
|
var PROVIDER_DEFAULTS = {
|
|
58
44
|
gcp: { region: "us-central1", credEnv: "GOOGLE_APPLICATION_CREDENTIALS", stateScheme: "gs://" },
|
|
@@ -217,7 +203,7 @@ var up_default = defineCommand2({
|
|
|
217
203
|
config: { type: "string", description: "Path to openclaw config overlay JSON (local provider only)" }
|
|
218
204
|
},
|
|
219
205
|
async run({ args }) {
|
|
220
|
-
const { buildContext } = await import("./context-
|
|
206
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
221
207
|
const ctx = buildContext(args);
|
|
222
208
|
const openclawVersion = typeof args["openclaw-version"] === "string" ? args["openclaw-version"] : "stable";
|
|
223
209
|
if (ctx.adapter.name === "local") {
|
|
@@ -228,7 +214,7 @@ var up_default = defineCommand2({
|
|
|
228
214
|
);
|
|
229
215
|
}
|
|
230
216
|
const { localOpts } = stackConfig;
|
|
231
|
-
const { localBootstrap } = await import("./bootstrap-
|
|
217
|
+
const { localBootstrap } = await import("./bootstrap-G4UZ2FKH.js");
|
|
232
218
|
const abortController = new AbortController();
|
|
233
219
|
process3.on("SIGINT", () => abortController.abort());
|
|
234
220
|
process3.on("SIGTERM", () => abortController.abort());
|
|
@@ -323,7 +309,7 @@ var up_default = defineCommand2({
|
|
|
323
309
|
async function applyLocalConfigOverlay(opts) {
|
|
324
310
|
const { connect } = await import("./ssh-IQXNME3D.js");
|
|
325
311
|
const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
|
|
326
|
-
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-
|
|
312
|
+
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-QF5TT7GU.js");
|
|
327
313
|
let overlay;
|
|
328
314
|
try {
|
|
329
315
|
overlay = JSON.parse(readFileSync(opts.configPath, "utf-8"));
|
|
@@ -363,7 +349,7 @@ var down_default = defineCommand3({
|
|
|
363
349
|
"dry-run": { type: "boolean", description: "Show what would be destroyed without destroying" }
|
|
364
350
|
},
|
|
365
351
|
async run({ args }) {
|
|
366
|
-
const { buildContext } = await import("./context-
|
|
352
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
367
353
|
const ctx = buildContext(args);
|
|
368
354
|
if (args["dry-run"]) {
|
|
369
355
|
info(`Dry run \u2014 would destroy stack "${ctx.stackName}"`);
|
|
@@ -427,7 +413,7 @@ var status_default = defineCommand4({
|
|
|
427
413
|
json: { type: "boolean", description: "Emit JSON" }
|
|
428
414
|
},
|
|
429
415
|
async run({ args }) {
|
|
430
|
-
const { buildContext } = await import("./context-
|
|
416
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
431
417
|
const ctx = buildContext(args);
|
|
432
418
|
if (ctx.adapter.name === "local") {
|
|
433
419
|
const state = ctx.localState;
|
|
@@ -499,8 +485,8 @@ var plan_default = defineCommand5({
|
|
|
499
485
|
out: { type: "string", description: "Write plan JSON to this absolute path (default: stdout)" }
|
|
500
486
|
},
|
|
501
487
|
async run({ args }) {
|
|
502
|
-
const { buildContext } = await import("./context-
|
|
503
|
-
const { generatePlan } = await import("./generate-
|
|
488
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
489
|
+
const { generatePlan } = await import("./generate-Z7DBMANX.js");
|
|
504
490
|
const ctx = buildContext(args);
|
|
505
491
|
if (ctx.adapter.name === "local") {
|
|
506
492
|
throw new UsageError(
|
|
@@ -590,7 +576,7 @@ var apply_default = defineCommand6({
|
|
|
590
576
|
},
|
|
591
577
|
async run({ args }) {
|
|
592
578
|
const { validatePlan } = await import("./validate-T5M5EHSJ.js");
|
|
593
|
-
const { applyPlan } = await import("./apply-
|
|
579
|
+
const { applyPlan } = await import("./apply-5RREMN3L.js");
|
|
594
580
|
const planPath = args._?.[0];
|
|
595
581
|
if (!planPath) {
|
|
596
582
|
throw new UsageError("Usage: clawops apply <plan.json>");
|
|
@@ -706,7 +692,7 @@ var destroy_default = defineCommand7({
|
|
|
706
692
|
"dry-run": { type: "boolean", description: "Show what would be destroyed without destroying" }
|
|
707
693
|
},
|
|
708
694
|
async run({ args }) {
|
|
709
|
-
const { buildContext } = await import("./context-
|
|
695
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
710
696
|
const ctx = buildContext(args);
|
|
711
697
|
if (ctx.adapter.name === "local") {
|
|
712
698
|
throw new UsageError(
|
|
@@ -773,7 +759,7 @@ var ssh_default = defineCommand8({
|
|
|
773
759
|
command: { type: "string", description: "Remote command to run (instead of interactive shell)" }
|
|
774
760
|
},
|
|
775
761
|
async run({ args }) {
|
|
776
|
-
const { buildContext } = await import("./context-
|
|
762
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
777
763
|
const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
|
|
778
764
|
const ctx = buildContext(args);
|
|
779
765
|
let conn;
|
|
@@ -860,7 +846,7 @@ var tunnel_default = defineCommand9({
|
|
|
860
846
|
"no-open": { type: "boolean", description: "Do not open browser after tunnel is ready" }
|
|
861
847
|
},
|
|
862
848
|
async run({ args }) {
|
|
863
|
-
const { buildContext } = await import("./context-
|
|
849
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
864
850
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
865
851
|
const { acquireSession } = await import("./pool-FBFHATDG.js");
|
|
866
852
|
const ctx = buildContext(args);
|
|
@@ -928,7 +914,7 @@ var logs_default = defineCommand10({
|
|
|
928
914
|
since: { type: "string", description: "Show logs since duration (e.g. 5m, 1h)" }
|
|
929
915
|
},
|
|
930
916
|
async run({ args }) {
|
|
931
|
-
const { buildContext } = await import("./context-
|
|
917
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
932
918
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
933
919
|
const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
|
|
934
920
|
const ctx = buildContext(args);
|
|
@@ -1045,7 +1031,7 @@ var config_default = defineCommand11({
|
|
|
1045
1031
|
"dry-run": { type: "boolean", description: "Show what would change without writing" }
|
|
1046
1032
|
},
|
|
1047
1033
|
async run({ args }) {
|
|
1048
|
-
const { buildContext } = await import("./context-
|
|
1034
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
1049
1035
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1050
1036
|
const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
|
|
1051
1037
|
const [action, key, value] = args._ ?? [];
|
|
@@ -1171,7 +1157,7 @@ var agents_default = defineCommand12({
|
|
|
1171
1157
|
json: { type: "boolean", description: "Emit JSON (for list)" }
|
|
1172
1158
|
},
|
|
1173
1159
|
async run({ args }) {
|
|
1174
|
-
const { buildContext } = await import("./context-
|
|
1160
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
1175
1161
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1176
1162
|
const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
|
|
1177
1163
|
const [action, name] = args._ ?? [];
|
|
@@ -1275,7 +1261,7 @@ var gateway_default = defineCommand13({
|
|
|
1275
1261
|
json: { type: "boolean", description: "Emit JSON (for status)" }
|
|
1276
1262
|
},
|
|
1277
1263
|
async run({ args }) {
|
|
1278
|
-
const { buildContext } = await import("./context-
|
|
1264
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
1279
1265
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1280
1266
|
const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
|
|
1281
1267
|
const [action, versionArg] = args._ ?? [];
|
|
@@ -1388,7 +1374,7 @@ var backup_default = defineCommand14({
|
|
|
1388
1374
|
yes: { type: "boolean", description: "[restore] Skip confirmation prompt" }
|
|
1389
1375
|
},
|
|
1390
1376
|
async run({ args }) {
|
|
1391
|
-
const { buildContext } = await import("./context-
|
|
1377
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
1392
1378
|
const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
|
|
1393
1379
|
const action = args.action;
|
|
1394
1380
|
if (action !== "create" && action !== "restore") {
|
|
@@ -1500,7 +1486,7 @@ var stacks_default = defineCommand15({
|
|
|
1500
1486
|
args: {
|
|
1501
1487
|
json: { type: "boolean", description: "Emit JSON (for list)" },
|
|
1502
1488
|
yes: { type: "boolean", description: "Skip confirmation prompt on delete" },
|
|
1503
|
-
force: { type: "boolean", description: "
|
|
1489
|
+
force: { type: "boolean", description: "Skip safety checks (allow deleting default or still-deployed stacks)" }
|
|
1504
1490
|
},
|
|
1505
1491
|
async run({ args }) {
|
|
1506
1492
|
const [action, name] = args._ ?? [];
|
|
@@ -1558,6 +1544,31 @@ var stacks_default = defineCommand15({
|
|
|
1558
1544
|
`"${name}" is the default stack. Use --force to delete it (clawops will switch the default to another stack).`
|
|
1559
1545
|
);
|
|
1560
1546
|
}
|
|
1547
|
+
let isDeployed = false;
|
|
1548
|
+
if (!args.force) {
|
|
1549
|
+
try {
|
|
1550
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
1551
|
+
const ctx = buildContext({ stack: name });
|
|
1552
|
+
if (ctx.adapter.name === "local") {
|
|
1553
|
+
isDeployed = !!ctx.localState;
|
|
1554
|
+
} else {
|
|
1555
|
+
const stack = await ctx.getStack();
|
|
1556
|
+
const outputMap = await stack.outputs();
|
|
1557
|
+
const outputs = Object.fromEntries(
|
|
1558
|
+
Object.entries(outputMap).map(([k, v]) => [k, v.value])
|
|
1559
|
+
);
|
|
1560
|
+
isDeployed = !!outputs["publicIp"];
|
|
1561
|
+
}
|
|
1562
|
+
} catch {
|
|
1563
|
+
warn(`Could not verify deployment status for "${name}" \u2014 proceeding anyway.`);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
if (isDeployed) {
|
|
1567
|
+
failure(
|
|
1568
|
+
`Stack "${name}" is still deployed. Run \`clawops down --stack ${name}\` first to destroy cloud resources, or pass --force to remove from registry only.`
|
|
1569
|
+
);
|
|
1570
|
+
process17.exit(1);
|
|
1571
|
+
}
|
|
1561
1572
|
warn(
|
|
1562
1573
|
`This removes "${name}" from clawops config only. Cloud resources are NOT destroyed. Run \`clawops destroy --stack ` + name + "` first if you want to remove cloud resources."
|
|
1563
1574
|
);
|
|
@@ -1657,9 +1668,9 @@ var doctor_default = defineCommand16({
|
|
|
1657
1668
|
info("Credential checks skipped \u2014 no config");
|
|
1658
1669
|
} else {
|
|
1659
1670
|
const { getProvider } = await import("./providers-2OABPW2E.js");
|
|
1660
|
-
await import("./aws-
|
|
1661
|
-
await import("./gcp-
|
|
1662
|
-
await import("./azure-
|
|
1671
|
+
await import("./aws-FRE2JVAZ.js");
|
|
1672
|
+
await import("./gcp-BXDTC6EK.js");
|
|
1673
|
+
await import("./azure-PVC3AQVC.js");
|
|
1663
1674
|
await import("./local-DXBEVZ5C.js");
|
|
1664
1675
|
const checkedProviders = /* @__PURE__ */ new Set();
|
|
1665
1676
|
for (const [stackName, stackCfg] of Object.entries(config.stacks)) {
|
|
@@ -1694,7 +1705,7 @@ var doctor_default = defineCommand16({
|
|
|
1694
1705
|
info("Remote health skipped \u2014 no config");
|
|
1695
1706
|
} else {
|
|
1696
1707
|
try {
|
|
1697
|
-
const { buildContext } = await import("./context-
|
|
1708
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
1698
1709
|
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
1699
1710
|
const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
|
|
1700
1711
|
const ctx = buildContext({ stack: args.stack });
|
|
@@ -1802,7 +1813,7 @@ var serveCmd = defineCommand17({
|
|
|
1802
1813
|
inspector: { type: "boolean", description: "Enable MCP inspector" }
|
|
1803
1814
|
},
|
|
1804
1815
|
async run({ args }) {
|
|
1805
|
-
const { serveMcp } = await import("./server-
|
|
1816
|
+
const { serveMcp } = await import("./server-IWLZJ4EU.js");
|
|
1806
1817
|
await serveMcp({
|
|
1807
1818
|
port: args.http ? Number(args.http) : void 0,
|
|
1808
1819
|
bind: args.bind,
|
|
@@ -1876,12 +1887,6 @@ var mcp_default = defineCommand17({
|
|
|
1876
1887
|
subCommands: {
|
|
1877
1888
|
serve: serveCmd,
|
|
1878
1889
|
install: installCmd
|
|
1879
|
-
},
|
|
1880
|
-
run() {
|
|
1881
|
-
process19.stdout.write("Usage: clawops mcp <serve | install>\n\n");
|
|
1882
|
-
process19.stdout.write(" serve Start the MCP server (stdio or HTTP)\n");
|
|
1883
|
-
process19.stdout.write(" install Wire clawops into AI editors interactively\n\n");
|
|
1884
|
-
process19.stdout.write("Run `clawops mcp <command> --help` for flags.\n");
|
|
1885
1890
|
}
|
|
1886
1891
|
});
|
|
1887
1892
|
|
|
@@ -2148,10 +2153,10 @@ var setup_default = defineCommand18({
|
|
|
2148
2153
|
}
|
|
2149
2154
|
}
|
|
2150
2155
|
const gatewayToken = randomBytes(24).toString("hex");
|
|
2151
|
-
const
|
|
2152
|
-
mkdirSync3(
|
|
2153
|
-
spawnSync("chmod", ["700",
|
|
2154
|
-
const tokenPath = path3.join(
|
|
2156
|
+
const secretsDir2 = path3.join(os.homedir(), ".clawops", "secrets");
|
|
2157
|
+
mkdirSync3(secretsDir2, { recursive: true });
|
|
2158
|
+
spawnSync("chmod", ["700", secretsDir2], { stdio: "ignore" });
|
|
2159
|
+
const tokenPath = path3.join(secretsDir2, `GATEWAY_TOKEN_${stackAnswers.stackName}`);
|
|
2155
2160
|
writeFileSync3(tokenPath, gatewayToken, { encoding: "utf-8", mode: 384 });
|
|
2156
2161
|
const openclawConfigOverlay = {
|
|
2157
2162
|
models: builtModelConfig,
|
|
@@ -2309,7 +2314,7 @@ var setup_default = defineCommand18({
|
|
|
2309
2314
|
default: false
|
|
2310
2315
|
}]);
|
|
2311
2316
|
if (applyNow) {
|
|
2312
|
-
const { applyPlan } = await import("./apply-
|
|
2317
|
+
const { applyPlan } = await import("./apply-5RREMN3L.js");
|
|
2313
2318
|
const plan = JSON.parse(readFileSync3(outputPath, "utf-8"));
|
|
2314
2319
|
try {
|
|
2315
2320
|
await applyPlan(plan, {
|
|
@@ -2331,9 +2336,9 @@ To deploy later: clawops apply ${outputPath}`);
|
|
|
2331
2336
|
});
|
|
2332
2337
|
async function runLocalDeploy(opts) {
|
|
2333
2338
|
const { setConfig: setConfig2, getConfig: getConfig2, getConfigDir: getConfigDir2 } = await import("./store-SDUR52Z5.js");
|
|
2334
|
-
const { localBootstrap } = await import("./bootstrap-
|
|
2339
|
+
const { localBootstrap } = await import("./bootstrap-G4UZ2FKH.js");
|
|
2335
2340
|
const { connect } = await import("./ssh-IQXNME3D.js");
|
|
2336
|
-
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-
|
|
2341
|
+
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-QF5TT7GU.js");
|
|
2337
2342
|
const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
|
|
2338
2343
|
const knownHostsPath = path3.join(getConfigDir2(), "known_hosts");
|
|
2339
2344
|
const existing = getConfig2();
|
|
@@ -2404,6 +2409,8 @@ async function runLocalDeploy(opts) {
|
|
|
2404
2409
|
const merged = deepMerge(remote, resolved);
|
|
2405
2410
|
await atomicWriteConfig(session, merged, opts.signal);
|
|
2406
2411
|
await restartGateway(session, opts.signal);
|
|
2412
|
+
const { saveOverlay } = await import("./overlay-store-ADZPD7EU.js");
|
|
2413
|
+
saveOverlay(opts.stackName, opts.overlay, opts.secrets);
|
|
2407
2414
|
} finally {
|
|
2408
2415
|
session.close();
|
|
2409
2416
|
}
|
|
@@ -2497,7 +2504,7 @@ function cliInstallUrl(provider) {
|
|
|
2497
2504
|
return "https://learn.microsoft.com/en-us/cli/azure/install-azure-cli";
|
|
2498
2505
|
}
|
|
2499
2506
|
async function promptSecret(name, label, envDefault, inquirer) {
|
|
2500
|
-
const
|
|
2507
|
+
const secretsDir2 = path3.join(os.homedir(), ".clawops", "secrets");
|
|
2501
2508
|
const { secretSource } = await inquirer.prompt([{
|
|
2502
2509
|
type: "list",
|
|
2503
2510
|
name: "secretSource",
|
|
@@ -2515,12 +2522,12 @@ async function promptSecret(name, label, envDefault, inquirer) {
|
|
|
2515
2522
|
message: `Paste your ${label}: (input is hidden)`,
|
|
2516
2523
|
validate: (v) => v.trim() !== "" || "Value cannot be empty"
|
|
2517
2524
|
}]);
|
|
2518
|
-
mkdirSync3(
|
|
2519
|
-
spawnSync("chmod", ["700",
|
|
2520
|
-
const
|
|
2521
|
-
writeFileSync3(
|
|
2522
|
-
success(`Secret saved to ${
|
|
2523
|
-
return { name, source: "file", ref:
|
|
2525
|
+
mkdirSync3(secretsDir2, { recursive: true });
|
|
2526
|
+
spawnSync("chmod", ["700", secretsDir2], { stdio: "ignore" });
|
|
2527
|
+
const secretPath2 = path3.join(secretsDir2, name);
|
|
2528
|
+
writeFileSync3(secretPath2, secretValue.trim(), { encoding: "utf-8", mode: 384 });
|
|
2529
|
+
success(`Secret saved to ${secretPath2} (chmod 600)`);
|
|
2530
|
+
return { name, source: "file", ref: secretPath2 };
|
|
2524
2531
|
}
|
|
2525
2532
|
if (secretSource === "env") {
|
|
2526
2533
|
const { envVar } = await inquirer.prompt([{
|
|
@@ -2845,8 +2852,287 @@ function instanceChoices(provider) {
|
|
|
2845
2852
|
return table[provider] ?? [];
|
|
2846
2853
|
}
|
|
2847
2854
|
|
|
2848
|
-
// src/cli/commands/
|
|
2855
|
+
// src/cli/commands/secret.ts
|
|
2849
2856
|
import { defineCommand as defineCommand19 } from "citty";
|
|
2857
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync4, unlinkSync, mkdirSync as mkdirSync4, statSync } from "fs";
|
|
2858
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
2859
|
+
import path4 from "path";
|
|
2860
|
+
import os2 from "os";
|
|
2861
|
+
import process21 from "process";
|
|
2862
|
+
var SECRETS_DIR = path4.join(os2.homedir(), ".clawops", "secrets");
|
|
2863
|
+
function secretsDir() {
|
|
2864
|
+
return SECRETS_DIR;
|
|
2865
|
+
}
|
|
2866
|
+
function secretPath(name) {
|
|
2867
|
+
return path4.join(secretsDir(), name);
|
|
2868
|
+
}
|
|
2869
|
+
function listSecretNames() {
|
|
2870
|
+
if (!existsSync3(secretsDir())) return [];
|
|
2871
|
+
return readdirSync(secretsDir()).filter((f) => !f.startsWith("."));
|
|
2872
|
+
}
|
|
2873
|
+
var listCmd = defineCommand19({
|
|
2874
|
+
meta: { name: "list", description: "List all stored secrets and their status" },
|
|
2875
|
+
args: {
|
|
2876
|
+
json: { type: "boolean", description: "Emit JSON" }
|
|
2877
|
+
},
|
|
2878
|
+
run({ args }) {
|
|
2879
|
+
const names = listSecretNames();
|
|
2880
|
+
if (names.length === 0) {
|
|
2881
|
+
info("No secrets stored in ~/.clawops/secrets/");
|
|
2882
|
+
info("Run `clawops secret set <name>` to add one.");
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
const rows = names.map((name) => {
|
|
2886
|
+
const p = secretPath(name);
|
|
2887
|
+
try {
|
|
2888
|
+
const stat = statSync(p);
|
|
2889
|
+
const content = readFileSync4(p, "utf-8").trim();
|
|
2890
|
+
const status = content.length > 0 ? "ok" : "empty";
|
|
2891
|
+
const modified = stat.mtime.toISOString().slice(0, 10);
|
|
2892
|
+
return { name, status, source: "file", path: p, modified, resolvable: content.length > 0 };
|
|
2893
|
+
} catch {
|
|
2894
|
+
return { name, status: "unreadable", source: "file", path: p, modified: "\u2014", resolvable: false };
|
|
2895
|
+
}
|
|
2896
|
+
});
|
|
2897
|
+
if (args.json) {
|
|
2898
|
+
process21.stdout.write(JSON.stringify(rows, null, 2) + "\n");
|
|
2899
|
+
return;
|
|
2900
|
+
}
|
|
2901
|
+
const tableRows = rows.map((r) => [
|
|
2902
|
+
r.name,
|
|
2903
|
+
r.resolvable ? chalk.green("ok") : chalk.red(r.status),
|
|
2904
|
+
r.modified,
|
|
2905
|
+
r.path
|
|
2906
|
+
]);
|
|
2907
|
+
process21.stdout.write("\n" + renderTable(["Name", "Status", "Modified", "Path"], tableRows) + "\n\n");
|
|
2908
|
+
}
|
|
2909
|
+
});
|
|
2910
|
+
var setCmd = defineCommand19({
|
|
2911
|
+
meta: { name: "set", description: "Create or update a secret" },
|
|
2912
|
+
args: {
|
|
2913
|
+
value: { type: "string", description: "Secret value (skips interactive prompt)" }
|
|
2914
|
+
},
|
|
2915
|
+
async run({ args }) {
|
|
2916
|
+
const [name] = args._ ?? [];
|
|
2917
|
+
if (!name) {
|
|
2918
|
+
failure("Usage: clawops secret set <name>");
|
|
2919
|
+
process21.exit(2);
|
|
2920
|
+
}
|
|
2921
|
+
const inquirer = (await import("inquirer")).default;
|
|
2922
|
+
let value;
|
|
2923
|
+
if (args.value) {
|
|
2924
|
+
value = args.value;
|
|
2925
|
+
} else {
|
|
2926
|
+
const { secretValue } = await inquirer.prompt([{
|
|
2927
|
+
type: "password",
|
|
2928
|
+
name: "secretValue",
|
|
2929
|
+
message: `Value for secret "${name}": (input is hidden)`,
|
|
2930
|
+
validate: (v) => typeof v === "string" && v.trim() !== "" || "Value cannot be empty"
|
|
2931
|
+
}]);
|
|
2932
|
+
value = secretValue;
|
|
2933
|
+
}
|
|
2934
|
+
mkdirSync4(secretsDir(), { recursive: true });
|
|
2935
|
+
spawnSync2("chmod", ["700", secretsDir()], { stdio: "ignore" });
|
|
2936
|
+
writeFileSync4(secretPath(name), value.trim(), { encoding: "utf-8", mode: 384 });
|
|
2937
|
+
success(`Secret "${name}" saved to ${secretPath(name)} (chmod 600)`);
|
|
2938
|
+
info("Run `clawops secret rotate <name>` to propagate it to a running stack.");
|
|
2939
|
+
}
|
|
2940
|
+
});
|
|
2941
|
+
var deleteCmd = defineCommand19({
|
|
2942
|
+
meta: { name: "delete", description: "Remove a stored secret" },
|
|
2943
|
+
args: {
|
|
2944
|
+
yes: { type: "boolean", description: "Skip confirmation prompt" }
|
|
2945
|
+
},
|
|
2946
|
+
async run({ args }) {
|
|
2947
|
+
const [name] = args._ ?? [];
|
|
2948
|
+
if (!name) {
|
|
2949
|
+
failure("Usage: clawops secret delete <name>");
|
|
2950
|
+
process21.exit(2);
|
|
2951
|
+
}
|
|
2952
|
+
const p = secretPath(name);
|
|
2953
|
+
if (!existsSync3(p)) {
|
|
2954
|
+
failure(`Secret "${name}" not found at ${p}`);
|
|
2955
|
+
process21.exit(1);
|
|
2956
|
+
}
|
|
2957
|
+
const { listOverlays } = await import("./overlay-store-ADZPD7EU.js");
|
|
2958
|
+
const referencingStacks = listOverlays().filter((o) => o.secrets.some((s) => s.name === name)).map((o) => o.stackName);
|
|
2959
|
+
if (referencingStacks.length > 0) {
|
|
2960
|
+
warn(`Secret "${name}" is referenced by stack(s): ${referencingStacks.join(", ")}`);
|
|
2961
|
+
warn("Deleting it will leave those stacks with an unresolvable $secret: ref.");
|
|
2962
|
+
}
|
|
2963
|
+
if (!args.yes) {
|
|
2964
|
+
const inquirer = (await import("inquirer")).default;
|
|
2965
|
+
const { confirmed } = await inquirer.prompt([{
|
|
2966
|
+
type: "confirm",
|
|
2967
|
+
name: "confirmed",
|
|
2968
|
+
message: `Delete secret "${name}"?`,
|
|
2969
|
+
default: false
|
|
2970
|
+
}]);
|
|
2971
|
+
if (!confirmed) {
|
|
2972
|
+
info("Aborted.");
|
|
2973
|
+
return;
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
unlinkSync(p);
|
|
2977
|
+
success(`Secret "${name}" deleted.`);
|
|
2978
|
+
}
|
|
2979
|
+
});
|
|
2980
|
+
var rotateCmd = defineCommand19({
|
|
2981
|
+
meta: { name: "rotate", description: "Update a secret and re-apply config to a running stack" },
|
|
2982
|
+
args: {
|
|
2983
|
+
stack: { type: "string", description: "Stack name to re-apply (defaults to config default)" },
|
|
2984
|
+
value: { type: "string", description: "New secret value (skips interactive prompt)" }
|
|
2985
|
+
},
|
|
2986
|
+
async run({ args }) {
|
|
2987
|
+
const [name] = args._ ?? [];
|
|
2988
|
+
if (!name) {
|
|
2989
|
+
failure("Usage: clawops secret rotate <name> [--stack <name>]");
|
|
2990
|
+
process21.exit(2);
|
|
2991
|
+
}
|
|
2992
|
+
const inquirer = (await import("inquirer")).default;
|
|
2993
|
+
let value;
|
|
2994
|
+
if (args.value) {
|
|
2995
|
+
value = args.value;
|
|
2996
|
+
} else {
|
|
2997
|
+
const { secretValue } = await inquirer.prompt([{
|
|
2998
|
+
type: "password",
|
|
2999
|
+
name: "secretValue",
|
|
3000
|
+
message: `New value for secret "${name}": (input is hidden)`,
|
|
3001
|
+
validate: (v) => typeof v === "string" && v.trim() !== "" || "Value cannot be empty"
|
|
3002
|
+
}]);
|
|
3003
|
+
value = secretValue;
|
|
3004
|
+
}
|
|
3005
|
+
mkdirSync4(secretsDir(), { recursive: true });
|
|
3006
|
+
spawnSync2("chmod", ["700", secretsDir()], { stdio: "ignore" });
|
|
3007
|
+
writeFileSync4(secretPath(name), value.trim(), { encoding: "utf-8", mode: 384 });
|
|
3008
|
+
success(`Secret "${name}" updated.`);
|
|
3009
|
+
const { getConfig: getConfig2 } = await import("./store-SDUR52Z5.js");
|
|
3010
|
+
const cfg = getConfig2();
|
|
3011
|
+
const targetStack = args.stack ?? cfg?.defaults?.stack;
|
|
3012
|
+
if (!targetStack) {
|
|
3013
|
+
warn("No stack specified and no default stack in config \u2014 skipping re-apply.");
|
|
3014
|
+
info("Run `clawops secret rotate <name> --stack <name>` to re-apply to a specific stack.");
|
|
3015
|
+
return;
|
|
3016
|
+
}
|
|
3017
|
+
const { loadOverlay } = await import("./overlay-store-ADZPD7EU.js");
|
|
3018
|
+
const stored = loadOverlay(targetStack);
|
|
3019
|
+
if (!stored) {
|
|
3020
|
+
warn(`No stored overlay for stack "${targetStack}" \u2014 cannot re-apply automatically.`);
|
|
3021
|
+
info("Re-run `clawops setup` or `clawops apply` to propagate the new secret.");
|
|
3022
|
+
return;
|
|
3023
|
+
}
|
|
3024
|
+
info(`Re-applying config overlay to stack "${targetStack}"\u2026`);
|
|
3025
|
+
try {
|
|
3026
|
+
const { buildContext } = await import("./context-PSGEX2D7.js");
|
|
3027
|
+
const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-QF5TT7GU.js");
|
|
3028
|
+
const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
|
|
3029
|
+
const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
|
|
3030
|
+
const { localStateToConnectionInfo } = await import("./state-PRBIIN7I.js");
|
|
3031
|
+
const ctx = buildContext({ stack: targetStack });
|
|
3032
|
+
let conn;
|
|
3033
|
+
if (ctx.adapter.name === "local") {
|
|
3034
|
+
if (!ctx.localState) throw new Error(`Stack "${targetStack}" has not been bootstrapped yet.`);
|
|
3035
|
+
conn = localStateToConnectionInfo(ctx.localState);
|
|
3036
|
+
} else {
|
|
3037
|
+
const stack = await ctx.getStack();
|
|
3038
|
+
const outputMap = await stack.outputs();
|
|
3039
|
+
const outputs = Object.fromEntries(
|
|
3040
|
+
Object.entries(outputMap).map(([k, v]) => [k, v.value])
|
|
3041
|
+
);
|
|
3042
|
+
const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
|
|
3043
|
+
const base = extractBaseOutputs(outputs);
|
|
3044
|
+
conn = ctx.adapter.getConnectionInfo({ ...base, privateKeyPath: ctx.config.ssh.keyPath, knownHostsPath: ctx.config.ssh.knownHostsPath });
|
|
3045
|
+
}
|
|
3046
|
+
const { session, release } = await acquireSession(conn);
|
|
3047
|
+
try {
|
|
3048
|
+
const remote = await readRemoteConfig(session);
|
|
3049
|
+
const resolved = resolveSecrets(stored.overlay, stored.secrets);
|
|
3050
|
+
const merged = deepMerge(remote, resolved);
|
|
3051
|
+
await atomicWriteConfig(session, merged);
|
|
3052
|
+
await restartGateway(session);
|
|
3053
|
+
} finally {
|
|
3054
|
+
release();
|
|
3055
|
+
drainPool();
|
|
3056
|
+
}
|
|
3057
|
+
success(`Config overlay re-applied and gateway restarted on "${targetStack}".`);
|
|
3058
|
+
} catch (err) {
|
|
3059
|
+
failure(`Re-apply failed: ${err.message}`);
|
|
3060
|
+
info("The secret file has been updated. Re-run `clawops setup` or `clawops apply` to propagate.");
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
});
|
|
3064
|
+
var auditCmd = defineCommand19({
|
|
3065
|
+
meta: { name: "audit", description: "Report missing secrets and unresolvable $secret: refs" },
|
|
3066
|
+
args: {
|
|
3067
|
+
json: { type: "boolean", description: "Emit JSON" }
|
|
3068
|
+
},
|
|
3069
|
+
async run({ args }) {
|
|
3070
|
+
const { listOverlays } = await import("./overlay-store-ADZPD7EU.js");
|
|
3071
|
+
const overlays = listOverlays();
|
|
3072
|
+
const issues = [];
|
|
3073
|
+
for (const name of listSecretNames()) {
|
|
3074
|
+
const p = secretPath(name);
|
|
3075
|
+
try {
|
|
3076
|
+
const content = readFileSync4(p, "utf-8").trim();
|
|
3077
|
+
if (content.length === 0) {
|
|
3078
|
+
issues.push({ kind: "empty-secret", secret: name, detail: `File exists but is empty: ${p}` });
|
|
3079
|
+
}
|
|
3080
|
+
} catch {
|
|
3081
|
+
issues.push({ kind: "unreadable-secret", secret: name, detail: `Cannot read: ${p}` });
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
for (const overlay of overlays) {
|
|
3085
|
+
for (const s of overlay.secrets) {
|
|
3086
|
+
if (s.source === "file") {
|
|
3087
|
+
const ref = s.ref ?? secretPath(s.name);
|
|
3088
|
+
if (!existsSync3(ref)) {
|
|
3089
|
+
issues.push({ kind: "missing-file", stack: overlay.stackName, secret: s.name, detail: `File not found: ${ref}` });
|
|
3090
|
+
}
|
|
3091
|
+
} else if (s.source === "env") {
|
|
3092
|
+
const envVar = s.ref ?? s.name;
|
|
3093
|
+
if (!process21.env[envVar]) {
|
|
3094
|
+
issues.push({ kind: "missing-env", stack: overlay.stackName, secret: s.name, detail: `Env var not set: ${envVar}` });
|
|
3095
|
+
}
|
|
3096
|
+
} else {
|
|
3097
|
+
issues.push({ kind: "cloud-sm-unresolved", stack: overlay.stackName, secret: s.name, detail: `Source "${s.source}" is not auto-resolved \u2014 set manually` });
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
if (args.json) {
|
|
3102
|
+
process21.stdout.write(JSON.stringify({ issues, ok: issues.length === 0 }, null, 2) + "\n");
|
|
3103
|
+
return;
|
|
3104
|
+
}
|
|
3105
|
+
if (issues.length === 0) {
|
|
3106
|
+
success("All secrets are resolvable. No issues found.");
|
|
3107
|
+
return;
|
|
3108
|
+
}
|
|
3109
|
+
failure(`${issues.length} issue${issues.length === 1 ? "" : "s"} found:
|
|
3110
|
+
`);
|
|
3111
|
+
for (const issue of issues) {
|
|
3112
|
+
const prefix = issue.stack ? `[${issue.stack}] ` : "";
|
|
3113
|
+
warn(`${prefix}${issue.secret}: ${issue.detail}`);
|
|
3114
|
+
}
|
|
3115
|
+
process21.stdout.write("\n");
|
|
3116
|
+
info("Run `clawops secret set <name>` to update a missing or empty secret.");
|
|
3117
|
+
}
|
|
3118
|
+
});
|
|
3119
|
+
var secret_default = defineCommand19({
|
|
3120
|
+
meta: {
|
|
3121
|
+
name: "secret",
|
|
3122
|
+
description: "Manage clawops secrets (list | set | delete | rotate | audit)"
|
|
3123
|
+
},
|
|
3124
|
+
args: {},
|
|
3125
|
+
subCommands: {
|
|
3126
|
+
list: listCmd,
|
|
3127
|
+
set: setCmd,
|
|
3128
|
+
delete: deleteCmd,
|
|
3129
|
+
rotate: rotateCmd,
|
|
3130
|
+
audit: auditCmd
|
|
3131
|
+
}
|
|
3132
|
+
});
|
|
3133
|
+
|
|
3134
|
+
// src/cli/commands/help.ts
|
|
3135
|
+
import { defineCommand as defineCommand20 } from "citty";
|
|
2850
3136
|
var COMMANDS = [
|
|
2851
3137
|
["setup", "Interactive first-run wizard \u2014 configure, deploy, and wire AI apps"],
|
|
2852
3138
|
["init", "Write config and generate SSH key pair (non-interactive)"],
|
|
@@ -2866,6 +3152,8 @@ var COMMANDS = [
|
|
|
2866
3152
|
["stacks", "List named stacks and their state"],
|
|
2867
3153
|
["doctor", "Check Node version, config, SSH key, provider credentials, and Pulumi home"],
|
|
2868
3154
|
["mcp", "MCP server operations (mcp serve | mcp install)"],
|
|
3155
|
+
["secret", "Manage secrets (secret list | set | delete | rotate | audit)"],
|
|
3156
|
+
["monitor", "Live dashboard: gateway health, container status, resource usage, log tail"],
|
|
2869
3157
|
["help", "Show this help message"]
|
|
2870
3158
|
];
|
|
2871
3159
|
var GLOBAL_FLAGS = [
|
|
@@ -2876,7 +3164,7 @@ var GLOBAL_FLAGS = [
|
|
|
2876
3164
|
["--dry-run", "Preview without applying (mutating commands)"],
|
|
2877
3165
|
["--yes", "Skip interactive confirmations (CI mode)"]
|
|
2878
3166
|
];
|
|
2879
|
-
var help_default =
|
|
3167
|
+
var help_default = defineCommand20({
|
|
2880
3168
|
meta: {
|
|
2881
3169
|
name: "help",
|
|
2882
3170
|
description: "Show available commands and global flags"
|
|
@@ -2916,25 +3204,25 @@ ${dim("Run `clawops <command> --help` for command-specific flags.")}
|
|
|
2916
3204
|
});
|
|
2917
3205
|
|
|
2918
3206
|
// src/cli/error-handler.ts
|
|
2919
|
-
import
|
|
3207
|
+
import process22 from "process";
|
|
2920
3208
|
function handleError(err) {
|
|
2921
3209
|
if (err instanceof ClawopsError) {
|
|
2922
3210
|
failure(err.message);
|
|
2923
|
-
|
|
3211
|
+
process22.exit(err.exitCode);
|
|
2924
3212
|
}
|
|
2925
3213
|
if (err instanceof Error) {
|
|
2926
3214
|
failure(`Unexpected error: ${err.message}`);
|
|
2927
|
-
if (
|
|
2928
|
-
|
|
3215
|
+
if (process22.env["DEBUG"]) {
|
|
3216
|
+
process22.stderr.write(err.stack ?? "\n");
|
|
2929
3217
|
}
|
|
2930
|
-
|
|
3218
|
+
process22.exit(1);
|
|
2931
3219
|
}
|
|
2932
3220
|
failure(`Unknown error: ${String(err)}`);
|
|
2933
|
-
|
|
3221
|
+
process22.exit(1);
|
|
2934
3222
|
}
|
|
2935
3223
|
|
|
2936
3224
|
// src/cli/index.ts
|
|
2937
|
-
var main =
|
|
3225
|
+
var main = defineCommand21({
|
|
2938
3226
|
meta: {
|
|
2939
3227
|
name: "clawops",
|
|
2940
3228
|
description: "Deploy and manage self-hosted OpenClaw instances across clouds",
|
|
@@ -2968,6 +3256,8 @@ var main = defineCommand20({
|
|
|
2968
3256
|
doctor: doctor_default,
|
|
2969
3257
|
mcp: mcp_default,
|
|
2970
3258
|
setup: setup_default,
|
|
3259
|
+
secret: secret_default,
|
|
3260
|
+
monitor: monitor_default,
|
|
2971
3261
|
help: help_default
|
|
2972
3262
|
}
|
|
2973
3263
|
});
|