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
9 changes: 9 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh

set -eu

repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"

echo "Ensuring local verify passed before commit..."
node scripts/enforce-local-verify.mjs pre-commit
9 changes: 9 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh

set -eu

repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"

echo "Ensuring local verify passed before push..."
node scripts/enforce-local-verify.mjs pre-push
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ jobs:

security-audit:
name: Security Audit
if: github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 15

Expand Down
9 changes: 7 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@ Current planning docs:
## Current storage and config

Canonical files:
- Plugin config: `~/.config/opencode/codex-config.json`
- Plugin config: `~/.config/opencode/codex-config.jsonc`
- Plugin accounts: `~/.config/opencode/codex-accounts.json`
- OpenCode provider auth marker: `${XDG_DATA_HOME:-~/.local/share}/opencode/auth.json`
- Optional request snapshots/logs: `<config-root>/logs/codex-plugin/`

Important:
- `opencode.json` should only contain plugin installation/enablement.
- Runtime flags and behavior go in `codex-config.json`.
- Runtime flags and behavior go in `codex-config.jsonc`.
- Legacy `codex-config.json` is compatibility-only; prefer `.jsonc` in code, docs, examples, and tests.
- Keep internal catalog/runtime defaults (for example `codexRuntimeDefaults.reasoningSummaryFormat`) out of public config unless schema, loader, examples, and docs are intentionally updated together.

## Modes

Expand Down Expand Up @@ -73,6 +75,9 @@ npm run verify

`npm run verify` is the default pre-release check.

- Treat `npm run verify` as required before both commits and PR/push updates. Local hooks should enforce it, and manual verification is still required if hooks are bypassed.
- After changing tests, test helpers, or TypeScript-only fixture shapes, run `npm run typecheck:test` before pushing. `npm test` and `npm run typecheck` do not cover the test TypeScript project on their own.

## Module sizing

- There is no hard max-lines or max-file-size rule in this repo.
Expand Down
21 changes: 19 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,31 @@ Thanks for contributing to `opencode-codex-auth`.

```bash
npm ci
npm run hooks:install
npm run verify
```

`npm run verify` is the baseline gate and runs:
Local hooks enforce `npm run verify` before both commits and pushes once you run `npm run hooks:install`.
The commit hook accepts staged-only commit-ready changes, while the push hook requires a clean tree so it validates the exact commits being pushed.

`npm run verify:local` is the recommended manual gate. It runs `npm run verify`, but skips reruns when the current tree already passed locally.

Pull request GitHub CI keeps only hosted-value checks: clean-room verify, Linux tarball smoke, Windows smoke, dependency review, and secret scanning. `npm audit` still runs in GitHub, but only on default-branch pushes rather than every PR.

`npm run verify` is the baseline full gate and runs:

- `npm run check:esm-imports`
- `npm run lint`
- `npm run format:check`
- `npm run typecheck`
- `npm test`
- `npm run typecheck:test`
- `npm run test:anti-mock`
- `npm run test:coverage`
- `npm run check:coverage-ratchet`
- `npm run check:docs`
- `npm run build`
- `npm run check:dist-esm-imports`
- `npm run smoke:cli:dist`

## Pull requests

Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ opencode run "say hi" --model=openai/gpt-5

## Configuration

Keep plugin install/enablement in `opencode.json`, and runtime behavior in `codex-config.json`.
Keep plugin install/enablement in `opencode.json`, and runtime behavior in `codex-config.jsonc`. The plugin still accepts commented legacy `codex-config.json` files for compatibility.

