@clawops/cli 1.7.1 → 1.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -92
- package/dist/{apply-BTZFFGAD.js → apply-PZ5IIU7Z.js} +5 -4
- package/dist/{aws-D7Y6LCGK.js → aws-L4RR2LSE.js} +1 -1
- package/dist/{azure-JQAMNVHN.js → azure-7OXQENQL.js} +1 -1
- package/dist/{bootstrap-RY4VOSDJ.js → bootstrap-HL4DNUNG.js} +13 -4
- package/dist/bootstrap.sh.tmpl +204 -0
- package/dist/chunk-2YOP6N7X.js +126 -0
- package/dist/{chunk-GJEF6UQA.js → chunk-3XK66DKK.js} +100 -16
- package/dist/{chunk-ISFHLA4G.js → chunk-5SPESD5N.js} +8 -6
- package/dist/{chunk-YTH4L2GN.js → chunk-6X45DDGH.js} +5 -4
- package/dist/{chunk-XKT42M34.js → chunk-C5X375RO.js} +14 -1
- package/dist/{chunk-LCKD7L7X.js → chunk-DQDAWNGB.js} +1 -1
- package/dist/{chunk-CG3Y7Y2R.js → chunk-FSUWPL7Q.js} +9 -9
- package/dist/{chunk-FQ4JXUCR.js → chunk-NESXDU5F.js} +7 -4
- package/dist/{chunk-ZFNPM2WG.js → chunk-O3F7OGH7.js} +40 -3
- package/dist/{chunk-3MFZ7E74.js → chunk-P5LKWRPL.js} +3 -3
- package/dist/chunk-PVUVI35Y.js +29 -0
- package/dist/cli.js +122 -68
- package/dist/{context-ALSJMTHE.js → context-5LCUG2QR.js} +1 -1
- package/dist/{gcp-6FT2A45S.js → gcp-UG6AWPIZ.js} +1 -1
- package/dist/{generate-SPT7PJKR.js → generate-6E7J6BTM.js} +4 -3
- package/dist/{harden-BDVYW567.js → harden-URPXLSVF.js} +2 -2
- package/dist/{mcp-wire-ZWIYMLEW.js → mcp-wire-SLL4TLSC.js} +1 -1
- package/dist/{package-OMPI6RMV.js → package-QE7V2JAT.js} +3 -2
- package/dist/{pool-JZGKPP6K.js → pool-QGFQRMGU.js} +2 -2
- package/dist/{remote-config-QF5TT7GU.js → remote-config-PFB66RPY.js} +3 -1
- package/dist/{server-RGLKKCCU.js → server-L22AJQZO.js} +19 -18
- package/dist/{ssh-E2AMBPZY.js → ssh-CNJNLM3E.js} +1 -1
- package/dist/{validate-T5M5EHSJ.js → validate-CFQ4FH2T.js} +2 -1
- package/dist/version-guard-MZAO4KHS.js +25 -0
- package/dist/versions-OEPAS2EL.js +24 -0
- package/package.json +3 -2
- package/spec/_gen-schemas.md +136 -0
- package/spec/deploy-plan.schema.json +193 -0
- package/spec/errors.yaml +175 -0
- package/spec/integrations.yaml +97 -0
- package/spec/invariants.yaml +81 -0
- package/spec/mcp-tools.yaml +532 -0
- package/spec/models.yaml +113 -0
- package/spec/openclaw-versions.yaml +67 -0
- package/spec/providers.schema.json +120 -0
|
@@ -8,6 +8,98 @@ import { readFileSync, appendFileSync, mkdirSync } from "fs";
|
|
|
8
8
|
import { createServer } from "net";
|
|
9
9
|
import path from "path";
|
|
10
10
|
import { Client } from "ssh2";
|
|
11
|
+
|
|
12
|
+
// src/transport/known-hosts.ts
|
|
13
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
14
|
+
function hostEntryFor(host, port) {
|
|
15
|
+
return port === 22 ? host : `[${host}]:${port}`;
|
|
16
|
+
}
|
|
17
|
+
function parseKnownHosts(content) {
|
|
18
|
+
const entries = [];
|
|
19
|
+
for (const raw of content.split("\n")) {
|
|
20
|
+
const line = raw.trim();
|
|
21
|
+
if (!line || line.startsWith("#")) continue;
|
|
22
|
+
let parts = line.split(/\s+/);
|
|
23
|
+
let marker;
|
|
24
|
+
if (parts[0]?.startsWith("@")) {
|
|
25
|
+
marker = parts[0];
|
|
26
|
+
parts = parts.slice(1);
|
|
27
|
+
}
|
|
28
|
+
const hostField = parts[0];
|
|
29
|
+
if (!hostField) continue;
|
|
30
|
+
const hosts = hostField.split(",");
|
|
31
|
+
if (parts.length >= 3) {
|
|
32
|
+
entries.push({ hosts, keyType: parts[1], base64Key: parts[2], ...marker ? { marker } : {} });
|
|
33
|
+
} else if (parts.length === 2 && /^[0-9a-f]+$/i.test(parts[1] ?? "")) {
|
|
34
|
+
entries.push({ hosts, hexKey: parts[1], ...marker ? { marker } : {} });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return entries;
|
|
38
|
+
}
|
|
39
|
+
function hashedHostMatches(token, hostEntry) {
|
|
40
|
+
const parts = token.split("|");
|
|
41
|
+
if (parts.length !== 4 || parts[1] !== "1") return false;
|
|
42
|
+
const salt = parts[2];
|
|
43
|
+
const expected = parts[3];
|
|
44
|
+
if (!salt || !expected) return false;
|
|
45
|
+
try {
|
|
46
|
+
const actual = createHmac("sha1", Buffer.from(salt, "base64")).update(hostEntry).digest("base64");
|
|
47
|
+
const a = Buffer.from(actual);
|
|
48
|
+
const b = Buffer.from(expected);
|
|
49
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function patternToRegExp(pattern) {
|
|
55
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
56
|
+
return new RegExp(`^${escaped}$`, "i");
|
|
57
|
+
}
|
|
58
|
+
var isPattern = (host) => host.includes("*") || host.includes("?");
|
|
59
|
+
function entryMatchesHost(entry, hostEntry) {
|
|
60
|
+
let matched = false;
|
|
61
|
+
for (const host of entry.hosts) {
|
|
62
|
+
if (host.startsWith("|1|")) {
|
|
63
|
+
if (hashedHostMatches(host, hostEntry)) matched = true;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const negated = host.startsWith("!");
|
|
67
|
+
const pattern = negated ? host.slice(1) : host;
|
|
68
|
+
const hit = isPattern(pattern) ? patternToRegExp(pattern).test(hostEntry) : pattern === hostEntry;
|
|
69
|
+
if (hit) {
|
|
70
|
+
if (negated) return false;
|
|
71
|
+
matched = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return matched;
|
|
75
|
+
}
|
|
76
|
+
function verifyAgainstKnownHosts(content, host, port, key) {
|
|
77
|
+
const hostEntry = hostEntryFor(host, port);
|
|
78
|
+
const base64Key = key.toString("base64");
|
|
79
|
+
const hexKey = key.toString("hex");
|
|
80
|
+
let sawHost = false;
|
|
81
|
+
for (const entry of parseKnownHosts(content)) {
|
|
82
|
+
if (!entryMatchesHost(entry, hostEntry)) continue;
|
|
83
|
+
if (entry.marker === "@cert-authority") continue;
|
|
84
|
+
sawHost = true;
|
|
85
|
+
const matches = entry.base64Key ? entry.base64Key === base64Key : entry.hexKey?.toLowerCase() === hexKey.toLowerCase();
|
|
86
|
+
if (matches) return entry.marker === "@revoked" ? "mismatch" : "match";
|
|
87
|
+
}
|
|
88
|
+
return sawHost ? "mismatch" : "unknown";
|
|
89
|
+
}
|
|
90
|
+
function formatKnownHostsLine(host, port, keyType, key) {
|
|
91
|
+
return `${hostEntryFor(host, port)} ${keyType} ${key.toString("base64")}
|
|
92
|
+
`;
|
|
93
|
+
}
|
|
94
|
+
function keyTypeFromBlob(key) {
|
|
95
|
+
if (key.length < 4) return void 0;
|
|
96
|
+
const len = key.readUInt32BE(0);
|
|
97
|
+
if (len === 0 || len > 64 || key.length < 4 + len) return void 0;
|
|
98
|
+
const type = key.subarray(4, 4 + len).toString("utf-8");
|
|
99
|
+
return /^[\x20-\x7e]+$/.test(type) ? type : void 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/transport/ssh.ts
|
|
11
103
|
var Ssh2Session = class {
|
|
12
104
|
constructor(client) {
|
|
13
105
|
this.client = client;
|
|
@@ -147,27 +239,19 @@ async function connect(opts) {
|
|
|
147
239
|
});
|
|
148
240
|
}
|
|
149
241
|
function verifyHostKey(host, port, keyHash, knownHostsPath) {
|
|
150
|
-
|
|
151
|
-
const hostEntry = port === 22 ? host : `[${host}]:${port}`;
|
|
152
|
-
let existing = null;
|
|
242
|
+
let content = "";
|
|
153
243
|
try {
|
|
154
|
-
|
|
155
|
-
for (const line of content.split("\n")) {
|
|
156
|
-
const parts = line.trim().split(/\s+/);
|
|
157
|
-
if (parts[0] === hostEntry && parts.length >= 2) {
|
|
158
|
-
existing = parts[1] ?? null;
|
|
159
|
-
break;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
244
|
+
content = readFileSync(knownHostsPath, "utf-8");
|
|
162
245
|
} catch {
|
|
163
246
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
247
|
+
const verdict = verifyAgainstKnownHosts(content, host, port, keyHash);
|
|
248
|
+
if (verdict === "match") return true;
|
|
249
|
+
if (verdict === "mismatch") return false;
|
|
250
|
+
const keyType = keyTypeFromBlob(keyHash);
|
|
251
|
+
if (!keyType) return false;
|
|
167
252
|
try {
|
|
168
253
|
mkdirSync(path.dirname(knownHostsPath), { recursive: true });
|
|
169
|
-
appendFileSync(knownHostsPath,
|
|
170
|
-
`, "utf-8");
|
|
254
|
+
appendFileSync(knownHostsPath, formatKnownHostsLine(host, port, keyType, keyHash), "utf-8");
|
|
171
255
|
} catch {
|
|
172
256
|
}
|
|
173
257
|
return true;
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
validatePlan
|
|
4
|
-
} from "./chunk-YTH4L2GN.js";
|
|
5
2
|
import {
|
|
6
3
|
saveOverlay
|
|
7
4
|
} from "./chunk-6ZFIFDBJ.js";
|
|
8
5
|
import {
|
|
9
6
|
resolveSecrets
|
|
10
7
|
} from "./chunk-BOPSG2LI.js";
|
|
8
|
+
import {
|
|
9
|
+
validatePlan
|
|
10
|
+
} from "./chunk-6X45DDGH.js";
|
|
11
11
|
import {
|
|
12
12
|
atomicWriteConfig,
|
|
13
13
|
deepMerge,
|
|
14
14
|
readRemoteConfig,
|
|
15
15
|
restartGateway
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-O3F7OGH7.js";
|
|
17
17
|
import {
|
|
18
18
|
buildContext
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-P5LKWRPL.js";
|
|
20
20
|
import {
|
|
21
21
|
UsageError
|
|
22
22
|
} from "./chunk-KGXPLI7W.js";
|
|
@@ -35,6 +35,8 @@ ${validation.errors.join("\n")}`
|
|
|
35
35
|
"plan/apply is not supported for the local provider. Use `clawops up` directly."
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
|
+
const { guardOpenclawVersion } = await import("./version-guard-MZAO4KHS.js");
|
|
39
|
+
await guardOpenclawVersion(plan.spec.openclaw.version);
|
|
38
40
|
const ctx = buildContext({
|
|
39
41
|
stack: plan.spec.stackName,
|
|
40
42
|
provider: plan.spec.provider
|
|
@@ -86,7 +88,7 @@ The diff you reviewed may no longer reflect what will be applied.
|
|
|
86
88
|
};
|
|
87
89
|
}
|
|
88
90
|
async function applyConfigOverlay(plan, outputs, ctx, signal) {
|
|
89
|
-
const { connect } = await import("./ssh-
|
|
91
|
+
const { connect } = await import("./ssh-CNJNLM3E.js");
|
|
90
92
|
const connInfo = ctx.adapter.getConnectionInfo(outputs);
|
|
91
93
|
const session = await connect({
|
|
92
94
|
host: connInfo.host,
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolveSpecDir
|
|
4
|
+
} from "./chunk-PVUVI35Y.js";
|
|
2
5
|
|
|
3
6
|
// src/plan/validate.ts
|
|
4
7
|
import Ajv from "ajv/dist/2020";
|
|
5
8
|
import addFormats from "ajv-formats";
|
|
6
9
|
import { readFileSync } from "fs";
|
|
7
|
-
import {
|
|
8
|
-
import { join, dirname } from "path";
|
|
9
|
-
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
import { join } from "path";
|
|
10
11
|
var _validate;
|
|
11
12
|
function getValidator() {
|
|
12
13
|
if (_validate) return _validate;
|
|
13
14
|
const ajv = new Ajv({ strict: false });
|
|
14
15
|
addFormats(ajv);
|
|
15
16
|
const schema = JSON.parse(
|
|
16
|
-
readFileSync(join(
|
|
17
|
+
readFileSync(join(resolveSpecDir(), "deploy-plan.schema.json"), "utf-8")
|
|
17
18
|
);
|
|
18
19
|
_validate = ajv.compile(schema);
|
|
19
20
|
return _validate;
|
|
@@ -42,6 +42,17 @@ usermod -aG docker clawops
|
|
|
42
42
|
OPENCLAW_VERSION="${openclawVersion}"
|
|
43
43
|
docker pull ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION}
|
|
44
44
|
|
|
45
|
+
# \u2500\u2500 Gateway auth token \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
46
|
+
# OpenClaw refuses a non-loopback bind without auth, and in a container it always
|
|
47
|
+
# binds 0.0.0.0. Without a token the gateway exits 78 and restart-loops.
|
|
48
|
+
OPENCLAW_ENV_FILE=/home/clawops/openclaw.env
|
|
49
|
+
if [ ! -s "\${OPENCLAW_ENV_FILE}" ]; then
|
|
50
|
+
OPENCLAW_TOKEN=$(openssl rand -hex 32 2>/dev/null || head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \\n')
|
|
51
|
+
printf 'OPENCLAW_GATEWAY_TOKEN=%s\\n' "\${OPENCLAW_TOKEN}" > "\${OPENCLAW_ENV_FILE}"
|
|
52
|
+
chmod 600 "\${OPENCLAW_ENV_FILE}"
|
|
53
|
+
chown clawops:clawops "\${OPENCLAW_ENV_FILE}"
|
|
54
|
+
fi
|
|
55
|
+
|
|
45
56
|
# \u2500\u2500 Default config (apply.ts will overwrite with plan overlay) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
46
57
|
OPENCLAW_CONFIG=/home/clawops/openclaw.json
|
|
47
58
|
if [ ! -f "\${OPENCLAW_CONFIG}" ]; then
|
|
@@ -58,9 +69,11 @@ docker run -d \\
|
|
|
58
69
|
--name openclaw \\
|
|
59
70
|
--restart unless-stopped \\
|
|
60
71
|
-p 18789:18789 \\
|
|
72
|
+
-e OPENCLAW_CONFIG_PATH=/app/config.json --add-host=host.docker.internal:host-gateway \\
|
|
73
|
+
--env-file "\${OPENCLAW_ENV_FILE}" \\
|
|
61
74
|
-v "\${OPENCLAW_CONFIG}":/app/config.json:ro \\
|
|
62
75
|
${bedrockEnvBlock} ghcr.io/openclaw/openclaw:\${OPENCLAW_VERSION} \\
|
|
63
|
-
node openclaw.mjs gateway run --allow-unconfigured
|
|
76
|
+
node openclaw.mjs gateway run --allow-unconfigured --port 18789
|
|
64
77
|
`;
|
|
65
78
|
}
|
|
66
79
|
function makeBedrockEnvBlock() {
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
acquireSession,
|
|
4
|
+
drainPool
|
|
5
|
+
} from "./chunk-DQDAWNGB.js";
|
|
2
6
|
import {
|
|
3
7
|
OPENCLAW_CONFIG,
|
|
4
8
|
atomicWriteConfig,
|
|
5
9
|
restartGateway
|
|
6
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-O3F7OGH7.js";
|
|
7
11
|
import {
|
|
8
12
|
buildContext
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import {
|
|
11
|
-
acquireSession,
|
|
12
|
-
drainPool
|
|
13
|
-
} from "./chunk-LCKD7L7X.js";
|
|
13
|
+
} from "./chunk-P5LKWRPL.js";
|
|
14
14
|
import {
|
|
15
15
|
chalk,
|
|
16
16
|
failure
|
|
@@ -131,7 +131,7 @@ function renderSnapshot(snap, opts) {
|
|
|
131
131
|
return lines.join("\n");
|
|
132
132
|
}
|
|
133
133
|
async function probeEntries() {
|
|
134
|
-
const { buildContext: buildContext2 } = await import("./context-
|
|
134
|
+
const { buildContext: buildContext2 } = await import("./context-5LCUG2QR.js");
|
|
135
135
|
const { getConfig } = await import("./store-SDUR52Z5.js");
|
|
136
136
|
const config = getConfig();
|
|
137
137
|
if (!config) return [];
|
|
@@ -358,8 +358,8 @@ var monitor_default = defineCommand({
|
|
|
358
358
|
const tailLines = Math.max(1, parseInt(String(args.tail ?? "10"), 10) || 10);
|
|
359
359
|
const isTTY = Boolean(process.stdout.isTTY);
|
|
360
360
|
const noColor = Boolean(args["no-color"]) || !isTTY;
|
|
361
|
-
const { buildContext: buildContext2 } = await import("./context-
|
|
362
|
-
const { acquireSession: acquireSession2, drainPool: drainPool2 } = await import("./pool-
|
|
361
|
+
const { buildContext: buildContext2 } = await import("./context-5LCUG2QR.js");
|
|
362
|
+
const { acquireSession: acquireSession2, drainPool: drainPool2 } = await import("./pool-QGFQRMGU.js");
|
|
363
363
|
const ac = new AbortController();
|
|
364
364
|
process.on("SIGINT", () => ac.abort());
|
|
365
365
|
process.on("SIGTERM", () => ac.abort());
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
validatePlan
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6X45DDGH.js";
|
|
5
5
|
import {
|
|
6
6
|
buildContext
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-P5LKWRPL.js";
|
|
8
8
|
import {
|
|
9
9
|
getConfig
|
|
10
10
|
} from "./chunk-CX5SL5HP.js";
|
|
@@ -45,10 +45,13 @@ async function generatePlan(intent, _opts) {
|
|
|
45
45
|
"plan/apply is not supported for the local provider. Use `clawops up` directly."
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
|
-
const { version } = await import("./package-
|
|
48
|
+
const { version } = await import("./package-QE7V2JAT.js");
|
|
49
49
|
const config = getConfig();
|
|
50
50
|
const instanceType = intent.instanceType ?? "small";
|
|
51
|
-
const
|
|
51
|
+
const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-MZAO4KHS.js");
|
|
52
|
+
const openclawVersion = await guardOpenclawVersion(
|
|
53
|
+
intent.openclawVersion ?? await defaultOpenclawVersion()
|
|
54
|
+
);
|
|
52
55
|
const network = intent.network ?? {
|
|
53
56
|
allowedSshCidrs: [],
|
|
54
57
|
allowedGatewayCidrs: []
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/openclaw/run-flags.ts
|
|
4
|
+
var CONFIG_MOUNT_PATH = "/app/config.json";
|
|
5
|
+
var GATEWAY_PORT = 18789;
|
|
6
|
+
var CONFIG_PATH_ENV = `-e OPENCLAW_CONFIG_PATH=${CONFIG_MOUNT_PATH}`;
|
|
7
|
+
var ADD_HOST_FLAG = "--add-host=host.docker.internal:host-gateway";
|
|
8
|
+
var PORT_PIN = `--port ${GATEWAY_PORT}`;
|
|
9
|
+
var COMMON_RUN_FLAGS = `${CONFIG_PATH_ENV} ${ADD_HOST_FLAG}`;
|
|
10
|
+
|
|
3
11
|
// src/plan/remote-config.ts
|
|
4
12
|
var OPENCLAW_CONFIG_LINUX = "/home/clawops/openclaw.json";
|
|
5
13
|
var OPENCLAW_CONFIG_MACOS = "~/.config/openclaw/config.json";
|
|
@@ -30,6 +38,18 @@ async function readRemoteConfig(session, signal) {
|
|
|
30
38
|
throw new Error(`Cannot parse ${configPath}: invalid JSON`);
|
|
31
39
|
}
|
|
32
40
|
}
|
|
41
|
+
function normaliseGatewayPort(cfg) {
|
|
42
|
+
const gateway = cfg["gateway"];
|
|
43
|
+
if (!gateway || typeof gateway !== "object" || Array.isArray(gateway)) return void 0;
|
|
44
|
+
const g = gateway;
|
|
45
|
+
const current = g["port"];
|
|
46
|
+
if (typeof current === "number" && current !== GATEWAY_PORT) {
|
|
47
|
+
g["port"] = GATEWAY_PORT;
|
|
48
|
+
return current;
|
|
49
|
+
}
|
|
50
|
+
if (current === void 0) g["port"] = GATEWAY_PORT;
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
33
53
|
async function atomicWriteConfig(session, cfg, signal) {
|
|
34
54
|
const os = await detectOS(session, signal);
|
|
35
55
|
const configPath = configPathForOS(os);
|
|
@@ -50,25 +70,41 @@ async function restartGateway(session, signal) {
|
|
|
50
70
|
const imgResult = os === "Darwin" ? await session.exec(imgCmd, signal) : await execWithFallbackSudo(session, imgCmd, signal);
|
|
51
71
|
const image = imgResult.stdout.trim();
|
|
52
72
|
const cfgResult = await session.exec(`cat ${configPath}`, signal);
|
|
53
|
-
let gatewayCmd =
|
|
73
|
+
let gatewayCmd = `node openclaw.mjs gateway run --allow-unconfigured ${PORT_PIN}`;
|
|
54
74
|
try {
|
|
55
75
|
const cfg = JSON.parse(cfgResult.stdout);
|
|
56
76
|
const token = cfg?.["gateway"]?.["auth"];
|
|
57
77
|
const tokenVal = token?.["token"];
|
|
58
78
|
if (tokenVal) {
|
|
59
|
-
gatewayCmd = `node openclaw.mjs gateway run --allow-unconfigured --token '${tokenVal}'`;
|
|
79
|
+
gatewayCmd = `node openclaw.mjs gateway run --allow-unconfigured ${PORT_PIN} --token '${tokenVal}'`;
|
|
60
80
|
}
|
|
61
81
|
} catch {
|
|
62
82
|
}
|
|
63
83
|
const restartCmd = pathPrefix + [
|
|
64
84
|
"docker stop openclaw 2>/dev/null || true",
|
|
65
85
|
"docker rm openclaw 2>/dev/null || true",
|
|
66
|
-
`docker run -d --name openclaw --restart unless-stopped -p
|
|
86
|
+
`docker run -d --name openclaw --restart unless-stopped -p ${GATEWAY_PORT}:${GATEWAY_PORT} ${COMMON_RUN_FLAGS} -v ${configPath}:/app/config.json:ro ${image} ${gatewayCmd}`
|
|
67
87
|
].join(" && ");
|
|
68
88
|
const result = os === "Darwin" ? await session.exec(restartCmd, signal) : await execWithFallbackSudo(session, restartCmd, signal);
|
|
69
89
|
if (result.code !== 0) {
|
|
70
90
|
throw new Error(`Gateway restart failed: ${result.stderr}`);
|
|
71
91
|
}
|
|
92
|
+
const healthy = await waitForGateway(session, pathPrefix, signal);
|
|
93
|
+
if (!healthy) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Gateway restarted but did not become healthy on port ${GATEWAY_PORT}. The previous container has already been replaced; inspect it with \`docker logs openclaw\`. If the newly-applied config is at fault, revert it and restart \u2014 before v1.7.2 this config was never applied, so a value that has sat unused may now be taking effect.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function waitForGateway(session, pathPrefix, signal, attempts = 15) {
|
|
100
|
+
const probe = `${pathPrefix}curl -fsS -m 3 http://127.0.0.1:${GATEWAY_PORT}/healthz >/dev/null 2>&1 && echo ok || echo waiting`;
|
|
101
|
+
for (let i = 0; i < attempts; i++) {
|
|
102
|
+
if (signal?.aborted) return false;
|
|
103
|
+
const r = await session.exec(probe, signal);
|
|
104
|
+
if (r.stdout.trim().endsWith("ok")) return true;
|
|
105
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
72
108
|
}
|
|
73
109
|
function deepMerge(base, overlay) {
|
|
74
110
|
const result = { ...base };
|
|
@@ -91,6 +127,7 @@ export {
|
|
|
91
127
|
OPENCLAW_CONFIG,
|
|
92
128
|
OPENCLAW_TMP,
|
|
93
129
|
readRemoteConfig,
|
|
130
|
+
normaliseGatewayPort,
|
|
94
131
|
atomicWriteConfig,
|
|
95
132
|
restartGateway,
|
|
96
133
|
deepMerge
|
|
@@ -56,17 +56,17 @@ function makeProviderProxy(name) {
|
|
|
56
56
|
if (resolved) return resolved;
|
|
57
57
|
switch (name) {
|
|
58
58
|
case "gcp": {
|
|
59
|
-
const mod = await import("./gcp-
|
|
59
|
+
const mod = await import("./gcp-UG6AWPIZ.js");
|
|
60
60
|
resolved = mod.default;
|
|
61
61
|
return resolved;
|
|
62
62
|
}
|
|
63
63
|
case "aws": {
|
|
64
|
-
const mod = await import("./aws-
|
|
64
|
+
const mod = await import("./aws-L4RR2LSE.js");
|
|
65
65
|
resolved = mod.default;
|
|
66
66
|
return resolved;
|
|
67
67
|
}
|
|
68
68
|
case "azure": {
|
|
69
|
-
const mod = await import("./azure-
|
|
69
|
+
const mod = await import("./azure-7OXQENQL.js");
|
|
70
70
|
resolved = mod.default;
|
|
71
71
|
return resolved;
|
|
72
72
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/spec-path.ts
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { dirname, join, parse } from "path";
|
|
7
|
+
var MARKER = "openclaw-versions.yaml";
|
|
8
|
+
var _cached;
|
|
9
|
+
function resolveSpecDir(startUrl = import.meta.url) {
|
|
10
|
+
if (_cached) return _cached;
|
|
11
|
+
let dir = dirname(fileURLToPath(startUrl));
|
|
12
|
+
const { root } = parse(dir);
|
|
13
|
+
while (true) {
|
|
14
|
+
const candidate = join(dir, "spec");
|
|
15
|
+
if (existsSync(join(candidate, MARKER))) {
|
|
16
|
+
_cached = candidate;
|
|
17
|
+
return candidate;
|
|
18
|
+
}
|
|
19
|
+
if (dir === root) break;
|
|
20
|
+
dir = dirname(dir);
|
|
21
|
+
}
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Cannot locate the spec/ directory (searched upward from ${dirname(fileURLToPath(startUrl))}). If this is an installed package, spec/ may be missing from the published files.`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
resolveSpecDir
|
|
29
|
+
};
|