ci / test (push) Successful in 45m25s
- Vendor codex-ui-stack host/ as cloud/codex-ui-host/ (original/ not vendored, 650MB) - Bridge emulates 7 methods missing in codex 0.164.0-alpha.9, exposes 29 experimental-protocol methods (150 -> 179), Linux binary candidates, read-only POST /__codex_git - GET /console 302s to the UI host (:8095) when reachable, else falls back to the classic React console; GET /console-classic always serves it - Alignment tables: docs/codex-native-ui-alignment.md - Tests: test_console 7, new test_codex_ui_host 4, handler method pin 247 -> 249
72 lines
2.2 KiB
JavaScript
72 lines
2.2 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Vendor the original webview verbatim into ./original.
|
|
*
|
|
* Source: the extracted ChatGPT.app/Contents/Resources/app.asar, i.e.
|
|
* <extracted-asar>/webview
|
|
*
|
|
* Nothing is rewritten. The copy is byte-for-byte identical, which is what makes
|
|
* the UI 100% the original rather than an approximation.
|
|
*
|
|
* Usage:
|
|
* node host/vendor-original.mjs --from /tmp/codex-asar/webview
|
|
* node host/vendor-original.mjs --from /path/to/webview --clean
|
|
*/
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import crypto from "node:crypto";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const projectRoot = path.resolve(__dirname, "..");
|
|
|
|
const args = process.argv.slice(2);
|
|
const getArg = (name, fallback) => {
|
|
const i = args.indexOf(name);
|
|
return i >= 0 && args[i + 1] ? args[i + 1] : fallback;
|
|
};
|
|
|
|
const from = path.resolve(getArg("--from", "/tmp/codex-asar/webview"));
|
|
const to = path.resolve(projectRoot, getArg("--to", "original"));
|
|
const clean = args.includes("--clean");
|
|
|
|
if (!fs.existsSync(path.join(from, "index.html"))) {
|
|
console.error(`[vendor] not a webview directory: ${from}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (clean && fs.existsSync(to)) {
|
|
console.log(`[vendor] removing ${to}`);
|
|
fs.rmSync(to, { recursive: true, force: true });
|
|
}
|
|
fs.mkdirSync(to, { recursive: true });
|
|
|
|
console.log(`[vendor] copying ${from} -> ${to}`);
|
|
fs.cpSync(from, to, { recursive: true, force: true });
|
|
|
|
// Manifest (written outside ./original so the vendored tree stays untouched).
|
|
function sha256(file) {
|
|
return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
|
}
|
|
function countFiles(dir) {
|
|
let n = 0;
|
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
n += entry.isDirectory() ? countFiles(path.join(dir, entry.name)) : 1;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
const manifest = {
|
|
source: from,
|
|
vendoredAt: new Date().toISOString(),
|
|
fileCount: countFiles(to),
|
|
indexHtmlSha256: sha256(path.join(to, "index.html")),
|
|
};
|
|
fs.writeFileSync(
|
|
path.join(projectRoot, "host", "vendor-manifest.json"),
|
|
JSON.stringify(manifest, null, 2),
|
|
);
|
|
|
|
console.log(`[vendor] done — ${manifest.fileCount} files`);
|
|
console.log(`[vendor] index.html sha256 ${manifest.indexHtmlSha256}`);
|