- Config reference: [docs/configuration.md](docs/configuration.md)
- Multi-account behavior: [docs/multi-account.md](docs/multi-account.md)
Expand All @@ -56,18 +56,23 @@ Keep plugin install/enablement in `opencode.json`, and runtime behavior in `code

```bash
npm install
npm run hooks:install
npm run verify
```

Helpful local commands:

```bash
npm run verify:local
npm run prepush
npm run lint
npm run test:coverage
npm run check:docs
```

`npm run verify` is the primary quality gate and includes lint, formatting, type-checking, anti-mock, coverage/ratchet, docs drift checks, build validation, and CLI smoke checks.
Local git hooks now enforce `npm run verify` before both `git commit` and `git push`. The commit hook accepts staged-only commit-ready changes, and the push hook requires a clean tree so it verifies the exact commits being pushed. `npm run verify:local` runs the same enforcement manually, with a cache so unchanged trees do not rerun the full suite twice in a row.

Pull request CI stays intentionally lean: GitHub still runs clean-room verify, tarball smoke, Windows smoke, dependency review, and secret scanning. Dependency vulnerability auditing via `npm audit` now runs on default-branch pushes instead of every PR.

## Usage Note

Expand Down
106 changes: 67 additions & 39 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
This plugin uses one runtime config file:

- resolved config path:
- `$XDG_CONFIG_HOME/opencode/codex-config.json` when `XDG_CONFIG_HOME` is set
- otherwise `~/.config/opencode/codex-config.json`
- `$XDG_CONFIG_HOME/opencode/codex-config.jsonc` when `XDG_CONFIG_HOME` is set
- otherwise `~/.config/opencode/codex-config.jsonc`

If the default config path does not exist, installer/bootstrap flows create it with defaults.

Expand All @@ -24,7 +24,7 @@ Known exceptions:

Use these schemas for validation/autocomplete:

- `schemas/codex-config.schema.json` -> `codex-config.json`
- `schemas/codex-config.schema.json` -> `codex-config.jsonc`
- `schemas/opencode.schema.json` -> `opencode.json`
- `schemas/codex-accounts.schema.json` -> `codex-accounts.json` (advanced/manual recovery only)

Expand All @@ -34,16 +34,17 @@ The plugin loads config in this order:

1. `OPENCODE_OPENAI_MULTI_CONFIG_PATH`
2. Resolved default config path:
- `$XDG_CONFIG_HOME/opencode/codex-config.json` when `XDG_CONFIG_HOME` is set
- otherwise `~/.config/opencode/codex-config.json`
- `$XDG_CONFIG_HOME/opencode/codex-config.jsonc` when `XDG_CONFIG_HOME` is set
- otherwise `~/.config/opencode/codex-config.jsonc`
- compatibility fallback: `codex-config.json` if the canonical `.jsonc` file is absent

`codex-config.json` supports JSON comments (`//` and `/* ... */`) for readability.
`codex-config.jsonc` supports JSON comments (`//` and `/* ... */`) for readability. The loader also accepts commented legacy `codex-config.json` files.

Known-field type validation is applied on load. If a known field has an invalid type/value, the plugin ignores that config file and logs an actionable warning.

## Default generated config

