fix: ease bundled browser plugin recovery

This commit is contained in:
Peter Steinberger
2026-03-29 22:39:13 +09:00
parent f1af7d66d2
commit 2dd29db464
6 changed files with 198 additions and 0 deletions

View File

@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import {
rewriteUpdateFlagArgv,
resolveMissingBrowserCommandMessage,
shouldEnsureCliPath,
shouldRegisterPrimarySubcommand,
shouldSkipPluginCommandRegistration,
@@ -136,3 +137,39 @@ describe("shouldUseRootHelpFastPath", () => {
expect(shouldUseRootHelpFastPath(["node", "openclaw", "--help", "status"])).toBe(false);
});
});
describe("resolveMissingBrowserCommandMessage", () => {
it("explains plugins.allow misses for the browser command", () => {
expect(
resolveMissingBrowserCommandMessage({
plugins: {
allow: ["telegram"],
},
}),
).toContain('`plugins.allow` excludes "browser"');
});
it("explains explicit bundled browser disablement", () => {
expect(
resolveMissingBrowserCommandMessage({
plugins: {
entries: {
browser: {
enabled: false,
},
},
},
}),
).toContain("plugins.entries.browser.enabled=false");
});
it("returns null when browser is already allowed", () => {
expect(
resolveMissingBrowserCommandMessage({
plugins: {
allow: ["browser"],
},
}),
).toBeNull();
});
});

View File

@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
import path from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
import type { OpenClawConfig } from "../config/config.js";
import { resolveStateDir } from "../config/paths.js";
import { normalizeEnv } from "../infra/env.js";
import { formatUncaughtError } from "../infra/errors.js";
@@ -9,6 +10,7 @@ import { isMainModule } from "../infra/is-main.js";
import { ensureOpenClawCliOnPath } from "../infra/path-env.js";
import { assertSupportedRuntime } from "../infra/runtime-guard.js";
import { enableConsoleCapture } from "../logging.js";
import { normalizePluginId } from "../plugins/config-state.js";
import { hasMemoryRuntime } from "../plugins/memory-state.js";
import {
getCommandPathWithRootOptions,
@@ -86,6 +88,28 @@ export function shouldUseRootHelpFastPath(argv: string[]): boolean {
return isRootHelpInvocation(argv);
}
export function resolveMissingBrowserCommandMessage(config?: OpenClawConfig): string | null {
const allow =
Array.isArray(config?.plugins?.allow) && config.plugins.allow.length > 0
? config.plugins.allow
.filter((entry): entry is string => typeof entry === "string")
.map((entry) => normalizePluginId(entry))
: [];
if (allow.length > 0 && !allow.includes("browser")) {
return (
'The `openclaw browser` command is unavailable because `plugins.allow` excludes "browser". ' +
'Add "browser" to `plugins.allow` if you want the bundled browser CLI and tool.'
);
}
if (config?.plugins?.entries?.browser?.enabled === false) {
return (
"The `openclaw browser` command is unavailable because `plugins.entries.browser.enabled=false`. " +
"Re-enable that entry if you want the bundled browser CLI and tool."
);
}
return null;
}
function shouldLoadCliDotEnv(env: NodeJS.ProcessEnv = process.env): boolean {
if (existsSync(path.join(process.cwd(), ".env"))) {
return true;
@@ -190,6 +214,15 @@ export async function runCli(argv: string[] = process.argv) {
const config = await loadValidatedConfigForPluginRegistration();
if (config) {
registerPluginCliCommands(program, config);
if (
primary === "browser" &&
!program.commands.some((command) => command.name() === "browser")
) {
const browserCommandMessage = resolveMissingBrowserCommandMessage(config);
if (browserCommandMessage) {
throw new Error(browserCommandMessage);
}
}
}
}