#!/usr/bin/env node /** * Patch OpenClaw's dev-time runtime files. * * 1. BROWSER_TOOL_MODEL_HINT: allow retries on transient errors. * 2. Bundled runtime deps materialization: accept already-satisfied local * runtime packages even when OpenClaw expands its generated manifest after * Gateway boot. Production builds are separately patched in * bundle-openclaw.mjs. * * The original hint ("Do NOT retry the browser tool — it will keep failing") * causes models to permanently refuse browser usage after a single transient error. */ import { readFileSync, writeFileSync, readdirSync } from 'fs'; import { join } from 'path'; const REPLACEMENTS = [ [ 'Do NOT retry the browser tool \u2014 it will keep failing. Use an alternative approach or inform the user that the browser is currently unavailable.', 'If this was a transient error (timeout, network), you may retry once. If the same error persists after retry, try an alternative approach and let the user know.', ], [ 'Do NOT retry the browser tool.', 'You may retry once if this was a transient error.', ], ]; const distDir = join(process.cwd(), 'node_modules', 'openclaw', 'dist'); let patchedCount = 0; let runtimeDepsPatchedCount = 0; let desktopFastChatPatchedCount = 0; try { for (const file of readdirSync(distDir)) { if (!file.endsWith('.js')) continue; const filePath = join(distDir, file); let content = readFileSync(filePath, 'utf-8'); let changed = false; for (const [search, replace] of REPLACEMENTS) { if (content.includes(search)) { content = content.replaceAll(search, replace); changed = true; } } if (changed) { writeFileSync(filePath, content, 'utf-8'); console.log(`[patch-browser-hint] Patched: ${file}`); patchedCount++; } } } catch { // openclaw not installed yet or dist not found — skip silently } try { for (const file of readdirSync(distDir)) { if (!file.endsWith('.js')) continue; const filePath = join(distDir, file); let content = readFileSync(filePath, 'utf-8'); const before = content; content = content.replace( 'const contextInjectionMode = resolveContextInjectionMode(params.config);\n\t\tconst isRawModelRun = params.modelRun === true || params.promptMode === "none";', 'const contextInjectionMode = resolveContextInjectionMode(params.config);\n\t\tconst yinianDesktopFastModelRun = (!params.messageChannel && !params.messageProvider || params.messageChannel === "webchat" || params.messageProvider === "webchat") && (params.config?.tools?.profile === "messaging" || params.config?.tools?.profile === "minimal");\n\t\tconst isRawModelRun = params.modelRun === true || params.promptMode === "none" || yinianDesktopFastModelRun;', ); content = content.replace( 'const builtAppendPrompt = resolveSystemPromptOverride({', 'const builtAppendPrompt = isRawModelRun ? "" : resolveSystemPromptOverride({', ); content = content.replace( 'const toolsRaw = params.disableTools || isRawModelRun ? [] : applyEmbeddedAttemptToolsAllow(createOpenClawCodingTools({', 'const yinianDesktopFastModelRun = (!params.messageChannel && !params.messageProvider || params.messageChannel === "webchat" || params.messageProvider === "webchat") && (params.config?.tools?.profile === "messaging" || params.config?.tools?.profile === "minimal");\n\t\tconst toolsRaw = params.disableTools || isRawModelRun || yinianDesktopFastModelRun ? [] : applyEmbeddedAttemptToolsAllow(createOpenClawCodingTools({', ); content = content.replace( 'const yinianDesktopFastModelRun = (!params.messageChannel && !params.messageProvider || params.messageChannel === "webchat" || params.messageProvider === "webchat") && (params.config?.tools?.profile === "messaging" || params.config?.tools?.profile === "minimal");\n\t\tconst toolsRaw = params.disableTools || isRawModelRun || yinianDesktopFastModelRun ? [] : applyEmbeddedAttemptToolsAllow(createOpenClawCodingTools({', 'const toolsRaw = params.disableTools || isRawModelRun || yinianDesktopFastModelRun ? [] : applyEmbeddedAttemptToolsAllow(createOpenClawCodingTools({', ); content = content.replace( 'const yinianDesktopFastModelRun = !params.messageChannel && !params.messageProvider && (params.config?.tools?.profile === "messaging" || params.config?.tools?.profile === "minimal");', 'const yinianDesktopFastModelRun = (!params.messageChannel && !params.messageProvider || params.messageChannel === "webchat" || params.messageProvider === "webchat") && (params.config?.tools?.profile === "messaging" || params.config?.tools?.profile === "minimal");', ); content = content.replace( '...listChannelAgentTools({ cfg: options?.config }),', '...(profile === "messaging" || profile === "minimal" ? [] : listChannelAgentTools({ cfg: options?.config })),', ); content = content.replace( 'allowGatewaySubagentBinding: options?.allowGatewaySubagentBinding\n\t\t})', 'allowGatewaySubagentBinding: options?.allowGatewaySubagentBinding,\n\t\t\tdisablePluginTools: options?.disablePluginTools ?? (profile === "messaging" || profile === "minimal")\n\t\t})', ); if (content !== before) { writeFileSync(filePath, content, 'utf-8'); console.log(`[patch-browser-hint] Patched desktop fast chat runtime: ${file}`); desktopFastChatPatchedCount++; } } } catch { // openclaw not installed yet or dist not found — skip silently } try { for (const file of readdirSync(distDir)) { if (!/^bundled-runtime-deps-.*\.js$/.test(file)) continue; const filePath = join(distDir, file); let content = readFileSync(filePath, 'utf-8'); const search = `function isRuntimeDepsPlanMaterialized(installRoot, installSpecs) { \tconst generatedManifestSpecs = readGeneratedInstallManifestSpecs(installRoot); \tconst packageManifestSpecs = generatedManifestSpecs !== null ? null : readPackageRuntimeDepSpecs(installRoot); \treturn (generatedManifestSpecs !== null && sameRuntimeDepSpecs(generatedManifestSpecs, installSpecs) || packageManifestSpecs !== null && sameRuntimeDepSpecs(packageManifestSpecs, installSpecs)) && hasSatisfiedInstallSpecPackages(installRoot, installSpecs); }`; const replace = `function isRuntimeDepsPlanMaterialized(installRoot, installSpecs) { \tconst generatedManifestSpecs = readGeneratedInstallManifestSpecs(installRoot); \tconst packageManifestSpecs = generatedManifestSpecs !== null ? null : readPackageRuntimeDepSpecs(installRoot); \treturn (generatedManifestSpecs !== null || packageManifestSpecs !== null) && hasSatisfiedInstallSpecPackages(installRoot, installSpecs); }`; if (content.includes(search)) { content = content.replace(search, replace); writeFileSync(filePath, content, 'utf-8'); console.log(`[patch-browser-hint] Patched runtime deps materialization: ${file}`); runtimeDepsPatchedCount++; } } } catch { // openclaw not installed yet or dist not found — skip silently } if (patchedCount > 0) { console.log(`[patch-browser-hint] Done. Patched ${patchedCount} file(s).`); } if (runtimeDepsPatchedCount > 0) { console.log(`[patch-browser-hint] Done. Patched ${runtimeDepsPatchedCount} runtime deps file(s).`); } if (desktopFastChatPatchedCount > 0) { console.log(`[patch-browser-hint] Done. Patched ${desktopFastChatPatchedCount} desktop fast chat file(s).`); }