feat: ship enterprise control and desktop governance
This commit is contained in:
169
scripts/codex-desktop-integration-probe.mjs
Normal file
169
scripts/codex-desktop-integration-probe.mjs
Normal file
@@ -0,0 +1,169 @@
|
||||
#!/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();
|
||||
}
|
||||
Reference in New Issue
Block a user