Skip to content
Closed
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
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Two commands, and every one of them runs any LLM you point it at.</p>

```bash
npm install -g @bitkyc08/opencodex
# Or with pnpm 10.4+: pnpm add -g --allow-build=bun @bitkyc08/opencodex
ocx start
```

Expand Down Expand Up @@ -82,6 +83,7 @@ account while existing threads stay pinned to the account that started them.

```bash
npm install -g @bitkyc08/opencodex # Node 18+; the Bun runtime is bundled automatically
# Or with pnpm 10.4+: pnpm add -g --allow-build=bun @bitkyc08/opencodex
ocx start # proxy + dashboard on localhost:10100
```

Expand Down Expand Up @@ -188,6 +190,7 @@ they reach the npm package.

```bash
npm install -g @bitkyc08/opencodex
# Or with pnpm 10.4+: pnpm add -g --allow-build=bun @bitkyc08/opencodex
ocx start # or `ocx service`
ocx init # interactive setup: writes ~/.opencodex/config.json and wires Codex
```
Expand All @@ -211,9 +214,9 @@ when it is unreachable). `ocx status` / `ocx doctor` / `ocx health` report the r
| Linux (x64 / arm64) | Fully supported | systemd (user unit) |
| Windows (x64) | Fully supported | Task Scheduler (hidden) / opt-in native service (`--native`, WinSW) |

