oc-codex-multi-auth 6.1.2 → 6.1.3
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/README.md +21 -21
- package/config/README.md +32 -32
- package/config/opencode-legacy.json +5 -80
- package/config/opencode-modern.json +1 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +124 -11
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts/persistence.d.ts.map +1 -1
- package/dist/lib/accounts/persistence.js +1 -0
- package/dist/lib/accounts/persistence.js.map +1 -1
- package/dist/lib/accounts/state.d.ts +2 -2
- package/dist/lib/accounts/state.d.ts.map +1 -1
- package/dist/lib/accounts/state.js +87 -19
- package/dist/lib/accounts/state.js.map +1 -1
- package/dist/lib/auth/auth.d.ts +2 -2
- package/dist/lib/auth/auth.d.ts.map +1 -1
- package/dist/lib/auth/auth.js +9 -6
- package/dist/lib/auth/auth.js.map +1 -1
- package/dist/lib/auth/login-runner.d.ts +1 -0
- package/dist/lib/auth/login-runner.d.ts.map +1 -1
- package/dist/lib/auth/login-runner.js +3 -0
- package/dist/lib/auth/login-runner.js.map +1 -1
- package/dist/lib/auth/scopes.d.ts +5 -0
- package/dist/lib/auth/scopes.d.ts.map +1 -0
- package/dist/lib/auth/scopes.js +23 -0
- package/dist/lib/auth/scopes.js.map +1 -0
- package/dist/lib/auth/server.d.ts +6 -1
- package/dist/lib/auth/server.d.ts.map +1 -1
- package/dist/lib/auth/server.js +95 -51
- package/dist/lib/auth/server.js.map +1 -1
- package/dist/lib/oauth-constants.d.ts +5 -5
- package/dist/lib/oauth-constants.d.ts.map +1 -1
- package/dist/lib/oauth-constants.js +5 -5
- package/dist/lib/oauth-constants.js.map +1 -1
- package/dist/lib/prompts/codex.d.ts +2 -0
- package/dist/lib/prompts/codex.d.ts.map +1 -1
- package/dist/lib/prompts/codex.js +43 -11
- package/dist/lib/prompts/codex.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +2 -2
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +37 -25
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/helpers/model-map.d.ts +1 -0
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
- package/dist/lib/request/helpers/model-map.js +28 -25
- package/dist/lib/request/helpers/model-map.js.map +1 -1
- package/dist/lib/request/request-transformer.d.ts +1 -0
- package/dist/lib/request/request-transformer.d.ts.map +1 -1
- package/dist/lib/request/request-transformer.js +46 -22
- package/dist/lib/request/request-transformer.js.map +1 -1
- package/dist/lib/schemas.d.ts +8 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +3 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/storage/migrations.d.ts +4 -0
- package/dist/lib/storage/migrations.d.ts.map +1 -1
- package/dist/lib/storage/migrations.js +1 -0
- package/dist/lib/storage/migrations.js.map +1 -1
- package/dist/lib/types.d.ts +3 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,10 +14,10 @@ Use your ChatGPT Plus/Pro subscription inside OpenCode with OAuth login, GPT-5/C
|
|
|
14
14
|
|
|
15
15
|
## What This Project Does
|
|
16
16
|
|
|
17
|
-
- Adds an OpenCode plugin that authenticates with ChatGPT Plus/Pro through official OAuth
|
|
18
|
-
- Ships ready-to-use GPT-5.5
|
|
19
|
-
- Routes requests through a stateless Codex-compatible request pipeline with automatic token refresh
|
|
20
|
-
- Supports multi-account rotation, per-project account storage, and guided onboarding commands
|
|
17
|
+
- Adds an OpenCode plugin that authenticates with ChatGPT Plus/Pro through official OAuth
|
|
18
|
+
- Ships ready-to-use GPT-5.5 templates, explicit `gpt-5.5-medium` / `gpt-5.5-high` selectors, `gpt-5-codex`, and related GPT-5 families
|
|
19
|
+
- Routes requests through a stateless Codex-compatible request pipeline with automatic token refresh
|
|
20
|
+
- Supports multi-account rotation, per-project account storage, and guided onboarding commands
|
|
21
21
|
|
|
22
22
|
## Quick Start
|
|
23
23
|
|
|
@@ -25,12 +25,12 @@ Use your ChatGPT Plus/Pro subscription inside OpenCode with OAuth login, GPT-5/C
|
|
|
25
25
|
# 1. Install or refresh the plugin config
|
|
26
26
|
npx -y oc-codex-multi-auth@latest
|
|
27
27
|
|
|
28
|
-
# 2. Sign in with ChatGPT Plus/Pro
|
|
29
|
-
opencode auth login
|
|
30
|
-
|
|
31
|
-
# 3. Run a prompt in OpenCode
|
|
32
|
-
opencode run "Explain this repository" --model=openai/gpt-5.5-medium
|
|
33
|
-
```
|
|
28
|
+
# 2. Sign in with ChatGPT Plus/Pro
|
|
29
|
+
opencode auth login
|
|
30
|
+
|
|
31
|
+
# 3. Run a prompt in OpenCode
|
|
32
|
+
opencode run "Explain this repository" --model=openai/gpt-5.5-medium
|
|
33
|
+
```
|
|
34
34
|
|
|
35
35
|
What the installer does:
|
|
36
36
|
|
|
@@ -39,20 +39,20 @@ What the installer does:
|
|
|
39
39
|
- normalizes the plugin entry to `"oc-codex-multi-auth"`
|
|
40
40
|
- clears the cached plugin copy so OpenCode reinstalls the latest package
|
|
41
41
|
|
|
42
|
-
By default, the installer now writes a full catalog config that includes both:
|
|
43
|
-
- modern base model entries such as `gpt-5.5` for `--variant` workflows
|
|
44
|
-
- explicit preset entries such as `gpt-5.5-medium` / `gpt-5.5-high` so the shipped catalog is visible directly in model pickers
|
|
45
|
-
|
|
46
|
-
Tested live on OpenCode `1.14.22`: explicit GPT-5.5 model IDs like `openai/gpt-5.5-medium` and `openai/gpt-5.5-high` work directly. Some OpenCode releases still reject bare `openai/gpt-5.5` on the CLI even when the base config entry exists in the effective config.
|
|
42
|
+
By default, the installer now writes a full catalog config that includes both:
|
|
43
|
+
- modern base model entries such as `gpt-5.5` for `--variant` workflows
|
|
44
|
+
- explicit preset entries such as `gpt-5.5-medium` / `gpt-5.5-high` so the shipped catalog is visible directly in model pickers
|
|
45
|
+
|
|
46
|
+
Tested live on OpenCode `1.14.22`: explicit GPT-5.5 model IDs like `openai/gpt-5.5-medium` and `openai/gpt-5.5-high` work directly. Some OpenCode releases still reject bare `openai/gpt-5.5` on the CLI even when the base config entry exists in the effective config.
|
|
47
47
|
|
|
48
48
|
## Example Usage
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
# General GPT-5 workflow
|
|
52
|
-
opencode run "Summarize the failing test and suggest a fix" --model=openai/gpt-5.5-medium
|
|
53
|
-
|
|
54
|
-
# Codex-focused workflow
|
|
55
|
-
opencode run "Refactor the retry logic and update the tests" --model=openai/gpt-5-codex --variant=high
|
|
51
|
+
# General GPT-5 workflow
|
|
52
|
+
opencode run "Summarize the failing test and suggest a fix" --model=openai/gpt-5.5-medium
|
|
53
|
+
|
|
54
|
+
# Codex-focused workflow
|
|
55
|
+
opencode run "Refactor the retry logic and update the tests" --model=openai/gpt-5-codex --variant=high
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
## Usage Notice
|
|
@@ -86,7 +86,7 @@ OpenCode users often want the same GPT-5 and Codex model experience they use in
|
|
|
86
86
|
|
|
87
87
|
## Common Workflows
|
|
88
88
|
|
|
89
|
-
- Personal coding sessions in OpenCode using `gpt-5.5-medium`, `gpt-5.5-high`, or `gpt-5-codex`
|
|
89
|
+
- Personal coding sessions in OpenCode using `gpt-5.5-medium`, `gpt-5.5-high`, or `gpt-5-codex`
|
|
90
90
|
- Switching between personal and workspace-linked ChatGPT accounts
|
|
91
91
|
- Keeping separate account pools per project or monorepo
|
|
92
92
|
- Recovering from unsupported-model, auth, or rate-limit issues with guided commands
|
package/config/README.md
CHANGED
|
@@ -35,13 +35,13 @@ opencode --version
|
|
|
35
35
|
|
|
36
36
|
OpenCode v1.0.210+ added model `variants`, so one model entry can expose multiple reasoning levels. That keeps modern config much smaller while preserving the same effective presets.
|
|
37
37
|
|
|
38
|
-
Both templates include:
|
|
39
|
-
- GPT-5.5, GPT-5.5 Pro, GPT-5.4 Mini, GPT-5.4 Nano, GPT-5 Codex, GPT-5.1, GPT-5.1 Codex, GPT-5.1 Codex Max, GPT-5.1 Codex Mini
|
|
40
|
-
- Reasoning variants per model family
|
|
41
|
-
- `store: false` and `include: ["reasoning.encrypted_content"]`
|
|
42
|
-
- Context metadata (`gpt-5.5`/`gpt-5.5-pro`: 1,050,000; `gpt-5.4-mini`/`gpt-5.4-nano`/Codex models: 400,000; `gpt-5.1`: 272,000; all output: 128,000)
|
|
43
|
-
|
|
44
|
-
Use `opencode debug config` to verify that these template entries were merged into your effective config. On tested OpenCode `1.14.22`, `opencode models openai` exposes explicit GPT-5.5 entries such as `gpt-5.5-medium` / `gpt-5.5-high`, while bare `gpt-5.5` may still be omitted or rejected by provider lookup even when it exists in the effective config.
|
|
38
|
+
Both templates include:
|
|
39
|
+
- GPT-5.5, GPT-5.5 Pro, GPT-5.4 Mini, GPT-5.4 Nano, GPT-5 Codex, GPT-5.1, GPT-5.1 Codex, GPT-5.1 Codex Max, GPT-5.1 Codex Mini
|
|
40
|
+
- Reasoning variants per model family
|
|
41
|
+
- `store: false` and `include: ["reasoning.encrypted_content"]`
|
|
42
|
+
- Context metadata (`gpt-5.5`/`gpt-5.5-pro`: 1,050,000; `gpt-5.4-mini`/`gpt-5.4-nano`/Codex models: 400,000; `gpt-5.1`: 272,000; all output: 128,000)
|
|
43
|
+
|
|
44
|
+
Use `opencode debug config` to verify that these template entries were merged into your effective config. On tested OpenCode `1.14.22`, `opencode models openai` exposes explicit GPT-5.5 entries such as `gpt-5.5-medium` / `gpt-5.5-high`, while bare `gpt-5.5` may still be omitted or rejected by provider lookup even when it exists in the effective config.
|
|
45
45
|
|
|
46
46
|
If your OpenCode runtime supports global compaction tuning, you can also set:
|
|
47
47
|
- `model_context_window = 1050000`
|
|
@@ -55,18 +55,18 @@ If your workspace is entitled, you can add Spark model IDs manually.
|
|
|
55
55
|
|
|
56
56
|
## Usage examples
|
|
57
57
|
|
|
58
|
-
Recommended real-session selectors (tested on OpenCode `1.14.22`):
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
opencode run "task" --model=openai/gpt-5.5-medium
|
|
62
|
-
opencode run "task" --model=openai/gpt-5-codex --variant=high
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
If your OpenCode release already exposes bare GPT-5.5 base entries, the compact modern template also supports:
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
opencode run "task" --model=openai/gpt-5.5 --variant=medium
|
|
69
|
-
```
|
|
58
|
+
Recommended real-session selectors (tested on OpenCode `1.14.22`):
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
opencode run "task" --model=openai/gpt-5.5-medium
|
|
62
|
+
opencode run "task" --model=openai/gpt-5-codex --variant=high
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If your OpenCode release already exposes bare GPT-5.5 base entries, the compact modern template also supports:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
opencode run "task" --model=openai/gpt-5.5 --variant=medium
|
|
69
|
+
```
|
|
70
70
|
|
|
71
71
|
## Minimal config (advanced)
|
|
72
72
|
|
|
@@ -76,19 +76,19 @@ A barebones debug template is available at [`minimal-opencode.json`](./minimal-o
|
|
|
76
76
|
|
|
77
77
|
Current defaults are strict entitlement handling:
|
|
78
78
|
- `unsupportedCodexPolicy: "strict"` returns entitlement errors directly
|
|
79
|
-
- set `unsupportedCodexPolicy: "fallback"` (or `CODEX_AUTH_UNSUPPORTED_MODEL_POLICY=fallback`) to enable automatic fallback retries
|
|
80
|
-
- `fallbackToGpt52OnUnsupportedGpt53: true` keeps the legacy `gpt-5.3-codex -> gpt-5.2-codex` edge inside fallback mode
|
|
81
|
-
- `gpt-5.5 -> gpt-5.4` is included by default for
|
|
82
|
-
- `gpt-5.5-pro -> gpt-5.5
|
|
83
|
-
- `gpt-5.4-pro -> gpt-5.4` remains available for older manual configs
|
|
84
|
-
- `unsupportedCodexFallbackChain` lets you override fallback order per model
|
|
85
|
-
|
|
86
|
-
Default fallback chain (when policy is `fallback`):
|
|
87
|
-
- `gpt-5.5 -> gpt-5.4`
|
|
88
|
-
- `gpt-5.5-pro -> gpt-5.5
|
|
89
|
-
- `gpt-5.4-pro -> gpt-5.4` (if you manually select `gpt-5.4-pro`)
|
|
90
|
-
- `gpt-5.3-codex -> gpt-5-codex -> gpt-5.2-codex`
|
|
91
|
-
- `gpt-5.3-codex-spark -> gpt-5-codex -> gpt-5.3-codex -> gpt-5.2-codex` (only relevant if Spark IDs are added manually)
|
|
79
|
+
- set `unsupportedCodexPolicy: "fallback"` (or `CODEX_AUTH_UNSUPPORTED_MODEL_POLICY=fallback`) to enable automatic fallback retries
|
|
80
|
+
- `fallbackToGpt52OnUnsupportedGpt53: true` keeps the legacy `gpt-5.3-codex -> gpt-5.2-codex` edge inside fallback mode
|
|
81
|
+
- `gpt-5.5 -> gpt-5.4` is included by default for accounts/workspaces that do not yet expose GPT-5.5
|
|
82
|
+
- `gpt-5.5-pro -> gpt-5.5` is included by default in fallback mode
|
|
83
|
+
- `gpt-5.4-pro -> gpt-5.4` remains available for older manual configs
|
|
84
|
+
- `unsupportedCodexFallbackChain` lets you override fallback order per model
|
|
85
|
+
|
|
86
|
+
Default fallback chain (when policy is `fallback`):
|
|
87
|
+
- `gpt-5.5 -> gpt-5.4`
|
|
88
|
+
- `gpt-5.5-pro -> gpt-5.5`
|
|
89
|
+
- `gpt-5.4-pro -> gpt-5.4` (if you manually select `gpt-5.4-pro`)
|
|
90
|
+
- `gpt-5.3-codex -> gpt-5-codex -> gpt-5.2-codex`
|
|
91
|
+
- `gpt-5.3-codex-spark -> gpt-5-codex -> gpt-5.3-codex -> gpt-5.2-codex` (only relevant if Spark IDs are added manually)
|
|
92
92
|
- `gpt-5.2-codex -> gpt-5-codex`
|
|
93
93
|
- `gpt-5.1-codex -> gpt-5-codex`
|
|
94
94
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"models": {
|
|
18
18
|
"gpt-5.5-none": {
|
|
19
|
-
"name": "GPT 5.5
|
|
19
|
+
"name": "GPT 5.5 None (OAuth)",
|
|
20
20
|
"limit": {
|
|
21
21
|
"context": 1050000,
|
|
22
22
|
"output": 128000
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
"gpt-5.5-low": {
|
|
44
|
-
"name": "GPT 5.5
|
|
44
|
+
"name": "GPT 5.5 Low (OAuth)",
|
|
45
45
|
"limit": {
|
|
46
46
|
"context": 1050000,
|
|
47
47
|
"output": 128000
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
"gpt-5.5-medium": {
|
|
69
|
-
"name": "GPT 5.5
|
|
69
|
+
"name": "GPT 5.5 Medium (OAuth)",
|
|
70
70
|
"limit": {
|
|
71
71
|
"context": 1050000,
|
|
72
72
|
"output": 128000
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
}
|
|
92
92
|
},
|
|
93
93
|
"gpt-5.5-high": {
|
|
94
|
-
"name": "GPT 5.5
|
|
94
|
+
"name": "GPT 5.5 High (OAuth)",
|
|
95
95
|
"limit": {
|
|
96
96
|
"context": 1050000,
|
|
97
97
|
"output": 128000
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
}
|
|
117
117
|
},
|
|
118
118
|
"gpt-5.5-xhigh": {
|
|
119
|
-
"name": "GPT 5.5
|
|
119
|
+
"name": "GPT 5.5 Extra High (OAuth)",
|
|
120
120
|
"limit": {
|
|
121
121
|
"context": 1050000,
|
|
122
122
|
"output": 128000
|
|
@@ -265,81 +265,6 @@
|
|
|
265
265
|
"store": false
|
|
266
266
|
}
|
|
267
267
|
},
|
|
268
|
-
"gpt-5.5-pro-medium": {
|
|
269
|
-
"name": "GPT 5.5 Pro 20260423 Medium (OAuth)",
|
|
270
|
-
"limit": {
|
|
271
|
-
"context": 1050000,
|
|
272
|
-
"output": 128000
|
|
273
|
-
},
|
|
274
|
-
"modalities": {
|
|
275
|
-
"input": [
|
|
276
|
-
"text",
|
|
277
|
-
"image"
|
|
278
|
-
],
|
|
279
|
-
"output": [
|
|
280
|
-
"text"
|
|
281
|
-
]
|
|
282
|
-
},
|
|
283
|
-
"options": {
|
|
284
|
-
"reasoningEffort": "medium",
|
|
285
|
-
"reasoningSummary": "detailed",
|
|
286
|
-
"textVerbosity": "medium",
|
|
287
|
-
"include": [
|
|
288
|
-
"reasoning.encrypted_content"
|
|
289
|
-
],
|
|
290
|
-
"store": false
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
"gpt-5.5-pro-high": {
|
|
294
|
-
"name": "GPT 5.5 Pro 20260423 High (OAuth)",
|
|
295
|
-
"limit": {
|
|
296
|
-
"context": 1050000,
|
|
297
|
-
"output": 128000
|
|
298
|
-
},
|
|
299
|
-
"modalities": {
|
|
300
|
-
"input": [
|
|
301
|
-
"text",
|
|
302
|
-
"image"
|
|
303
|
-
],
|
|
304
|
-
"output": [
|
|
305
|
-
"text"
|
|
306
|
-
]
|
|
307
|
-
},
|
|
308
|
-
"options": {
|
|
309
|
-
"reasoningEffort": "high",
|
|
310
|
-
"reasoningSummary": "detailed",
|
|
311
|
-
"textVerbosity": "medium",
|
|
312
|
-
"include": [
|
|
313
|
-
"reasoning.encrypted_content"
|
|
314
|
-
],
|
|
315
|
-
"store": false
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
"gpt-5.5-pro-xhigh": {
|
|
319
|
-
"name": "GPT 5.5 Pro 20260423 Extra High (OAuth)",
|
|
320
|
-
"limit": {
|
|
321
|
-
"context": 1050000,
|
|
322
|
-
"output": 128000
|
|
323
|
-
},
|
|
324
|
-
"modalities": {
|
|
325
|
-
"input": [
|
|
326
|
-
"text",
|
|
327
|
-
"image"
|
|
328
|
-
],
|
|
329
|
-
"output": [
|
|
330
|
-
"text"
|
|
331
|
-
]
|
|
332
|
-
},
|
|
333
|
-
"options": {
|
|
334
|
-
"reasoningEffort": "xhigh",
|
|
335
|
-
"reasoningSummary": "detailed",
|
|
336
|
-
"textVerbosity": "medium",
|
|
337
|
-
"include": [
|
|
338
|
-
"reasoning.encrypted_content"
|
|
339
|
-
],
|
|
340
|
-
"store": false
|
|
341
|
-
}
|
|
342
|
-
},
|
|
343
268
|
"gpt-5.4-nano-none": {
|
|
344
269
|
"name": "GPT 5.4 Nano None (OAuth)",
|
|
345
270
|
"limit": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"models": {
|
|
18
18
|
"gpt-5.5": {
|
|
19
|
-
"name": "GPT 5.5
|
|
19
|
+
"name": "GPT 5.5 (OAuth)",
|
|
20
20
|
"limit": {
|
|
21
21
|
"context": 1050000,
|
|
22
22
|
"output": 128000
|
|
@@ -58,39 +58,6 @@
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
|
-
"gpt-5.5-pro": {
|
|
62
|
-
"name": "GPT 5.5 Pro 20260423 (OAuth)",
|
|
63
|
-
"limit": {
|
|
64
|
-
"context": 1050000,
|
|
65
|
-
"output": 128000
|
|
66
|
-
},
|
|
67
|
-
"modalities": {
|
|
68
|
-
"input": [
|
|
69
|
-
"text",
|
|
70
|
-
"image"
|
|
71
|
-
],
|
|
72
|
-
"output": [
|
|
73
|
-
"text"
|
|
74
|
-
]
|
|
75
|
-
},
|
|
76
|
-
"variants": {
|
|
77
|
-
"medium": {
|
|
78
|
-
"reasoningEffort": "medium",
|
|
79
|
-
"reasoningSummary": "detailed",
|
|
80
|
-
"textVerbosity": "medium"
|
|
81
|
-
},
|
|
82
|
-
"high": {
|
|
83
|
-
"reasoningEffort": "high",
|
|
84
|
-
"reasoningSummary": "detailed",
|
|
85
|
-
"textVerbosity": "medium"
|
|
86
|
-
},
|
|
87
|
-
"xhigh": {
|
|
88
|
-
"reasoningEffort": "xhigh",
|
|
89
|
-
"reasoningSummary": "detailed",
|
|
90
|
-
"textVerbosity": "medium"
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
61
|
"gpt-5.4-mini": {
|
|
95
62
|
"name": "GPT 5.4 Mini (OAuth)",
|
|
96
63
|
"limit": {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AA2L/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MA60G/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
* @repository https://github.com/ndycode/oc-codex-multi-auth
|
|
23
23
|
|
|
24
24
|
*/
|
|
25
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
26
|
+
import { homedir } from "node:os";
|
|
27
|
+
import { join } from "node:path";
|
|
25
28
|
import { createAuthorizationFlow, exchangeAuthorizationCode, parseAuthorizationInput, REDIRECT_URI, } from "./lib/auth/auth.js";
|
|
26
29
|
import { buildDeviceCodeInstructions, completeDeviceCodeSession, createDeviceCodeSession, } from "./lib/auth/device-code.js";
|
|
27
30
|
import { applyAccountSelectionFallbacks, persistResolvedAccountSelection, persistAccountPool, resolveAndPersistAccountSelection, resolveAccountSelection, } from "./lib/auth/login-runner.js";
|
|
@@ -38,7 +41,7 @@ import { AccountManager, extractAccountEmail, extractAccountId, formatAccountLab
|
|
|
38
41
|
import { getStoragePath, loadAccounts, saveAccounts, clearAccounts, setStoragePath, loadFlaggedAccounts, saveFlaggedAccounts, withFlaggedAccountStorageTransaction, clearFlaggedAccounts, getWorkspaceIdentityKey, StorageError, formatStorageErrorHint, } from "./lib/storage.js";
|
|
39
42
|
import { createCodexHeaders, extractRequestUrl, handleErrorResponse, handleSuccessResponse, isDeactivatedWorkspaceError, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, refreshAndUpdateToken, rewriteUrlForCodex, shouldRefreshToken, transformRequestForCodex, } from "./lib/request/fetch-helpers.js";
|
|
40
43
|
import { createDeactivatedWorkspaceError, DEACTIVATED_WORKSPACE_ERROR_CODE, isDeactivatedWorkspaceErrorMessage, } from "./lib/error-sentinels.js";
|
|
41
|
-
import { applyFastSessionDefaults } from "./lib/request/request-transformer.js";
|
|
44
|
+
import { applyFastSessionDefaults, upsertBackendModelIdentityMessage, } from "./lib/request/request-transformer.js";
|
|
42
45
|
import { getRateLimitBackoff, RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS, resetRateLimitBackoff, } from "./lib/request/rate-limit-backoff.js";
|
|
43
46
|
import { isEmptyResponse } from "./lib/request/response-handler.js";
|
|
44
47
|
import { getCircuitBreaker } from "./lib/circuit-breaker.js";
|
|
@@ -68,7 +71,6 @@ import { createToolRegistry, } from "./lib/tools/index.js";
|
|
|
68
71
|
* }
|
|
69
72
|
* ```
|
|
70
73
|
*/
|
|
71
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
72
74
|
export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
73
75
|
initLogger(client);
|
|
74
76
|
let cachedAccountManager = null;
|
|
@@ -252,7 +254,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
252
254
|
openBrowserUrl(url);
|
|
253
255
|
if (!serverInfo || !serverInfo.ready) {
|
|
254
256
|
serverInfo?.close();
|
|
255
|
-
const message = `OAuth callback server failed to start on
|
|
257
|
+
const message = `OAuth callback server failed to start on localhost loopback port 1455. ` +
|
|
256
258
|
`Retry with "${AUTH_LABELS.OAUTH_DEVICE_CODE}" or "${AUTH_LABELS.OAUTH_MANUAL}".`;
|
|
257
259
|
logWarn(`\n[${PLUGIN_NAME}] ${message}\n`);
|
|
258
260
|
return {
|
|
@@ -296,6 +298,67 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
296
298
|
const raw = Number.isFinite(rawCandidate) ? rawCandidate : 0;
|
|
297
299
|
return Math.max(0, Math.min(raw, total - 1));
|
|
298
300
|
};
|
|
301
|
+
const backfillHostOpenAIAuthFromPool = async () => {
|
|
302
|
+
const authPath = join(homedir(), ".local", "share", "opencode", "auth.json");
|
|
303
|
+
let authStore = {};
|
|
304
|
+
try {
|
|
305
|
+
const authRaw = await readFile(authPath, "utf8");
|
|
306
|
+
const parsed = JSON.parse(authRaw);
|
|
307
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
308
|
+
authStore = parsed;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
const errorCode = error?.code;
|
|
313
|
+
if (errorCode !== "ENOENT") {
|
|
314
|
+
logWarn(`[${PLUGIN_NAME}] Failed to read host auth store for OpenAI backfill: ${error?.message ?? String(error)}`);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const existing = authStore[PROVIDER_ID];
|
|
319
|
+
const hasExistingOAuth = existing?.type === "oauth" &&
|
|
320
|
+
typeof existing.access === "string" &&
|
|
321
|
+
existing.access.trim().length > 0 &&
|
|
322
|
+
typeof existing.refresh === "string" &&
|
|
323
|
+
existing.refresh.trim().length > 0 &&
|
|
324
|
+
typeof existing.expires === "number" &&
|
|
325
|
+
Number.isFinite(existing.expires);
|
|
326
|
+
if (hasExistingOAuth) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const storage = await loadAccounts();
|
|
330
|
+
if (!storage || storage.accounts.length === 0) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const hasUsableTokens = (account) => typeof account?.accessToken === "string" &&
|
|
334
|
+
account.accessToken.trim().length > 0 &&
|
|
335
|
+
typeof account?.refreshToken === "string" &&
|
|
336
|
+
account.refreshToken.trim().length > 0 &&
|
|
337
|
+
typeof account?.expiresAt === "number" &&
|
|
338
|
+
Number.isFinite(account.expiresAt);
|
|
339
|
+
const activeIndex = resolveActiveIndex(storage, "codex");
|
|
340
|
+
const activeAccount = storage.accounts[activeIndex];
|
|
341
|
+
const candidate = hasUsableTokens(activeAccount)
|
|
342
|
+
? activeAccount
|
|
343
|
+
: storage.accounts.find(hasUsableTokens);
|
|
344
|
+
if (!candidate || !hasUsableTokens(candidate)) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
authStore[PROVIDER_ID] = {
|
|
348
|
+
type: "oauth",
|
|
349
|
+
access: candidate.accessToken,
|
|
350
|
+
refresh: candidate.refreshToken,
|
|
351
|
+
expires: candidate.expiresAt,
|
|
352
|
+
};
|
|
353
|
+
try {
|
|
354
|
+
await mkdir(join(homedir(), ".local", "share", "opencode"), { recursive: true });
|
|
355
|
+
await writeFile(authPath, `${JSON.stringify(authStore, null, 2)}\n`, "utf8");
|
|
356
|
+
logInfo(`[${PLUGIN_NAME}] Restored missing host OpenAI auth entry from stored account pool`);
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
logWarn(`[${PLUGIN_NAME}] Failed to backfill host OpenAI auth entry: ${error?.message ?? String(error)}`);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
299
362
|
const hydrateEmails = async (storage) => {
|
|
300
363
|
if (!storage)
|
|
301
364
|
return storage;
|
|
@@ -339,6 +402,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
339
402
|
account.expiresAt = refreshed.expires;
|
|
340
403
|
changed = true;
|
|
341
404
|
}
|
|
405
|
+
if (refreshed.scope && refreshed.scope !== account.oauthScope) {
|
|
406
|
+
account.oauthScope = refreshed.scope;
|
|
407
|
+
changed = true;
|
|
408
|
+
}
|
|
342
409
|
if (refreshed.refresh && refreshed.refresh !== account.refreshToken) {
|
|
343
410
|
account.refreshToken = refreshed.refresh;
|
|
344
411
|
changed = true;
|
|
@@ -844,6 +911,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
844
911
|
invalidateAccountManagerCache,
|
|
845
912
|
upsertFlaggedAccountRecord,
|
|
846
913
|
};
|
|
914
|
+
const startupPluginConfig = loadPluginConfig();
|
|
915
|
+
const startupPerProjectAccounts = getPerProjectAccounts(startupPluginConfig);
|
|
916
|
+
setStoragePath(startupPerProjectAccounts ? process.cwd() : null);
|
|
917
|
+
await backfillHostOpenAIAuthFromPool();
|
|
847
918
|
return {
|
|
848
919
|
event: eventHandler,
|
|
849
920
|
auth: {
|
|
@@ -900,9 +971,17 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
900
971
|
if (needsPersist) {
|
|
901
972
|
await accountManager.saveToDisk();
|
|
902
973
|
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
974
|
+
const accountCount = accountManager.getAccountCount();
|
|
975
|
+
const storagePath = getStoragePath();
|
|
976
|
+
logDebug(`[${PLUGIN_NAME}] Loader auth bootstrap`, {
|
|
977
|
+
authType: auth.type,
|
|
978
|
+
authHasRefresh: !!authFallback?.refresh,
|
|
979
|
+
authHasMultiAccount: !!authWithMulti?.multiAccount,
|
|
980
|
+
accountCount,
|
|
981
|
+
storagePath,
|
|
982
|
+
});
|
|
983
|
+
if (accountCount === 0) {
|
|
984
|
+
logWarn(`[${PLUGIN_NAME}] No Codex accounts available (run opencode auth login)`);
|
|
906
985
|
}
|
|
907
986
|
// Extract user configuration (global + per-model options)
|
|
908
987
|
const providerConfig = provider;
|
|
@@ -1500,15 +1579,31 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1500
1579
|
fallbackFrom = previousModel;
|
|
1501
1580
|
fallbackTo = model;
|
|
1502
1581
|
fallbackReason = "fallback-unsupported-model-entitlement";
|
|
1582
|
+
const fallbackInstructions = await getCodexInstructions(model);
|
|
1503
1583
|
if (transformedBody && typeof transformedBody === "object") {
|
|
1504
|
-
transformedBody = {
|
|
1584
|
+
transformedBody = {
|
|
1585
|
+
...transformedBody,
|
|
1586
|
+
model,
|
|
1587
|
+
instructions: fallbackInstructions,
|
|
1588
|
+
input: upsertBackendModelIdentityMessage(transformedBody.input, model),
|
|
1589
|
+
};
|
|
1505
1590
|
}
|
|
1506
1591
|
else {
|
|
1507
|
-
let fallbackBody = {
|
|
1592
|
+
let fallbackBody = {
|
|
1593
|
+
model,
|
|
1594
|
+
instructions: fallbackInstructions,
|
|
1595
|
+
};
|
|
1508
1596
|
if (requestInit?.body && typeof requestInit.body === "string") {
|
|
1509
1597
|
try {
|
|
1510
1598
|
const parsed = JSON.parse(requestInit.body);
|
|
1511
|
-
fallbackBody = {
|
|
1599
|
+
fallbackBody = {
|
|
1600
|
+
...parsed,
|
|
1601
|
+
model,
|
|
1602
|
+
instructions: fallbackInstructions,
|
|
1603
|
+
};
|
|
1604
|
+
if (Array.isArray(fallbackBody.input)) {
|
|
1605
|
+
fallbackBody.input = upsertBackendModelIdentityMessage(fallbackBody.input, model);
|
|
1606
|
+
}
|
|
1512
1607
|
}
|
|
1513
1608
|
catch {
|
|
1514
1609
|
// Keep minimal fallback body if parsing fails.
|
|
@@ -1715,14 +1810,28 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1715
1810
|
continue;
|
|
1716
1811
|
}
|
|
1717
1812
|
const waitLabel = waitMs > 0 ? formatWaitTime(waitMs) : "a bit";
|
|
1813
|
+
const wasEntitlementExhaustion = runtimeMetrics.lastErrorCategory === "unsupported-model";
|
|
1814
|
+
const entitlementModel = typeof runtimeMetrics.lastError === "string"
|
|
1815
|
+
? runtimeMetrics.lastError.replace(/^Unsupported model.*?:\s*/i, "").trim()
|
|
1816
|
+
: "";
|
|
1817
|
+
const entitlementDetail = entitlementModel.length > 0
|
|
1818
|
+
? ` The backend rejected '${entitlementModel}' as not entitled for Codex OAuth on every pooled account.`
|
|
1819
|
+
: "";
|
|
1718
1820
|
const message = count === 0
|
|
1719
1821
|
? "No Codex accounts configured. Run `opencode auth login`."
|
|
1720
1822
|
: waitMs > 0
|
|
1721
1823
|
? `All ${count} account(s) are rate-limited. Try again in ${waitLabel} or add another account with \`opencode auth login\`.`
|
|
1722
|
-
:
|
|
1824
|
+
: wasEntitlementExhaustion
|
|
1825
|
+
? `All ${count} account(s) returned 'model not supported' for the requested model.${entitlementDetail} If this is a GPT-5.5 request during the rollout period, set \`unsupportedCodexPolicy: "fallback"\` (or \`CODEX_AUTH_UNSUPPORTED_MODEL_POLICY=fallback\`) to auto-fallback to gpt-5.4. See \`codex-health\` for per-account details.`
|
|
1826
|
+
: `All ${count} account(s) failed (server errors or auth issues). Check account health with \`codex-health\`.`;
|
|
1723
1827
|
runtimeMetrics.failedRequests++;
|
|
1724
1828
|
runtimeMetrics.lastError = message;
|
|
1725
|
-
runtimeMetrics.lastErrorCategory =
|
|
1829
|
+
runtimeMetrics.lastErrorCategory =
|
|
1830
|
+
waitMs > 0
|
|
1831
|
+
? "rate-limit"
|
|
1832
|
+
: wasEntitlementExhaustion
|
|
1833
|
+
? "unsupported-model"
|
|
1834
|
+
: "account-failure";
|
|
1726
1835
|
return new Response(JSON.stringify({ error: { message } }), {
|
|
1727
1836
|
status: waitMs > 0 ? 429 : 503,
|
|
1728
1837
|
headers: {
|
|
@@ -2127,6 +2236,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2127
2236
|
account.expiresAt = refreshResult.expires;
|
|
2128
2237
|
storageChanged = true;
|
|
2129
2238
|
}
|
|
2239
|
+
if (refreshResult.scope && refreshResult.scope !== account.oauthScope) {
|
|
2240
|
+
account.oauthScope = refreshResult.scope;
|
|
2241
|
+
storageChanged = true;
|
|
2242
|
+
}
|
|
2130
2243
|
const hydratedEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
|
|
2131
2244
|
if (hydratedEmail && hydratedEmail !== account.email) {
|
|
2132
2245
|
account.email = hydratedEmail;
|