mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:00:42 +00:00
fix(cli): keep startup help metadata on fast path
This commit is contained in:
@@ -12,6 +12,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- CLI/startup: read generated startup metadata from the bundled `dist` layout before falling back to live help rendering, so root/browser help and channel-option bootstrap stay on the fast path. Thanks @vincentkoc.
|
||||
- Matrix/E2EE: stabilize recovery and broken-device QA flows while avoiding Matrix device-cleanup sync races that could leave shutdown-time crypto work running. Thanks @gumadeiras.
|
||||
- Cron: classify isolated runs as errors from structured embedded-run execution-denial metadata, with final-output marker fallback for `SYSTEM_RUN_DENIED`, `INVALID_REQUEST`, and approval-binding refusals, so blocked commands no longer appear green in cron history. Fixes #67172; carries forward #67186. Thanks @oc-gh-dr, @hclsys, and @1yihui.
|
||||
- Onboarding/GitHub Copilot: add manifest-owned `--github-copilot-token` support for non-interactive setup, including env fallback, tokenRef storage in ref mode, saved-profile reuse, and current Copilot default-model wiring. Refs #50002 and supersedes #50003. Thanks @scottgl9.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { CHAT_CHANNEL_ORDER } from "../channels/ids.js";
|
||||
import { readCliStartupMetadata } from "./startup-metadata.js";
|
||||
|
||||
function dedupe(values: string[]): string[] {
|
||||
const seen = new Set<string>();
|
||||
@@ -23,14 +21,8 @@ function loadPrecomputedChannelOptions(): string[] | null {
|
||||
return precomputedChannelOptions;
|
||||
}
|
||||
try {
|
||||
const metadataPath = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
"cli-startup-metadata.json",
|
||||
);
|
||||
const raw = fs.readFileSync(metadataPath, "utf8");
|
||||
const parsed = JSON.parse(raw) as { channelOptions?: unknown };
|
||||
if (Array.isArray(parsed.channelOptions)) {
|
||||
const parsed = readCliStartupMetadata(import.meta.url) as { channelOptions?: unknown } | null;
|
||||
if (parsed && Array.isArray(parsed.channelOptions)) {
|
||||
precomputedChannelOptions = dedupe(
|
||||
parsed.channelOptions.filter((value): value is string => typeof value === "string"),
|
||||
);
|
||||
|
||||
@@ -36,6 +36,20 @@ describe("command-registration-policy", () => {
|
||||
hasBuiltinPrimary: false,
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldSkipPluginCommandRegistration({
|
||||
argv: ["node", "openclaw", "help", "--help"],
|
||||
primary: "help",
|
||||
hasBuiltinPrimary: false,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldSkipPluginCommandRegistration({
|
||||
argv: ["node", "openclaw", "help", "voicecall"],
|
||||
primary: "help",
|
||||
hasBuiltinPrimary: false,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("matches lazy subcommand registration policy", () => {
|
||||
|
||||
@@ -14,6 +14,9 @@ export function shouldSkipPluginCommandRegistration(params: {
|
||||
if (params.hasBuiltinPrimary) {
|
||||
return true;
|
||||
}
|
||||
if (params.primary === "help" && resolveCliArgvInvocation(params.argv).hasHelpOrVersion) {
|
||||
return true;
|
||||
}
|
||||
if (!params.primary) {
|
||||
return resolveCliArgvInvocation(params.argv).hasHelpOrVersion;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { readCliStartupMetadata } from "./startup-metadata.js";
|
||||
|
||||
let precomputedRootHelpText: string | null | undefined;
|
||||
let precomputedBrowserHelpText: string | null | undefined;
|
||||
@@ -14,17 +12,13 @@ function loadPrecomputedHelpText(
|
||||
return cache;
|
||||
}
|
||||
try {
|
||||
const metadataPath = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
"cli-startup-metadata.json",
|
||||
);
|
||||
const raw = fs.readFileSync(metadataPath, "utf8");
|
||||
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
||||
const value = parsed[key];
|
||||
if (typeof value === "string" && value.length > 0) {
|
||||
setCache(value);
|
||||
return value;
|
||||
const parsed = readCliStartupMetadata(import.meta.url);
|
||||
if (parsed) {
|
||||
const value = parsed[key];
|
||||
if (typeof value === "string" && value.length > 0) {
|
||||
setCache(value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Fall back to live help rendering.
|
||||
|
||||
@@ -146,8 +146,10 @@ describe("shouldUseRootHelpFastPath", () => {
|
||||
it("uses the fast path for root help only", () => {
|
||||
expect(shouldUseRootHelpFastPath(["node", "openclaw", "--help"])).toBe(true);
|
||||
expect(shouldUseRootHelpFastPath(["node", "openclaw", "--profile", "work", "-h"])).toBe(true);
|
||||
expect(shouldUseRootHelpFastPath(["node", "openclaw", "help", "--help"])).toBe(true);
|
||||
expect(shouldUseRootHelpFastPath(["node", "openclaw", "status", "--help"])).toBe(false);
|
||||
expect(shouldUseRootHelpFastPath(["node", "openclaw", "--help", "status"])).toBe(false);
|
||||
expect(shouldUseRootHelpFastPath(["node", "openclaw", "help", "gateway"])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -69,9 +69,13 @@ export function shouldEnsureCliPath(argv: string[]): boolean {
|
||||
}
|
||||
|
||||
export function shouldUseRootHelpFastPath(argv: string[]): boolean {
|
||||
const invocation = resolveCliArgvInvocation(argv);
|
||||
return (
|
||||
process.env.OPENCLAW_DISABLE_CLI_STARTUP_HELP_FAST_PATH !== "1" &&
|
||||
resolveCliArgvInvocation(argv).isRootHelpInvocation
|
||||
(invocation.isRootHelpInvocation ||
|
||||
(invocation.commandPath.length === 1 &&
|
||||
invocation.commandPath[0] === "help" &&
|
||||
invocation.hasHelpOrVersion))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
16
src/cli/startup-metadata.test.ts
Normal file
16
src/cli/startup-metadata.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { __testing } from "./startup-metadata.js";
|
||||
|
||||
describe("startup metadata path resolution", () => {
|
||||
it("checks metadata beside the bundled chunk before the legacy parent path", () => {
|
||||
const moduleDir = path.resolve("dist");
|
||||
const moduleUrl = pathToFileURL(path.join(moduleDir, "root-help-metadata-abc123.js")).href;
|
||||
|
||||
expect(__testing.resolveStartupMetadataPathCandidates(moduleUrl)).toEqual([
|
||||
path.join(moduleDir, "cli-startup-metadata.json"),
|
||||
path.join(path.dirname(moduleDir), "cli-startup-metadata.json"),
|
||||
]);
|
||||
});
|
||||
});
|
||||
28
src/cli/startup-metadata.ts
Normal file
28
src/cli/startup-metadata.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const STARTUP_METADATA_FILE = "cli-startup-metadata.json";
|
||||
|
||||
function resolveStartupMetadataPathCandidates(moduleUrl: string): string[] {
|
||||
const moduleDir = path.dirname(fileURLToPath(moduleUrl));
|
||||
return [
|
||||
path.resolve(moduleDir, STARTUP_METADATA_FILE),
|
||||
path.resolve(moduleDir, "..", STARTUP_METADATA_FILE),
|
||||
];
|
||||
}
|
||||
|
||||
export function readCliStartupMetadata(moduleUrl: string): Record<string, unknown> | null {
|
||||
for (const metadataPath of resolveStartupMetadataPathCandidates(moduleUrl)) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(metadataPath, "utf8")) as Record<string, unknown>;
|
||||
} catch {
|
||||
// Try the next bundled/source layout before falling back to dynamic startup work.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const __testing = {
|
||||
resolveStartupMetadataPathCandidates,
|
||||
};
|
||||
Reference in New Issue
Block a user