Codex Script Loader starts Codex with a local Chrome DevTools Protocol (CDP) debugging endpoint and loads user-installed renderer scripts. It manages the full script lifecycle automatically: discovery, manifest and hash validation, permission setup, injection into current and future documents, in-place reload, cleanup, and shutdown with Codex.
Windows uses the native .NET 10 background host with no console or tray icon and is the tested primary platform. macOS uses the Node.js live runtime; the implementation is available but has not yet been tested on macOS hardware.
| Area | What it provides |
|---|---|
| Debug-enabled launch | Opens Codex with a local debugging endpoint ready for renderer scripts. |
| User scripts | Loads manifest.json + index.js packages from the Loader data directory. |
| Bundled skills (new source capability) | Native schema-v2 packages can install renderer and skill together, with shared enable, update, rollback, and removal. Requires a host built with this capability. |
| Automatic lifecycle | Validates, injects, reloads, replaces, and cleans up scripts across renderer documents. |
| Native Windows host | Runs quietly with Codex, recovers a managed session after an in-app Codex update restart, and exits after an ordinary Codex close. |
| macOS runtime | Discovers Codex.app and provides the same managed CDP/script flow through Node.js; currently untested. |
| Diagnostics and reload | Starting a second Windows instance opens diagnostics; --reload replaces scripts in place. |
| Online host updates | Checks the stable GitHub Release after startup and switches verified Loader hosts without restarting Codex. |
| Plugin release updates | Scans update-aware public GitHub plugins, then verifies and atomically reloads an opted-in update per plugin. |
| Example plugin included | Installs a small Loader-owned UI plugin that demonstrates the package contract. |
| Windows packaging | Produces per-user NSIS setup executables and portable ZIP archives for x64 and arm64. |
- Windows 11 x64 or arm64 with the Microsoft Store Codex app.
- macOS with
Codex.appin/Applicationsor~/Applicationsand Node.js 22 or newer (untested). - Windows source builds require .NET 10 SDK, Windows SDK 10.0.26100 or newer, and the Visual Studio C++ desktop build tools. Cross-publishing
win-arm64also requiresMicrosoft.VisualStudio.Component.VC.Tools.ARM64.
Close any running Codex instance before starting the Loader. For a managed session, launch Codex through the Loader.
Download the matching NSIS installer from GitHub Releases. Its standard setup wizard lets you choose the installation folder and Start menu folder before installation begins. It creates desktop and Start menu shortcuts, installs for the current user, appears in Windows Installed apps, and requires no administrator access. A portable ZIP is published beside each installer.
Version 0.5.0 is the one-time installer migration from the older flat 0.4.x layout. Starting with 0.5.1, standard NSIS installations can update the versioned host from Codex Settings → Script-Loader → Settings while Codex and the current task stay open. Portable copies cannot hot-swap; use the installer path instead.
The update card shows checking, up-to-date, progress and failure states, the last check time, and release links. Choose Download installer when a full installation is required or preferred. Download and SHA-256 verification leave Codex running. Exit and install then asks for confirmation before closing Codex and Loader normally and opening setup; it never force-kills them. Hosts without this capability show a manual download link.
Version 0.5.2 isolates update errors inside the update card and uses the Windows system curl.exe to resolve the latest stable GitHub Release and download its assets directly. It does not use the GitHub API or CLI credentials. The transport is restricted to HTTPS and official GitHub download hosts and remains subject to size, SHA-256, archive, manifest, and per-file verification.
Version 0.5.3 adds opt-in, per-plugin GitHub Release updates. Update-aware third-party plugins are scanned independently, downloaded through the same restricted transport, verified against a required .sha256 asset, replaced transactionally, and reloaded in place. The Loader-owned example plugin remains bundled and is not part of the third-party update flow.
Version 0.6.0 improves Codex lifecycle recovery, plugin compatibility and the settings page. Update checks now report their result, and verified installer updates ask before closing Codex. This stable release contains no experimental account-routing code. See release notes.
Version 0.5.10 added Install from GitHub, verified same-ID package replacement, single-package optional agent Skills, and an allowlisted native command client for renderer plugins. Existing standard installations can use the normal online host update; launcher/handoff protocols are unchanged. Plugin automatic updates remain opt-in. The new GitHub installation entry uses the public Releases API only to discover package assets and does not read GitHub credentials.
git clone https://github.com/JHees/codex-script-loader.git
Set-Location .\codex-script-loader
.\windows\scripts\package.ps1 -RuntimeIdentifier win-x64Use win-arm64 instead on Windows on Arm. Packaging clears only generated files in the repository-level build directory and leaves the latest architecture and version there. It does not touch bin:
build/
├── README.md
├── app/CodexScriptLoader.exe # stable NativeAOT launcher
├── app/active.json
├── app/previous.json
├── app/update-manifest.json
├── app/versions/0.6.0/win-x64/ # complete Loader host
├── CodexScriptLoader-0.6.0-windows-x64-setup.exe
├── CodexScriptLoader-0.6.0-windows-x64-setup.exe.sha256
├── CodexScriptLoader-0.6.0-windows-x64.zip
├── CodexScriptLoader-0.6.0-windows-x64.zip.sha256
└── CodexScriptLoader-0.6.0-x64.spdx.json
The setup executable at the top of build is the normal local installation entry. The build\app directory is packaging payload, not the recommended launch path. The installer keeps scripts and settings under %LOCALAPPDATA%\CodexScriptLoader when upgrading or uninstalling.
git clone https://github.com/JHees/codex-script-loader.git
cd codex-script-loader
node src/cli.mjs run --liveThe macOS runtime discovers Codex.app, starts it with a random loopback CDP port, loads the same script package format, and stays in the terminal while supervising the session. Its data directory is ~/Library/Application Support/codex-script-loader.
- The Loader validates its data directory and installed script manifests.
- It finds the Store Codex package and actual AUMID without reading
C:\Program Files\WindowsApps. - It activates Codex with a random loopback CDP port.
- It verifies the listener PID, package family, and exact renderer URL before injection.
- Enabled renderer plugins start through the same manifest and lifecycle contract.
- The Loader observes Codex's in-app update request and package lifecycle. After an update it reconnects to the replacement process on the inherited managed CDP port, or launches the newer package on a fresh random port once the old processes have exited. Ordinary close exits the Loader after a short process-exit grace period; a temporarily unregistered Store package gets up to three minutes to return. Recovery failures remain visible in diagnostics without closing Codex.
- After an ordinary managed Codex close, the Loader releases its connections and exits automatically.
Start the Loader executable a second time to open diagnostics. To reload installed scripts without focusing or refreshing Codex, run the same compatible executable as a second instance:
& "$env:LOCALAPPDATA\Programs\CodexScriptLoader\CodexScriptLoader.exe" --reloadUser
└─ CodexScriptLoader.exe (stable NativeAOT launcher)
└─ versions/<version>/<rid>/CodexScriptLoader.exe
├─ Windows package APIs ──> Microsoft Store Codex
├─ random loopback CDP ───> verified Codex renderer
├─ script registry ───────> manifest / permissions / SHA-256
└─ lifecycle supervisor ──> inject / reload / host handoff / cleanup
Production data lives under:
%LOCALAPPDATA%\CodexScriptLoader\
├── config.json
├── update-preferences.json
├── scripts\
├── quarantine\
├── logs\
└── state\
Logs use UTF-8 JSON Lines. Diagnostic exports redact user-specific paths and unrelated command-line details.
The Windows host uses official package APIs to start Codex, keeps CDP on a random loopback port, and verifies the owning process and exact renderer target before injection. Codex application files remain untouched, and the launch path needs neither WindowsApps access nor administrator privileges.
Online updates are fixed to the stable releases of JHees/codex-script-loader. The Loader follows the repository's releases/latest redirect with the Windows system curl.exe, constructs only the expected versioned asset names, and never calls the GitHub API. It verifies the tag, asset name, architecture, declared response size, official HTTPS download host, the archive's matching .sha256 asset, safe ZIP structure, update protocol, and every payload file hash before staging. This GitHub Release + SHA-256 model detects corruption and mismatched assets, but it cannot protect against a release and its checksum file being replaced together; independent signing and Authenticode remain future hardening work.
Installed plugins are managed under Codex Settings → Script-Loader → Settings. The page shows live status, supports enable/disable, targeted or full reload, local folder/ZIP installation, quarantine and restore, update checks, and a controlled Codex restart. Plugins that declare a settings page appear directly below the Settings entry.
The native Windows Loader also offers Install from GitHub. Paste a public repository URL, releases/latest, releases/tag/v1.2.3, or a Release ZIP download link. It discovers assets through the unauthenticated GitHub Releases API, downloads the ZIP and matching .sha256 with system curl.exe, verifies the hash and manifest version/repository/asset name, then asks you to confirm permissions and installation. Multiple eligible ZIPs require a selection. Source archives, private repositories, and prereleases are not supported. Download and verification have a 120-second deadline; failures do not trigger automatic retries or remove installed plugins.
There is no need to uninstall a local package first: the same plugin ID uses a confirmed replacement, preserving its enabled state and rolling back installation failures. Update support comes from the package's update declaration, not its installation origin. After installing, opt in to automatic updates from that plugin's menu; the default remains off. Packages declaring agentSkill provide their bundled Skill as part of the same enabled installation, with no second manual install. GitHub installation is native-host-only; the Node development host reports that requirement explicitly.
Third-party packages may optionally declare their own public GitHub Release source. The native Windows host scans those declarations once after becoming healthy and when requested from Settings. Per-plugin automatic replacement defaults to off. Updates require a stable vMAJOR.MINOR.PATCH Release, a versioned ZIP, and its exact same-name .sha256 asset. Added permissions and local package changes require confirmation; disabled plugins and enabled plugins without a renderer are never replaced. This feature does not create a marketplace or couple third-party source and release work to the Loader repository.
The complete authoring and lifecycle contract is documented in docs/PLUGIN_SPEC.md.
The versioned layout, release verification, and no-Codex-restart handoff are documented in docs/UPDATE_PROTOCOL.md.
A renderer package contains manifest.json and an entry script:
{
"schemaVersion": 1,
"id": "local.example",
"name": "Example",
"version": "1.0.0",
"main": "index.js",
"scope": "renderer",
"runAt": "document-end",
"permissions": ["dom", "local-storage", "settings"],
"hostCommands": { "operations": ["inspect"] }
}module.exports = {
start(api) {
const page = api.settings.registerPage({
id: "main",
title: "Example",
render(root) {
root.textContent = "Hello from Codex Script Loader";
},
});
return () => page.unregister();
},
invokeHostCommand(operation, payload) {
if (operation !== "inspect") throw new Error("Operation is not allowed");
return { value: payload.value };
},
};CodexScriptLoader.Command.exe plugin invoke --id <plugin-id> --operation <operation> reads one UTF-8 JSON object from stdin and writes one structured JSON envelope to stdout. The operation must appear in the enabled plugin's hostCommands.operations manifest allowlist. Payloads and results are limited to 64 KiB and a call may wait up to 120 seconds.
The client uses the existing current-user Loader pipe. Loader selects only its verified app://-/index.html renderer and evaluates a fixed invocation wrapper; callers cannot supply a CDP endpoint, target, method, selector, or JavaScript source. The command client is shipped beside the native Windows host in the active version directory.
A renderer plugin may additionally declare trusted-input and return the fixed $loaderHostAction press-enter directive from an allowlisted operation. Loader will press Enter once in its verified renderer session, then resume the same operation with the same payload. The caller cannot choose the key or request the action directly, and repeated or malformed action requests fail closed.
The loopback-websocket manifest permission is an explicit capability opt-in.
Only a package that declares it receives api.localTransport.openWebSocket(endpoint);
packages without it have no localTransport property and keep the existing API
shape. Permissions are capability declarations for trusted local JavaScript, not
a security sandbox, so review a plugin before enabling it.
{ "permissions": ["dom", "loopback-websocket"] }const socket = await api.localTransport.openWebSocket(
"ws://127.0.0.1:43127/renderer",
);
socket.addEventListener("message", event => handleMessage(event.data));
socket.send("hello");The host accepts only an exact ws://127.0.0.1:<port>/<safe-path> endpoint. It
rejects localhost, IPv6, LAN addresses, wss:, credentials, query strings,
fragments, unsafe paths, CDP paths, and the managed CDP port. The transport is
text-only and bounded to 64 KiB frames, 32 messages/256 KiB per inbound queue,
8 connections per target, 32 connections total, 32 in-flight binding dispatches,
1-second polls, and 5-second binding requests; closed connections are retained
only briefly (up to 30 seconds)
to drain terminal events. Every request carries the plugin ID and the host
re-checks the current enabled descriptor and permission. Invalid or unauthorized
requests fail closed with sanitized errors; transport content, endpoints, and
secrets are not logged and the CDP endpoint is never exposed.
The transport uses its own binding and protocol, separate from the Loader management bridge. It does not change any existing management command or behavior and contains no Bridge-specific ID, path, or protocol. Connections are cleaned up on plugin stop/reload/disable, target drop or replacement, reconnect, and Loader shutdown. Native Windows and Node.js runtimes implement the same public seam and limits.
The browser-page-companion permission is a narrow host-owned browser seam.
The manifest fixes an allowlisted origin, a package-relative companion bundle,
and a small operation allowlist. Loader selects a unique matching page target,
injects only that installed bundle, and exposes api.pageCompanion.probe(),
bind(), invoke(operation, payload), and unbind() to the renderer plugin.
It never exposes CDP, arbitrary targets, URLs, selectors, scripts, cookies, or
browser profiles. Any top-level navigation or reload ends the binding and
requires a fresh bind. See the Plugin Specification for
the complete contract.
The bundled Example UI Plugin is the reference adapter for the plugin package interface. It is owned and versioned by this repository and demonstrates manifest permissions, settings registration, Loader-scoped storage, reversible DOM changes, and lifecycle cleanup.
Third-party plugins are independent projects. Their source, tests, versions, releases, and deployment instructions stay in their own repositories. Loader development never requires copying or synchronizing third-party source into this repository; install a released folder or ZIP through Settings instead.
To add a custom package on Windows, place its directory under %LOCALAPPDATA%\CodexScriptLoader\scripts\<script-id> and run CodexScriptLoader.exe --reload. With the Node.js runtime, install a package or a single .js file with:
node src/cli.mjs install /path/to/script --enableThe repository pins the .NET SDK in global.json, locks NuGet dependencies, treats warnings as errors, and enables deterministic builds.
# Native build and tests
dotnet build .\windows\CodexScriptLoader.Windows.sln -c Release --configfile .\NuGet.Config
dotnet run --project .\windows\tests\CodexScriptLoader.Tests\CodexScriptLoader.Tests.csproj -c Release
# Node compatibility and parity checks
npm run check
npm test
# Reproducible pre-sign payload verification
.\windows\scripts\verify-reproducible.ps1 -RuntimeIdentifier win-x64
.\windows\scripts\verify-reproducible.ps1 -RuntimeIdentifier win-arm64Run the development-only ActivationProbe with Codex completely closed. A passing probe discovers the real application ID, activates Codex with CDP arguments, verifies listener ownership, and reports ACTIVATION_PASS.
See windows/README.md for NSIS, portable ZIP, optional signing, and release-gate details.
| Path | Responsibility |
|---|---|
windows/src/CodexScriptLoader.Core |
Configuration, manifests, permissions, hashes, quarantine, and injection plans. |
windows/src/CodexScriptLoader.Interop |
Windows package, activation, process identity, and TCP owner APIs. |
windows/src/CodexScriptLoader.Launcher |
Stable NativeAOT launcher, health check, active-version selection, and fallback. |
windows/src/CodexScriptLoader.Windows |
WinForms background host, CDP, lifecycle supervision, diagnostics, and single instance. |
windows/packaging |
NSIS installer definition and Windows image assets. |
windows/scripts |
Build-time packaging, icon generation, validation, and reproducibility tools. |
packages/example-ui-plugin |
Loader-owned reference adapter for the renderer plugin package interface. |
src |
Legacy Node development/parity implementation; not the Windows production entry point. |
- “Codex is already running” — close all Codex windows, wait for its process to exit, and start the Loader again.
- Loader starts but no window appears — this is expected. Start it a second time to open diagnostics.
- A script is degraded — inspect diagnostics and
%LOCALAPPDATA%\CodexScriptLoader\logs, then correct the manifest, permission, or lifecycle error. - Setup cannot replace a running Loader — exit the managed Codex session and Loader, then run setup again. The installer never force-terminates either process.
- A Codex update breaks activation — close Codex and run Activation Probe to capture the package and CDP diagnostics.
| Platform | Runtime | Status |
|---|---|---|
| Windows 11 x64/arm64 | Native .NET 10 background host | Tested and packaged |
| macOS | Node.js 22 live runtime | Implemented, not yet tested on macOS hardware |
Codex updates may require Loader or script compatibility changes. This independent project is not affiliated with OpenAI or Microsoft.
Issues and focused pull requests are welcome. For setup, architecture, testing, and packaging details, see windows/README.md.
- Editorial icon method: ZzzLc0405/photo-abstract-editorial.
Loader code and the bundled example are released under the MIT License. Third-party plugins retain their own licenses outside this repository. Adapted assets under windows/branding are excluded from the MIT code license and remain subject to the non-commercial restrictions documented in windows/branding/README.md; obtain the original author's authorization before commercial distribution of those assets.
