@clawops/cli 1.7.8 → 2.0.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.
Files changed (57) hide show
  1. package/README.md +412 -70
  2. package/dist/{apply-F3KY2HTB.js → apply-AM5HERE7.js} +8 -6
  3. package/dist/{aws-L4RR2LSE.js → aws-AMON7DA7.js} +20 -9
  4. package/dist/{azure-7OXQENQL.js → azure-T4G6OBJQ.js} +19 -8
  5. package/dist/{bootstrap-HL4DNUNG.js → bootstrap-DMRPWJGN.js} +28 -19
  6. package/dist/bootstrap.sh.tmpl +51 -31
  7. package/dist/channels-KBUR42IU.js +53 -0
  8. package/dist/{chunk-DQDAWNGB.js → chunk-3WYA2QDM.js} +1 -1
  9. package/dist/chunk-6TIYVHPX.js +152 -0
  10. package/dist/{chunk-3XK66DKK.js → chunk-AHAAEXWC.js} +41 -0
  11. package/dist/{chunk-QQNKBTYZ.js → chunk-AX74VXTV.js} +292 -94
  12. package/dist/{chunk-C5X375RO.js → chunk-CKSPROD5.js} +59 -15
  13. package/dist/{chunk-UKTP332Z.js → chunk-GAZPPSKU.js} +6 -7
  14. package/dist/{chunk-FNS56UQY.js → chunk-GYV4KNW7.js} +319 -252
  15. package/dist/{chunk-PVUVI35Y.js → chunk-JW5A7E3X.js} +5 -1
  16. package/dist/chunk-LT4XMO5D.js +50 -0
  17. package/dist/{chunk-P5LKWRPL.js → chunk-LU2V3LXA.js} +3 -3
  18. package/dist/chunk-N44K3LHE.js +140 -0
  19. package/dist/chunk-NUDIHZEH.js +55 -0
  20. package/dist/chunk-SBLQH2TJ.js +56 -0
  21. package/dist/chunk-UFRO4H5W.js +228 -0
  22. package/dist/{chunk-RAAONHW3.js → chunk-WHJMKDV7.js} +24 -4
  23. package/dist/chunk-YR3O2I3S.js +101 -0
  24. package/dist/cli.js +866 -577
  25. package/dist/config-validate-K2N6XLDX.js +76 -0
  26. package/dist/{context-5LCUG2QR.js → context-KHKWJJMC.js} +1 -1
  27. package/dist/{firewall-XQGO7HWK.js → firewall-T3NTPZJD.js} +10 -0
  28. package/dist/{gcp-UG6AWPIZ.js → gcp-2JBTD7ZM.js} +19 -8
  29. package/dist/{generate-ELD7QF5Z.js → generate-2XV2GSP2.js} +4 -4
  30. package/dist/{harden-URPXLSVF.js → harden-PCDK3UQ5.js} +48 -11
  31. package/dist/health-5CCOXYGG.js +11 -0
  32. package/dist/logs-P7YCKVF4.js +15 -0
  33. package/dist/{mcp-wire-ZMIVFAUC.js → mcp-wire-BFEY2Y6H.js} +23 -17
  34. package/dist/{package-QYZFN3FP.js → package-3HUW57XH.js} +3 -3
  35. package/dist/plugins-DTST3OER.js +49 -0
  36. package/dist/{pool-QGFQRMGU.js → pool-EX4ZTA5P.js} +2 -2
  37. package/dist/privileged-JFA6LLTV.js +17 -0
  38. package/dist/{remote-config-QDLN4UMG.js → remote-config-Z6IIZR3D.js} +3 -3
  39. package/dist/runtime-LZ7NA53V.js +42 -0
  40. package/dist/{server-FWTOSSZP.js → server-A5AB67GQ.js} +16 -12
  41. package/dist/spec-path-KQOOIQCA.js +9 -0
  42. package/dist/{ssh-CNJNLM3E.js → ssh-B3NZAU4I.js} +1 -1
  43. package/dist/upgrade-ULD4KFYC.js +88 -0
  44. package/dist/validate-BOQRWX4F.js +14 -0
  45. package/dist/{version-guard-VUCNZXQS.js → version-guard-GPSTCPT7.js} +3 -3
  46. package/dist/{versions-2UVAT6PQ.js → versions-W5LRQCAJ.js} +2 -2
  47. package/package.json +3 -3
  48. package/spec/deploy-plan.schema.json +114 -25
  49. package/spec/integrations.yaml +135 -28
  50. package/spec/mcp-tools.yaml +68 -35
  51. package/spec/models.yaml +9 -0
  52. package/spec/openclaw-2.0.config.schema.json +1 -0
  53. package/spec/openclaw-versions.yaml +116 -27
  54. package/dist/chunk-35WVQY3X.js +0 -122
  55. package/dist/chunk-6X45DDGH.js +0 -42
  56. package/dist/chunk-SEZMVVAS.js +0 -156
  57. package/dist/validate-CFQ4FH2T.js +0 -10
