mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-22 20:38:10 +00:00
206 lines
8.2 KiB
TypeScript
206 lines
8.2 KiB
TypeScript
// Plugin Isolation script supports OpenClaw repository automation.
|
|
import { shellQuote } from "./host-command.ts";
|
|
import { providerIdFromModelId } from "./provider-auth.ts";
|
|
|
|
interface PluginIsolationOptions {
|
|
fallbackPluginId: string;
|
|
homeFallback?: string;
|
|
modelId: string;
|
|
nodeCommand?: string;
|
|
}
|
|
|
|
export function posixCodexPlatformPackageRepairFunction(): string {
|
|
return `repair_missing_codex_platform_package() {
|
|
output_file="$1"
|
|
grep -F 'Missing optional dependency @openai/codex-' "$output_file" >/dev/null 2>&1 || return 1
|
|
state_home="\${OPENCLAW_PARALLELS_HOME:-\${HOME:-}}"
|
|
codex_manifest=""
|
|
for candidate in "$state_home"/.openclaw/npm/projects/*/node_modules/@openclaw/codex/package.json; do
|
|
[ -f "$candidate" ] || continue
|
|
codex_manifest="$candidate"
|
|
break
|
|
done
|
|
if [ -z "$codex_manifest" ]; then
|
|
echo "codex-platform-repair: managed Codex project not found" >&2
|
|
return 1
|
|
fi
|
|
project_root="\${codex_manifest%/node_modules/@openclaw/codex/package.json}"
|
|
cache_dir="$(mktemp -d "\${TMPDIR:-/tmp}/openclaw-npm-cache.XXXXXX")"
|
|
echo "codex-platform-repair: retrying managed npm install once with a fresh cache" >&2
|
|
repair_rc=0
|
|
(
|
|
cd "$project_root"
|
|
NPM_CONFIG_CACHE="$cache_dir" npm_config_cache="$cache_dir" npm install --omit=dev --omit=peer --legacy-peer-deps --ignore-scripts --no-audit --no-fund
|
|
) || repair_rc=$?
|
|
rm -rf "$cache_dir"
|
|
if [ "$repair_rc" -ne 0 ]; then
|
|
echo "codex-platform-repair: npm install failed with exit code $repair_rc" >&2
|
|
return "$repair_rc"
|
|
fi
|
|
echo "codex-platform-repair: managed npm install completed" >&2
|
|
}`;
|
|
}
|
|
|
|
export function windowsCodexPlatformPackageRepairFunction(): string {
|
|
return String.raw`function Repair-MissingCodexPlatformPackage {
|
|
param([object[]] $Output)
|
|
$outputText = $Output | Out-String
|
|
if ($outputText -notmatch [regex]::Escape('Missing optional dependency @openai/codex-')) {
|
|
return $false
|
|
}
|
|
$projectsRoot = Join-Path $env:USERPROFILE '.openclaw\npm\projects'
|
|
$codexManifest = Get-ChildItem -Path $projectsRoot -Filter package.json -File -Recurse -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.FullName -match 'node_modules[\\/]@openclaw[\\/]codex[\\/]package\.json$' } |
|
|
Select-Object -First 1
|
|
if (-not $codexManifest) {
|
|
Write-Warning 'codex-platform-repair: managed Codex project not found'
|
|
return $false
|
|
}
|
|
$projectRoot = $codexManifest.Directory.Parent.Parent.Parent.FullName
|
|
$cacheDir = Join-Path ([System.IO.Path]::GetTempPath()) ('openclaw-npm-cache-' + [guid]::NewGuid().ToString('N'))
|
|
$oldUpperCache = [Environment]::GetEnvironmentVariable('NPM_CONFIG_CACHE', 'Process')
|
|
$oldLowerCache = [Environment]::GetEnvironmentVariable('npm_config_cache', 'Process')
|
|
$pushedLocation = $false
|
|
$repairExit = 1
|
|
try {
|
|
New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null
|
|
[Environment]::SetEnvironmentVariable('NPM_CONFIG_CACHE', $cacheDir, 'Process')
|
|
[Environment]::SetEnvironmentVariable('npm_config_cache', $cacheDir, 'Process')
|
|
Push-Location $projectRoot
|
|
$pushedLocation = $true
|
|
Write-Host 'codex-platform-repair: retrying managed npm install once with a fresh cache'
|
|
$repairOutput = & npm.cmd install --omit=dev --omit=peer --legacy-peer-deps --ignore-scripts --no-audit --no-fund 2>&1
|
|
$repairExit = $LASTEXITCODE
|
|
if ($null -ne $repairOutput) { $repairOutput | ForEach-Object { Write-Host $_ } }
|
|
} finally {
|
|
if ($pushedLocation) { Pop-Location }
|
|
[Environment]::SetEnvironmentVariable('NPM_CONFIG_CACHE', $oldUpperCache, 'Process')
|
|
[Environment]::SetEnvironmentVariable('npm_config_cache', $oldLowerCache, 'Process')
|
|
Remove-Item $cacheDir -Force -Recurse -ErrorAction SilentlyContinue
|
|
}
|
|
if ($repairExit -ne 0) {
|
|
Write-Warning "codex-platform-repair: npm install failed with exit code $repairExit"
|
|
return $false
|
|
}
|
|
Write-Host 'codex-platform-repair: managed npm install completed'
|
|
return $true
|
|
}`;
|
|
}
|
|
|
|
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");`;
|
|
}
|