```json
```jsonc
{
"$schema": "https://schemas.iam-brain.dev/opencode-codex-auth/codex-config.schema.json",
"debug": false,
Expand All @@ -65,9 +66,11 @@ Known-field type validation is applied on load. If a known field has an invalid
},
"global": {
"personality": "pragmatic",
"verbosityEnabled": true,
"verbosity": "default"
"reasoningEffort": "high",
"reasoningSummary": "auto",
"textVerbosity": "default"
},
"customModels": {},
"perModel": {}
}
```
Expand Down Expand Up @@ -144,45 +147,68 @@ Mode-derived runtime defaults when omitted:

- `global.personality: string`
- Personality key applied to all models unless overridden.
- `global.thinkingSummaries: boolean`
- Global thinking-summary preference. Omit to use model/catalog default.
- `global.verbosityEnabled: boolean`
- Enables/disables `textVerbosity` injection globally (`true` default).
- `global.verbosity: "default" | "low" | "medium" | "high"`
- Verbosity preference (`"default"` uses each model catalog default).
- `global.serviceTier: "default" | "priority" | "flex"`
- Global service tier preference.
- `global.reasoningEffort: string`
- Global reasoning effort override forwarded upstream when the request does not already set one.
- `global.reasoningSummary: "auto" | "concise" | "detailed" | "none"`
- Global reasoning summary format override forwarded upstream as `reasoning.summary`.
- `"none"` disables reasoning summaries.
- Deprecated boolean aliases still load:
- `reasoningSummaries: true` => `"auto"`
- `reasoningSummaries: false` => `"none"`
- `thinkingSummaries` behaves the same way and warns on load.
- `global.textVerbosity: "default" | "low" | "medium" | "high" | "none"`
- Global text verbosity override forwarded upstream as `text.verbosity`.
- `"default"` uses each model catalog default.
- `"none"` disables text verbosity.
- Deprecated aliases still load:
- `verbosityEnabled: false` => `"none"`
- `verbosity: "medium"` => `textVerbosity: "medium"`
- `global.serviceTier: "auto" | "priority" | "flex"`
- Global Fast Mode preference (`serviceTier`).
- `"priority"` maps to request-body `service_tier: "priority"` only for `gpt-5.4*`.
- `"flex"` passes through `service_tier: "flex"`.
- `"default"` or omission leaves `service_tier` unset unless the request body already sets it.
- `"auto"` or omission leaves `service_tier` unset unless the request body already sets it.
- Deprecated alias: `"default"` => `"auto"`.
- `global.include: ("reasoning.encrypted_content" | "file_search_call.results" | "message.output_text.logprobs")[]`
- Global response include values merged into host-provided `include`.
- `global.parallelToolCalls: boolean`
- Global override for `parallel_tool_calls` when the request does not already set one.
- `customModels.<slug>.targetModel: string`
- Required target model slug inherited by the selectable custom model alias.
- `customModels.<slug>.name: string`
- Optional display name for the custom selectable model.
- `customModels.<slug>.personality`, `customModels.<slug>.reasoningEffort`, `customModels.<slug>.reasoningSummary`, `customModels.<slug>.textVerbosity`, `customModels.<slug>.serviceTier`, `customModels.<slug>.include`, `customModels.<slug>.parallelToolCalls`
- Defaults applied when that custom slug is selected.
- `customModels.<slug>.variants.<variant>.personality`
- Variant-level override for the selected custom slug.
- `customModels.<slug>.variants.<variant>.reasoningEffort`, `customModels.<slug>.variants.<variant>.reasoningSummary`, `customModels.<slug>.variants.<variant>.textVerbosity`, `customModels.<slug>.variants.<variant>.serviceTier`, `customModels.<slug>.variants.<variant>.include`, `customModels.<slug>.variants.<variant>.parallelToolCalls`
- Variant-level overrides for the selected custom slug.
- `perModel.<model>.personality: string`
- Model-specific personality override.
- `perModel.<model>.thinkingSummaries: boolean`
- Model-specific summary override (`true` force-on, `false` force-off).
- `perModel.<model>.verbosityEnabled: boolean`
- Model-specific enable/disable for `textVerbosity`.
- `perModel.<model>.verbosity: "default" | "low" | "medium" | "high"`
- Model-specific verbosity setting.
- `perModel.<model>.serviceTier: "default" | "priority" | "flex"`
- Model-specific service tier override.
- `perModel.<model>.reasoningEffort`, `perModel.<model>.reasoningSummary`, `perModel.<model>.textVerbosity`, `perModel.<model>.serviceTier`, `perModel.<model>.include`, `perModel.<model>.parallelToolCalls`
- Model-specific overrides with the same semantics as `global.*`.
- `perModel.<model>.variants.<variant>.personality: string`
- Variant-level personality override.
- `perModel.<model>.variants.<variant>.thinkingSummaries: boolean`
- Variant-level summary override (`true` force-on, `false` force-off).
- `perModel.<model>.variants.<variant>.verbosityEnabled: boolean`
- Variant-level enable/disable for `textVerbosity`.
- `perModel.<model>.variants.<variant>.verbosity: "default" | "low" | "medium" | "high"`
- Variant-level verbosity setting.
- `perModel.<model>.variants.<variant>.serviceTier: "default" | "priority" | "flex"`
- Variant-level service tier override.
- `perModel.<model>.variants.<variant>.reasoningEffort`, `perModel.<model>.variants.<variant>.reasoningSummary`, `perModel.<model>.variants.<variant>.textVerbosity`, `perModel.<model>.variants.<variant>.serviceTier`, `perModel.<model>.variants.<variant>.include`, `perModel.<model>.variants.<variant>.parallelToolCalls`
- Variant-level overrides with the same semantics as `global.*`.

If a model reports `supportsVerbosity=false` in catalog/runtime defaults, verbosity overrides are ignored.

Precedence for `personality`, `thinkingSummaries`, verbosity, and `serviceTier` settings:
Precedence for `personality`, `reasoningEffort`, `reasoningSummary`, `textVerbosity`, `serviceTier`, `include`, and `parallelToolCalls` settings:

1. `perModel.<model>.variants.<variant>`
2. `perModel.<model>`
3. `global`
3. `customModels.<selected-slug>.variants.<variant>`
4. `customModels.<selected-slug>`
5. `global`

Custom model notes:

- `customModels` creates selectable aliases like `openai/my-fast-codex`.
- The selected custom slug inherits instructions, runtime defaults, capabilities, limits, and supported variants from `targetModel`.
- The backend request still uses `targetModel` as the API model id.
- If `targetModel` is not present in the active catalog/provider, the plugin warns and skips that custom model instead of inventing metadata.
- `reasoningSummaryFormat` remains internal-only. Users control request summaries with `reasoningSummary`; internal catalog defaults may still populate `reasoning.summary` when no explicit config override is set.

### GPT-5.4 fast mode and long context

Expand Down Expand Up @@ -238,7 +264,7 @@ Flow:
2. The assistant interviews you (inspiration, tone, coding style, guardrails, examples).
3. The assistant calls `create-personality`.
4. A new profile is written under `personalities/<key>.md`.
5. Set the key in `codex-config.json` via `global.personality` or `perModel`.
5. Set the key in `codex-config.jsonc` via `global.personality` or `perModel`.

Advanced path:

Expand All @@ -247,7 +273,7 @@ Advanced path:

## Why `runtime.mode` exists (and no `identityMode`)

- `runtime.mode` is the canonical persisted mode setting in `codex-config.json`.
- `runtime.mode` is the canonical persisted mode setting in `codex-config.jsonc`.
- Identity behavior is derived from mode:
- `native` -> native identity
- `codex` -> codex identity
Expand All @@ -258,6 +284,8 @@ Advanced path:
### Config/mode overrides

- `OPENCODE_OPENAI_MULTI_CONFIG_PATH`: explicit config file path (absolute path recommended).
- `OPENCODE_OPENAI_MULTI_REASONING_SUMMARIES`: global reasoning-summary env override.
- `OPENCODE_OPENAI_MULTI_THINKING_SUMMARIES`: deprecated alias for `OPENCODE_OPENAI_MULTI_REASONING_SUMMARIES`.
- `OPENCODE_OPENAI_MULTI_MODE`: `native|codex`.
- `OPENCODE_OPENAI_MULTI_SPOOF_MODE`: advanced temporary identity override (`native|codex`).
- If `OPENCODE_OPENAI_MULTI_MODE` is set, runtime mode takes precedence.
Expand Down Expand Up @@ -305,7 +333,7 @@ Advanced path:

## Legacy keys

Legacy behavior keys are no longer parsed from `codex-config.json`.
Legacy behavior keys are no longer parsed from `codex-config.jsonc`.

- `personality`
- `customSettings` and all nested `customSettings.*`
Expand Down
2 changes: 1 addition & 1 deletion docs/development/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This plugin bridges OpenCode's OpenAI provider hooks to ChatGPT Codex backend en
## Runtime overview

1. OpenCode initializes plugin hooks (`index.ts`).
2. Config is resolved from `codex-config.json` + env overrides through `lib/config.ts` (stable barrel over `lib/config/types.ts`, `lib/config/file.ts`, and `lib/config/resolve.ts`).
2. Config is resolved from `codex-config.jsonc` + env overrides through `lib/config.ts` (stable barrel over `lib/config/types.ts`, `lib/config/file.ts`, and `lib/config/resolve.ts`). Commented legacy `codex-config.json` is still accepted as a compatibility fallback.
3. Auth loader selects a healthy account through `lib/storage.ts` + `lib/rotation.ts`, with storage normalization/migration helpers consolidated in `lib/storage/auth-state.ts`.
4. `CodexAuthPlugin` wires focused auth/request helpers under `lib/codex-native/` and routes Codex backend requests.
5. Failures (`429`, refresh/auth) trigger cooldown/disable semantics and retry orchestration (`lib/fetch-orchestrator.ts`).
Expand Down
Loading