@@ -1,37 +1,117 @@
1
1
  # OpenClaw Version Compatibility Matrix
2
2
  # Enforced by src/openclaw/versions.ts — `doctor`, `plan`, `up` and `apply` refuse
3
- # an OpenClaw version outside this range. Update via /openclaw-config skill.
3
+ # an OpenClaw version outside `support`. Update via /openclaw-config skill.
4
4
  #
5
- # This is the clawops 1.x line: it supports the pre-2.0 OpenClaw runtime contract only.
6
- # OpenClaw 2026.8.1 ("OpenClaw 2.0") changed that contract — state moved into SQLite,
7
- # config moved to a writable path, and provider plugins became install-gated. Deploying
8
- # it from this line produces a crash-looping gateway (exit 78). Use clawops 2.x instead.
5
+ # `support.min`, `support.max` and `support.recommended` are LIVE. Nothing reads
6
+ # `runtime`, `quirks`, `incompatible` or `tested`; they are documentation and the
7
+ # machine-readable source WO-38 will build the runtime contract from.
9
8
 
10
9
  version: 1
11
10
 
12
- # Minimum and maximum versions clawops supports
11
+ # Which release line this checkout is. Flipping this to "2.x" is gated by a test
12
+ # (tests/openclaw/line-interlock.test.ts) that fails until the runtime actually
13
+ # implements the 2.0 contract — see `runtime` below.
14
+ line: "2.x"
15
+
16
+ # Minimum and maximum versions clawops supports.
17
+ #
18
+ # Flipped to the 2.x range by WO-40, once the runtime could actually honour it: the
19
+ # config directory is mounted writable, provisioning writes gateway.mode, and
20
+ # --allow-unconfigured is gone. tests/openclaw/line-interlock.test.ts holds those
21
+ # together — setting `line: "2.x"` fails until each is true.
22
+ #
23
+ # The floor is 2026.9.2 rather than 2026.9.1 because the official Bedrock provider
24
+ # plugin declares "requires plugin API >=2026.9.2" and refuses to install on 9.1.
13
25
  support:
