@lyupro/codex-bridge 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +98 -0
- package/README.md +152 -152
- package/bin/codex-bridge.mjs +188 -174
- package/cli/doctor-format.mjs +23 -0
- package/cli/doctor-hooks.mjs +114 -0
- package/cli/doctor-installation.mjs +111 -0
- package/cli/doctor-runs.mjs +55 -0
- package/cli/doctor.mjs +150 -399
- package/cli/host-contract.mjs +135 -0
- package/cli/hosts.mjs +4 -3
- package/cli/install.mjs +81 -11
- package/cli/permissions.mjs +7 -1
- package/cli/probe-contract.mjs +180 -0
- package/package.json +49 -49
- package/scripts/run-tests.mjs +4 -1
- package/src/agents/codex-build.md +265 -265
- package/src/agents/codex-review.md +219 -219
- package/src/agents/codex-scout.md +232 -232
- package/src/home/hooks/shell-write-intent.mjs +44 -15
- package/src/home/lib/brand-home.mjs +39 -0
- package/src/home/lib/meta/reply.mjs +27 -0
- package/src/home/lib/run-config.mjs +18 -6
- package/src/home/lib/runner/codex-args.mjs +154 -0
- package/src/home/lib/runner/codex-cmd.mjs +268 -398
- package/src/home/lib/runner/launcher.mjs +18 -30
- package/src/home/lib/runner/worker-order.mjs +56 -0
- package/src/home/lib/write-meta.mjs +11 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,104 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.5.5] - 2026-08-28
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- The write guard now reads quoting, so a redirect character inside a quoted argument is text rather
|
|
12
|
+
than an operator. While a delegated run held the repository, `sed -E 's#(A ).*#<redacted>#'` was
|
|
13
|
+
refused as a write to a file named `#'`: the scan ran over raw text, so the `>` looked like a
|
|
14
|
+
redirect and the token after it looked like its destination. The file already carried a defence
|
|
15
|
+
against this class, added after a `node -e` expression was misread the same way, but that defence
|
|
16
|
+
filtered candidates holding characters no file name may contain and the one refused here held
|
|
17
|
+
none. The guard still fails closed: when quotes cannot be balanced the scan behaves exactly as
|
|
18
|
+
before, because answering "no write here" to a line it cannot parse would be worse than the false
|
|
19
|
+
refusal being removed.
|
|
20
|
+
- The installation summary no longer reads as a bug. Four `PreToolUse` guards printed as
|
|
21
|
+
`PreToolUse and PreToolUse and PreToolUse and PreToolUse` because the sentence joined every hook's
|
|
22
|
+
event; it now groups by event and counts.
|
|
23
|
+
- The installed-file count agrees with `doctor`. The summary counted the Codex rules file among the
|
|
24
|
+
files while `doctor` reports it on its own line, so `68` sat next to `67` and read as a lost file.
|
|
25
|
+
- `Granted 0 permission rule(s)` no longer reports the good outcome as if it were a failure. When
|
|
26
|
+
the set is complete the summary says so; on a host whose settings were edited by hand it says how
|
|
27
|
+
many rules are missing and how to restore them, instead of claiming all are in place while the
|
|
28
|
+
operator gets permission windows. The already-installed branch reports the set by reading it —
|
|
29
|
+
granting there would have restored a deleted rule under the heading "nothing to do".
|
|
30
|
+
- A contract probe that refuses to judge now carries the host's own words. `The host exited with
|
|
31
|
+
status 1.` was the entire account of a failure on a live host; the last non-empty line the host
|
|
32
|
+
printed is now part of the reason. What counts as honoured, ignored or inconclusive is unchanged.
|
|
33
|
+
|
|
34
|
+
## [0.5.4] - 2026-08-28
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- A gate that holds every registered hook to one rule: a hook may not add anything to what the host
|
|
39
|
+
runs. A neighbouring package spent a whole plan undoing the opposite habit — its `PreToolUse` hook
|
|
40
|
+
wrapped the agent's command in a pipe, and because a host permission rule is matched against the
|
|
41
|
+
beginning of the final string and is not applied at all once that string carries a substitution or
|
|
42
|
+
a compound operator, every command it touched became unresolvable by the operator's own rules.
|
|
43
|
+
This package reported those windows on 2026-08-15 while carrying none of the cause. The gate reads
|
|
44
|
+
both behaviour and sources: a hook may return only the keys that refuse or speak
|
|
45
|
+
(`permissionDecision` of `deny` or `ask`, `permissionDecisionReason`, `additionalContext`), and no
|
|
46
|
+
hook source may mention a key that replaces what the host is about to run. `permissionDecision:
|
|
47
|
+
"allow"` is refused as well — the host stopped honouring it between 2.1.119 and 2.1.231, and while
|
|
48
|
+
it worked it was a blanket auto-approve over the operator's own deny rules.
|
|
49
|
+
|
|
50
|
+
- `doctor` and `install` now say whether this host still honours a hook refusal. Every guard in this
|
|
51
|
+
package refuses work with `permissionDecision: "deny"`, and the sibling key of that same object
|
|
52
|
+
died quietly — a neighbouring package established that `"allow"` stopped being honoured somewhere
|
|
53
|
+
between host 2.1.119 and 2.1.231, with no error printed on the way. Nothing here would have
|
|
54
|
+
noticed `deny` going the same way, because the suite checks that a hook RETURNED a refusal, never
|
|
55
|
+
that the host APPLIED it. The record is bound to a host version, since that is what changes
|
|
56
|
+
underneath an installation, and reports five states: never probed, probed on another version,
|
|
57
|
+
refusals ignored (which names the guards it makes inert and exits non-zero), verified, and a host
|
|
58
|
+
whose version cannot be read.
|
|
59
|
+
|
|
60
|
+
- `codex-bridge doctor --probe-contract` performs that measurement on a live host, and every message
|
|
61
|
+
that a probe could change now names it. The probe builds a throwaway rig outside the repository,
|
|
62
|
+
registers one temporary hook there that refuses one marker command, runs the real host inside it
|
|
63
|
+
and judges by whether the marker file appeared. Three conditions were bought by a measurement that
|
|
64
|
+
first reported a working host as dead: only the tool under test is allowed (`--allowedTools Bash`),
|
|
65
|
+
because the nested session otherwise sidesteps the refusal with `Write` and writes its own report
|
|
66
|
+
of being blocked into the marker file; the verdict comes from what executed, never from what the
|
|
67
|
+
model said about it; and settings are read from the project alone, or the operator's own hooks get
|
|
68
|
+
measured instead. A crash, a timeout or a hook that never fired records nothing and exits 2 — an
|
|
69
|
+
absent marker is only evidence when the host survived to the end, and a probe that announced a
|
|
70
|
+
contract as verified without measuring it would be the very silence this record exists to break.
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- A run now reads the operator's configuration instead of the copy shipped inside the package. The
|
|
75
|
+
installer seeds `config.json` into `~/.lyupro/.codex-bridge/`, the CLI read it from there, and the
|
|
76
|
+
runner built its own path from the module it happened to live in — so `codex-bridge run` opened the
|
|
77
|
+
package's seed, in which no profile is ever configured. A model and a reasoning depth pinned by the
|
|
78
|
+
operator on 5 August reached no run at all: every run silently used defaults, `env.json` recorded
|
|
79
|
+
`"models": {}`, `meta.json` recorded no model because no `-m` was ever passed, and `doctor` kept
|
|
80
|
+
confirming the setting because it resolved the path correctly. Three releases shipped that way, and
|
|
81
|
+
it surfaced only when a service refused a reasoning level and the command line in the run folder
|
|
82
|
+
turned out to name no model. Installed from npm, the file the runner read also sat inside
|
|
83
|
+
`node_modules` and was replaced by every update, contradicting the documented promise that
|
|
84
|
+
configured profiles survive one. There is now one resolver of the host home for both halves of the
|
|
85
|
+
repository, the printed state names the file together with where that path came from, and an
|
|
86
|
+
end-to-end test fails if a configured profile stops reaching the assembled Codex command line — the
|
|
87
|
+
existing unit tests injected the profile as an argument and could not see the file at all.
|
|
88
|
+
|
|
89
|
+
- Every reply now names the worker that ran it: `Model: <model> at <depth> effort (<where the depth
|
|
90
|
+
came from>)`, beside the log link. `worker.json` and `meta.json` carry the same profile, including
|
|
91
|
+
whether the model came from the configuration or was left to Codex, and whether the depth came
|
|
92
|
+
from the request, the configuration or the fallback. A dispatcher reads these rows and nothing
|
|
93
|
+
else, and for three releases none of them named a model at all — which is why a run on a model
|
|
94
|
+
nobody ordered could go unnoticed. A depth silently served by the fallback is the same silence one
|
|
95
|
+
level down, so it is reported the same way. Runs recorded before the field simply keep their old
|
|
96
|
+
replies.
|
|
97
|
+
|
|
98
|
+
- `minimal` is no longer accepted as a reasoning effort. It passed validation and was then rejected
|
|
99
|
+
by the model itself (`Unsupported value: 'minimal' is not supported with the … model`), so an order
|
|
100
|
+
died four seconds after launch on quota already spent. The allowed set is now exactly what the
|
|
101
|
+
service enumerates: `none`, `low`, `medium`, `high`, `xhigh`, `max`. A validator that admits a
|
|
102
|
+
known-dead value is worse than no validator: it moves the refusal past the point where refusing is
|
|
103
|
+
free.
|
|
104
|
+
|
|
7
105
|
## [0.5.3] - 2026-08-23
|
|
8
106
|
|
|
9
107
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
# codex-bridge
|
|
2
|
-
|
|
3
|
-
**Delegate implementation, reconnaissance and review from Claude Code to Codex — six host guards, zero runtime dependencies, zero build steps.**
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@lyupro/codex-bridge)
|
|
6
|
-
[](https://www.npmjs.com/package/@lyupro/codex-bridge)
|
|
7
|
-
[](LICENSE)
|
|
8
|
-
|
|
9
|
-
[](https://www.npmjs.com/package/@lyupro/codex-bridge)
|
|
10
|
-
[](https://www.npmjs.com/package/@lyupro/codex-bridge)
|
|
11
|
-
[](package.json)
|
|
12
|
-
|
|
13
|
-
Agentic coding spends one model subscription on everything, and implementation is the expensive part. codex-bridge moves that work to a separate [Codex CLI](https://developers.openai.com/codex/cli) subscription: Claude Code plans, dispatches and accepts, Codex executes — and every delegated run is guarded, verified and audited on disk.
|
|
14
|
-
|
|
15
|
-
## Install
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm i -g @lyupro/codex-bridge # puts both command names on PATH
|
|
19
|
-
codex-bridge install # into ~/.claude and ~/.lyupro/.codex-bridge/
|
|
20
|
-
codexb install # the short name, same command
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Two other entry points reach the same command: `npx @lyupro/codex-bridge install` for a one-off run without a global install, and `node bin/codex-bridge.mjs install` from a clone. Every command below works through all three.
|
|
24
|
-
|
|
25
|
-
Shipped agents invoke delegated runs through `codex-bridge run`. This is a permission contract,
|
|
26
|
-
not a style choice: the stable package command lets host permission rules match the invocation
|
|
27
|
-
without exposing an installation-specific absolute path and prompting on every delegation.
|
|
28
|
-
|
|
29
|
-
Use `--scope project` to install the Claude Code-facing files under `<repo>/.claude`; the default user scope uses `~/.claude`.
|
|
30
|
-
|
|
31
|
-
Installation has two roots:
|
|
32
|
-
|
|
33
|
-
- `~/.lyupro/.codex-bridge/` contains package runtime files, guards, configuration, conventions, and the installation record.
|
|
34
|
-
- The selected Claude Code root (`~/.claude/` or `<repo>/.claude/`) contains the three agents, two slash commands, merged hook registrations in `settings.json`, and run artifacts under `codex-runs/`.
|
|
35
|
-
|
|
36
|
-
The repository's `src/home/` directory is the literal image copied into the branded root: its
|
|
37
|
-
`hooks/`, `lib/`, `config.json`, and `conventions.md` paths are the host paths without remapping.
|
|
38
|
-
Only npm's `package.json` is copied into that root from outside the image.
|
|
39
|
-
|
|
40
|
-
Before every `settings.json` write, the existing file is backed up. Existing settings and foreign hooks are preserved. `--dry-run` prints the plan without writing.
|
|
41
|
-
|
|
42
|
-
If a global install and a clone coexist, you have two package copies. `codex-bridge update`, `codexb update`, and the clone entry point copy files from whichever copy launched the command; using the wrong one can replace a newer host installation with older files.
|
|
43
|
-
|
|
44
|
-
`codex-runs/` contains operator data. Uninstall never removes it.
|
|
45
|
-
|
|
46
|
-
## Requirements
|
|
47
|
-
|
|
48
|
-
- Node.js >= 24
|
|
49
|
-
- An authenticated [Codex CLI](https://developers.openai.com/codex/cli): `codex --version` and `codex login`
|
|
50
|
-
- Claude Code, which hosts the agents, commands, and guards
|
|
51
|
-
|
|
52
|
-
## What it gives you
|
|
53
|
-
|
|
54
|
-
- **Runs survive the caller.** A detached worker keeps the run and its artifacts alive after the dispatcher exits.
|
|
55
|
-
- **Invalid work is rejected before quota is spent.** Scope, order, retry, branch, and continuation checks run before a run folder is created.
|
|
56
|
-
- **Verdicts come from artifacts.** CLI events, git state, and the declared report decide the result, not model claims about itself.
|
|
57
|
-
- **Identical retries attach safely.** Repeating the same command joins the active run instead of starting another one.
|
|
58
|
-
- **6 host guards enforce the boundary.** `reply-guard`, `order-gate`, `worktree-lock`, `prune-guard`, and `stop-guard` cover replies, orders, edits, deletion, and stopping; `worktree-witness` stands behind the lock and names a write that reached the tree anyway.
|
|
59
|
-
- **Installation is identifiable and reversible.** Writes to `settings.json` are merged and backed up; uninstall removes recorded package files only.
|
|
60
|
-
- **Every run leaves an audit folder.** The verbatim task, scope, before/after git state, events, report, verdict, and reason remain together.
|
|
61
|
-
- **Zero runtime dependencies and zero build steps.** The package is plain `.mjs` on the Node.js standard library.
|
|
62
|
-
|
|
63
|
-
The current suite contains **
|
|
64
|
-
|
|
65
|
-
## Verify
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
codex-bridge doctor
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Output on a healthy host (trimmed):
|
|
72
|
-
|
|
73
|
-
```text
|
|
74
|
-
[ok] host: ~/.claude (user, exists)
|
|
75
|
-
[ok] installation: @lyupro/codex-bridge@0.5.1 (matches package)
|
|
76
|
-
[ok] files: 53 installed file(s) present
|
|
77
|
-
[ok] rules: ~/.codex/rules/codex-bridge.rules (matches record)
|
|
78
|
-
[ok] permissions: installed (24/24 own strings in allow/deny)
|
|
79
|
-
[ok] hook:SubagentStop: matcher * -> ~/.lyupro/.codex-bridge/hooks/reply-guard.mjs
|
|
80
|
-
[ok] hook:PreToolUse: matcher Agent|Task -> ~/.lyupro/.codex-bridge/hooks/order-gate.mjs
|
|
81
|
-
[ok] hook:PreToolUse: matcher Write|Edit|MultiEdit|NotebookEdit -> ~/.lyupro/.codex-bridge/hooks/worktree-lock.mjs
|
|
82
|
-
[ok] hook:PreToolUse: matcher Bash|PowerShell -> ~/.lyupro/.codex-bridge/hooks/prune-guard.mjs
|
|
83
|
-
[ok] hook:PreToolUse: matcher TaskStop -> ~/.lyupro/.codex-bridge/hooks/stop-guard.mjs
|
|
84
|
-
[warn] retention: Automatic cleanup is ON — run transport older than 30 days is removed to reclaim disk space.
|
|
85
|
-
[ok] conventions: ~/.lyupro/.codex-bridge/conventions.md (found)
|
|
86
|
-
[ok] codex: codex-cli 0.146.1
|
|
87
|
-
[ok] node: 24.18.0 (requires >=24)
|
|
88
|
-
[ok] runsRoot: ~/.claude/codex-runs
|
|
89
|
-
[ok] liveRuns: 0 runs working right now
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
`doctor` reports the selected host, package source, installed files and guards, Codex availability, retention policy, permissions, live runs, and the current repository's run folder. The `agents` line is a read, not a file count: each installed definition is parsed and its `name` checked against the dispatcher it claims to be, because a definition the host cannot read is a dispatcher that does not exist while every other line still says `[ok]`. Content that drifts from the packaged definition is a failure, not a warning: `doctor` exits 1 and names `codex-bridge update --force`, because a host running someone else's agent files may be calling the runner in a form no permission rule matches.
|
|
93
|
-
|
|
94
|
-
If a session answers `Agent type 'codex-build' not found`, a run can still be started without the dispatcher — the agent is a wrapper that types one command:
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
codex-bridge run --agent codex-build --repo <repository> --order-id <id> --scope <globs> --task-file <absolute path>
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
The trade-off is explicit: `order-gate` and `reply-guard` sit on the dispatcher call, so a direct run is unguarded and its result is judged by the worktree and the suite.
|
|
101
|
-
|
|
102
|
-
## Update
|
|
103
|
-
|
|
104
|
-
Update the global package, then refresh the selected host installation:
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
npm i -g @lyupro/codex-bridge
|
|
108
|
-
codex-bridge update
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
`update` refreshes unchanged package files, refuses to overwrite operator edits unless `--force` is supplied, and removes obsolete files only when their installed fingerprint is unchanged.
|
|
112
|
-
|
|
113
|
-
## Limitations
|
|
114
|
-
|
|
115
|
-
- **Runs are not isolated from one another.** They share one worktree; guards prevent concurrent writers instead of creating sandboxes.
|
|
116
|
-
- **A build run has a 25-minute deadline.** Work and artifacts survive a timeout, but the run may need an explicit continuation.
|
|
117
|
-
- **A dispatcher verdict is not the work outcome.** Judge completion from the worktree and tests as well as the recorded verdict.
|
|
118
|
-
|
|
119
|
-
Transport files from runs at least **30 days** old are pruned automatically when a new run starts. Configuration can change or disable that retention policy; reports, accounting, and worktree snapshots are retained.
|
|
120
|
-
|
|
121
|
-
## Command reference
|
|
122
|
-
|
|
123
|
-
| Command | Purpose |
|
|
124
|
-
| --- | --- |
|
|
125
|
-
| `install [--scope user\|project] [--host <path>] [--dry-run] [--force]` | Install into a Claude Code host. |
|
|
126
|
-
| `update [--scope user\|project] [--host <path>] [--dry-run] [--force]` | Refresh a recorded installation. |
|
|
127
|
-
| `permissions [add\|remove] [--scope user\|project] [--host <path>]` | Inspect or manage optional shell rules. |
|
|
128
|
-
| `uninstall [--scope user\|project] [--host <path>] [--dry-run]` | Remove recorded package files while preserving run artifacts. |
|
|
129
|
-
| `doctor [--scope user\|project] [--host <path>]` | Diagnose the selected host and Codex connection. |
|
|
130
|
-
| `run <runner options> --task-file <abs path>` | Start or attach to a delegated run through the permission-stable package command. The task file carries the statement, the scout questions and the verification command; its path must be absolute. Piping the statement on stdin is the alternative channel, and the two cannot be combined. |
|
|
131
|
-
| `projects [<name>] [--json]` | List projects or runs in the run store. |
|
|
132
|
-
| `prune <project> [<run>] [--purge] [--older-than <age>] [-f] [--json]` | Plan or perform operator-confirmed cleanup. |
|
|
133
|
-
| `unlock [<project>\|--all]` | Close records whose runner is gone. |
|
|
134
|
-
| `read <run>` | Render a run's structured event stream. |
|
|
135
|
-
| `stop <run>` | Stop a run and record a `FAIL` verdict. |
|
|
136
|
-
|
|
137
|
-
Both `codex-bridge` and `codexb` invoke the same command. Run `codex-bridge --help` for exact forms.
|
|
138
|
-
|
|
139
|
-
### Development
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
git clone https://github.com/lyupro/codex-bridge.git
|
|
143
|
-
cd codex-bridge
|
|
144
|
-
git config core.hooksPath .githooks
|
|
145
|
-
npm test
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
The Git hook enforces the 400-line source-file limit.
|
|
149
|
-
|
|
150
|
-
## License
|
|
151
|
-
|
|
152
|
-
[MIT](LICENSE)
|
|
1
|
+
# codex-bridge
|
|
2
|
+
|
|
3
|
+
**Delegate implementation, reconnaissance and review from Claude Code to Codex — six host guards, zero runtime dependencies, zero build steps.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@lyupro/codex-bridge)
|
|
6
|
+
[](https://www.npmjs.com/package/@lyupro/codex-bridge)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/@lyupro/codex-bridge)
|
|
10
|
+
[](https://www.npmjs.com/package/@lyupro/codex-bridge)
|
|
11
|
+
[](package.json)
|
|
12
|
+
|
|
13
|
+
Agentic coding spends one model subscription on everything, and implementation is the expensive part. codex-bridge moves that work to a separate [Codex CLI](https://developers.openai.com/codex/cli) subscription: Claude Code plans, dispatches and accepts, Codex executes — and every delegated run is guarded, verified and audited on disk.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm i -g @lyupro/codex-bridge # puts both command names on PATH
|
|
19
|
+
codex-bridge install # into ~/.claude and ~/.lyupro/.codex-bridge/
|
|
20
|
+
codexb install # the short name, same command
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Two other entry points reach the same command: `npx @lyupro/codex-bridge install` for a one-off run without a global install, and `node bin/codex-bridge.mjs install` from a clone. Every command below works through all three.
|
|
24
|
+
|
|
25
|
+
Shipped agents invoke delegated runs through `codex-bridge run`. This is a permission contract,
|
|
26
|
+
not a style choice: the stable package command lets host permission rules match the invocation
|
|
27
|
+
without exposing an installation-specific absolute path and prompting on every delegation.
|
|
28
|
+
|
|
29
|
+
Use `--scope project` to install the Claude Code-facing files under `<repo>/.claude`; the default user scope uses `~/.claude`.
|
|
30
|
+
|
|
31
|
+
Installation has two roots:
|
|
32
|
+
|
|
33
|
+
- `~/.lyupro/.codex-bridge/` contains package runtime files, guards, configuration, conventions, and the installation record.
|
|
34
|
+
- The selected Claude Code root (`~/.claude/` or `<repo>/.claude/`) contains the three agents, two slash commands, merged hook registrations in `settings.json`, and run artifacts under `codex-runs/`.
|
|
35
|
+
|
|
36
|
+
The repository's `src/home/` directory is the literal image copied into the branded root: its
|
|
37
|
+
`hooks/`, `lib/`, `config.json`, and `conventions.md` paths are the host paths without remapping.
|
|
38
|
+
Only npm's `package.json` is copied into that root from outside the image.
|
|
39
|
+
|
|
40
|
+
Before every `settings.json` write, the existing file is backed up. Existing settings and foreign hooks are preserved. `--dry-run` prints the plan without writing.
|
|
41
|
+
|
|
42
|
+
If a global install and a clone coexist, you have two package copies. `codex-bridge update`, `codexb update`, and the clone entry point copy files from whichever copy launched the command; using the wrong one can replace a newer host installation with older files.
|
|
43
|
+
|
|
44
|
+
`codex-runs/` contains operator data. Uninstall never removes it.
|
|
45
|
+
|
|
46
|
+
## Requirements
|
|
47
|
+
|
|
48
|
+
- Node.js >= 24
|
|
49
|
+
- An authenticated [Codex CLI](https://developers.openai.com/codex/cli): `codex --version` and `codex login`
|
|
50
|
+
- Claude Code, which hosts the agents, commands, and guards
|
|
51
|
+
|
|
52
|
+
## What it gives you
|
|
53
|
+
|
|
54
|
+
- **Runs survive the caller.** A detached worker keeps the run and its artifacts alive after the dispatcher exits.
|
|
55
|
+
- **Invalid work is rejected before quota is spent.** Scope, order, retry, branch, and continuation checks run before a run folder is created.
|
|
56
|
+
- **Verdicts come from artifacts.** CLI events, git state, and the declared report decide the result, not model claims about itself.
|
|
57
|
+
- **Identical retries attach safely.** Repeating the same command joins the active run instead of starting another one.
|
|
58
|
+
- **6 host guards enforce the boundary.** `reply-guard`, `order-gate`, `worktree-lock`, `prune-guard`, and `stop-guard` cover replies, orders, edits, deletion, and stopping; `worktree-witness` stands behind the lock and names a write that reached the tree anyway.
|
|
59
|
+
- **Installation is identifiable and reversible.** Writes to `settings.json` are merged and backed up; uninstall removes recorded package files only.
|
|
60
|
+
- **Every run leaves an audit folder.** The verbatim task, scope, before/after git state, events, report, verdict, and reason remain together.
|
|
61
|
+
- **Zero runtime dependencies and zero build steps.** The package is plain `.mjs` on the Node.js standard library.
|
|
62
|
+
|
|
63
|
+
The current suite contains **817 automated tests: 816 passing and 1 skipped**.
|
|
64
|
+
|
|
65
|
+
## Verify
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
codex-bridge doctor
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Output on a healthy host (trimmed):
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
[ok] host: ~/.claude (user, exists)
|
|
75
|
+
[ok] installation: @lyupro/codex-bridge@0.5.1 (matches package)
|
|
76
|
+
[ok] files: 53 installed file(s) present
|
|
77
|
+
[ok] rules: ~/.codex/rules/codex-bridge.rules (matches record)
|
|
78
|
+
[ok] permissions: installed (24/24 own strings in allow/deny)
|
|
79
|
+
[ok] hook:SubagentStop: matcher * -> ~/.lyupro/.codex-bridge/hooks/reply-guard.mjs
|
|
80
|
+
[ok] hook:PreToolUse: matcher Agent|Task -> ~/.lyupro/.codex-bridge/hooks/order-gate.mjs
|
|
81
|
+
[ok] hook:PreToolUse: matcher Write|Edit|MultiEdit|NotebookEdit -> ~/.lyupro/.codex-bridge/hooks/worktree-lock.mjs
|
|
82
|
+
[ok] hook:PreToolUse: matcher Bash|PowerShell -> ~/.lyupro/.codex-bridge/hooks/prune-guard.mjs
|
|
83
|
+
[ok] hook:PreToolUse: matcher TaskStop -> ~/.lyupro/.codex-bridge/hooks/stop-guard.mjs
|
|
84
|
+
[warn] retention: Automatic cleanup is ON — run transport older than 30 days is removed to reclaim disk space.
|
|
85
|
+
[ok] conventions: ~/.lyupro/.codex-bridge/conventions.md (found)
|
|
86
|
+
[ok] codex: codex-cli 0.146.1
|
|
87
|
+
[ok] node: 24.18.0 (requires >=24)
|
|
88
|
+
[ok] runsRoot: ~/.claude/codex-runs
|
|
89
|
+
[ok] liveRuns: 0 runs working right now
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`doctor` reports the selected host, package source, installed files and guards, Codex availability, retention policy, permissions, live runs, and the current repository's run folder. The `agents` line is a read, not a file count: each installed definition is parsed and its `name` checked against the dispatcher it claims to be, because a definition the host cannot read is a dispatcher that does not exist while every other line still says `[ok]`. Content that drifts from the packaged definition is a failure, not a warning: `doctor` exits 1 and names `codex-bridge update --force`, because a host running someone else's agent files may be calling the runner in a form no permission rule matches.
|
|
93
|
+
|
|
94
|
+
If a session answers `Agent type 'codex-build' not found`, a run can still be started without the dispatcher — the agent is a wrapper that types one command:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
codex-bridge run --agent codex-build --repo <repository> --order-id <id> --scope <globs> --task-file <absolute path>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The trade-off is explicit: `order-gate` and `reply-guard` sit on the dispatcher call, so a direct run is unguarded and its result is judged by the worktree and the suite.
|
|
101
|
+
|
|
102
|
+
## Update
|
|
103
|
+
|
|
104
|
+
Update the global package, then refresh the selected host installation:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm i -g @lyupro/codex-bridge
|
|
108
|
+
codex-bridge update
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`update` refreshes unchanged package files, refuses to overwrite operator edits unless `--force` is supplied, and removes obsolete files only when their installed fingerprint is unchanged.
|
|
112
|
+
|
|
113
|
+
## Limitations
|
|
114
|
+
|
|
115
|
+
- **Runs are not isolated from one another.** They share one worktree; guards prevent concurrent writers instead of creating sandboxes.
|
|
116
|
+
- **A build run has a 25-minute deadline.** Work and artifacts survive a timeout, but the run may need an explicit continuation.
|
|
117
|
+
- **A dispatcher verdict is not the work outcome.** Judge completion from the worktree and tests as well as the recorded verdict.
|
|
118
|
+
|
|
119
|
+
Transport files from runs at least **30 days** old are pruned automatically when a new run starts. Configuration can change or disable that retention policy; reports, accounting, and worktree snapshots are retained.
|
|
120
|
+
|
|
121
|
+
## Command reference
|
|
122
|
+
|
|
123
|
+
| Command | Purpose |
|
|
124
|
+
| --- | --- |
|
|
125
|
+
| `install [--scope user\|project] [--host <path>] [--dry-run] [--force]` | Install into a Claude Code host. |
|
|
126
|
+
| `update [--scope user\|project] [--host <path>] [--dry-run] [--force]` | Refresh a recorded installation. |
|
|
127
|
+
| `permissions [add\|remove] [--scope user\|project] [--host <path>]` | Inspect or manage optional shell rules. |
|
|
128
|
+
| `uninstall [--scope user\|project] [--host <path>] [--dry-run]` | Remove recorded package files while preserving run artifacts. |
|
|
129
|
+
| `doctor [--scope user\|project] [--host <path>] [--probe-contract]` | Diagnose the selected host and Codex connection. `--probe-contract` measures on a live host whether it still honours a hook refusal, and records the answer. |
|
|
130
|
+
| `run <runner options> --task-file <abs path>` | Start or attach to a delegated run through the permission-stable package command. The task file carries the statement, the scout questions and the verification command; its path must be absolute. Piping the statement on stdin is the alternative channel, and the two cannot be combined. |
|
|
131
|
+
| `projects [<name>] [--json]` | List projects or runs in the run store. |
|
|
132
|
+
| `prune <project> [<run>] [--purge] [--older-than <age>] [-f] [--json]`<br>`prune --all-projects [--older-than <age>] [-f] [--json]` | Plan or perform operator-confirmed cleanup, for one project or across the whole run store. |
|
|
133
|
+
| `unlock [<project>\|--all]` | Close records whose runner is gone. |
|
|
134
|
+
| `read <run>` | Render a run's structured event stream. |
|
|
135
|
+
| `stop <run>` | Stop a run and record a `FAIL` verdict. |
|
|
136
|
+
|
|
137
|
+
Both `codex-bridge` and `codexb` invoke the same command. Run `codex-bridge --help` for exact forms.
|
|
138
|
+
|
|
139
|
+
### Development
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
git clone https://github.com/lyupro/codex-bridge.git
|
|
143
|
+
cd codex-bridge
|
|
144
|
+
git config core.hooksPath .githooks
|
|
145
|
+
npm test
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The Git hook enforces the 400-line source-file limit.
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
[MIT](LICENSE)
|