Requires [Node](https://nodejs.org) 18+. The Bun runtime is bundled on `npm install` — no separate
Bun install needed, no WSL needed on Windows. If npm blocked the bundled runtime's install scripts,
see the [installation docs](https://opencodex.me/getting-started/installation/).
Requires [Node](https://nodejs.org) 18+. The Bun runtime is bundled on npm or pnpm install — no
separate Bun install needed, no WSL needed on Windows. If npm blocked the bundled runtime's install
scripts, see the [installation docs](https://opencodex.me/getting-started/installation/).

## Highlights

Expand Down
293 changes: 222 additions & 71 deletions bin/ocx.mjs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs-site/src/content/docs/getting-started/for-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Install the published package and confirm that `ocx` is on `PATH`:

```bash
npm install -g @bitkyc08/opencodex
# Or with pnpm 10.4+: pnpm add -g --allow-build=bun @bitkyc08/opencodex
ocx --version
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ vision and web-search sidecars can also use your ChatGPT login when a routed mod

| Requirement | Why |
| --- | --- |
| **[Node](https://nodejs.org) ≥ 18** | `ocx` runs on the Bun runtime, but the runtime is bundled automatically on `npm install` — you do **not** need to install Bun yourself. |
| **[Node](https://nodejs.org) ≥ 18** | `ocx` runs on the Bun runtime, but the runtime is bundled automatically by the npm or pnpm install — you do **not** need to install Bun yourself. |
| **[OpenAI Codex](https://openai.com/codex)** (CLI, App, or SDK) | The client opencodex sits in front of. opencodex writes to `$CODEX_HOME/config.toml` (default `~/.codex/config.toml`). |
| A provider account or API key | Anthropic, xAI, Kimi, Ollama Cloud, OpenRouter, an OpenAI-compatible endpoint, or your ChatGPT login. |

Expand All @@ -21,6 +21,12 @@ vision and web-search sidecars can also use your ChatGPT login when a routed mod
npm install -g @bitkyc08/opencodex
```

With pnpm 10.4 or later:

```bash
pnpm add -g --allow-build=bun @bitkyc08/opencodex
```

:::note[npm blocked the bun postinstall?]
Recent npm versions may block bun's postinstall script (`npm warn
install-scripts ... blocked because they are not covered by allowScripts`),
Expand Down
13 changes: 8 additions & 5 deletions docs-site/src/content/docs/reference/cli/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -535,14 +535,17 @@ package registry or install an update.

### `ocx update [--tag latest|preview]`

Self-update opencodex from npm. Stable installs use `@latest`; preview installs stay on `@preview`
Self-update opencodex through the package manager that installed it. npm and pnpm installs use
their native global update paths; pnpm verifies the active global group and rolls back through pnpm
if the new package is incomplete. Stable installs use `@latest`; preview installs stay on `@preview`
unless you pass `--tag latest|preview`. It detects a source checkout and tells you to
`git pull && bun install` instead, and is a no-op if you are already on the newest version for that
tag. Before stopping anything, npm installations run a bounded Unix cache ownership and access
check. Nested symlinks are checked with `lstat` but not followed; Windows explicitly skips this
Unix-only check. A failure aborts while the tray and proxy are still running. A running proxy is
then stopped before files are replaced; an installed service is rebuilt and started automatically,
while a foreground installation prints `ocx start` as the next step. Dashboard update records
check; pnpm installations verify the active global package before mutation. Nested symlinks are
checked with `lstat` but not followed; Windows explicitly skips this Unix-only check. A failure
aborts while the tray and proxy are still running. A running proxy is then stopped before files
are replaced; an installed service is rebuilt and started automatically, while a foreground
installation prints an exact active-launcher command as the next step. Dashboard update records
redact profile/cache paths and UID/GID values before they are persisted.

```bash
Expand Down
1 change: 1 addition & 0 deletions scripts/test-layout/layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,7 @@
"update-notify.test.ts": "update",
"update-npm-cache-preflight.test.ts": "update",
"update-npm-invocation.test.ts": "update",
"update-pnpm.test.ts": "update",
"update-stop-classification.test.ts": "update",
"update-stop-first.test.ts": "update",
"update-transactional.test.ts": "update",
Expand Down
4 changes: 2 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
//
// Before the src/ restructure the CLI lived at src/cli.ts, and durable launchers
// (codex shim wrappers, installed service definitions) baked that absolute path
// into their command lines. Users who upgrade in place with a bare
// `npm install -g @bitkyc08/opencodex` (instead of `ocx update`, which repairs the
// into their command lines. Users who upgrade in place with a bare package-manager
// install (`npm install -g` or `pnpm add -g`) (instead of `ocx update`, which repairs the
// shim/service) would otherwise be stranded on a dead path. Keep this stub for at
// least one release cycle after the restructure ships.
import "./cli/index.ts";
2 changes: 1 addition & 1 deletion src/cli/launcher-context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Trusted facts captured by the plain-Node npm launcher before Bun auto-loads
* Trusted facts captured by the plain-Node package launcher before Bun auto-loads
* project dotenv files. The random proof travels in argv while the context
* travels in the environment, so a project `.env` cannot forge the pair during
* an ordinary `ocx ...` invocation.
Expand Down
2 changes: 1 addition & 1 deletion src/config/pending-teardown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export function listPendingTeardowns(): OutstandingTeardown[] {
}
const out: OutstandingTeardown[] = [];
for (const name of names) {
// One naming rule, shared with the npm launcher: the two lanes drifting apart is
// One naming rule, shared with the package launcher: the two lanes drifting apart is
// exactly how the Node updater stopped seeing receipts at all.
if (!isPendingTeardownFileName(name)) continue;
const nonce = pendingTeardownNonceFromFileName(name)!;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/bun-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export function durableBunRuntime(): DurableBunRuntime {
/**
* Bun path to bake into durable artifacts (launchd/systemd/Task Scheduler and
* the Codex auto-start shim). Prefer the bundled binary — it lives under the
* npm global prefix and survives across `ocx update` — and fall back to the
* manager-owned global package directory and survives across `ocx update` — and fall back to the
* current runtime, which is Bun when launched normally.
*/
export function durableBunPath(): string {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/package-tree-integrity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface PackageTreeIntegrityGuard {
}

type ObservePackageTree = () => PackageTreeObservation | null;
type PackageTreeRuntimeInstall = "bun" | "npm" | "source";
type PackageTreeRuntimeInstall = "bun" | "npm" | "pnpm" | "source";

const packageManifestUrl = new URL("../../package.json", import.meta.url);

Expand Down
2 changes: 1 addition & 1 deletion src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const TASK = "opencodex-proxy";
export type ServiceBackend = "scheduler" | "native";

function cliEntry(runtime: DurableBunRuntime = durableBunRuntime()): { bun: string; bunRuntimeSource: BunRuntimeSource; cli: string } {
// Bake the bundled Bun (npm global prefix, survives `ocx update`) rather than
// Bake the bundled Bun (manager-owned global package directory, survives `ocx update`) rather than
// a transient system Bun, so launchd/systemd/schtasks keep resolving even if a
// standalone Bun is later removed. The CLI entry lives at src/cli/index.ts.
//
Expand Down
5 changes: 3 additions & 2 deletions src/update/badge.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/**
* Cached "is an update available?" answer for the GUI sidebar badge.
*
* `/api/update/check` spawns `npm view` on every call (~1s, network-bound), so a
* `/api/update/check` spawns the installing manager's `view` command on every call
* (~1s, network-bound), so a
* sidebar that polls it would spawn a process per tick on every page of the GUI.
* The badge instead READS the 20h version cache the CLI update prompt already
* maintains (`~/.opencodex/version.json`).
*
* This is deliberately read-only: it must never trigger a registry refresh. The GUI
* polls it, so a refresh-on-read would let repeated polls launch repeated `npm view`
* polls it, so a refresh-on-read would let repeated polls launch repeated manager `view`
* helpers with no coalescing. Cache warming stays with `ocx start`
* (`triggerBackgroundRefreshIfStale` in `src/update/notify.ts`) and with the explicit
* `/api/update/check` the user reaches by clicking the sidebar update button.
Expand Down
Loading
Loading