

























@@ -27,6 +27,7 @@ const outputPath = path.join(distDir, "cli-startup-metadata.json");
2727const extensionsDir = path.join(rootDir, "extensions");
2828const ROOT_HELP_RENDER_TIMEOUT_MS = 120_000;
2929const BROWSER_HELP_RENDER_TIMEOUT_MS = 120_000;
30+const COMMAND_HELP_RENDER_TIMEOUT_MS = 120_000;
3031const CORE_CHANNEL_ORDER = [
3132"telegram",
3233"whatsapp",
@@ -71,26 +72,74 @@ function resolveRootHelpBundleIdentity(
7172};
7273}
737474-function updateHashFromFiles(hash: ReturnType<typeof createHash>, files: string[]) {
75+function updateHashFromFiles(
76+hash: ReturnType<typeof createHash>,
77+files: string[],
78+sourceRootDir: string = rootDir,
79+): void {
7580for (const file of files.toSorted()) {
76-hash.update(`${path.relative(rootDir, file)}\0`);
81+hash.update(`${path.relative(sourceRootDir, file)}\0`);
7782hash.update(readFileSync(file));
7883hash.update("\0");
7984}
8085}
818682-function resolveBrowserHelpSourceSignature(): string {
87+function resolveBrowserHelpSourceSignature(sourceRootDir: string = rootDir): string {
8388const hash = createHash("sha1");
84-const browserCliDir = path.join(rootDir, "extensions/browser/src/cli");
89+const browserCliDir = path.join(sourceRootDir, "extensions/browser/src/cli");
8590const browserCliFiles = readdirSync(browserCliDir)
8691.filter((entry) => entry.endsWith(".ts"))
8792.map((entry) => path.join(browserCliDir, entry));
88-updateHashFromFiles(hash, browserCliFiles);
89-updateHashFromFiles(hash, [
90-path.join(rootDir, "src/cli/program/help.ts"),
91-path.join(rootDir, "src/cli/program/context.ts"),
92-path.join(rootDir, "src/cli/banner.ts"),
93-]);
93+updateHashFromFiles(hash, browserCliFiles, sourceRootDir);
94+updateHashFromFiles(
95+hash,
96+[
97+path.join(sourceRootDir, "src/cli/program/help.ts"),
98+path.join(sourceRootDir, "src/cli/program/context.ts"),
99+path.join(sourceRootDir, "src/cli/banner.ts"),
100+],
101+sourceRootDir,
102+);
103+return hash.digest("hex");
104+}
105+106+function resolveSecretsHelpSourceSignature(sourceRootDir: string = rootDir): string {
107+const hash = createHash("sha1");
108+updateHashFromFiles(
109+hash,
110+[
111+path.join(sourceRootDir, "src/cli/secrets-cli.ts"),
112+path.join(sourceRootDir, "src/cli/program/help.ts"),
113+path.join(sourceRootDir, "src/cli/program/context.ts"),
114+path.join(sourceRootDir, "src/cli/banner.ts"),
115+],
116+sourceRootDir,
117+);
118+return hash.digest("hex");
119+}
120+121+function resolveNodesHelpSourceSignature(sourceRootDir: string = rootDir): string {
122+const hash = createHash("sha1");
123+const nodesCliDir = path.join(sourceRootDir, "src/cli/nodes-cli");
124+const nodesCliFiles = readdirSync(nodesCliDir)
125+.filter((entry) => entry.endsWith(".ts") && !entry.endsWith(".test.ts"))
126+.map((entry) => path.join(nodesCliDir, entry));
127+updateHashFromFiles(hash, nodesCliFiles, sourceRootDir);
128+updateHashFromFiles(
129+hash,
130+[
131+path.join(sourceRootDir, "extensions/canvas/cli-metadata.ts"),
132+path.join(sourceRootDir, "extensions/canvas/index.ts"),
133+path.join(sourceRootDir, "extensions/canvas/src/a2ui-jsonl.ts"),
134+path.join(sourceRootDir, "extensions/canvas/src/cli-helpers.ts"),
135+path.join(sourceRootDir, "extensions/canvas/src/cli.ts"),
136+path.join(sourceRootDir, "src/cli/program/help.ts"),
137+path.join(sourceRootDir, "src/cli/program/context.ts"),
138+path.join(sourceRootDir, "src/cli/banner.ts"),
139+path.join(sourceRootDir, "src/plugins/register-plugin-cli-command-groups.ts"),
140+],
141+sourceRootDir,
142+);
94143return hash.digest("hex");
95144}
96145@@ -312,20 +361,68 @@ function renderSourceBrowserHelpText(
312361return result.stdout ?? "";
313362}
314363364+function renderSourceCommandHelpText(
365+command: "nodes" | "secrets",
366+renderContext: RootHelpRenderContext = createIsolatedRootHelpRenderContext(),
367+): string {
368+const result = spawnSync(
369+process.execPath,
370+["--import", "tsx", "openclaw.mjs", command, "--help"],
371+{
372+cwd: rootDir,
373+encoding: "utf8",
374+env: {
375+ ...renderContext.env,
376+OPENCLAW_DISABLE_CLI_STARTUP_HELP_FAST_PATH: "1",
377+},
378+timeout: COMMAND_HELP_RENDER_TIMEOUT_MS,
379+},
380+);
381+if (result.error) {
382+throw result.error;
383+}
384+if (result.status !== 0) {
385+const stderr = result.stderr?.trim();
386+throw new Error(
387+`Failed to render source ${command} help` +
388+(stderr ? `: ${stderr}` : result.signal ? `: terminated by ${result.signal}` : ""),
389+);
390+}
391+return result.stdout ?? "";
392+}
393+394+function renderSourceSecretsHelpText(
395+renderContext: RootHelpRenderContext = createIsolatedRootHelpRenderContext(),
396+): string {
397+return renderSourceCommandHelpText("secrets", renderContext);
398+}
399+400+function renderSourceNodesHelpText(
401+renderContext: RootHelpRenderContext = createIsolatedRootHelpRenderContext(),
402+): string {
403+return renderSourceCommandHelpText("nodes", renderContext);
404+}
405+315406export async function writeCliStartupMetadata(options?: {
316407distDir?: string;
317408outputPath?: string;
318409extensionsDir?: string;
410+sourceRootDir?: string;
319411renderBundledRootHelpText?: typeof renderBundledRootHelpText;
320412renderSourceRootHelpText?: typeof renderSourceRootHelpText;
321413renderSourceBrowserHelpText?: typeof renderSourceBrowserHelpText;
414+renderSourceSecretsHelpText?: typeof renderSourceSecretsHelpText;
415+renderSourceNodesHelpText?: typeof renderSourceNodesHelpText;
322416}): Promise<void> {
323417const resolvedDistDir = options?.distDir ?? distDir;
324418const resolvedOutputPath = options?.outputPath ?? outputPath;
325419const resolvedExtensionsDir = options?.extensionsDir ?? extensionsDir;
420+const resolvedSourceRootDir = options?.sourceRootDir ?? rootDir;
326421const channelCatalog = readBundledChannelCatalog(resolvedExtensionsDir);
327422const bundleIdentity = resolveRootHelpBundleIdentity(resolvedDistDir);
328-const browserHelpSourceSignature = resolveBrowserHelpSourceSignature();
423+const browserHelpSourceSignature = resolveBrowserHelpSourceSignature(resolvedSourceRootDir);
424+const secretsHelpSourceSignature = resolveSecretsHelpSourceSignature(resolvedSourceRootDir);
425+const nodesHelpSourceSignature = resolveNodesHelpSourceSignature(resolvedSourceRootDir);
329426const bundledPluginsDir = path.join(resolvedDistDir, "extensions");
330427const renderContext = createIsolatedRootHelpRenderContext(
331428existsSync(bundledPluginsDir) ? bundledPluginsDir : resolvedExtensionsDir,
@@ -336,16 +433,26 @@ export async function writeCliStartupMetadata(options?: {
336433const existing = JSON.parse(readFileSync(resolvedOutputPath, "utf8")) as {
337434rootHelpBundleSignature?: unknown;
338435browserHelpSourceSignature?: unknown;
436+secretsHelpSourceSignature?: unknown;
437+nodesHelpSourceSignature?: unknown;
339438channelCatalogSignature?: unknown;
340439browserHelpText?: unknown;
440+secretsHelpText?: unknown;
441+nodesHelpText?: unknown;
341442};
342443if (
343444bundleIdentity &&
344445existing.rootHelpBundleSignature === bundleIdentity.signature &&
345446existing.browserHelpSourceSignature === browserHelpSourceSignature &&
447+existing.secretsHelpSourceSignature === secretsHelpSourceSignature &&
448+existing.nodesHelpSourceSignature === nodesHelpSourceSignature &&
346449existing.channelCatalogSignature === channelCatalog.signature &&
347450typeof existing.browserHelpText === "string" &&
348-existing.browserHelpText.length > 0
451+existing.browserHelpText.length > 0 &&
452+typeof existing.secretsHelpText === "string" &&
453+existing.secretsHelpText.length > 0 &&
454+typeof existing.nodesHelpText === "string" &&
455+existing.nodesHelpText.length > 0
349456) {
350457return;
351458}
@@ -365,6 +472,12 @@ export async function writeCliStartupMetadata(options?: {
365472const browserHelpText = (options?.renderSourceBrowserHelpText ?? renderSourceBrowserHelpText)(
366473renderContext,
367474);
475+const secretsHelpText = (options?.renderSourceSecretsHelpText ?? renderSourceSecretsHelpText)(
476+renderContext,
477+);
478+const nodesHelpText = (options?.renderSourceNodesHelpText ?? renderSourceNodesHelpText)(
479+renderContext,
480+);
368481369482mkdirSync(resolvedDistDir, { recursive: true });
370483writeFileSync(
@@ -376,7 +489,11 @@ export async function writeCliStartupMetadata(options?: {
376489 channelCatalogSignature: channelCatalog.signature,
377490 rootHelpBundleSignature: bundleIdentity?.signature ?? null,
378491 browserHelpSourceSignature,
492+ secretsHelpSourceSignature,
493+ nodesHelpSourceSignature,
379494 browserHelpText,
495+ secretsHelpText,
496+ nodesHelpText,
380497 rootHelpText,
381498 },
382499 null,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。