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,80 @@
|
||||
"""Codex 原生 UI host 入仓清单单测(见 docs/codex-native-ui-alignment.md §4 第 4 项)。
|
||||
|
||||
只做静态断言,不起服务、不调网络、不读密钥:
|
||||
- appserver-methods.json 是合法 JSON、179 个方法、无重复;
|
||||
- app-server-bridge.mjs 里 7 个仿真方法都在(读文件文本断言方法名出现);
|
||||
- 仓内无 cloud/codex-ui-host/original/ 目录(650MB 原版栈不入仓);
|
||||
- scripts/run-codex-ui.sh 存在且可执行。
|
||||
|
||||
路径一律以仓库根(本文件向上两级)为基准,不写死绝对路径。
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import unittest
|
||||
|
||||
REPO_ROOT = os.path.dirname(os.path.dirname(
|
||||
os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
# §4 适配 A5:150 个 UI 清单方法 + 29 个实验协议方法 = 179。
|
||||
EXPECTED_METHOD_COUNT = 179
|
||||
|
||||
# §4 适配 A1–A4:桥接层仿真的 7 个缺失方法。
|
||||
SIMULATED_METHODS = [
|
||||
"thread/stop",
|
||||
"thread/rollback",
|
||||
"thread/startAeon",
|
||||
"interactive/liveSessions/list",
|
||||
"interactive/session/upload",
|
||||
"interactive/sessionSandbox/list",
|
||||
"interactive/sessionSandbox/read",
|
||||
]
|
||||
|
||||
|
||||
class TestAppserverMethodsJson(unittest.TestCase):
|
||||
def test_valid_json_179_methods_no_duplicates(self):
|
||||
path = os.path.join(
|
||||
REPO_ROOT, "cloud", "codex-ui-host", "host",
|
||||
"appserver-methods.json")
|
||||
with open(path, encoding="utf-8") as f:
|
||||
methods = json.load(f)
|
||||
self.assertIsInstance(methods, list)
|
||||
self.assertEqual(len(methods), EXPECTED_METHOD_COUNT)
|
||||
self.assertTrue(all(isinstance(m, str) for m in methods))
|
||||
self.assertEqual(len(set(methods)), len(methods),
|
||||
"appserver-methods.json must not list a method twice")
|
||||
|
||||
|
||||
class TestAppServerBridgeSimulations(unittest.TestCase):
|
||||
def test_seven_simulated_methods_present(self):
|
||||
path = os.path.join(
|
||||
REPO_ROOT, "cloud", "codex-ui-host", "host",
|
||||
"app-server-bridge.mjs")
|
||||
with open(path, encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
for name in SIMULATED_METHODS:
|
||||
self.assertIn(name, text,
|
||||
"%s must be simulated in app-server-bridge.mjs"
|
||||
% name)
|
||||
|
||||
|
||||
class TestOriginalNotVendored(unittest.TestCase):
|
||||
def test_no_original_directory_in_repo(self):
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(
|
||||
REPO_ROOT, "cloud", "codex-ui-host", "original")),
|
||||
"cloud/codex-ui-host/original/ must not be vendored "
|
||||
"(650MB original stack stays out of the repo)")
|
||||
|
||||
|
||||
class TestRunScript(unittest.TestCase):
|
||||
def test_run_script_exists_and_is_executable(self):
|
||||
path = os.path.join(REPO_ROOT, "scripts", "run-codex-ui.sh")
|
||||
self.assertTrue(os.path.isfile(path),
|
||||
"scripts/run-codex-ui.sh must exist")
|
||||
self.assertTrue(os.access(path, os.X_OK),
|
||||
"scripts/run-codex-ui.sh must be executable")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user