14
- min: "2026.4.5"
15
- max: "2026.7.1-2" # last release before the 2.0 runtime contract
16
- recommended: "2026.7.1-2"
26
+ min: "2026.9.2"
27
+ max: "" # no upper bound: this line tracks the 2.x runtime contract
28
+ recommended: "2026.9.2"
29
+
30
+ # The 2.0 runtime contract, measured against ghcr.io/openclaw/openclaw:2026.9.1.
31
+ # See docs/spikes/SP-10-openclaw-2.0-startup-contract.md for how each was observed.
32
+ # WO-38 consumes this; today nothing reads it.
33
+ runtime:
34
+ # 2026.9.2, not 2026.9.1: the official Bedrock provider plugin declares
35
+ # "requires plugin API >=2026.9.2" and refuses to install on 9.1. SP-10b §3.
36
+ targetMin: "2026.9.2"
37
+ image: "ghcr.io/openclaw/openclaw"
38
+ variants: [standard, slim, browser] # `-slim`, `-browser` tag suffixes
39
+ # Mount the config DIRECTORY, never the file. OpenClaw writes config by atomic
40
+ # rename, and renaming over a bind-mounted file fails EBUSY regardless of :ro.
41
+ # Verified — `plugins install` cannot record a plugin against a file mount. SP-10b §4.
42
+ configDir: "/cfg"
43
+ configPath: "/cfg/config.json" # via OPENCLAW_CONFIG_PATH
44
+ # The OpenClaw release spec/openclaw-2.0.config.schema.json was captured from.
45
+ # Validation compares the deployed version against this: a config key the schema
46
+ # does not know is an ERROR on an older-or-equal runtime and a WARNING on a newer
47
+ # one, because there it more likely means the schema is stale than the config wrong.
48
+ configSchemaCapturedFrom: "2026.9.2"
49
+ configMount: directory
50
+ gatewayPort: 18789
51
+ env:
52
+ configPath: OPENCLAW_CONFIG_PATH
53
+ token: OPENCLAW_GATEWAY_TOKEN
54
+ stateDir: OPENCLAW_STATE_DIR
55
+ startup:
56
+ # Verified: without gateway.mode the gateway exits 78 unless --allow-unconfigured
57
+ # is passed. Setting the mode is the supported answer; the flag exists to bypass a
58
+ # check for "suspicious or clobbered config", so clawops should not rely on it.
59
+ requiresGatewayMode: true
60
+ gatewayMode: "local" # schema accepts local|remote
61
+ allowUnconfigured: false # what 2.x should pass — i.e. do not pass it
62
+ exitCodeOnBlockedStart: 78
63
+ # Provider plugins are not all bundled. A configured-but-missing plugin is
64
+ # auto-installed from ClawHub on first boot, after which the gateway refuses to
65
+ # report ready and exits 1 ("startup convergence"); the next boot succeeds.
66
+ # The install lands in the container's writable layer, so it is lost — and refetched —
67
+ # on every container replacement, which is what clawops's restart path does.
68
+ plugins:
69
+ bundledSourceRoot: "/app/dist/extensions"
70
+ # Two different paths, and the difference matters. An explicit
71
+ # `openclaw plugins install` lands beside the config, so it persists with the
72
+ # config directory clawops already keeps as host state — no extra volume. The
73
+ # startup auto-install instead writes to the container's writable layer, which is
74
+ # lost on every container replacement. Pre-install; do not rely on auto-install.
75
+ installedPath: "<configDir>/extensions"
76
+ autoInstallPath: "/app/npm/projects" # ephemeral — container layer
77
+ convergenceRestartRequired: true # only when auto-installing at startup
78
+ # Reconcile configured providers against loaded plugins. `plugins doctor` is NOT
79
+ # usable for this: it never names a missing provider and exits 1 on duplicate-id
80
+ # warnings during normal operation. SP-10b §1-2.
81
+ verifyCommand: "openclaw plugins list --json"
82
+ verifyField: providerIds
83
+ providers:
84
+ amazon-bedrock:
85
+ bundled: false
86
+ package: "@openclaw/amazon-bedrock-provider"
87
+ install: "openclaw plugins install clawhub:@openclaw/amazon-bedrock-provider"
88
+ requiresEgress: true
89
+ # With no egress the gateway starts cleanly WITHOUT the provider — a healthy
90
+ # deployment missing the model backend it was configured for. clawops defaults
91
+ # to deny-all egress, so this is the default outcome, not an edge case.
92
+ silentlyAbsentWithoutEgress: true
93
+ ollama: { bundled: true }
94
+ anthropic: { bundled: true }
95
+ openai: { bundled: true }
17
96
 
