feat: wire native Codex UI as /console entry, keep classic console at /console-classic
ci / test (push) Successful in 45m25s
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
This commit is contained in:
1 parent
41f3985248
commit
3e0ff898e0
20 files changed
+3018
-24
No files matched your search
@@ -0,0 +1,71 @@
|
||||
#!/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}`);
|
||||
Reference in new issue
Block a user