mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 03:15:46 -06:00
fix(update): support npm before lifecycle allowlists (#125452)
This commit is contained in:
committed by
GitHub
parent
b228c83bfc
commit
a996ea25d9
@@ -39,10 +39,10 @@ Already manage Node.js? Install the published package instead (Node 22.22.3+, 24
|
||||
npm install -g openclaw@latest --allow-scripts=openclaw
|
||||
```
|
||||
|
||||
That command is for npm 12 or npm 11.16+. On npm 11.12 and earlier, omit
|
||||
`--allow-scripts=openclaw`. If you have npm 11.13–11.15, upgrade npm first. See
|
||||
the [installation guide](https://docs.openclaw.ai/install) for the lifecycle
|
||||
script contract, Docker, Nix, and other deployment paths.
|
||||
That command is for npm 12 or npm 11.16+. On npm 11.15 and earlier, omit
|
||||
`--allow-scripts=openclaw`. See the
|
||||
[installation guide](https://docs.openclaw.ai/install) for the lifecycle script
|
||||
contract, Docker, Nix, and other deployment paths.
|
||||
|
||||
## Quick start
|
||||
|
||||
|
||||
+1
-2
@@ -148,8 +148,7 @@ The Gateway is the single source of truth for sessions, routing, and channel con
|
||||
npm install -g openclaw@latest --allow-scripts=openclaw
|
||||
```
|
||||
|
||||
On npm 11.12 and earlier, omit `--allow-scripts=openclaw`. Upgrade npm
|
||||
11.13–11.15 before installing.
|
||||
On npm 11.15 and earlier, omit `--allow-scripts=openclaw`.
|
||||
|
||||
</Step>
|
||||
<Step title="Onboard and install the service">
|
||||
|
||||
@@ -242,8 +242,8 @@ personal-number mode, and self-chat details: [WhatsApp](/channels/whatsapp).
|
||||
The snapshot's global npm tree is owned by root, so plain `openclaw update`
|
||||
cannot write to it. Update from the sandbox SSH session with:
|
||||
|
||||
The command below is for npm 12 or npm 11.16+. On npm 11.12 and earlier,
|
||||
omit `--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
The command below is for npm 12 or npm 11.16+. On npm 11.15 and earlier,
|
||||
omit `--allow-scripts=openclaw`.
|
||||
|
||||
```bash
|
||||
sudo env "PATH=$PATH" npm install --global openclaw@latest --allow-scripts=openclaw
|
||||
|
||||
@@ -82,9 +82,8 @@ If you already manage Node yourself:
|
||||
openclaw onboard --install-daemon
|
||||
```
|
||||
|
||||
On npm 11.12 and earlier, use the same command without
|
||||
`--allow-scripts=openclaw`. Do not use npm 11.13–11.15 for this install;
|
||||
upgrade to npm 11.16+ first.
|
||||
On npm 11.15 and earlier, use the same command without
|
||||
`--allow-scripts=openclaw`.
|
||||
|
||||
<Note>
|
||||
npm 12 blocks unapproved package lifecycle scripts by default. The
|
||||
@@ -93,11 +92,9 @@ If you already manage Node yourself:
|
||||
they are not covered by allowScripts`.
|
||||
|
||||
npm 11.16 accepts the option but otherwise only warns that the scripts are
|
||||
`not yet covered by allowScripts` and still runs them. npm 11.12 and earlier
|
||||
`not yet covered by allowScripts` and still runs them. npm 11.15 and earlier
|
||||
have neither the policy nor the option, so their command must be unflagged.
|
||||
npm 11.13–11.15 also lack the option, but they are transitional upstream
|
||||
releases outside this documented install contract; upgrade rather than
|
||||
relying on their unflagged behavior. The `npm approve-scripts openclaw`
|
||||
The `npm approve-scripts openclaw`
|
||||
command suggested by npm 11.16 does not work for a global install — it fails
|
||||
with `ENOMATCH No installed packages match: openclaw`.
|
||||
</Note>
|
||||
|
||||
@@ -116,8 +116,8 @@ Replace `youruser` with the account you created, and the IP with your VM's IP.
|
||||
|
||||
## 6) Install OpenClaw
|
||||
|
||||
Inside the VM, use the following command on npm 12 or npm 11.16+. On npm 11.12
|
||||
and earlier, omit `--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
Inside the VM, use the following command on npm 12 or npm 11.16+. On npm 11.15
|
||||
and earlier, omit `--allow-scripts=openclaw`.
|
||||
|
||||
```bash
|
||||
npm install -g openclaw@latest --allow-scripts=openclaw
|
||||
|
||||
+11
-11
@@ -49,8 +49,8 @@ successful core update; a direct
|
||||
`npm install -g openclaw@extended-stable --allow-scripts=openclaw` does not
|
||||
update `update.channel`, but a final extended-stable package version still
|
||||
checks only the verified `extended-stable` selector for update availability.
|
||||
That direct command is for npm 12 or npm 11.16+. On npm 11.12 and earlier,
|
||||
omit `--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
That direct command is for npm 12 or npm 11.16+. On npm 11.15 and earlier,
|
||||
omit `--allow-scripts=openclaw`.
|
||||
After the core swap, eligible official npm plugins with bare/default or
|
||||
`latest` intent converge to that exact core version. Exact pins and explicit
|
||||
non-`latest` tags, third-party plugins, and non-npm sources remain unchanged.
|
||||
@@ -157,8 +157,8 @@ curl -fsSL https://openclaw.ai/install.sh | bash -s -- --install-method npm --ve
|
||||
|
||||
## Alternative: manual npm, pnpm, or bun
|
||||
|
||||
The npm command below is for npm 12 or npm 11.16+. On npm 11.12 and earlier,
|
||||
omit `--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
The npm command below is for npm 12 or npm 11.16+. On npm 11.15 and earlier,
|
||||
omit `--allow-scripts=openclaw`.
|
||||
|
||||
```bash
|
||||
npm i -g openclaw@latest --allow-scripts=openclaw
|
||||
@@ -178,7 +178,7 @@ that Gateway. Replace `/usr/bin/npm` with the system npm that owns the
|
||||
root-owned global prefix on your host:
|
||||
|
||||
The npm command below follows the same version contract: use the flag on npm 12
|
||||
or npm 11.16+, omit it on npm 11.12 and earlier, and upgrade npm 11.13–11.15.
|
||||
or npm 11.16+, and omit it on npm 11.15 and earlier.
|
||||
|
||||
```bash
|
||||
openclaw gateway stop
|
||||
@@ -203,10 +203,10 @@ Node version during `preinstall`; only then does OpenClaw verify the packaged
|
||||
`dist` inventory and swap the clean package tree into the real global prefix. A
|
||||
packed completion guard is omitted from the expected inventory and removed only
|
||||
after `preinstall` succeeds, so skipped lifecycle scripts also fail before the
|
||||
swap. The updater probes the owning npm before mutation. On npm 11.12 and
|
||||
earlier it omits the unsupported lifecycle-policy flag; on npm 11.13–11.15 it
|
||||
stops with upgrade guidance. On npm 12 and npm 11.16+, it approves only the
|
||||
candidate OpenClaw lifecycle; transitive dependency scripts remain unapproved.
|
||||
swap. The updater probes the owning npm before mutation. On npm 11.15 and
|
||||
earlier it omits the unsupported lifecycle-policy flag. On npm 12 and npm
|
||||
11.16+, it approves only the candidate OpenClaw lifecycle; transitive
|
||||
dependency scripts remain unapproved.
|
||||
This avoids npm overlaying a new package onto stale files from the old one. If
|
||||
the install command fails, OpenClaw retries once with `--omit=optional`, which
|
||||
helps hosts where native optional dependencies cannot compile.
|
||||
@@ -445,8 +445,8 @@ automatically replacing the package again.
|
||||
If the CLI update path is unavailable, use the same package manager and install
|
||||
scope that own the current Gateway:
|
||||
|
||||
The npm command below is for npm 12 or npm 11.16+. On npm 11.12 and earlier,
|
||||
omit `--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
The npm command below is for npm 12 or npm 11.16+. On npm 11.15 and earlier,
|
||||
omit `--allow-scripts=openclaw`.
|
||||
|
||||
```bash
|
||||
openclaw gateway stop
|
||||
|
||||
@@ -39,8 +39,8 @@ The keepalive options reduce idle tunnel drops during onboarding.
|
||||
|
||||
## Install OpenClaw
|
||||
|
||||
Inside the Box, use the following command on npm 12 or npm 11.16+. On npm 11.12
|
||||
and earlier, omit `--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
Inside the Box, use the following command on npm 12 or npm 11.16+. On npm 11.15
|
||||
and earlier, omit `--allow-scripts=openclaw`.
|
||||
|
||||
```bash
|
||||
sudo npm install -g openclaw --allow-scripts=openclaw
|
||||
|
||||
@@ -88,7 +88,7 @@ RPCs: `environments.create`, `environments.destroy`, extended `environments.list
|
||||
No bespoke worker artifact, and no dependence on npm availability:
|
||||
|
||||
- Canonical install for all modes: a gateway-produced, content-hashed worker bundle (the gateway's own build output packed as a tarball), pushed over SSH and installed on the box. This covers dev builds and unreleased commits by construction.
|
||||
- On npm 12 or npm 11.16+, `npm i -g openclaw@<exact gateway version> --allow-scripts=openclaw` is an optimization when the gateway runs a released version; never `latest`. On npm 11.12 and earlier, omit `--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
- On npm 12 or npm 11.16+, `npm i -g openclaw@<exact gateway version> --allow-scripts=openclaw` is an optimization when the gateway runs a released version; never `latest`. On npm 11.15 and earlier, omit `--allow-scripts=openclaw`.
|
||||
- Bootstrap is idempotent; a warm lease with a matching bundle hash skips install. Raw machines may need a networked toolchain phase (Node runtime) — part of the setup phase, closed afterwards.
|
||||
- Handshake verifies worker build hash, protocol feature set, and runtime compatibility. The existing gateway version/protocol checks are insufficient for this (SSH-tunneled nodes are exempted from exact-version rejection), so worker admission does its own exact-build check.
|
||||
|
||||
|
||||
@@ -54,8 +54,8 @@ Full server guidance lives in the [Linux guide](/platforms/linux) and the
|
||||
|
||||
On a single user Chromebook, use the native npm install (the installer script,
|
||||
or `npm i -g openclaw@latest --allow-scripts=openclaw` on npm 12 or npm
|
||||
11.16+) rather than [Docker](/install/docker). On npm 11.12 and earlier, omit
|
||||
`--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
11.16+) rather than [Docker](/install/docker). On npm 11.15 and earlier, omit
|
||||
`--allow-scripts=openclaw`.
|
||||
|
||||
Docker works inside Crostini, but Docker in Crostini adds friction: if you use
|
||||
the Claude Code CLI as your model runtime, it has to be installed and logged in
|
||||
|
||||
@@ -130,7 +130,7 @@ Linux v1 uses one Canvas window. HTTP and HTTPS pages are renderable, but A2UI a
|
||||
The CLI remains the simplest option for a headless server, a VPS, or a remote Gateway:
|
||||
|
||||
1. Install Node 26 (recommended), or another supported release: Node 22.22.3+, Node 24.15+, or Node 25.9+.
|
||||
2. On npm 12 or npm 11.16+, run `npm i -g openclaw@latest --allow-scripts=openclaw`. On npm 11.12 and earlier, omit `--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
2. On npm 12 or npm 11.16+, run `npm i -g openclaw@latest --allow-scripts=openclaw`. On npm 11.15 and earlier, omit `--allow-scripts=openclaw`.
|
||||
3. `openclaw onboard --install-daemon`
|
||||
4. From your laptop: `ssh -N -L 18789:127.0.0.1:18789 <user>@<host>`
|
||||
5. Open `http://127.0.0.1:18789/` and authenticate with the configured shared
|
||||
|
||||
@@ -29,8 +29,8 @@ OpenClaw package.
|
||||
For a manual install, use Node 26 (recommended) or another supported release:
|
||||
Node 22.22.3+, Node 24.15+, or Node 25.9+. Install `openclaw` globally:
|
||||
|
||||
The command below is for npm 12 or npm 11.16+. On npm 11.12 and earlier,
|
||||
omit `--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
The command below is for npm 12 or npm 11.16+. On npm 11.15 and earlier,
|
||||
omit `--allow-scripts=openclaw`.
|
||||
|
||||
```bash
|
||||
npm install -g openclaw@<version> --allow-scripts=openclaw
|
||||
|
||||
@@ -55,8 +55,8 @@ matching user-space CLI and runtime before starting the Gateway wizard.
|
||||
|
||||
For manual development recovery, install the matching CLI yourself:
|
||||
|
||||
The npm command below is for npm 12 or npm 11.16+. On npm 11.12 and earlier,
|
||||
omit `--allow-scripts=openclaw`; upgrade npm 11.13–11.15 first.
|
||||
The npm command below is for npm 12 or npm 11.16+. On npm 11.15 and earlier,
|
||||
omit `--allow-scripts=openclaw`.
|
||||
|
||||
```bash
|
||||
npm install -g openclaw@<version> --allow-scripts=openclaw
|
||||
|
||||
@@ -207,9 +207,9 @@ the repository root does not prepare bundled plugin dependencies.
|
||||
|
||||
For the global npm row, use
|
||||
`npm install -g openclaw --allow-scripts=openclaw` on npm 12 or npm 11.16+.
|
||||
On npm 11.12 and earlier, omit `--allow-scripts=openclaw`; upgrade npm
|
||||
11.13–11.15 first. Plugin dependency convergence remains intentionally
|
||||
script-disabled and continues to use the `--ignore-scripts` commands above.
|
||||
On npm 11.15 and earlier, omit `--allow-scripts=openclaw`. Plugin dependency
|
||||
convergence remains intentionally script-disabled and continues to use the
|
||||
`--ignore-scripts` commands above.
|
||||
|
||||
## Legacy cleanup
|
||||
|
||||
|
||||
@@ -4717,34 +4717,6 @@ describe("update-cli", () => {
|
||||
expect(defaultRuntime.exit).not.toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it.each(["11.13.0", "11.15.9"])(
|
||||
"refuses npm %s before stopping the managed gateway or cleaning update backups",
|
||||
async (npmVersion) => {
|
||||
const tempDir = await createTrackedTempDir("openclaw-update-npm-policy-");
|
||||
const { nodeModules, entryPath } = await setupInstalledPackageRoot(tempDir);
|
||||
const backupDir = path.join(nodeModules, ".openclaw-interrupted");
|
||||
await fs.mkdir(backupDir, { recursive: true });
|
||||
mockRunningManagedGateway(["node", entryPath, "gateway", "run"]);
|
||||
mockFileBackedPathExists();
|
||||
vi.mocked(runCommandWithTimeout).mockImplementation(async (argv) => {
|
||||
if (argv[0] === "npm" && argv[1] === "--version") {
|
||||
return commandResult({ stdout: `${npmVersion}\n` });
|
||||
}
|
||||
if (argv[0] === "npm" && argv[1] === "root" && argv[2] === "-g") {
|
||||
return commandResult({ stdout: `${nodeModules}\n` });
|
||||
}
|
||||
return commandResult();
|
||||
});
|
||||
|
||||
await updateCommand({ yes: true });
|
||||
|
||||
expect(serviceStop).not.toHaveBeenCalled();
|
||||
expect(packageInstallCommandCall()).toBeUndefined();
|
||||
await expect(fs.access(backupDir)).resolves.toBeUndefined();
|
||||
expect(getErrorOutput()).toContain(`npm ${npmVersion} cannot safely approve`);
|
||||
},
|
||||
);
|
||||
|
||||
it("stops a running managed gateway before package replacement", async () => {
|
||||
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
|
||||
const processOnSpy = vi.spyOn(process, "on");
|
||||
@@ -6929,32 +6901,6 @@ describe("update-cli", () => {
|
||||
expect(defaultRuntime.exit).not.toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it.each(["11.13.0", "11.15.9"])(
|
||||
"refuses npm %s package-to-dev updates before checkout or install",
|
||||
async (npmVersion) => {
|
||||
const packageRoot = createCaseDir("openclaw-npm-transition");
|
||||
mockPackageInstallStatus(packageRoot);
|
||||
vi.mocked(runCommandWithTimeout).mockImplementation(async (argv) => {
|
||||
if (argv[0] === "npm" && argv[1] === "--version") {
|
||||
return commandResult({ stdout: `${npmVersion}\n` });
|
||||
}
|
||||
if (argv[0] === "npm" && argv[1] === "root" && argv[2] === "-g") {
|
||||
return commandResult({ stdout: `${path.dirname(packageRoot)}\n` });
|
||||
}
|
||||
return commandResult();
|
||||
});
|
||||
|
||||
await updateCommand({ channel: "dev", yes: true, restart: false });
|
||||
|
||||
expect(runGatewayUpdate).not.toHaveBeenCalled();
|
||||
expect(commandCalls().some(([argv]) => argv[0] === "git")).toBe(false);
|
||||
expect(
|
||||
commandCalls().some(([argv]) => argv[0] === "npm" && argv[1] === "i" && argv[2] === "-g"),
|
||||
).toBe(false);
|
||||
expect(getErrorOutput()).toContain(`npm ${npmVersion} cannot safely approve`);
|
||||
},
|
||||
);
|
||||
|
||||
it("explains why git updates cannot run with edited files", async () => {
|
||||
vi.mocked(defaultRuntime.log).mockClear();
|
||||
vi.mocked(defaultRuntime.error).mockClear();
|
||||
|
||||
@@ -163,18 +163,14 @@ describe("markPackagePostInstallDoctorAdvisory", () => {
|
||||
});
|
||||
|
||||
describe("npm lifecycle policy preflight", () => {
|
||||
it.each([
|
||||
{ version: "11.13.0", message: "Upgrade the owning npm to 11.16" },
|
||||
{ version: "11.15.9", message: "Upgrade the owning npm to 11.16" },
|
||||
{ version: null, message: "Unable to determine the owning npm version" },
|
||||
])("stops before mutation for npm $version", async ({ version, message }) => {
|
||||
it("stops before mutation when the owning npm version is unknown", async () => {
|
||||
const runStep = vi.fn();
|
||||
const runCommand = vi.fn<CommandRunner>();
|
||||
const installTarget = createNpmTarget("/tmp/npm-policy-test/lib/node_modules");
|
||||
installTarget.npmOwner = {
|
||||
version,
|
||||
lifecyclePolicy: version ? "unsupported-transition" : null,
|
||||
...(version ? {} : { probeError: "version probe failed" }),
|
||||
version: null,
|
||||
lifecyclePolicy: null,
|
||||
probeError: "version probe failed",
|
||||
};
|
||||
|
||||
const result = await runGlobalPackageUpdateSteps({
|
||||
@@ -187,7 +183,7 @@ describe("npm lifecycle policy preflight", () => {
|
||||
});
|
||||
|
||||
expect(runCommand).not.toHaveBeenCalled();
|
||||
expect(result.failedStep?.stderrTail).toContain(message);
|
||||
expect(result.failedStep?.stderrTail).toContain("Unable to determine the owning npm version");
|
||||
expect(runStep).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -213,9 +213,9 @@ describe("update global helpers", () => {
|
||||
|
||||
it.each([
|
||||
["11.12.0", "unflagged"],
|
||||
["11.13.0", "unsupported-transition"],
|
||||
["11.14.0", "unsupported-transition"],
|
||||
["11.15.9", "unsupported-transition"],
|
||||
["11.13.0", "unflagged"],
|
||||
["11.14.0", "unflagged"],
|
||||
["11.15.9", "unflagged"],
|
||||
["11.16.0", "allow-scripts"],
|
||||
["12.0.0", "allow-scripts"],
|
||||
] as const)("binds npm %s lifecycle policy to the owning executable", async (version, policy) => {
|
||||
@@ -775,6 +775,12 @@ describe("update global helpers", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("omits npm's lifecycle allowlist before npm 11.16", () => {
|
||||
expect(
|
||||
globalInstallArgs("npm", "openclaw@latest", null, null, null, "unflagged"),
|
||||
).not.toContain("--allow-scripts=openclaw");
|
||||
});
|
||||
|
||||
it("allows only the resolved npm candidate lifecycle identity", () => {
|
||||
expect(globalInstallArgs("npm", "/tmp/openclaw-2026.7.2.tgz")).toContain(
|
||||
"--allow-scripts=/tmp/openclaw-2026.7.2.tgz",
|
||||
|
||||
@@ -83,9 +83,9 @@ export type NpmGlobalPrefixLayout = {
|
||||
binDir: string;
|
||||
};
|
||||
|
||||
type NpmLifecyclePolicy = "unsupported-transition" | "unflagged" | "allow-scripts";
|
||||
type NpmLifecyclePolicy = "unflagged" | "allow-scripts";
|
||||
|
||||
type SupportedNpmLifecyclePolicy = Exclude<NpmLifecyclePolicy, "unsupported-transition">;
|
||||
type SupportedNpmLifecyclePolicy = NpmLifecyclePolicy;
|
||||
|
||||
type NpmLifecyclePolicyGate =
|
||||
| { policy: SupportedNpmLifecyclePolicy | null; error: null }
|
||||
@@ -97,13 +97,9 @@ function resolveNpmLifecyclePolicy(version: string): NpmLifecyclePolicy | null {
|
||||
if (!parsed) {
|
||||
return null;
|
||||
}
|
||||
if (parsed.major !== 11) {
|
||||
return parsed.major >= 12 ? "allow-scripts" : "unflagged";
|
||||
}
|
||||
if (parsed.minor <= 12) {
|
||||
return "unflagged";
|
||||
}
|
||||
return parsed.minor >= 16 ? "allow-scripts" : "unsupported-transition";
|
||||
return parsed.major >= 12 || (parsed.major === 11 && parsed.minor >= 16)
|
||||
? "allow-scripts"
|
||||
: "unflagged";
|
||||
}
|
||||
|
||||
/** Resolves the owning npm policy once, before any update mutation. */
|
||||
@@ -113,17 +109,10 @@ export function resolveNpmLifecyclePolicyGate(
|
||||
if (installTarget.manager !== "npm") {
|
||||
return { policy: null, error: null };
|
||||
}
|
||||
const version = installTarget.npmOwner?.version ?? "";
|
||||
const policy = installTarget.npmOwner?.lifecyclePolicy ?? null;
|
||||
if (policy === "unflagged" || policy === "allow-scripts") {
|
||||
return { policy, error: null };
|
||||
}
|
||||
if (policy === "unsupported-transition") {
|
||||
return {
|
||||
policy: null,
|
||||
error: `npm ${version} cannot safely approve OpenClaw lifecycle scripts. Upgrade the owning npm to 11.16 or newer before updating; no package changes were made.`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
policy: null,
|
||||
error: `Unable to determine the owning npm version before updating; no package changes were made.${installTarget.npmOwner?.probeError ? ` ${installTarget.npmOwner.probeError}` : ""}`,
|
||||
|
||||
@@ -3013,27 +3013,6 @@ describe("runGatewayUpdate", () => {
|
||||
expect(await pathExists(staleDir)).toBe(false);
|
||||
});
|
||||
|
||||
it("refuses unsupported npm before global update cleanup mutates backups", async () => {
|
||||
const { nodeModules, pkgRoot } = await createGlobalPackageFixture(tempDir);
|
||||
const backupDir = path.join(nodeModules, ".openclaw-interrupted");
|
||||
await fs.mkdir(backupDir, { recursive: true });
|
||||
const { runCommand } = createGlobalInstallHarness({
|
||||
pkgRoot,
|
||||
npmRootOutput: nodeModules,
|
||||
npmVersion: "11.15.9",
|
||||
installCommand: npmGlobalInstallCommand("openclaw@latest"),
|
||||
});
|
||||
|
||||
const result = await runWithCommand(runCommand, { cwd: pkgRoot });
|
||||
|
||||
expect(result).toMatchObject({
|
||||
status: "error",
|
||||
reason: "unexpected-error",
|
||||
});
|
||||
expect(result.steps.at(-1)?.name).toBe("npm lifecycle policy preflight");
|
||||
await expect(fs.access(backupDir)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("retries global npm update with --omit=optional when initial install fails", async () => {
|
||||
const nodeModules = path.join(tempDir, "node_modules");
|
||||
const pkgRoot = path.join(nodeModules, "openclaw");
|
||||
|
||||
Reference in New Issue
Block a user