import { shellQuote } from "./host-command.ts"; import { providerIdFromModelId } from "./provider-auth.ts"; interface PluginIsolationOptions { fallbackPluginId: string; homeFallback?: string; modelId: string; nodeCommand?: string; } export function providerOnlyPluginId(modelId: string, fallbackPluginId: string): string { return providerIdFromModelId(modelId) || fallbackPluginId; } export function posixProviderOnlyPluginIsolationScript(options: PluginIsolationOptions): string { const nodeCommand = shellQuote(options.nodeCommand ?? "node"); const homeEnv = options.homeFallback ? `OPENCLAW_PARALLELS_HOME=${shellQuote(options.homeFallback)} ` : ""; return `/usr/bin/env ${homeEnv}${nodeCommand} - <<'JS' ${providerOnlyPluginIsolationNodeScript(options)} JS`; } export function windowsProviderOnlyPluginIsolationScript(options: PluginIsolationOptions): string { const payloadJson = JSON.stringify({ modelId: options.modelId, pluginId: providerOnlyPluginId(options.modelId, options.fallbackPluginId), }); return `$env:OPENCLAW_PARALLELS_PLUGIN_ISOLATION = @' ${payloadJson} '@ $isolationScriptPath = Join-Path ([System.IO.Path]::GetTempPath()) 'openclaw-parallels-plugin-isolation.cjs' @' ${providerOnlyPluginIsolationNodeSource()} '@ | Set-Content -Path $isolationScriptPath -Encoding UTF8 node.exe $isolationScriptPath if ($LASTEXITCODE -ne 0) { throw "plugin isolation failed with exit code $LASTEXITCODE" } Remove-Item $isolationScriptPath -Force -ErrorAction SilentlyContinue Remove-Item Env:OPENCLAW_PARALLELS_PLUGIN_ISOLATION -Force -ErrorAction SilentlyContinue`; } function providerOnlyPluginIsolationNodeScript(options: PluginIsolationOptions): string { const payloadJson = JSON.stringify({ homeFallback: options.homeFallback, modelId: options.modelId, pluginId: providerOnlyPluginId(options.modelId, options.fallbackPluginId), }); return `process.env.OPENCLAW_PARALLELS_PLUGIN_ISOLATION = ${JSON.stringify(payloadJson)}; ${providerOnlyPluginIsolationNodeSource()}`; } function providerOnlyPluginIsolationNodeSource(): string { return String.raw`const fs = require("node:fs"); const path = require("node:path"); const payload = JSON.parse(process.env.OPENCLAW_PARALLELS_PLUGIN_ISOLATION || "{}"); const home = process.env.OPENCLAW_PARALLELS_HOME || payload.homeFallback || process.env.HOME || process.env.USERPROFILE || "/root"; const configPath = path.join(home, ".openclaw", "openclaw.json"); const stateDir = path.dirname(configPath); const modelId = String(payload.modelId || ""); const allowedPluginId = String(payload.pluginId || "").trim(); if (!allowedPluginId || !modelId) { throw new Error("missing plugin isolation payload"); } const readConfig = () => { if (!fs.existsSync(configPath)) { return {}; } return JSON.parse(fs.readFileSync(configPath, "utf8")); }; const objectRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {}; const config = readConfig(); config.plugins = objectRecord(config.plugins); config.plugins.entries = { [allowedPluginId]: { enabled: true } }; config.plugins.allow = [allowedPluginId]; config.agents = objectRecord(config.agents); config.agents.defaults = objectRecord(config.agents.defaults); config.agents.defaults.model = { ...objectRecord(config.agents.defaults.model), primary: modelId, }; config.agents.defaults.models = objectRecord(config.agents.defaults.models); const selectedModelEntry = config.agents.defaults.models[modelId]; if (selectedModelEntry && typeof selectedModelEntry === "object" && !Array.isArray(selectedModelEntry)) { delete selectedModelEntry.agentRuntime; } const providerId = modelId.split("/", 1)[0] || ""; const providerModelId = modelId.slice(providerId.length + 1); const providers = objectRecord(objectRecord(config.models).providers); const providerEntry = providers[providerId]; if (providerEntry && typeof providerEntry === "object" && !Array.isArray(providerEntry)) { delete providerEntry.agentRuntime; if (Array.isArray(providerEntry.models)) { for (const model of providerEntry.models) { if ( model && typeof model === "object" && (model.id === providerModelId || model.id === modelId || model.name === providerModelId || model.name === modelId) ) { delete model.agentRuntime; } } } } fs.rmSync(path.join(stateDir, "npm", "node_modules", "@openclaw", "codex"), { recursive: true, force: true, }); fs.mkdirSync(stateDir, { recursive: true }); fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");`; }