Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions scripts/codex_lab_package/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

This helper builds a macOS `Codex Lab.app` launcher bundle. The bundle does not
contain or modify OpenAI's signed desktop app. Instead, it embeds a Codex Lab
CLI binary, binds its source commit, version, and SHA-256 digest, sets
`CODEX_CLI_PATH` to that exact path, and launches through LaunchServices. The
official app is bound to the persistent engine by setting `CODEX_HOME` and
`CODEX_LAB_HOME` to the same Lab home and enabling
`CODEX_APP_SERVER_USE_LOCAL_DAEMON=1`.
CLI binary and binds its source commit, version, and SHA-256 digest. The official
app is bound to the persistent engine by setting `CODEX_HOME` and
`CODEX_LAB_HOME` to the same Lab home, enabling
`CODEX_APP_SERVER_USE_LOCAL_DAEMON=1`, and explicitly setting
`CODEX_CLI_PATH` and `CODEX_APP_SERVER_FORCE_CLI` to empty values. Current
official clients use a non-empty CLI path or `CODEX_APP_SERVER_FORCE_CLI=1` to
select stdio instead of the local daemon.

The launcher accepts only intact `com.openai.codex` bundles signed by OpenAI
team `2DC432GLL2`. After an optional build-time override, it checks system and
Expand Down
6 changes: 4 additions & 2 deletions scripts/codex_lab_package/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ def _launcher_script(
fi
expected_daemon_socket="$LAB_HOME/app-server-control/app-server-control.sock"
if [ "$daemon_backend" != "pid" ] \
|| [ "$daemon_managed_codex_path" != "$MANAGED_CLI" ] \
|| [ ! "$daemon_managed_codex_path" -ef "$MANAGED_CLI" ] \
|| [ "$daemon_socket_path" != "$expected_daemon_socket" ] \
|| [ "$daemon_app_server_version" != "$version" ]; then
echo "Persistent Codex Lab daemon does not match the managed engine under $LAB_HOME." >&2
Expand All @@ -410,8 +410,10 @@ def _launcher_script(

echo "Selected OpenAI coding desktop app: $CODEX_APP" >&2
echo "Codex Lab CLI provenance: commit=$source_commit dirty=$dirty_state profile=$build_profile channel=$build_channel version=$version" >&2
unset CODEX_CLI_PATH CODEX_APP_SERVER_FORCE_CLI
exec "$OPEN" -n \
--env "CODEX_CLI_PATH=$LAB_CLI" \
--env "CODEX_CLI_PATH=" \
--env "CODEX_APP_SERVER_FORCE_CLI=" \
--env "CODEX_HOME=$LAB_HOME" \
--env "CODEX_LAB_HOME=$LAB_HOME" \
--env "CODEX_APP_SERVER_USE_LOCAL_DAEMON=1" \
Expand Down
125 changes: 106 additions & 19 deletions scripts/codex_lab_package/live_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import ctypes
import ctypes.util
import json
import math
import os
from pathlib import Path
import re
Expand All @@ -25,6 +26,9 @@
SELECTED_APP_PREFIX = "Selected OpenAI coding desktop app: "
SOURCE_COMMIT_PATTERN = re.compile(r"^(?:[0-9a-f]{40}|[0-9a-f]{64})$")
MANAGED_CLI_RELATIVE_PATH = Path("packages/standalone/current/codex")
STABILITY_WINDOW_SECONDS = 5.0
MIN_TIMEOUT_SECONDS = 10.0
MAX_DESKTOP_LOG_BYTES = 512 * 1024


def main() -> None:
Expand All @@ -44,8 +48,7 @@ def main() -> None:
def run_live_smoke(app_dir: Path, timeout_seconds: float) -> dict[str, Any]:
if sys.platform != "darwin":
raise RuntimeError("live Codex Lab desktop smoke requires macOS")
if timeout_seconds <= 0:
raise ValueError("timeout seconds must be greater than zero")
validate_timeout_seconds(timeout_seconds)

launcher = app_dir / "Contents/MacOS/Codex Lab Launcher"
cli_path = app_dir / "Contents/Resources/codex-lab"
Expand All @@ -66,7 +69,16 @@ def run_live_smoke(app_dir: Path, timeout_seconds: float) -> dict[str, Any]:
validate_matching_build_provenance(provenance, managed_provenance)
before_rows = read_process_rows()
before_pids = {pid for pid, _ppid, _command in before_rows}
launch = subprocess.run([str(launcher)], capture_output=True, text=True, timeout=20)
deadline = time.monotonic() + timeout_seconds
try:
launch = subprocess.run(
[str(launcher)],
capture_output=True,
text=True,
timeout=max(0.1, deadline - time.monotonic()),
)
except subprocess.TimeoutExpired as exc:
raise TimeoutError("timed out launching Codex Lab") from exc
if launch.returncode != 0:
raise RuntimeError(
"Codex Lab launcher failed: "
Expand All @@ -77,11 +89,12 @@ def run_live_smoke(app_dir: Path, timeout_seconds: float) -> dict[str, Any]:
gui_executable = official_app_executable_path(selected_app)
expected_environment = {
"CODEX_APP_SERVER_USE_LOCAL_DAEMON": "1",
"CODEX_CLI_PATH": str(cli_path.resolve()),
"CODEX_APP_SERVER_FORCE_CLI": "",
"CODEX_CLI_PATH": "",
"CODEX_HOME": str(lab_home),
"CODEX_LAB_HOME": str(lab_home),
}
deadline = time.monotonic() + timeout_seconds
stable_since = None
while time.monotonic() < deadline:
rows = read_process_rows()
gui_pids = {
Expand All @@ -98,25 +111,32 @@ def run_live_smoke(app_dir: Path, timeout_seconds: float) -> dict[str, Any]:
pid
for pid, _ppid, command in rows
if pid not in before_pids
and "app-server" in command.split()
and is_serving_app_server_command(command)
and process_has_ancestor(pid, gui_pids, rows)
]
if new_gui_app_servers:
raise RuntimeError(
"official app launched a bundled stdio app-server instead of the persistent daemon"
)
managed_servers = sorted(matching_app_server_pids(rows, managed_cli_path))
if matching_gui_pids and managed_servers:
return {
"appServerExecutablePath": str(managed_cli_path.resolve()),
"appServerPid": managed_servers[0],
"guiPids": matching_gui_pids,
"managedProvenance": managed_provenance,
"mode": "persistentLocalDaemon",
"officialAppPath": str(selected_app),
"provenance": provenance,
"schemaVersion": 1,
}
transport_proof = desktop_transport_proof(matching_gui_pids)
if matching_gui_pids and managed_servers and transport_proof is not None:
stable_since = stable_since or time.monotonic()
if time.monotonic() - stable_since >= STABILITY_WINDOW_SECONDS:
return {
"appServerExecutablePath": str(managed_cli_path.resolve()),
"appServerPid": managed_servers[0],
**transport_proof,
"guiPids": matching_gui_pids,
"managedProvenance": managed_provenance,
"mode": "persistentLocalDaemon",
"officialAppPath": str(selected_app),
"provenance": provenance,
"schemaVersion": 1,
"stabilityWindowSeconds": STABILITY_WINDOW_SECONDS,
}
else:
stable_since = None
time.sleep(0.2)
raise TimeoutError(
f"timed out waiting for the official app to use {managed_cli_path}"
Expand All @@ -141,6 +161,57 @@ def read_cli_provenance(cli_path: Path) -> dict[str, Any]:
return {field: provenance[field] for field in PROVENANCE_FIELDS}


def validate_timeout_seconds(timeout_seconds: float) -> None:
if not math.isfinite(timeout_seconds) or timeout_seconds < MIN_TIMEOUT_SECONDS:
raise ValueError(
f"timeout seconds must be finite and at least {MIN_TIMEOUT_SECONDS}"
)


def desktop_transport_proof(
gui_pids: list[int], *, log_root: Path | None = None
) -> dict[str, str] | None:
root = log_root or Path.home() / "Library/Logs/com.openai.codex"
for pid in gui_pids:
paths = sorted(
root.rglob(f"*-{pid}-t0-*.log"),
key=lambda path: path.stat().st_mtime,
reverse=True,
)
for path in paths:
log = _bounded_file_tail(path, MAX_DESKTOP_LOG_BYTES)
if "stdio_transport_spawned" in log:
raise RuntimeError(
"official app log reported a bundled stdio app-server"
)
lines = log.splitlines()
transport_ready = any(
"Transport start success" in line
and "hostId=local" in line
and "transport=websocket" in line
for line in lines
)
initialized = any(
"initialize_handshake_result" in line
and "outcome=success" in line
and "transportKind=websocket" in line
for line in lines
)
if transport_ready and initialized:
return {
"desktopLogPath": str(path.resolve()),
"desktopTransport": "websocket",
}
return None


def _bounded_file_tail(path: Path, limit: int) -> str:
with path.open("rb") as handle:
handle.seek(0, os.SEEK_END)
handle.seek(max(0, handle.tell() - limit))
return handle.read(limit).decode(errors="replace")


def validate_cli_provenance(provenance: Any, cli_path: Path) -> None:
if not isinstance(provenance, dict) or provenance.get("schema_version") != 1:
raise ValueError("Codex Lab CLI provenance schema is unsupported")
Expand Down Expand Up @@ -207,10 +278,19 @@ def matching_app_server_pids(
return [
pid
for pid, _ppid, command in rows
if "app-server" in command.split() and executable_path(pid) == expected
if is_serving_app_server_command(command) and executable_path(pid) == expected
]


def is_serving_app_server_command(command: str) -> bool:
tokens = command.split()
return any(
token == "app-server"
and (index + 1 == len(tokens) or tokens[index + 1] != "daemon")
for index, token in enumerate(tokens)
)


def process_executable_path(pid: int) -> Path | None:
try:
libproc = ctypes.CDLL(
Expand All @@ -230,16 +310,23 @@ def process_has_environment(
pid: int,
expected: dict[str, str],
reader: Callable[[int], str] | None = None,
*,
forbidden: set[str] | None = None,
) -> bool:
environment_reader = reader or read_process_environment
try:
process = environment_reader(pid)
except (OSError, subprocess.SubprocessError):
return False
return all(
has_expected = all(
re.search(rf"(?:^|\s){re.escape(name)}={re.escape(value)}(?:\s|$)", process)
for name, value in expected.items()
)
forbidden = forbidden or set()
has_forbidden = any(
re.search(rf"(?:^|\s){re.escape(name)}=", process) for name in forbidden
)
return has_expected and not has_forbidden


def read_process_environment(pid: int) -> str:
Expand Down
12 changes: 11 additions & 1 deletion scripts/codex_lab_package/smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@ def smoke_check(
)

launcher = launcher_path.read_text(encoding="utf-8")
_require_contains(launcher, "CODEX_CLI_PATH", launcher_path)
_require_contains(
launcher, "unset CODEX_CLI_PATH CODEX_APP_SERVER_FORCE_CLI", launcher_path
)
_require_contains(launcher, '--env "CODEX_CLI_PATH="', launcher_path)
_require_contains(launcher, '--env "CODEX_APP_SERVER_FORCE_CLI="', launcher_path)
_require_not_contains(launcher, '--env "CODEX_CLI_PATH=$LAB_CLI"', launcher_path)
_require_contains(launcher, "CODEX_HOME=$LAB_HOME", launcher_path)
_require_contains(launcher, "CODEX_LAB_HOME=$LAB_HOME", launcher_path)
_require_contains(launcher, "CODEX_APP_SERVER_USE_LOCAL_DAEMON=1", launcher_path)
Expand Down Expand Up @@ -156,5 +161,10 @@ def _require_contains(contents: str, needle: str, path: Path) -> None:
raise ValueError(f"{path}: missing {needle!r}")


def _require_not_contains(contents: str, needle: str, path: Path) -> None:
if needle in contents:
raise ValueError(f"{path}: unexpected {needle!r}")


if __name__ == "__main__":
raise SystemExit(main())
25 changes: 13 additions & 12 deletions scripts/codex_lab_package/test_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ def test_launcher_executes_exact_cli_and_fails_closed_for_running_app(self) -> N
encoding="utf-8",
)
open_log = root / "open.log"
child_log = root / "child.log"
fake_open = root / "open"
fake_open.write_text(
"""#!/bin/sh
Expand All @@ -306,10 +305,10 @@ def test_launcher_executes_exact_cli_and_fails_closed_for_running_app(self) -> N
*) shift ;;
esac
done
printf 'cli=%s\\ncodex_home=%s\\nlab_home=%s\\nlocal_daemon=%s\\nargs=%s\\n' \\
"$CODEX_CLI_PATH" "$CODEX_HOME" "$CODEX_LAB_HOME" \\
"$CODEX_APP_SERVER_USE_LOCAL_DAEMON" "$args" > "$OPEN_LOG"
"$CODEX_CLI_PATH" -c features.code_mode_host=true app-server
printf 'cli=%s\\nforce_cli=%s\\ncodex_home=%s\\nlab_home=%s\\nlocal_daemon=%s\\nargs=%s\\n' \\
"${CODEX_CLI_PATH:-}" "${CODEX_APP_SERVER_FORCE_CLI:-}" \\
"$CODEX_HOME" "$CODEX_LAB_HOME" "$CODEX_APP_SERVER_USE_LOCAL_DAEMON" \\
"$args" > "$OPEN_LOG"
""",
encoding="utf-8",
)
Expand Down Expand Up @@ -342,7 +341,8 @@ def test_launcher_executes_exact_cli_and_fails_closed_for_running_app(self) -> N
os.chmod(launcher, 0o755)
environment = {
**os.environ,
"CHILD_LOG": str(child_log),
"CODEX_APP_SERVER_FORCE_CLI": "1",
"CODEX_CLI_PATH": "/tmp/force-stdio",
"CODEX_LAB_HOME": str(root / "codex-lab-home"),
"OPEN_LOG": str(open_log),
}
Expand All @@ -353,6 +353,9 @@ def test_launcher_executes_exact_cli_and_fails_closed_for_running_app(self) -> N
managed_cli.parent.mkdir(parents=True)
managed_cli.write_bytes(embedded_cli.read_bytes())
os.chmod(managed_cli, 0o755)
managed_cli_alias = root / "managed-codex-alias"
managed_cli_alias.symlink_to(managed_cli)
environment["DAEMON_MANAGED_PATH"] = str(managed_cli_alias)

completed = subprocess.run(
[str(launcher)],
Expand All @@ -366,15 +369,14 @@ def test_launcher_executes_exact_cli_and_fails_closed_for_running_app(self) -> N
)
self.assertIn("commit=" + source_commit, completed.stderr)
self.assertNotIn("executable_path", completed.stderr)
self.assertEqual(
child_log.read_text(encoding="utf-8").strip(), str(embedded_cli)
)
open_contents = open_log.read_text(encoding="utf-8")
self.assertIn(f"cli={embedded_cli}", open_contents)
self.assertIn("cli=\n", open_contents)
self.assertIn("force_cli=\n", open_contents)
self.assertIn(f"codex_home={environment['CODEX_LAB_HOME']}", open_contents)
self.assertIn(f"lab_home={environment['CODEX_LAB_HOME']}", open_contents)
self.assertIn("local_daemon=1", open_contents)
self.assertIn(f"--env CODEX_CLI_PATH={embedded_cli}", open_contents)
self.assertIn("--env CODEX_CLI_PATH=", open_contents)
self.assertIn("--env CODEX_APP_SERVER_FORCE_CLI=", open_contents)
self.assertIn(
f"--env CODEX_HOME={environment['CODEX_LAB_HOME']}", open_contents
)
Expand All @@ -386,7 +388,6 @@ def test_launcher_executes_exact_cli_and_fails_closed_for_running_app(self) -> N
self.assertIn(str(official_app), open_contents)

open_log.unlink()
child_log.unlink()
environment["PROVENANCE_EXECUTABLE_PATH"] = str(root / "wrong-codex")
completed = subprocess.run(
[str(launcher)],
Expand Down
Loading
Loading