@clawops/cli 1.7.9 → 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 +504 -76
- package/dist/apply-KYFPWI5U.js +27 -0
- package/dist/automation-RMAIBNVC.js +44 -0
- package/dist/bedrock-XVASJX2K.js +61 -0
- package/dist/{bootstrap-HL4DNUNG.js → bootstrap-HNGKY6WW.js} +31 -19
- package/dist/bootstrap.sh.tmpl +51 -31
- package/dist/channels-KBUR42IU.js +53 -0
- package/dist/chunk-2I7KCMXZ.js +142 -0
- package/dist/{chunk-DQDAWNGB.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-BSLIYB5Q.js +106 -0
- package/dist/{chunk-3XK66DKK.js → chunk-E4NJSJ54.js} +57 -93
- package/dist/chunk-FEK7VJAY.js +16 -0
- package/dist/{chunk-UKTP332Z.js → chunk-GAZPPSKU.js} +6 -7
- package/dist/chunk-GIEVAGEK.js +126 -0
- package/dist/chunk-HSPHXG6W.js +67 -0
- package/dist/chunk-IVWP4QUJ.js +901 -0
- package/dist/{chunk-PVUVI35Y.js → chunk-JW5A7E3X.js} +5 -1
- package/dist/{chunk-GZN7PKP4.js → chunk-KC3GC3PE.js} +328 -121
- package/dist/chunk-LBE6COC4.js +244 -0
- package/dist/chunk-LEVIW2PV.js +890 -0
- package/dist/chunk-LT4XMO5D.js +50 -0
- package/dist/chunk-MC5ATCXS.js +108 -0
- package/dist/chunk-N4WZLWJ4.js +16 -0
- package/dist/chunk-NUDIHZEH.js +55 -0
- package/dist/chunk-NUL5F6YV.js +56 -0
- package/dist/chunk-RHOWYIWT.js +52 -0
- package/dist/chunk-STLRHLLV.js +193 -0
- 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 +1296 -695
- package/dist/config-validate-K2N6XLDX.js +76 -0
- package/dist/context-7EVC6W45.js +20 -0
- package/dist/docker-3PDO4TS7.js +12 -0
- package/dist/{firewall-XQGO7HWK.js → firewall-5VKZ4WBH.js} +32 -1
- package/dist/generate-I6YQ5V2T.js +29 -0
- package/dist/{harden-URPXLSVF.js → harden-72JMRT4E.js} +51 -11
- package/dist/health-5CCOXYGG.js +11 -0
- package/dist/index.d.ts +56 -0
- package/dist/known-hosts-file-I3Q3NBYQ.js +22 -0
- package/dist/logs-5A7YYA53.js +15 -0
- package/dist/{mcp-wire-7E5JQY47.js → mcp-wire-3SWBM7D6.js} +32 -16
- package/dist/models-AGTQWYDL.js +32 -0
- package/dist/network-args-PYJRJBYO.js +64 -0
- package/dist/{package-5AR4ZPK4.js → package-5BCMEBTE.js} +11 -4
- package/dist/passphrase-MJBE45N7.js +15 -0
- package/dist/plugins-DTST3OER.js +49 -0
- package/dist/{pool-QGFQRMGU.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/privileged-JFA6LLTV.js +17 -0
- package/dist/providers-FW6H6OPF.js +11 -0
- package/dist/ready-KDNRBNSU.js +95 -0
- package/dist/{remote-config-QDLN4UMG.js → remote-config-3HZLUWEA.js} +4 -3
- package/dist/runtime-X3KW5TCP.js +44 -0
- package/dist/server-GBA7BJGK.js +46 -0
- package/dist/spec-path-KQOOIQCA.js +9 -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/upgrade-ULD4KFYC.js +88 -0
- package/dist/validate-YVI3BJ55.js +14 -0
- package/dist/{version-guard-VUCNZXQS.js → version-guard-XVYW5WFJ.js} +2 -2
- package/dist/{versions-2UVAT6PQ.js → versions-W5LRQCAJ.js} +2 -2
- package/dist/wait-WO72TRRQ.js +79 -0
- package/package.json +11 -4
- package/spec/deploy-plan.schema.json +114 -25
- package/spec/integrations.yaml +135 -28
- package/spec/mcp-tools.yaml +89 -37
- package/spec/models.yaml +23 -3
- package/spec/openclaw-2.0.config.schema.json +1 -0
- package/spec/openclaw-versions.yaml +116 -27
- package/spec/providers.schema.json +163 -28
- package/dist/apply-F3KY2HTB.js +0 -17
- package/dist/automation-RULUSJBW.js +0 -30
- package/dist/aws-L4RR2LSE.js +0 -254
- package/dist/azure-7OXQENQL.js +0 -262
- package/dist/chunk-35WVQY3X.js +0 -122
- package/dist/chunk-6X45DDGH.js +0 -42
- package/dist/chunk-A4QR4TXW.js +0 -120
- package/dist/chunk-C5X375RO.js +0 -85
- package/dist/chunk-FNS56UQY.js +0 -741
- package/dist/chunk-P5LKWRPL.js +0 -114
- package/dist/chunk-SEZMVVAS.js +0 -156
- package/dist/context-5LCUG2QR.js +0 -10
- package/dist/gcp-UG6AWPIZ.js +0 -193
- package/dist/generate-OAGYT3VH.js +0 -15
- package/dist/local-DXBEVZ5C.js +0 -47
- package/dist/server-FIZCOLHL.js +0 -32
- package/dist/ssh-CNJNLM3E.js +0 -8
- package/dist/validate-CFQ4FH2T.js +0 -10
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
compareVersions
|
|
4
|
+
} from "./chunk-GAZPPSKU.js";
|
|
5
|
+
import "./chunk-JW5A7E3X.js";
|
|
6
|
+
import "./chunk-EI7CQQ7D.js";
|
|
7
|
+
|
|
8
|
+
// src/openclaw/config-validate.ts
|
|
9
|
+
var cached;
|
|
10
|
+
async function getValidator() {
|
|
11
|
+
if (cached) return cached;
|
|
12
|
+
const [{ default: Ajv }, { default: addFormats }, { readFileSync }, { join }, { resolveSpecDir }] = await Promise.all([
|
|
13
|
+
import("ajv"),
|
|
14
|
+
import("ajv-formats"),
|
|
15
|
+
import("fs"),
|
|
16
|
+
import("path"),
|
|
17
|
+
import("./spec-path-KQOOIQCA.js")
|
|
18
|
+
]);
|
|
19
|
+
const schema = JSON.parse(
|
|
20
|
+
readFileSync(join(resolveSpecDir(), "openclaw-2.0.config.schema.json"), "utf-8")
|
|
21
|
+
);
|
|
22
|
+
const ajv = new Ajv({ strict: false, allErrors: true });
|
|
23
|
+
addFormats(ajv);
|
|
24
|
+
cached = ajv.compile(schema);
|
|
25
|
+
return cached;
|
|
26
|
+
}
|
|
27
|
+
function isUnknownKey(e) {
|
|
28
|
+
return e.keyword === "additionalProperties";
|
|
29
|
+
}
|
|
30
|
+
function dotPath(instancePath) {
|
|
31
|
+
if (!instancePath) return "(root)";
|
|
32
|
+
return instancePath.slice(1).split("/").map((seg) => seg.replace(/~1/g, "/").replace(/~0/g, "~")).join(".");
|
|
33
|
+
}
|
|
34
|
+
function describe(e) {
|
|
35
|
+
const where = dotPath(e.instancePath);
|
|
36
|
+
if (isUnknownKey(e)) {
|
|
37
|
+
const extra = e.params.additionalProperty;
|
|
38
|
+
return `${where}: unknown key "${extra}"`;
|
|
39
|
+
}
|
|
40
|
+
return `${where}: ${e.message ?? "invalid"}`;
|
|
41
|
+
}
|
|
42
|
+
function deploymentContractErrors(cfg) {
|
|
43
|
+
const errors = [];
|
|
44
|
+
if (cfg === null || typeof cfg !== "object") return errors;
|
|
45
|
+
const gateway = cfg["gateway"];
|
|
46
|
+
const mode = gateway !== null && typeof gateway === "object" ? gateway["mode"] : void 0;
|
|
47
|
+
if (mode === void 0) {
|
|
48
|
+
errors.push(
|
|
49
|
+
'gateway.mode: required by clawops. OpenClaw treats it as optional only because --allow-unconfigured can bypass the check; clawops does not pass that flag, so a config without gateway.mode exits 78 with "Gateway start blocked". Set it to "local".'
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return errors;
|
|
53
|
+
}
|
|
54
|
+
async function validateConfig(cfg, opts = {}) {
|
|
55
|
+
const validate = await getValidator();
|
|
56
|
+
const contract = deploymentContractErrors(cfg);
|
|
57
|
+
if (validate(cfg)) return { errors: contract, warnings: [] };
|
|
58
|
+
const { openclawVersion, schemaCapturedFrom } = opts;
|
|
59
|
+
const runtimeIsNewer = openclawVersion !== void 0 && schemaCapturedFrom !== void 0 && compareVersions(openclawVersion, schemaCapturedFrom) > 0;
|
|
60
|
+
const errors = [...contract];
|
|
61
|
+
const warnings = [];
|
|
62
|
+
for (const e of validate.errors ?? []) {
|
|
63
|
+
const text = describe(e);
|
|
64
|
+
if (isUnknownKey(e) && runtimeIsNewer) {
|
|
65
|
+
warnings.push(
|
|
66
|
+
`${text} \u2014 not in the schema captured from OpenClaw ${schemaCapturedFrom}; you are deploying ${openclawVersion}, so this may be a newer setting clawops does not know about yet.`
|
|
67
|
+
);
|
|
68
|
+
} else {
|
|
69
|
+
errors.push(text);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { errors, warnings };
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
validateConfig
|
|
76
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
buildContext,
|
|
4
|
+
loadAdapterModule
|
|
5
|
+
} from "./chunk-5XT5IAJK.js";
|
|
6
|
+
import "./chunk-LEVIW2PV.js";
|
|
7
|
+
import "./chunk-GIEVAGEK.js";
|
|
8
|
+
import "./chunk-VOEJYRIK.js";
|
|
9
|
+
import "./chunk-EI7CQQ7D.js";
|
|
10
|
+
import "./chunk-FEK7VJAY.js";
|
|
11
|
+
import "./chunk-RHOWYIWT.js";
|
|
12
|
+
import "./chunk-N4WZLWJ4.js";
|
|
13
|
+
import "./chunk-A2I76FTA.js";
|
|
14
|
+
import "./chunk-CX5SL5HP.js";
|
|
15
|
+
import "./chunk-KGXPLI7W.js";
|
|
16
|
+
import "./chunk-VUFMZEIM.js";
|
|
17
|
+
export {
|
|
18
|
+
buildContext,
|
|
19
|
+
loadAdapterModule
|
|
20
|
+
};
|
|
@@ -32,7 +32,10 @@ function resolveIngressCidrs(accessMode, allowedCidrs, portOverride, egressResul
|
|
|
32
32
|
}
|
|
33
33
|
async function detectEgressIp(checkUrl) {
|
|
34
34
|
try {
|
|
35
|
-
const res = await fetch(checkUrl, {
|
|
35
|
+
const res = await fetch(checkUrl, {
|
|
36
|
+
signal: AbortSignal.timeout(3e3),
|
|
37
|
+
headers: { accept: "text/plain" }
|
|
38
|
+
});
|
|
36
39
|
if (!res.ok) {
|
|
37
40
|
return { ok: false, error: `HTTP ${res.status} from ${checkUrl}` };
|
|
38
41
|
}
|
|
@@ -40,12 +43,40 @@ async function detectEgressIp(checkUrl) {
|
|
|
40
43
|
if (!ip) {
|
|
41
44
|
return { ok: false, error: `empty response from ${checkUrl}` };
|
|
42
45
|
}
|
|
46
|
+
if (!isIpAddress(ip)) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
error: `${checkUrl} did not return an IP address (got ${summarise(ip)})`
|
|
50
|
+
};
|
|
51
|
+
}
|
|
43
52
|
return { ok: true, ip };
|
|
44
53
|
} catch (err) {
|
|
45
54
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
46
55
|
}
|
|
47
56
|
}
|
|
57
|
+
function isIpAddress(value) {
|
|
58
|
+
const v = value.trim();
|
|
59
|
+
if (/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.test(v)) {
|
|
60
|
+
return v.split(".").every((o) => Number(o) <= 255);
|
|
61
|
+
}
|
|
62
|
+
return /^[0-9a-fA-F:]+$/.test(v) && v.includes(":");
|
|
63
|
+
}
|
|
64
|
+
function summarise(body) {
|
|
65
|
+
const oneLine = body.replace(/\s+/g, " ").trim();
|
|
66
|
+
return oneLine.length > 40 ? `${oneLine.slice(0, 40)}\u2026` : oneLine;
|
|
67
|
+
}
|
|
68
|
+
function resolveGatewayIngressCidrs(publishGateway, accessMode, allowedCidrs, portOverride, egressResult) {
|
|
69
|
+
if (publishGateway !== "all") return [];
|
|
70
|
+
return resolveIngressCidrs(accessMode, allowedCidrs, portOverride, egressResult);
|
|
71
|
+
}
|
|
72
|
+
function resolveGatewayPort(raw, fallback) {
|
|
73
|
+
const port = Number(raw);
|
|
74
|
+
return Number.isInteger(port) && port > 0 && port < 65536 ? port : fallback;
|
|
75
|
+
}
|
|
48
76
|
export {
|
|
49
77
|
detectEgressIp,
|
|
78
|
+
isIpAddress,
|
|
79
|
+
resolveGatewayIngressCidrs,
|
|
80
|
+
resolveGatewayPort,
|
|
50
81
|
resolveIngressCidrs
|
|
51
82
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
INSTANCE_ALIASES,
|
|
4
|
+
generatePlan,
|
|
5
|
+
isInstanceAlias,
|
|
6
|
+
parseDiffForTest,
|
|
7
|
+
planId
|
|
8
|
+
} from "./chunk-STLRHLLV.js";
|
|
9
|
+
import "./chunk-BSLIYB5Q.js";
|
|
10
|
+
import "./chunk-5XT5IAJK.js";
|
|
11
|
+
import "./chunk-JW5A7E3X.js";
|
|
12
|
+
import "./chunk-LEVIW2PV.js";
|
|
13
|
+
import "./chunk-GIEVAGEK.js";
|
|
14
|
+
import "./chunk-VOEJYRIK.js";
|
|
15
|
+
import "./chunk-EI7CQQ7D.js";
|
|
16
|
+
import "./chunk-FEK7VJAY.js";
|
|
17
|
+
import "./chunk-RHOWYIWT.js";
|
|
18
|
+
import "./chunk-N4WZLWJ4.js";
|
|
19
|
+
import "./chunk-A2I76FTA.js";
|
|
20
|
+
import "./chunk-CX5SL5HP.js";
|
|
21
|
+
import "./chunk-KGXPLI7W.js";
|
|
22
|
+
import "./chunk-VUFMZEIM.js";
|
|
23
|
+
export {
|
|
24
|
+
INSTANCE_ALIASES,
|
|
25
|
+
generatePlan,
|
|
26
|
+
isInstanceAlias,
|
|
27
|
+
parseDiffForTest,
|
|
28
|
+
planId
|
|
29
|
+
};
|
|
@@ -2,8 +2,18 @@
|
|
|
2
2
|
import {
|
|
3
3
|
acquireSession,
|
|
4
4
|
drainPool
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-2Z7J3ERQ.js";
|
|
6
|
+
import "./chunk-E4NJSJ54.js";
|
|
7
|
+
import "./chunk-MC5ATCXS.js";
|
|
8
|
+
import {
|
|
9
|
+
PUBLISH_INSPECT_CMD,
|
|
10
|
+
publishForRestart,
|
|
11
|
+
publishedGatewayPort
|
|
12
|
+
} from "./chunk-GIEVAGEK.js";
|
|
13
|
+
import {
|
|
14
|
+
GATEWAY_PORT
|
|
15
|
+
} from "./chunk-VOEJYRIK.js";
|
|
16
|
+
import "./chunk-EI7CQQ7D.js";
|
|
7
17
|
import "./chunk-KGXPLI7W.js";
|
|
8
18
|
|
|
9
19
|
// src/harden/types.ts
|
|
@@ -154,7 +164,11 @@ var sshModule = {
|
|
|
154
164
|
|
|
155
165
|
// src/harden/modules/ufw.ts
|
|
156
166
|
var SENTINEL2 = `${SENTINEL_DIR}/ufw.applied`;
|
|
157
|
-
|
|
167
|
+
async function publishedGateway(exec) {
|
|
168
|
+
const { stdout } = await exec(`${PUBLISH_INSPECT_CMD} 2>/dev/null || true`);
|
|
169
|
+
if (publishForRestart(stdout) !== "all") return { exposed: false };
|
|
170
|
+
return { exposed: true, port: publishedGatewayPort(stdout) };
|
|
171
|
+
}
|
|
158
172
|
function makeUfwModule(sshPort = 22) {
|
|
159
173
|
return {
|
|
160
174
|
id: "ufw",
|
|
@@ -176,14 +190,24 @@ function makeUfwModule(sshPort = 22) {
|
|
|
176
190
|
return { status: "missing", detail: "UFW installed but not active" };
|
|
177
191
|
},
|
|
178
192
|
async apply(exec) {
|
|
193
|
+
const gateway = await publishedGateway(exec);
|
|
194
|
+
const rules = [`ufw allow ${sshPort}/tcp comment "clawops SSH"`];
|
|
195
|
+
let gatewayNote;
|
|
196
|
+
if (!gateway.exposed) {
|
|
197
|
+
gatewayNote = "gateway port not opened (published on 127.0.0.1 \u2014 reach it with `clawops tunnel`)";
|
|
198
|
+
} else if (gateway.port === void 0) {
|
|
199
|
+
gatewayNote = "gateway published on every interface but its port could not be read \u2014 no rule added; run `clawops doctor --stack <name>` and open it yourself if that is intended";
|
|
200
|
+
} else {
|
|
201
|
+
rules.push(`ufw allow ${gateway.port}/tcp comment "OpenClaw gateway"`);
|
|
202
|
+
gatewayNote = `allow ${gateway.port}/tcp`;
|
|
203
|
+
}
|
|
179
204
|
const script = [
|
|
180
205
|
`mkdir -p ${SENTINEL_DIR}`,
|
|
181
206
|
"apt-get install -y -q ufw",
|
|
182
207
|
"ufw --force reset",
|
|
183
208
|
"ufw default deny incoming",
|
|
184
209
|
"ufw default allow outgoing",
|
|
185
|
-
|
|
186
|
-
`ufw allow ${GATEWAY_PORT}/tcp comment "OpenClaw gateway"`,
|
|
210
|
+
...rules,
|
|
187
211
|
"ufw --force enable",
|
|
188
212
|
`touch ${SENTINEL2}`
|
|
189
213
|
].join(" && ");
|
|
@@ -191,7 +215,10 @@ function makeUfwModule(sshPort = 22) {
|
|
|
191
215
|
if (code !== 0) {
|
|
192
216
|
throw new Error(`UFW setup failed (exit ${code}): ${stderr.slice(0, 200)}`);
|
|
193
217
|
}
|
|
194
|
-
return {
|
|
218
|
+
return {
|
|
219
|
+
changed: true,
|
|
220
|
+
detail: `UFW enabled: deny-all in, allow ${sshPort}/tcp; ${gatewayNote}`
|
|
221
|
+
};
|
|
195
222
|
}
|
|
196
223
|
};
|
|
197
224
|
}
|
|
@@ -473,7 +500,14 @@ var sysctlModule = {
|
|
|
473
500
|
};
|
|
474
501
|
|
|
475
502
|
// src/harden/modules/aws-sg-audit.ts
|
|
476
|
-
var
|
|
503
|
+
var WORLD = /* @__PURE__ */ new Set(["0.0.0.0/0"]);
|
|
504
|
+
var WORLD_V6 = /* @__PURE__ */ new Set(["::/0"]);
|
|
505
|
+
var NAMED_PORTS = { 22: "SSH", [GATEWAY_PORT]: "the OpenClaw gateway" };
|
|
506
|
+
function describe(fromPort, toPort) {
|
|
507
|
+
const range = fromPort === toPort ? `port ${fromPort}` : `ports ${fromPort}-${toPort}`;
|
|
508
|
+
const named = NAMED_PORTS[fromPort];
|
|
509
|
+
return named && fromPort === toPort ? `${range} (${named})` : range;
|
|
510
|
+
}
|
|
477
511
|
var awsSgAuditModule = {
|
|
478
512
|
id: "aws-sg-audit",
|
|
479
513
|
label: "AWS SG audit (check-only)",
|
|
@@ -492,19 +526,25 @@ var awsSgAuditModule = {
|
|
|
492
526
|
for (const rule of sg.IpPermissions ?? []) {
|
|
493
527
|
const fromPort = rule.FromPort ?? 0;
|
|
494
528
|
const toPort = rule.ToPort ?? 65535;
|
|
529
|
+
const where = describe(fromPort, toPort);
|
|
495
530
|
for (const range of rule.IpRanges ?? []) {
|
|
496
|
-
if (range.CidrIp
|
|
497
|
-
openRules.push(
|
|
531
|
+
if (range.CidrIp && WORLD.has(range.CidrIp)) {
|
|
532
|
+
openRules.push(`${where} open to ${range.CidrIp} in ${sg.GroupId}`);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
for (const range of rule.Ipv6Ranges ?? []) {
|
|
536
|
+
if (range.CidrIpv6 && WORLD_V6.has(range.CidrIpv6)) {
|
|
537
|
+
openRules.push(`${where} open to ${range.CidrIpv6} in ${sg.GroupId}`);
|
|
498
538
|
}
|
|
499
539
|
}
|
|
500
540
|
}
|
|
501
541
|
}
|
|
502
542
|
if (openRules.length === 0) {
|
|
503
|
-
return { status: "applied", detail: "No
|
|
543
|
+
return { status: "applied", detail: "No ingress rule admits the whole internet" };
|
|
504
544
|
}
|
|
505
545
|
return {
|
|
506
546
|
status: "drifted",
|
|
507
|
-
detail: `
|
|
547
|
+
detail: `Ingress open to the internet: ${openRules.join("; ")}`
|
|
508
548
|
};
|
|
509
549
|
} catch (err) {
|
|
510
550
|
return {
|
package/dist/index.d.ts
CHANGED
|
@@ -47,6 +47,62 @@ interface ProviderAdapter {
|
|
|
47
47
|
stateBackendUrl(bucket: string): string;
|
|
48
48
|
/** Validate provider-specific config (env vars, profiles) at startup. */
|
|
49
49
|
validateConfig(): Promise<ValidationResult>;
|
|
50
|
+
/**
|
|
51
|
+
* Account-level setup this provider needs before anything can be provisioned.
|
|
52
|
+
*
|
|
53
|
+
* Not infrastructure: enabling an API or creating a state bucket happens outside the
|
|
54
|
+
* Pulumi program, because the state backend has to exist before Pulumi can run at all.
|
|
55
|
+
* Optional — a provider with nothing to check omits it.
|
|
56
|
+
*/
|
|
57
|
+
preflight?(opts: PreflightOpts): Promise<PreflightCheck[]>;
|
|
58
|
+
}
|
|
59
|
+
interface PreflightOpts {
|
|
60
|
+
/** Where the stack will be deployed, when that changes what must be enabled. */
|
|
61
|
+
region?: string;
|
|
62
|
+
/** State backend bucket or container, when the check covers it. */
|
|
63
|
+
bucket?: string;
|
|
64
|
+
/**
|
|
65
|
+
* The machine size about to be deployed, when the caller knows it.
|
|
66
|
+
*
|
|
67
|
+
* Azure offers SKU families per subscription, so "is this size available here" can only be
|
|
68
|
+
* answered about a specific size. Without this the check falls back to the provider's
|
|
69
|
+
* default alias, which is right for a default deploy and wrong for anyone passing
|
|
70
|
+
* --instance-type: a deployment using an available size would be reported as broken.
|
|
71
|
+
*/
|
|
72
|
+
instanceType?: string;
|
|
73
|
+
signal?: AbortSignal;
|
|
74
|
+
}
|
|
75
|
+
interface PreflightCheck {
|
|
76
|
+
/** Stable kebab-case id, e.g. "compute-api-enabled". */
|
|
77
|
+
id: string;
|
|
78
|
+
/** What is being checked, in the operator's terms. */
|
|
79
|
+
label: string;
|
|
80
|
+
ok: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* clawops could not put the question — a denied read permission, an API that would not
|
|
83
|
+
* answer. Neither a pass nor a failure: the operator's account may be perfectly set up, and
|
|
84
|
+
* saying so would be a claim clawops cannot make.
|
|
85
|
+
*
|
|
86
|
+
* Dropping the check instead is worse. An operator whose credentials lack a read permission
|
|
87
|
+
* then sees one fewer line than everyone else and never learns why. The detail field carries
|
|
88
|
+
* the reason, and doctor renders these as warnings rather than failing the report over a
|
|
89
|
+
* question it could not ask.
|
|
90
|
+
*/
|
|
91
|
+
unknown?: boolean;
|
|
92
|
+
/** Why it failed, and what it means — shown when ok is false. */
|
|
93
|
+
detail?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Applies the fix. Absent when clawops cannot fix it: a missing permission or an unpaid
|
|
96
|
+
* billing account is not clawops's to resolve, and pretending otherwise wastes a prompt.
|
|
97
|
+
*/
|
|
98
|
+
fix?: () => Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* What the fix changes in the operator's cloud account.
|
|
101
|
+
*
|
|
102
|
+
* Shown in the prompt, and required alongside fix(): consent to 'fix it' is not consent to
|
|
103
|
+
* something unnamed.
|
|
104
|
+
*/
|
|
105
|
+
mutates?: string;
|
|
50
106
|
}
|
|
51
107
|
|
|
52
108
|
export type { BaseStackOutputs, ConnectionInfo, CredentialSource, InstanceAlias, ProviderAdapter, ProviderName, PulumiFn, StackOutputs, StateBackendScheme, ValidationResult };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
withoutHost
|
|
4
|
+
} from "./chunk-MC5ATCXS.js";
|
|
5
|
+
|
|
6
|
+
// src/transport/known-hosts-file.ts
|
|
7
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
8
|
+
function forgetHost(knownHostsPath, host, port) {
|
|
9
|
+
let content;
|
|
10
|
+
try {
|
|
11
|
+
content = readFileSync(knownHostsPath, "utf-8");
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const next = withoutHost(content, host, port);
|
|
16
|
+
if (next === content) return false;
|
|
17
|
+
writeFileSync(knownHostsPath, next, "utf-8");
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
forgetHost
|
|
22
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
GATEWAY_LOGS_PROBE,
|
|
4
|
+
agentAuditCommand,
|
|
5
|
+
chooseLogSource,
|
|
6
|
+
containerLogsCommand,
|
|
7
|
+
gatewayLogsCommand
|
|
8
|
+
} from "./chunk-NUL5F6YV.js";
|
|
9
|
+
export {
|
|
10
|
+
GATEWAY_LOGS_PROBE,
|
|
11
|
+
agentAuditCommand,
|
|
12
|
+
chooseLogSource,
|
|
13
|
+
containerLogsCommand,
|
|
14
|
+
gatewayLogsCommand
|
|
15
|
+
};
|
|
@@ -1,24 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
MCP_HTTP_PORT
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-6ZFIFDBJ.js";
|
|
4
|
+
} from "./chunk-KC3GC3PE.js";
|
|
5
|
+
import "./chunk-LBE6COC4.js";
|
|
7
6
|
import "./chunk-BOPSG2LI.js";
|
|
7
|
+
import "./chunk-6ZFIFDBJ.js";
|
|
8
8
|
import "./chunk-3QJBNAHW.js";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-2I7KCMXZ.js";
|
|
10
|
+
import "./chunk-HSPHXG6W.js";
|
|
11
|
+
import "./chunk-AEEMRFZV.js";
|
|
12
|
+
import "./chunk-STLRHLLV.js";
|
|
13
|
+
import "./chunk-BSLIYB5Q.js";
|
|
14
|
+
import "./chunk-2Z7J3ERQ.js";
|
|
15
|
+
import "./chunk-E4NJSJ54.js";
|
|
16
|
+
import "./chunk-MC5ATCXS.js";
|
|
17
|
+
import "./chunk-5XT5IAJK.js";
|
|
18
|
+
import "./chunk-IVWP4QUJ.js";
|
|
19
|
+
import "./chunk-NUL5F6YV.js";
|
|
20
|
+
import "./chunk-3OLUJPCB.js";
|
|
21
|
+
import {
|
|
22
|
+
execPrivileged
|
|
23
|
+
} from "./chunk-NUDIHZEH.js";
|
|
24
|
+
import "./chunk-JW5A7E3X.js";
|
|
25
|
+
import "./chunk-LT4XMO5D.js";
|
|
26
|
+
import "./chunk-LEVIW2PV.js";
|
|
27
|
+
import "./chunk-GIEVAGEK.js";
|
|
28
|
+
import "./chunk-VOEJYRIK.js";
|
|
15
29
|
import "./chunk-EI7CQQ7D.js";
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
30
|
+
import "./chunk-FEK7VJAY.js";
|
|
31
|
+
import "./chunk-RHOWYIWT.js";
|
|
18
32
|
import "./chunk-Q7NQY5HV.js";
|
|
33
|
+
import "./chunk-N4WZLWJ4.js";
|
|
19
34
|
import "./chunk-A2I76FTA.js";
|
|
20
35
|
import "./chunk-CX5SL5HP.js";
|
|
21
36
|
import "./chunk-KGXPLI7W.js";
|
|
37
|
+
import "./chunk-VUFMZEIM.js";
|
|
22
38
|
|
|
23
39
|
// src/cli/mcp-wire.ts
|
|
24
40
|
var GATEWAY_MCP_NAME = "clawops";
|
|
@@ -30,17 +46,17 @@ function shellQuote(value) {
|
|
|
30
46
|
}
|
|
31
47
|
var OC = "docker exec openclaw openclaw mcp";
|
|
32
48
|
async function supportsMcpAdd(session, signal) {
|
|
33
|
-
const help = await session
|
|
49
|
+
const help = await execPrivileged(session, `${OC} add --help`, signal);
|
|
34
50
|
return help.code === 0;
|
|
35
51
|
}
|
|
36
52
|
async function wireGatewayMcp(session, signal, opts = {}) {
|
|
37
53
|
const url = opts.url ?? defaultGatewayMcpUrl();
|
|
38
54
|
if (!await supportsMcpAdd(session, signal)) return { status: "unsupported", url };
|
|
39
|
-
const existing = await session
|
|
55
|
+
const existing = await execPrivileged(session, `${OC} show ${GATEWAY_MCP_NAME}`, signal);
|
|
40
56
|
const alreadyWired = existing.code === 0;
|
|
41
57
|
if (alreadyWired && !opts.rewire) return { status: "exists", url };
|
|
42
58
|
if (alreadyWired) {
|
|
43
|
-
const removed = await session
|
|
59
|
+
const removed = await execPrivileged(session, `${OC} unset ${GATEWAY_MCP_NAME}`, signal);
|
|
44
60
|
if (removed.code !== 0) {
|
|
45
61
|
return {
|
|
46
62
|
status: "probe-failed",
|
|
@@ -54,7 +70,7 @@ async function wireGatewayMcp(session, signal, opts = {}) {
|
|
|
54
70
|
`--url ${shellQuote(url)}`,
|
|
55
71
|
...opts.token ? [`--header ${shellQuote(`Authorization=Bearer ${opts.token}`)}`] : []
|
|
56
72
|
].join(" ");
|
|
57
|
-
const added = await session
|
|
73
|
+
const added = await execPrivileged(session, `${OC} add ${GATEWAY_MCP_NAME} ${flags}`, signal);
|
|
58
74
|
if (added.code !== 0) {
|
|
59
75
|
return {
|
|
60
76
|
status: "probe-failed",
|
|
@@ -62,7 +78,7 @@ async function wireGatewayMcp(session, signal, opts = {}) {
|
|
|
62
78
|
url
|
|
63
79
|
};
|
|
64
80
|
}
|
|
65
|
-
await session
|
|
81
|
+
await execPrivileged(session, `${OC} reload`, signal);
|
|
66
82
|
return { status: "wired", rewired: alreadyWired, url };
|
|
67
83
|
}
|
|
68
84
|
export {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/openclaw/models.ts
|
|
4
|
+
function providerKeyOf(provider) {
|
|
5
|
+
const fromPath = provider.configPath?.split(".").pop();
|
|
6
|
+
return fromPath && fromPath.length > 0 ? fromPath : provider.id;
|
|
7
|
+
}
|
|
8
|
+
function buildModelsBlock(opts) {
|
|
9
|
+
const { provider, model, apiKeyRef, baseUrl, region } = opts;
|
|
10
|
+
const key = providerKeyOf(provider);
|
|
11
|
+
const modelId = opts.resolvedModelId ?? model.modelId ?? model.id;
|
|
12
|
+
const entry = {};
|
|
13
|
+
if (provider.api) entry["api"] = provider.api;
|
|
14
|
+
if (region) entry["region"] = region;
|
|
15
|
+
if (apiKeyRef) entry["apiKey"] = apiKeyRef;
|
|
16
|
+
if (baseUrl) entry["baseUrl"] = baseUrl;
|
|
17
|
+
if (provider.credentialSource === "aws-profile") {
|
|
18
|
+
entry["auth"] = "aws-sdk";
|
|
19
|
+
}
|
|
20
|
+
entry["models"] = [
|
|
21
|
+
{
|
|
22
|
+
id: modelId,
|
|
23
|
+
name: model.displayName,
|
|
24
|
+
...provider.api ? { api: provider.api } : {}
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
return { providers: { [key]: entry } };
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
buildModelsBlock,
|
|
31
|
+
providerKeyOf
|
|
32
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
UsageError
|
|
4
|
+
} from "./chunk-KGXPLI7W.js";
|
|
5
|
+
|
|
6
|
+
// src/plan/network-args.ts
|
|
7
|
+
function isCidr(value) {
|
|
8
|
+
const v = value.trim();
|
|
9
|
+
if (v.includes(":")) return true;
|
|
10
|
+
return /^(\d{1,3}\.){3}\d{1,3}\/(3[0-2]|[12]?\d)$/.test(v);
|
|
11
|
+
}
|
|
12
|
+
function parseCidrList(flag, value) {
|
|
13
|
+
const parts = value.split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
14
|
+
for (const p of parts) {
|
|
15
|
+
if (!isCidr(p)) {
|
|
16
|
+
throw new UsageError(
|
|
17
|
+
`${flag}: "${p}" is not a CIDR. Use an address and a prefix, e.g. 203.0.113.4/32.`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return parts;
|
|
22
|
+
}
|
|
23
|
+
function parsePublishGateway(value) {
|
|
24
|
+
const v = value.trim();
|
|
25
|
+
if (v === "loopback" || v === "all") return v;
|
|
26
|
+
throw new UsageError(`--publish-gateway: expected "loopback" or "all", got "${v}".`);
|
|
27
|
+
}
|
|
28
|
+
async function resolveNetworkFlags(flags, deps) {
|
|
29
|
+
const resolved = { allowedSshCidrs: [], allowedGatewayCidrs: [] };
|
|
30
|
+
for (const [flag, raw, key] of [
|
|
31
|
+
["--ssh-cidr", flags.sshCidr, "allowedSshCidrs"],
|
|
32
|
+
["--gateway-cidr", flags.gatewayCidr, "allowedGatewayCidrs"]
|
|
33
|
+
]) {
|
|
34
|
+
if (raw === void 0) continue;
|
|
35
|
+
resolved[key] = raw.trim() === "auto" ? [await autoCidr(flag, deps.detectEgressIp)] : parseCidrList(flag, raw);
|
|
36
|
+
}
|
|
37
|
+
if (flags.publishGateway !== void 0) {
|
|
38
|
+
resolved.publishGateway = parsePublishGateway(flags.publishGateway);
|
|
39
|
+
}
|
|
40
|
+
return resolved;
|
|
41
|
+
}
|
|
42
|
+
async function autoCidr(flag, detect) {
|
|
43
|
+
const result = await detect();
|
|
44
|
+
if (!result.ok || result.ip.trim() === "") {
|
|
45
|
+
const why = result.ok ? "the lookup returned an empty address" : result.error;
|
|
46
|
+
throw new UsageError(
|
|
47
|
+
`${flag} auto: could not detect this machine's public IP \u2014 ${why}. Pass the CIDR explicitly, e.g. 203.0.113.4/32.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const ip = result.ip.trim();
|
|
51
|
+
const cidr = ip.includes("/") ? ip : `${ip}/32`;
|
|
52
|
+
if (!isCidr(cidr)) {
|
|
53
|
+
throw new UsageError(
|
|
54
|
+
`${flag} auto: the detected address "${ip}" is not usable as a CIDR. Pass the CIDR explicitly, e.g. 203.0.113.4/32.`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return cidr;
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
isCidr,
|
|
61
|
+
parseCidrList,
|
|
62
|
+
parsePublishGateway,
|
|
63
|
+
resolveNetworkFlags
|
|
64
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// package.json
|
|
4
4
|
var name = "@clawops/cli";
|
|
5
|
-
var version = "
|
|
5
|
+
var version = "2.0.1";
|
|
6
6
|
var description = "Deploy and manage self-hosted OpenClaw instances across clouds";
|
|
7
7
|
var type = "module";
|
|
8
8
|
var bin = {
|
|
@@ -35,19 +35,24 @@ var scripts = {
|
|
|
35
35
|
test: "vitest run",
|
|
36
36
|
"test:pulumi": "vitest run tests/providers/aws/program.test.ts tests/providers/azure/program.test.ts",
|
|
37
37
|
"test:changed": "vitest --changed",
|
|
38
|
-
"test:mutation": "node scripts/dev/mutation-check.mjs",
|
|
39
38
|
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
39
|
+
"test:cloud": "bash scripts/e2e/cloud.sh",
|
|
40
40
|
typecheck: "NODE_OPTIONS=--max-old-space-size=4096 tsc --noEmit",
|
|
41
41
|
lint: "eslint --max-warnings=0 src tests scripts",
|
|
42
42
|
"gen:schemas": "tsx scripts/gen-schemas.ts",
|
|
43
43
|
changeset: "changeset",
|
|
44
|
-
release: "pnpm build && changeset publish"
|
|
44
|
+
release: "pnpm build && changeset publish",
|
|
45
|
+
"test:mutation": "node scripts/dev/mutation-check.mjs",
|
|
46
|
+
graph: "node scripts/dev/graph.mjs"
|
|
45
47
|
};
|
|
46
48
|
var dependencies = {
|
|
49
|
+
"@aws-sdk/client-bedrock": "^3.1063.0",
|
|
47
50
|
"@aws-sdk/client-cloudwatch-logs": "^3.1063.0",
|
|
48
51
|
"@aws-sdk/client-ec2": "^3.1063.0",
|
|
49
52
|
"@aws-sdk/client-guardduty": "^3.1063.0",
|
|
50
53
|
"@aws-sdk/client-iam": "^3.1063.0",
|
|
54
|
+
"@aws-sdk/client-s3": "3.1132.0",
|
|
55
|
+
"@aws-sdk/client-sts": "3.1132.0",
|
|
51
56
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
52
57
|
"@pulumi/aws": "^6.0.0",
|
|
53
58
|
"@pulumi/azure-native": "^2.0.0",
|
|
@@ -61,6 +66,7 @@ var dependencies = {
|
|
|
61
66
|
chalk: "^5.0.0",
|
|
62
67
|
citty: "^0.1.0",
|
|
63
68
|
conf: "^12.0.0",
|
|
69
|
+
"google-auth-library": "^11.0.2",
|
|
64
70
|
inquirer: "^9.0.0",
|
|
65
71
|
ora: "^8.0.0",
|
|
66
72
|
ssh2: "^1.0.0",
|
|
@@ -85,7 +91,8 @@ var devDependencies = {
|
|
|
85
91
|
};
|
|
86
92
|
var pnpm = {
|
|
87
93
|
overrides: {
|
|
88
|
-
"@pulumi/pulumi": "^3.0.0"
|
|
94
|
+
"@pulumi/pulumi": "^3.0.0",
|
|
95
|
+
"@smithy/types": "4.14.3"
|
|
89
96
|
},
|
|
90
97
|
onlyBuiltDependencies: [
|
|
91
98
|
"@pulumi/command",
|