Files
boss/scripts/codex-desktop-integration-probe.mjs

170 lines
5.4 KiB
JavaScript

#!/usr/bin/env node
import { spawn } from "node:child_process";
import { access, open } from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
const DEFAULT_CODEX_APP_PATH = "/Applications/Codex.app";
const DEFAULT_BRIDGE_EVENTS_URL = "http://127.0.0.1:4318/api/v1/codex-desktop/events";
function runCommand(command, args) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.setEncoding("utf8");
child.stderr.setEncoding("utf8");
child.stdout.on("data", (chunk) => {
stdout += chunk;
});
child.stderr.on("data", (chunk) => {
stderr += chunk;
});
child.on("error", reject);
child.on("close", (code) => {
if (code !== 0) {
reject(new Error(stderr.trim() || `${command} exit code ${code}`));
return;
}
resolve(stdout);
});
});
}
async function pathExists(filePath) {
try {
await access(filePath);
return true;
} catch {
return false;
}
}
async function readPlistAsJson(plistPath) {
const output = await runCommand("plutil", ["-convert", "json", "-o", "-", plistPath]);
return JSON.parse(output);
}
function extractUrlSchemes(info) {
const urlTypes = Array.isArray(info?.CFBundleURLTypes) ? info.CFBundleURLTypes : [];
return [
...new Set(
urlTypes.flatMap((item) =>
Array.isArray(item?.CFBundleURLSchemes)
? item.CFBundleURLSchemes.map((scheme) => String(scheme || "").trim()).filter(Boolean)
: [],
),
),
];
}
async function fileContains(filePath, needle) {
if (!(await pathExists(filePath))) {
return false;
}
const needleBuffer = Buffer.from(needle);
const handle = await open(filePath, "r");
const chunkSize = 1024 * 1024;
const overlapSize = Math.max(needleBuffer.length - 1, 0);
let carry = Buffer.alloc(0);
try {
const buffer = Buffer.alloc(chunkSize);
let position = 0;
while (true) {
const { bytesRead } = await handle.read(buffer, 0, chunkSize, position);
if (bytesRead === 0) {
return false;
}
const chunk = Buffer.concat([carry, buffer.subarray(0, bytesRead)]);
if (chunk.includes(needleBuffer)) {
return true;
}
carry = overlapSize > 0 ? chunk.subarray(Math.max(0, chunk.length - overlapSize)) : Buffer.alloc(0);
position += bytesRead;
}
} finally {
await handle.close();
}
}
export async function detectCodexDesktopIntegration(options = {}) {
const appPath =
String(options.appPath || process.env.BOSS_CODEX_DESKTOP_APP_PATH || DEFAULT_CODEX_APP_PATH).trim() ||
DEFAULT_CODEX_APP_PATH;
const bridgeEventsUrl =
String(options.bridgeEventsUrl || process.env.BOSS_CODEX_DESKTOP_EVENTS_URL || DEFAULT_BRIDGE_EVENTS_URL).trim() ||
DEFAULT_BRIDGE_EVENTS_URL;
const infoPlistPath = path.join(appPath, "Contents", "Info.plist");
const resourcesDir = path.join(appPath, "Contents", "Resources");
const appAsarPath = path.join(resourcesDir, "app.asar");
const appExists = await pathExists(appPath);
if (!appExists || !(await pathExists(infoPlistPath))) {
return {
ok: false,
app: {
path: appPath,
found: false,
},
capabilities: {},
reason: "CODEX_DESKTOP_APP_NOT_FOUND",
};
}
const info = await readPlistAsJson(infoPlistPath);
const urlSchemes = extractUrlSchemes(info);
const hasCodexScheme = urlSchemes.includes("codex");
const hasThreadDeepLinkResource = await fileContains(appAsarPath, "codex://threads/");
const hasRouteThreadResource = await fileContains(appAsarPath, "hotkey-window/thread");
const threadDeepLinkSupported = hasCodexScheme && (hasThreadDeepLinkResource || hasRouteThreadResource);
return {
ok: true,
app: {
path: appPath,
found: true,
bundleIdentifier: info.CFBundleIdentifier,
shortVersion: info.CFBundleShortVersionString,
version: info.CFBundleVersion,
urlSchemes,
},
capabilities: {
threadDeepLink: {
supported: threadDeepLinkSupported,
template: threadDeepLinkSupported ? "codex://threads/{threadId}" : undefined,
evidence: threadDeepLinkSupported
? "CFBundleURLSchemes contains codex and app resources contain codex://threads/"
: "thread deep link route not detected",
},
desktopBridgeSse: {
supported: true,
url: bridgeEventsUrl,
evidence: "Boss Codex Desktop Bridge exposes local SSE metadata stream",
},
inAppSubscription: {
supported: false,
evidence: "No stable public Codex Desktop plugin or IPC subscription API detected by this probe",
},
packagePatch: {
supported: false,
evidence: "Boss policy does not patch or modify the signed Codex Desktop application bundle",
},
},
recommendedPath: threadDeepLinkSupported
? "Use Boss rollout mirror + codex://threads/{threadId} + local bridge SSE; only move to in-app subscription if Codex Desktop exposes a stable plugin/IPC API."
: "Keep Boss rollout mirror and local bridge SSE; avoid Desktop package patching until a stable supported integration exists.",
};
}
async function main() {
const result = await detectCodexDesktopIntegration();
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
await main();
}