18
- # Provider-specific quirks discovered through testing
19
97
  quirks:
20
- - id: aws-bedrock-aws-profile
98
+ - id: openclaw-2-provider-plugin-install
21
99
  description: |
22
- OpenClaw 2026.4.5+ ignores `auth: "aws-sdk"` in openclaw.json's Bedrock
23
- provider config. Instead, AWS_PROFILE must be set in the systemd
24
- EnvironmentFile so the AWS SDK resolves credentials via IMDS.
100
+ OpenClaw 2.0 does not bundle every model provider. A provider named in
101
+ models.providers whose plugin is missing is installed from ClawHub during
102
+ startup convergence; the gateway then exits 1 and requires one restart. With no
103
+ network egress the gateway starts successfully but without the provider.
25
104
  affected:
26
- versions: ">=2026.4.5"
105
+ versions: ">=2026.8.1"
27
106
  providers: [aws]
28
107
  configs: [openclaw.models.providers.amazon-bedrock]
29
108
  workaround: |
30
- AWS adapter MUST emit BOTH:
31
- 1. openclaw.json with `auth: "aws-sdk"` (for older versions, ignored by newer)
32
- 2. systemd EnvironmentFile with `AWS_PROFILE=default` (or the profile name)
33
- Adapter should test the rendered systemd unit includes the EnvironmentFile.
34
- discoveredFrom: aws-samples/sample-OpenClaw-on-AWS-with-Bedrock troubleshooting docs
109
+ Install the plugin at provisioning time, before the first gateway start:
110
+ openclaw plugins install clawhub:@openclaw/amazon-bedrock-provider
111
+ and/or persist /app/npm/projects so a container replacement does not refetch it.
112
+ Verify after deploy rather than trusting a healthy gateway: a missing provider
113
+ does not fail the health check.
114
+ discoveredFrom: docs/spikes/SP-10-openclaw-2.0-startup-contract.md
35
115
 
36
116
  - id: pnpm-pulumi-hoisting
37
117
  description: |
@@ -43,25 +123,34 @@ quirks:
43
123
  providers: ["*"]
44
124
  workaround: See docs/decisions/0003-pulumi-pnpm.md
45
125
 
46
- # Tested combinations — populated by CI matrix
126
+ # Retired: `aws-bedrock-aws-profile` (AWS_PROFILE in the systemd EnvironmentFile).
127
+ # It described a native/systemd install; clawops is Docker-only per ADR 0013, and on
128
+ # 2.0 the Bedrock question is plugin installation, not credential resolution.
129
+
47
130
  tested:
48
131
  - openclawVersion: "2026.4.5"
49
132
  providers: [aws, gcp, local]
50
133
  nodeVersions: ["20.x", "22.x"]
51
134
  lastTestedAt: "2026-04-15"
135
+ - openclawVersion: "2026.9.1"
136
+ providers: [] # runtime contract characterised only; no deploy path yet
137
+ nodeVersions: ["24.x"]
138
+ lastTestedAt: "2026-09-06"
139
+ notes: "SP-10 — startup contract, plugin install behaviour, config schema capture."
52
140
 
53
- # Known incompatible combinations
54
141
  incompatible:
55
142
  - openclawVersionRange: "<2026.4.5"
56
143
  reason: "Below clawops's declared minimum support. Earlier OpenClaw versions used different config paths and channel registration semantics."
57
144
  - openclawVersionRange: ">=2026.8.1"
58
145
  reason: |
59
- OpenClaw 2.0 changed the container runtime contract. Verified against 2026.8.1:
60
- - No gateway token on first boot => "Refusing to bind gateway to auto without
61
- auth", exit 78, crash-loop under --restart unless-stopped.
146
+ OpenClaw 2.0 changed the container runtime contract. Verified on 2026.8.1 and
147
+ re-verified on 2026.9.1:
148
+ - Without gateway.mode the gateway exits 78 unless --allow-unconfigured is
149
+ passed; clawops writes no mode.
62
150
  - Sessions, transcripts and credentials moved into SQLite under the state
