UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

378 lines (377 loc) 15.3 kB
import { d as resolveCodexAppServerRuntimeOptions, f as resolveCodexComputerUseConfig } from "./config-i2AzQJy8.js"; import { n as describeControlFailure } from "./capabilities-5Jb_gIw6.js"; import { t as requestCodexAppServerJson } from "./request-D-miWXFr.js"; import { existsSync } from "node:fs"; //#region extensions/codex/src/app-server/computer-use.ts /** * Computer Use plugin/MCP readiness checks and optional install flow for Codex * app-server sessions. */ var CodexComputerUseSetupError = class extends Error { constructor(status) { super(status.message); this.name = "CodexComputerUseSetupError"; this.status = status; } }; const CURATED_MARKETPLACE_POLL_INTERVAL_MS = 2e3; const COMPUTER_USE_MARKETPLACE_NAME_PRIORITY = [ "openai-bundled", "openai-curated", "local" ]; const DEFAULT_CODEX_BUNDLED_MARKETPLACE_PATH = "/Applications/Codex.app/Contents/Resources/plugins/openai-bundled"; /** Reads Computer Use readiness without installing or mutating app-server state. */ async function readCodexComputerUseStatus(params = {}) { const config = resolveComputerUseConfig(params); if (!config.enabled) return disabledStatus(config); try { return await inspectCodexComputerUse({ ...params, config, installPlugin: false }); } catch (error) { return unavailableStatus(config, "check_failed", `Computer Use check failed: ${describeControlFailure(error)}`); } } /** * Ensures Computer Use is ready when enabled, optionally installing when config * allows safe auto-install. */ async function ensureCodexComputerUse(params = {}) { const config = resolveComputerUseConfig(params); if (!config.enabled) return disabledStatus(config); const status = await inspectCodexComputerUse({ ...params, config, installPlugin: false }); if (status.ready) return status; if (config.autoInstall) { const blockedAutoInstallStatus = blockUnsafeAutoInstallStatus(config); if (blockedAutoInstallStatus) throw new CodexComputerUseSetupError(blockedAutoInstallStatus); const installedStatus = await inspectCodexComputerUse({ ...params, config, installPlugin: true }); if (!installedStatus.ready) throw new CodexComputerUseSetupError(installedStatus); return installedStatus; } if (!status.ready) throw new CodexComputerUseSetupError(status); return status; } /** Forces Computer Use plugin installation and returns the ready status. */ async function installCodexComputerUse(params = {}) { const config = resolveComputerUseConfig({ ...params, forceEnable: true, overrides: { ...params.overrides, enabled: true, autoInstall: true } }); const status = await inspectCodexComputerUse({ ...params, config, installPlugin: true }); if (!status.ready) throw new CodexComputerUseSetupError(status); return status; } async function inspectCodexComputerUse(params) { const request = createComputerUseRequest(params); if (params.installPlugin) await request("experimentalFeature/enablement/set", { enablement: { plugins: true } }); const marketplace = await resolveMarketplaceRef({ request, config: params.config, allowAdd: params.installPlugin, signal: params.signal, defaultBundledMarketplacePath: params.defaultBundledMarketplacePath }); if (!marketplace.marketplace) return unavailableStatus(params.config, "marketplace_missing", marketplace.message ?? `No Codex marketplace containing ${params.config.pluginName} is registered. Configure computerUse.marketplaceSource or computerUse.marketplacePath, then run /codex computer-use install.`); const pluginInspection = await ensureComputerUsePlugin({ request, config: params.config, marketplace: marketplace.marketplace, installPlugin: params.installPlugin }); if (!pluginInspection.ok) return pluginInspection.status; return await readComputerUseTools({ request, config: params.config, plugin: pluginInspection.plugin, installPlugin: params.installPlugin }); } async function ensureComputerUsePlugin(params) { let plugin = await readComputerUsePlugin(params.request, params.marketplace, params.config.pluginName); if (!plugin.summary.installed || !plugin.summary.enabled) { if (!params.installPlugin) return { ok: false, status: statusFromPlugin({ config: params.config, plugin, tools: [], reason: pluginSetupReason(plugin, params.marketplace), message: pluginSetupMessage(params.config, plugin, params.marketplace) }) }; if (params.marketplace.kind === "remote") return { ok: false, status: statusFromPlugin({ config: params.config, plugin, tools: [], reason: "remote_install_unsupported", message: remoteInstallUnsupportedMessage(plugin, params.marketplace) }) }; await params.request("plugin/install", pluginRequestParams(params.marketplace, params.config.pluginName)); await reloadMcpServers(params.request); plugin = await readComputerUsePlugin(params.request, params.marketplace, params.config.pluginName); } if (!plugin.summary.installed || !plugin.summary.enabled) return { ok: false, status: statusFromPlugin({ config: params.config, plugin, tools: [], reason: pluginSetupReason(plugin, params.marketplace), message: pluginSetupMessage(params.config, plugin, params.marketplace) }) }; return { ok: true, plugin }; } async function readComputerUseTools(params) { let server = await readMcpServerStatus(params.request, params.config.mcpServerName); if (!server && params.installPlugin) { await reloadMcpServers(params.request); server = await readMcpServerStatus(params.request, params.config.mcpServerName); } if (!server) return statusFromPlugin({ config: params.config, plugin: params.plugin, tools: [], reason: "mcp_missing", message: `Computer Use is installed, but the ${params.config.mcpServerName} MCP server is not available.` }); return statusFromPlugin({ config: params.config, plugin: params.plugin, tools: Object.keys(server.tools).toSorted(), reason: "ready", message: "Computer Use is ready." }); } async function resolveMarketplaceRef(params) { let preferredMarketplaceName = params.config.marketplaceName; if (params.config.marketplaceSource && params.allowAdd) { const added = await params.request("marketplace/add", { source: params.config.marketplaceSource }); preferredMarketplaceName ??= added.marketplaceName; } if (params.config.marketplacePath) return { marketplace: preferredMarketplaceName ? { kind: "local", name: preferredMarketplaceName, path: params.config.marketplacePath } : { kind: "local", path: params.config.marketplacePath } }; let candidates = await listComputerUseMarketplaceCandidates(params.request, params.config); if (candidates.length === 0 && shouldAddBundledComputerUseMarketplace(params)) { const bundledMarketplacePath = params.defaultBundledMarketplacePath ?? DEFAULT_CODEX_BUNDLED_MARKETPLACE_PATH; const added = await params.request("marketplace/add", { source: bundledMarketplacePath }); preferredMarketplaceName ??= added.marketplaceName; candidates = await listComputerUseMarketplaceCandidates(params.request, params.config); } const waitUntil = marketplaceDiscoveryWaitUntil(params); while (candidates.length === 0) { if (Date.now() >= waitUntil) break; await delay(Math.min(CURATED_MARKETPLACE_POLL_INTERVAL_MS, waitUntil - Date.now()), params.signal); candidates = await listComputerUseMarketplaceCandidates(params.request, params.config); } if (preferredMarketplaceName) { const preferred = candidates.find((candidate) => candidate.name === preferredMarketplaceName); if (preferred) return { marketplace: preferred }; return { message: `Configured Codex marketplace ${preferredMarketplaceName} was not found or does not contain ${params.config.pluginName}. Run /codex computer-use install with a source or path to install from a new marketplace.` }; } if (candidates.length > 1) { const preferred = chooseKnownComputerUseMarketplace(candidates); if (preferred) return { marketplace: preferred }; return { message: `Multiple Codex marketplaces contain ${params.config.pluginName}. Configure computerUse.marketplaceName or computerUse.marketplacePath to choose one.` }; } if (params.config.marketplaceSource && !params.allowAdd && candidates.length === 0) return { message: "Computer Use marketplace source is configured but has not been registered. Run /codex computer-use install to register it." }; const marketplace = candidates[0]; return marketplace ? { marketplace } : {}; } async function listComputerUseMarketplaceCandidates(request, config) { return findComputerUseMarketplaces(await request("plugin/list", { cwds: [] }), config.pluginName); } function blockUnsafeAutoInstallStatus(config) { if (!config.marketplaceSource) return; return unavailableStatus(config, "auto_install_blocked", "Computer Use auto-install only uses marketplaces Codex app-server has already discovered. Run /codex computer-use install to install from a configured marketplace source."); } function shouldAddBundledComputerUseMarketplace(params) { const bundledMarketplacePath = params.defaultBundledMarketplacePath ?? DEFAULT_CODEX_BUNDLED_MARKETPLACE_PATH; return params.allowAdd && !params.config.marketplaceSource && !params.config.marketplacePath && !params.config.marketplaceName && existsSync(bundledMarketplacePath); } function findComputerUseMarketplaces(listed, pluginName) { return listed.marketplaces.filter((marketplace) => marketplace.plugins.some((plugin) => plugin.name === pluginName || plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}`)).map((marketplace) => { if (marketplace.path) return { kind: "local", name: marketplace.name, path: marketplace.path }; return { kind: "remote", name: marketplace.name, remoteMarketplaceName: marketplace.name }; }); } function chooseKnownComputerUseMarketplace(candidates) { for (const marketplaceName of COMPUTER_USE_MARKETPLACE_NAME_PRIORITY) { const candidate = candidates.find((marketplace) => marketplace.name === marketplaceName); if (candidate) return candidate; } } function marketplaceDiscoveryWaitUntil(params) { if (params.allowAdd && !params.config.marketplaceSource && !params.config.marketplacePath && !params.config.marketplaceName) return Date.now() + params.config.marketplaceDiscoveryTimeoutMs; return 0; } async function delay(ms, signal) { if (signal?.aborted) throw abortError(signal); await new Promise((resolve, reject) => { const onAbort = () => { clearTimeout(timer); signal?.removeEventListener("abort", onAbort); reject(abortError(signal)); }; const timer = setTimeout(() => { signal?.removeEventListener("abort", onAbort); resolve(); }, ms); signal?.addEventListener("abort", onAbort, { once: true }); }); } function abortError(signal) { const reason = signal?.reason; return reason instanceof Error ? reason : /* @__PURE__ */ new Error("Computer Use setup was aborted."); } async function readComputerUsePlugin(request, marketplace, pluginName) { return (await request("plugin/read", pluginRequestParams(marketplace, pluginName))).plugin; } async function readMcpServerStatus(request, serverName) { let cursor; do { const response = await request("mcpServerStatus/list", { cursor, limit: 100, detail: "toolsAndAuthOnly" }); const found = response.data.find((server) => server.name === serverName); if (found) return found; cursor = response.nextCursor; } while (cursor); } async function reloadMcpServers(request) { await request("config/mcpServer/reload", void 0); } function pluginRequestParams(marketplace, pluginName) { return { ...marketplace.kind === "local" ? { marketplacePath: marketplace.path } : {}, ...marketplace.kind === "remote" ? { remoteMarketplaceName: marketplace.remoteMarketplaceName } : {}, pluginName }; } function pluginSetupReason(plugin, marketplace) { if (marketplace.kind === "remote") return "remote_install_unsupported"; return plugin.summary.installed ? "plugin_disabled" : "plugin_not_installed"; } function pluginSetupMessage(config, plugin, marketplace) { if (marketplace.kind === "remote") return remoteInstallUnsupportedMessage(plugin, marketplace); if (!plugin.summary.installed) return "Computer Use is available but not installed. Run /codex computer-use install or enable computerUse.autoInstall."; return `Computer Use is installed, but the ${config.pluginName} plugin is disabled. Run /codex computer-use install or enable computerUse.autoInstall to re-enable it.`; } function remoteInstallUnsupportedMessage(plugin, marketplace) { const marketplaceName = marketplace.name ?? plugin.marketplaceName; return `Computer Use is ${plugin.summary.installed ? "installed but disabled" : "available"} in remote Codex marketplace ${marketplaceName}, but Codex app-server does not support remote plugin install yet. Configure computerUse.marketplaceSource or computerUse.marketplacePath for a local marketplace, then run /codex computer-use install.`; } function statusFromPlugin(params) { return { enabled: true, ready: params.plugin.summary.installed && params.plugin.summary.enabled && params.tools.length > 0, reason: params.reason, installed: params.plugin.summary.installed, pluginEnabled: params.plugin.summary.enabled, mcpServerAvailable: params.tools.length > 0, pluginName: params.config.pluginName, mcpServerName: params.config.mcpServerName, marketplaceName: params.plugin.marketplaceName, ...params.plugin.marketplacePath ? { marketplacePath: params.plugin.marketplacePath } : {}, tools: params.tools, message: params.message }; } function disabledStatus(config) { return { enabled: false, ready: false, reason: "disabled", installed: false, pluginEnabled: false, mcpServerAvailable: false, pluginName: config.pluginName, mcpServerName: config.mcpServerName, tools: [], message: "Computer Use is disabled." }; } function unavailableStatus(config, reason, message) { return { enabled: true, ready: false, reason, installed: false, pluginEnabled: false, mcpServerAvailable: false, pluginName: config.pluginName, mcpServerName: config.mcpServerName, ...config.marketplaceName ? { marketplaceName: config.marketplaceName } : {}, ...config.marketplacePath ? { marketplacePath: config.marketplacePath } : {}, tools: [], message }; } function createComputerUseRequest(params) { if (params.request) return params.request; if (params.client) return async (method, requestParams) => await params.client.request(method, requestParams, { timeoutMs: params.timeoutMs, signal: params.signal }); const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig }); return async (method, requestParams) => await requestCodexAppServerJson({ method, requestParams, timeoutMs: params.timeoutMs ?? runtime.requestTimeoutMs, startOptions: runtime.start }); } function resolveComputerUseConfig(params) { const overrides = params.forceEnable ? { ...params.overrides, enabled: true } : params.overrides; return resolveCodexComputerUseConfig({ pluginConfig: params.pluginConfig, overrides }); } //#endregion export { installCodexComputerUse as n, readCodexComputerUseStatus as r, ensureCodexComputerUse as t };