63
151
  directory, which this line does not mount => total state loss on restart.
64
- - Provider plugins (Bedrock included) are install-gated; a configured but
65
- uninstalled provider prevents startup entirely.
152
+ - Provider plugins are not all bundled. A configured provider whose plugin is
153
+ missing is fetched at startup, costing a convergence restart — or is silently
154
+ absent when egress is denied, which is clawops's default.
66
155
  Use clawops 2.x, which targets OpenClaw >= 2026.9.1.
67
156
  useInstead: "@clawops/cli@2"
@@ -1,122 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- saveOverlay
4
- } from "./chunk-6ZFIFDBJ.js";
5
- import {
6
- resolveSecrets
7
- } from "./chunk-BOPSG2LI.js";
8
- import {
9
- validatePlan
10
- } from "./chunk-6X45DDGH.js";
11
- import {
12
- atomicWriteConfig,
13
- deepMerge,
14
- readRemoteConfig,
15
- restartGateway
16
- } from "./chunk-SEZMVVAS.js";
17
- import {
18
- buildContext
19
- } from "./chunk-P5LKWRPL.js";
20
- import {
21
- UsageError
22
- } from "./chunk-KGXPLI7W.js";
23
-
24
- // src/plan/apply.ts
25
- async function applyPlan(plan, opts) {
26
- const validation = validatePlan(plan);
27
- if (!validation.ok) {
28
- throw new UsageError(
29
- `Invalid deploy plan:
30
- ${validation.errors.join("\n")}`
31
- );
32
- }
33
- if (plan.spec.provider === "local") {
34
- throw new UsageError(
35
- "plan/apply is not supported for the local provider. Use `clawops up` directly."
36
- );
37
- }
38
- const { guardOpenclawVersion } = await import("./version-guard-VUCNZXQS.js");
39
- await guardOpenclawVersion(plan.spec.openclaw.version);
40
- const ctx = buildContext({
41
- stack: plan.spec.stackName,
42
- provider: plan.spec.provider
43
- });
44
- const stack = await ctx.getStack();
45
- await stack.setConfig("instanceType", { value: plan.spec.instanceType });
46
- if (plan.spec.region) {
47
- await stack.setConfig("region", { value: plan.spec.region });
48
- }
49
- await stack.setConfig("openclawVersion", { value: plan.spec.openclaw.version });
50
- const modelProvider = plan.spec.openclaw.config?.["models"]?.["provider"];
51
- if (modelProvider === "bedrock") {
52
- await stack.setConfig("bedrockEnabled", { value: "true" });
53
- }
54
- if (plan.metadata.stackVersion !== void 0) {
55
- const currentInfo = await stack.info();
56
- if (currentInfo !== void 0 && currentInfo.version !== plan.metadata.stackVersion) {
57
- process.stderr.write(
58
- `
59
- Warning: stack "${plan.spec.stackName}" has changed since this plan was generated (plan version: ${plan.metadata.stackVersion}, current: ${currentInfo.version}).
60
- The diff you reviewed may no longer reflect what will be applied.
61
-
62
- `
63
- );
64
- if (opts?.confirmDrift) {
65
- await opts.confirmDrift();
66
- }
67
- }
68
- }
69
- const start = Date.now();
70
- const result = await stack.up({ onOutput: opts?.onOutput, signal: opts?.signal });
71
- const outputs = Object.fromEntries(
72
- Object.entries(result.outputs).map(([k, v]) => [k, v.value])
73
- );
74
- const changeSummary = {};
75
- if (result.summary.resourceChanges) {
76
- for (const [op, count] of Object.entries(result.summary.resourceChanges)) {
77
- changeSummary[op] = count;
78
- }
79
- }
80
- const hasOverlay = plan.spec.openclaw.config !== void 0 || plan.spec.openclaw.channels !== void 0;
81
- if (hasOverlay) {
82
- await applyConfigOverlay(plan, outputs, ctx, opts?.signal);
83
- }
84
- return {
85
- outputs,
86
- changeSummary,
87
- durationMs: Date.now() - start
88
- };
89
- }
90
- async function applyConfigOverlay(plan, outputs, ctx, signal) {
91
- const { connect } = await import("./ssh-CNJNLM3E.js");
92
- const connInfo = ctx.adapter.getConnectionInfo(outputs);
93
- const session = await connect({
94
- host: connInfo.host,
95
- port: connInfo.port,
96
- user: connInfo.user,
97
- privateKeyPath: connInfo.privateKeyPath,
98
- knownHostsPath: connInfo.knownHostsPath,
99
- signal
100
- });
101
- try {
102
- const remote = await readRemoteConfig(session, signal);
103
- const configOverlay = plan.spec.openclaw.config ?? {};
104
- const resolvedOverlay = resolveSecrets(
105
- configOverlay,
106
- plan.spec.secrets ?? []
107
- );
108
- const merged = deepMerge(remote, {
109
- ...resolvedOverlay,
110
- ...plan.spec.openclaw.channels !== void 0 ? { channels: plan.spec.openclaw.channels } : {}
111
- });
112
- await atomicWriteConfig(session, merged, signal);
113
- await restartGateway(session, signal);
114
- saveOverlay(plan.spec.stackName, configOverlay, plan.spec.secrets ?? []);
115
- } finally {
116
- session.close();
117
- }
118
- }
119
-
120
- export {
121
- applyPlan
122
- };
@@ -1,42 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- resolveSpecDir
4
- } from "./chunk-PVUVI35Y.js";
5
-
6
- // src/plan/validate.ts
7
- import Ajv from "ajv/dist/2020";
8
- import addFormats from "ajv-formats";
9
- import { readFileSync } from "fs";
10
- import { join } from "path";
11
- var _validate;
12
- function getValidator() {
13
- if (_validate) return _validate;
14
- const ajv = new Ajv({ strict: false });
15
- addFormats(ajv);
16
- const schema = JSON.parse(
17
- readFileSync(join(resolveSpecDir(), "deploy-plan.schema.json"), "utf-8")
18
- );
19
- _validate = ajv.compile(schema);
20
- return _validate;
21
- }
22
- function validatePlan(plan) {
23
- const validate = getValidator();
24
- const ok = validate(plan);
25
- if (ok) return { ok: true, errors: [] };
26
- const errors = (validate.errors ?? []).map(
27
- (e) => `${e.instancePath} ${e.message ?? ""}`.trim()
28
- );
29
- return { ok: false, errors };
30
- }
31
- function assertValidPlan(plan) {
32
- const result = validatePlan(plan);
33
- if (!result.ok) {
34
- throw new Error(`Invalid deploy plan:
35
- ${result.errors.join("\n")}`);
36
- }
37
- }
38
-
39
- export {
40
- validatePlan,
41
- assertValidPlan
42
- };
@@ -1,156 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- err,
4
- ok
5
- } from "./chunk-EI7CQQ7D.js";
6
-
7
- // src/openclaw/run-flags.ts
8
- var CONFIG_MOUNT_PATH = "/app/config.json";
9
- var GATEWAY_PORT = 18789;
10
- var CONFIG_PATH_ENV = `-e OPENCLAW_CONFIG_PATH=${CONFIG_MOUNT_PATH}`;
11
- var ADD_HOST_FLAG = "--add-host=host.docker.internal:host-gateway";
12
- var PORT_PIN = `--port ${GATEWAY_PORT}`;
13
- var COMMON_RUN_FLAGS = `${CONFIG_PATH_ENV} ${ADD_HOST_FLAG}`;
14
- var ENV_FILE_PATH = "/home/clawops/openclaw.env";
15
- var IMAGE_INSPECT_CMD = `docker inspect openclaw --format '{{.Config.Image}}'`;
16
- function imageForRestart(inspectStdout) {
17
- const image = inspectStdout.trim();
18
- if (!image || image.includes("Error") || !image.includes(":")) {
19
- return err(
20
- "No running OpenClaw container found, so there is no image version to reuse. Deploy one with `clawops up --openclaw-version <version>`. clawops does not fall back to `latest` or `stable` here: both now point at OpenClaw 2.0, which this release line does not support."
21
- );
22
- }
23
- return ok(image);
24
- }
25
- function versionOf(image) {
26
- return image.split(":")[1] ?? "unknown";
27
- }
28
- function gatewayRunCommand(opts) {
29
- const { image, configPath, envFilePath = ENV_FILE_PATH, pathPrefix = "" } = opts;
30
- const envFileArg = `$([ -s ${envFilePath} ] && echo --env-file ${envFilePath})`;
31
- return pathPrefix + [
32
- "docker stop openclaw 2>/dev/null || true",
33
- "docker rm openclaw 2>/dev/null || true",
34
- `docker run -d --name openclaw --restart unless-stopped -p ${GATEWAY_PORT}:${GATEWAY_PORT} ${COMMON_RUN_FLAGS} ${envFileArg} -v ${configPath}:${CONFIG_MOUNT_PATH}:ro ${image} node openclaw.mjs gateway run --allow-unconfigured ${PORT_PIN}`
35
- ].join(" && ");
36
- }
37
-
38
- // src/plan/remote-config.ts
39
- var OPENCLAW_CONFIG_LINUX = "/home/clawops/openclaw.json";
40
- var OPENCLAW_CONFIG_MACOS = "~/.config/openclaw/config.json";
41
- var OPENCLAW_CONFIG = OPENCLAW_CONFIG_LINUX;
42
- var OPENCLAW_TMP = "/tmp/clawops-config.json.tmp";
43
- async function detectOS(session, signal) {
44
- const result = await session.exec("uname -s", signal);
45
- return result.stdout.trim() === "Darwin" ? "Darwin" : "Linux";
46
- }
47
- function configPathForOS(os) {
48
- return os === "Darwin" ? OPENCLAW_CONFIG_MACOS : OPENCLAW_CONFIG_LINUX;
49
- }
50
- async function execWithFallbackSudo(session, cmd, signal) {
51
- const result = await session.exec(cmd, signal);
52
- if (result.code === 0) return result;
53
- return session.exec(`sudo -n bash -c "${cmd}"`, signal);
54
- }
55
- async function readRemoteConfig(session, signal) {
56
- const os = await detectOS(session, signal);
57
- const configPath = configPathForOS(os);
58
- const result = os === "Darwin" ? await session.exec(`cat ${configPath}`, signal) : await execWithFallbackSudo(session, `cat ${configPath}`, signal);
59
- if (result.code !== 0) {
60
- throw new Error(`Cannot read ${configPath}: ${result.stderr}`);
61
- }
62
- try {
63
- return JSON.parse(result.stdout);
64
- } catch {
65
- throw new Error(`Cannot parse ${configPath}: invalid JSON`);
66
- }
67
- }
68
- function normaliseGatewayPort(cfg) {
69
- const gateway = cfg["gateway"];
70
- if (!gateway || typeof gateway !== "object" || Array.isArray(gateway)) return void 0;
71
- const g = gateway;
72
- const current = g["port"];
73
- if (typeof current === "number" && current !== GATEWAY_PORT) {
74
- g["port"] = GATEWAY_PORT;
75
- return current;
76
- }
77
- if (current === void 0) g["port"] = GATEWAY_PORT;
78
- return void 0;
79
- }
80
- async function atomicWriteConfig(session, cfg, signal) {
81
- const os = await detectOS(session, signal);
82
- const configPath = configPathForOS(os);
83
- const json = JSON.stringify(cfg, null, 2);
84
- const b64 = Buffer.from(json, "utf-8").toString("base64");
85
- const chown = os === "Linux" ? ` && chown clawops:clawops ${configPath}` : "";
86
- const cmd = `echo '${b64}' | base64 -d > ${OPENCLAW_TMP} && mv ${OPENCLAW_TMP} ${configPath}` + chown;
87
- const result = os === "Darwin" ? await session.exec(cmd, signal) : await execWithFallbackSudo(session, cmd, signal);
88
- if (result.code !== 0) {
89
- throw new Error(`Failed to write config: ${result.stderr}`);
90
- }
91
- }
92
- async function restartGateway(session, signal) {
93
- const os = await detectOS(session, signal);
94
- const configPath = configPathForOS(os);
95
- const pathPrefix = os === "Darwin" ? 'export PATH="/usr/local/bin:/opt/homebrew/bin:/Applications/Docker.app/Contents/Resources/bin:$PATH" && ' : "";
96
- const imgCmd = `${pathPrefix}${IMAGE_INSPECT_CMD}`;
97
- const imgResult = os === "Darwin" ? await session.exec(imgCmd, signal) : await execWithFallbackSudo(session, imgCmd, signal);
98
- const resolved = imageForRestart(imgResult.stdout);
99
- if (!resolved.ok) throw new Error(resolved.error);
100
- const image = resolved.value;
101
- const restartCmd = gatewayRunCommand({
102
- image,
103
- configPath,
104
- pathPrefix
105
- });
106
- const result = os === "Darwin" ? await session.exec(restartCmd, signal) : await execWithFallbackSudo(session, restartCmd, signal);
107
- if (result.code !== 0) {
108
- throw new Error(`Gateway restart failed: ${result.stderr}`);
109
- }
110
- const healthy = await waitForGateway(session, pathPrefix, signal);
111
- if (!healthy) {
112
- throw new Error(
113
- `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.`
114
- );
115
- }
116
- }
117
- async function waitForGateway(session, pathPrefix, signal, attempts = 15) {
118
- const probe = `${pathPrefix}curl -fsS -m 3 http://127.0.0.1:${GATEWAY_PORT}/healthz >/dev/null 2>&1 && echo ok || echo waiting`;
119
- for (let i = 0; i < attempts; i++) {
120
- if (signal?.aborted) return false;
121
- const r = await session.exec(probe, signal);
122
- if (r.stdout.trim().endsWith("ok")) return true;
123
- await new Promise((resolve) => setTimeout(resolve, 2e3));
124
- }
125
- return false;
126
- }
127
- function deepMerge(base, overlay) {
128
- const result = { ...base };
129
- for (const [key, val] of Object.entries(overlay)) {
130
- if (val !== null && typeof val === "object" && !Array.isArray(val) && typeof result[key] === "object" && result[key] !== null && !Array.isArray(result[key])) {
131
- result[key] = deepMerge(
132
- result[key],
133
- val
134
- );
135
- } else {
136
- result[key] = val;
137
- }
138
- }
139
- return result;
140
- }
141
-
142
- export {
143
- IMAGE_INSPECT_CMD,
144
- imageForRestart,
145
- versionOf,
146
- gatewayRunCommand,
147
- OPENCLAW_CONFIG_LINUX,
148
- OPENCLAW_CONFIG_MACOS,
149
- OPENCLAW_CONFIG,
150
- OPENCLAW_TMP,
151
- readRemoteConfig,
152
- normaliseGatewayPort,
153
- atomicWriteConfig,
154
- restartGateway,
155
- deepMerge
156
- };
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- assertValidPlan,
4
- validatePlan
5
- } from "./chunk-6X45DDGH.js";
6
- import "./chunk-PVUVI35Y.js";
7
- export {
8
- assertValidPlan,
9
- validatePlan
10
- };