@bitkyc08/opencodex 0.2.2 → 1.9.1
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 +3 -1
- package/gui/dist/assets/{index-Dt5t57MW.js → index-CDhJ0DI7.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +3 -1
- package/src/abort.ts +29 -0
- package/src/adapters/anthropic.ts +15 -5
- package/src/adapters/google.ts +27 -11
- package/src/adapters/openai-chat.ts +38 -12
- package/src/adapters/openai-responses.ts +18 -1
- package/src/bridge.ts +155 -17
- package/src/cli.ts +0 -0
- package/src/codex-catalog.ts +102 -11
- package/src/codex-inject.ts +47 -4
- package/src/config.ts +5 -0
- package/src/debug.ts +10 -0
- package/src/errors.ts +47 -0
- package/src/generated/jawcode-model-metadata.ts +69 -0
- package/src/init.ts +5 -32
- package/src/oauth/index.ts +19 -33
- package/src/oauth/key-providers.ts +2 -63
- package/src/providers/derive.ts +163 -0
- package/src/providers/registry.ts +140 -0
- package/src/responses/parser.ts +6 -1
- package/src/server.ts +182 -9
- package/src/types.ts +6 -0
- package/src/vision/describe.ts +6 -1
- package/src/vision/index.ts +2 -1
- package/src/web-search/executor.ts +6 -1
- package/src/web-search/loop.ts +9 -3
- package/src/ws-bridge.ts +359 -0
package/src/codex-catalog.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { atomicWriteFile } from "./config";
|
|
4
|
+
import { atomicWriteFile, websocketsEnabled } from "./config";
|
|
5
5
|
import { CODEX_CONFIG_PATH, CODEX_MODELS_CACHE_PATH, DEFAULT_CATALOG_PATH, readRootTomlString, resolveCodexConfigPath } from "./codex-paths";
|
|
6
6
|
import { DEFAULT_MODEL_CACHE_TTL_MS, getFreshCached, getStaleCached, setCached } from "./model-cache";
|
|
7
7
|
import { buildModelsRequest, resolveModelsAuthToken } from "./oauth/index";
|
|
8
8
|
import type { OcxConfig, OcxProviderConfig } from "./types";
|
|
9
|
+
import { getJawcodeModelMetadata, getJawcodeModelMetadataCaseInsensitive, listJawcodeModelMetadata, resolveJawcodeProvider } from "./generated/jawcode-model-metadata";
|
|
10
|
+
import { shouldCaseFoldMetadataModelId } from "./providers/derive";
|
|
9
11
|
|
|
10
12
|
const OCX_DIR = join(homedir(), ".opencodex");
|
|
11
13
|
const CATALOG_BACKUP_PATH = join(OCX_DIR, "catalog-backup.json");
|
|
@@ -33,6 +35,7 @@ export function nativeOpenAiSlugs(): string[] {
|
|
|
33
35
|
|
|
34
36
|
export interface CatalogModel { id: string; provider: string; owned_by?: string; }
|
|
35
37
|
type RawEntry = Record<string, unknown>;
|
|
38
|
+
const JAWCODE_CATALOG_AUGMENT_PROVIDERS = new Set(["opencode-go"]);
|
|
36
39
|
|
|
37
40
|
/** Resolve the `model_catalog_json` path from Codex config.toml, else the default. */
|
|
38
41
|
export function readCodexCatalogPath(): string {
|
|
@@ -55,11 +58,13 @@ function readCatalog(path: string): { models?: RawEntry[]; [k: string]: unknown
|
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
function normalizeServiceTiers(entry: RawEntry): RawEntry {
|
|
58
|
-
|
|
61
|
+
// Codex stores the user-facing config spelling as "fast", but the catalog/request
|
|
62
|
+
// service tier id is "priority" in current codex-rs. Keep legacy catalogs working.
|
|
63
|
+
if (entry.service_tier === "fast") entry.service_tier = "priority";
|
|
59
64
|
if (Array.isArray(entry.service_tiers)) {
|
|
60
65
|
entry.service_tiers = entry.service_tiers.map(tier => {
|
|
61
|
-
if (tier && typeof tier === "object" && "id" in tier && tier.id === "
|
|
62
|
-
return { ...tier, id: "
|
|
66
|
+
if (tier && typeof tier === "object" && "id" in tier && tier.id === "fast") {
|
|
67
|
+
return { ...tier, id: "priority" };
|
|
63
68
|
}
|
|
64
69
|
return tier;
|
|
65
70
|
});
|
|
@@ -67,6 +72,54 @@ function normalizeServiceTiers(entry: RawEntry): RawEntry {
|
|
|
67
72
|
return entry;
|
|
68
73
|
}
|
|
69
74
|
|
|
75
|
+
function ensureAutoCompactTokenLimit(entry: RawEntry): RawEntry {
|
|
76
|
+
if (
|
|
77
|
+
typeof entry.context_window === "number"
|
|
78
|
+
&& entry.context_window > 0
|
|
79
|
+
&& typeof entry.auto_compact_token_limit !== "number"
|
|
80
|
+
) {
|
|
81
|
+
entry.auto_compact_token_limit = Math.floor(entry.context_window * 0.9);
|
|
82
|
+
}
|
|
83
|
+
return entry;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function normalizeRoutedCatalogEntry(entry: RawEntry): RawEntry {
|
|
87
|
+
delete entry.model_messages;
|
|
88
|
+
delete entry.tool_mode;
|
|
89
|
+
delete entry.multi_agent_version;
|
|
90
|
+
delete entry.use_responses_lite;
|
|
91
|
+
delete entry.supports_websockets;
|
|
92
|
+
delete entry.additional_speed_tiers;
|
|
93
|
+
delete entry.service_tier;
|
|
94
|
+
delete entry.service_tiers;
|
|
95
|
+
delete entry.default_service_tier;
|
|
96
|
+
// Routed providers use opencodex sidecars and client-executed tool discovery. The sidecar
|
|
97
|
+
// runs through native gpt-5.4-mini, so image search is available and verbalized for text-only models.
|
|
98
|
+
entry.web_search_tool_type = "text_and_image";
|
|
99
|
+
entry.supports_search_tool = true;
|
|
100
|
+
return ensureAutoCompactTokenLimit(entry);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function applyJawcodeCatalogMetadata(entry: RawEntry, slug: string): void {
|
|
104
|
+
const slash = slug.indexOf("/");
|
|
105
|
+
if (slash < 0) return;
|
|
106
|
+
const provider = slug.slice(0, slash);
|
|
107
|
+
const modelId = slug.slice(slash + 1);
|
|
108
|
+
const jawcodeProvider = resolveJawcodeProvider(provider);
|
|
109
|
+
if (!jawcodeProvider) return;
|
|
110
|
+
const meta = getJawcodeModelMetadata(jawcodeProvider, modelId)
|
|
111
|
+
?? (shouldCaseFoldMetadataModelId(provider) ? getJawcodeModelMetadataCaseInsensitive(jawcodeProvider, modelId) : undefined);
|
|
112
|
+
if (!meta) return;
|
|
113
|
+
if (typeof meta.contextWindow === "number" && meta.contextWindow > 0) {
|
|
114
|
+
entry.context_window = meta.contextWindow;
|
|
115
|
+
entry.max_context_window = meta.contextWindow;
|
|
116
|
+
entry.auto_compact_token_limit = Math.floor(meta.contextWindow * 0.9);
|
|
117
|
+
}
|
|
118
|
+
if (Array.isArray(meta.input) && meta.input.length > 0) {
|
|
119
|
+
entry.input_modalities = meta.input;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
70
123
|
function loadCatalogForSync(path: string): { models?: RawEntry[]; [k: string]: unknown } | null {
|
|
71
124
|
const catalog = readCatalog(path);
|
|
72
125
|
if (catalog) return catalog;
|
|
@@ -130,17 +183,22 @@ function deriveEntry(template: RawEntry | null, slug: string, desc: string, prio
|
|
|
130
183
|
);
|
|
131
184
|
e.supported_reasoning_levels = ROUTED_REASONING_LEVELS.map(l => byEffort.get(l.effort) ?? { ...l });
|
|
132
185
|
e.default_reasoning_level = "medium";
|
|
186
|
+
normalizeRoutedCatalogEntry(e);
|
|
187
|
+
applyJawcodeCatalogMetadata(e, slug);
|
|
133
188
|
}
|
|
134
|
-
return normalizeServiceTiers(e);
|
|
189
|
+
return ensureAutoCompactTokenLimit(normalizeServiceTiers(e));
|
|
135
190
|
}
|
|
136
191
|
// Fallback when no template is available (best-effort; strict parser may need more).
|
|
137
|
-
|
|
192
|
+
const entry: RawEntry = {
|
|
138
193
|
slug, display_name: slug, description: desc,
|
|
139
194
|
default_reasoning_level: "medium",
|
|
140
195
|
supported_reasoning_levels: ROUTED_REASONING_LEVELS.map(l => ({ ...l })),
|
|
141
196
|
shell_type: "shell_command", visibility: "list", supported_in_api: true,
|
|
142
197
|
priority, base_instructions: "You are a helpful coding assistant.",
|
|
143
|
-
|
|
198
|
+
...(slug.includes("/") ? { web_search_tool_type: "text_and_image", supports_search_tool: true } : {}),
|
|
199
|
+
};
|
|
200
|
+
applyJawcodeCatalogMetadata(entry, slug);
|
|
201
|
+
return ensureAutoCompactTokenLimit(normalizeServiceTiers(entry));
|
|
144
202
|
}
|
|
145
203
|
|
|
146
204
|
/**
|
|
@@ -148,7 +206,7 @@ function deriveEntry(template: RawEntry | null, slug: string, desc: string, prio
|
|
|
148
206
|
* catalog sync and the proxy `/v1/models?client_version` branch.
|
|
149
207
|
* Native gpt slugs stay bare; routed models are namespaced `<provider>/<model>`.
|
|
150
208
|
*/
|
|
151
|
-
export function buildCatalogEntries(template: RawEntry | null, gptSlugs: string[], goModels: CatalogModel[], featured?: string[]): RawEntry[] {
|
|
209
|
+
export function buildCatalogEntries(template: RawEntry | null, gptSlugs: string[], goModels: CatalogModel[], featured?: string[], wsEnabled = true): RawEntry[] {
|
|
152
210
|
// Codex's models-manager sorts by `priority` ASC and advertises the first 5 picker-visible
|
|
153
211
|
// models to spawn_agent (sort_by_key(priority) + MAX_MODEL_OVERRIDES_IN_SPAWN_AGENT=5). Catalog
|
|
154
212
|
// ARRAY order is discarded — so "featuring" a model = giving it the LOWEST priority (0..N-1) so
|
|
@@ -166,6 +224,13 @@ export function buildCatalogEntries(template: RawEntry | null, gptSlugs: string[
|
|
|
166
224
|
if (rank.has(slug)) e.priority = rank.get(slug)!;
|
|
167
225
|
out.push(e);
|
|
168
226
|
}
|
|
227
|
+
// Central capability override (phase 120.4): the advertised flag must match the implemented WS
|
|
228
|
+
// endpoint. Overrides both the routed strip (normalizeRoutedCatalogEntry) and any native template
|
|
229
|
+
// leak (deriveEntry clones the template as-is for native slugs).
|
|
230
|
+
for (const entry of out) {
|
|
231
|
+
if (wsEnabled) entry.supports_websockets = true;
|
|
232
|
+
else delete entry.supports_websockets;
|
|
233
|
+
}
|
|
169
234
|
return out;
|
|
170
235
|
}
|
|
171
236
|
|
|
@@ -233,11 +298,28 @@ export async function gatherRoutedModels(config: OcxConfig): Promise<CatalogMode
|
|
|
233
298
|
const lists = await Promise.all(
|
|
234
299
|
Object.entries(config.providers).map(([name, prov]) => fetchProviderModels(name, prov, ttlMs)),
|
|
235
300
|
);
|
|
236
|
-
const all = lists.flat();
|
|
301
|
+
const all = augmentRoutedModelsWithJawcodeMetadata(lists.flat(), Object.keys(config.providers));
|
|
237
302
|
all.sort((a, b) => (a.provider === b.provider ? a.id.localeCompare(b.id) : a.provider.localeCompare(b.provider)));
|
|
238
303
|
return all;
|
|
239
304
|
}
|
|
240
305
|
|
|
306
|
+
export function augmentRoutedModelsWithJawcodeMetadata(models: CatalogModel[], providerNames: string[]): CatalogModel[] {
|
|
307
|
+
const out = [...models];
|
|
308
|
+
const seen = new Set(out.map(m => `${m.provider}/${m.id}`));
|
|
309
|
+
for (const provider of providerNames) {
|
|
310
|
+
if (!JAWCODE_CATALOG_AUGMENT_PROVIDERS.has(provider)) continue;
|
|
311
|
+
const jawcodeProvider = resolveJawcodeProvider(provider);
|
|
312
|
+
if (!jawcodeProvider) continue;
|
|
313
|
+
for (const meta of listJawcodeModelMetadata(jawcodeProvider)) {
|
|
314
|
+
const key = `${provider}/${meta.id}`;
|
|
315
|
+
if (seen.has(key)) continue;
|
|
316
|
+
seen.add(key);
|
|
317
|
+
out.push({ provider, id: meta.id, owned_by: provider });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return out;
|
|
321
|
+
}
|
|
322
|
+
|
|
241
323
|
/**
|
|
242
324
|
* Reorder routed models so the configured subagent picks come FIRST (in the chosen order).
|
|
243
325
|
* Codex's spawn_agent advertises only the first 5 routed catalog entries, so putting the chosen
|
|
@@ -283,7 +365,7 @@ export async function syncCatalogModels(config: OcxConfig): Promise<{ added: num
|
|
|
283
365
|
const featured = config.subagentModels ?? [];
|
|
284
366
|
const rank = new Map(featured.map((slug, i) => [slug, i] as const));
|
|
285
367
|
const orderedGoModels = orderForSubagents(enabledGo, featured); // stable tie-break among equal priorities
|
|
286
|
-
const goEntries = buildCatalogEntries(template ? JSON.parse(JSON.stringify(template)) : null, [], orderedGoModels, featured);
|
|
368
|
+
const goEntries = buildCatalogEntries(template ? JSON.parse(JSON.stringify(template)) : null, [], orderedGoModels, featured, websocketsEnabled(config));
|
|
287
369
|
// Keep genuine native entries (gpt-*, codex-*) with their real per-model fields, but drop bare
|
|
288
370
|
// duplicates of routed models (replaced by namespaced entries) + any prior "/" entries. Re-derive
|
|
289
371
|
// each native's priority from the pristine baseline so featuring a native is reversible.
|
|
@@ -296,7 +378,16 @@ export async function syncCatalogModels(config: OcxConfig): Promise<{ added: num
|
|
|
296
378
|
const priority = rank.has(slug) ? rank.get(slug)! : (baseline.get(slug) ?? (m.priority as number));
|
|
297
379
|
return normalizeServiceTiers({ ...m, priority });
|
|
298
380
|
});
|
|
299
|
-
catalog
|
|
381
|
+
// Central WS capability override on the FINAL on-disk catalog (the file Codex reads). Applies to
|
|
382
|
+
// native AND routed so the advertised flag matches the implemented endpoint (phase 120.4) and a
|
|
383
|
+
// native template can never leak supports_websockets while the flag is off.
|
|
384
|
+
const wsEnabled = websocketsEnabled(config);
|
|
385
|
+
catalog.models = [...native, ...goEntries].map(m => {
|
|
386
|
+
const e = ensureAutoCompactTokenLimit(normalizeServiceTiers(m));
|
|
387
|
+
if (wsEnabled) e.supports_websockets = true;
|
|
388
|
+
else delete e.supports_websockets;
|
|
389
|
+
return e;
|
|
390
|
+
});
|
|
300
391
|
|
|
301
392
|
try {
|
|
302
393
|
if (!existsSync(OCX_DIR)) mkdirSync(OCX_DIR, { recursive: true });
|
package/src/codex-inject.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { atomicWriteFile } from "./config";
|
|
2
|
+
import { atomicWriteFile, websocketsEnabled } from "./config";
|
|
3
3
|
import { restoreCodexCatalog } from "./codex-catalog";
|
|
4
4
|
import { CODEX_CONFIG_PATH, CODEX_PROFILE_PATH, DEFAULT_CATALOG_PATH, parseTomlString, readRootTomlString, tomlString } from "./codex-paths";
|
|
5
5
|
import type { OcxConfig } from "./types";
|
|
@@ -14,7 +14,7 @@ const OCX_SECTION_MARKER = "# Auto-injected by opencodex";
|
|
|
14
14
|
* whatever `[table]` happened to be open last (e.g. `[plugins."chrome@openai-bundled"]`), so Codex
|
|
15
15
|
* never saw a global model_provider and silently fell back to the `openai` (ChatGPT) provider.
|
|
16
16
|
*/
|
|
17
|
-
function buildProviderTableBlock(port: number): string {
|
|
17
|
+
export function buildProviderTableBlock(port: number, supportsWebsockets = true): string {
|
|
18
18
|
const lines = [
|
|
19
19
|
"",
|
|
20
20
|
OCX_SECTION_MARKER,
|
|
@@ -22,7 +22,9 @@ function buildProviderTableBlock(port: number): string {
|
|
|
22
22
|
'name = "OpenCodex Proxy"',
|
|
23
23
|
`base_url = "http://localhost:${port}/v1"`,
|
|
24
24
|
'wire_api = "responses"',
|
|
25
|
+
"requires_openai_auth = true",
|
|
25
26
|
];
|
|
27
|
+
if (supportsWebsockets) lines.push("supports_websockets = true");
|
|
26
28
|
return lines.join("\n") + "\n";
|
|
27
29
|
}
|
|
28
30
|
|
|
@@ -48,6 +50,17 @@ function stripExistingModelProvider(content: string): string {
|
|
|
48
50
|
return out.join("\n");
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
function stripRootContextWindowOverrides(content: string): string {
|
|
54
|
+
const lines = content.split("\n");
|
|
55
|
+
const firstTable = lines.findIndex(l => /^\s*\[/.test(l));
|
|
56
|
+
return lines
|
|
57
|
+
.filter((line, i) => {
|
|
58
|
+
const isRoot = firstTable === -1 || i < firstTable;
|
|
59
|
+
return !isRoot || !/^\s*model_(?:context_window|auto_compact_token_limit)\s*=/.test(line);
|
|
60
|
+
})
|
|
61
|
+
.join("\n");
|
|
62
|
+
}
|
|
63
|
+
|
|
51
64
|
/**
|
|
52
65
|
* Insert `model_provider = "opencodex"` at the document ROOT — immediately before the first table
|
|
53
66
|
* header (TOML root keys must precede all tables). If there are no tables, append it to the root body.
|
|
@@ -108,6 +121,28 @@ function normalizeServiceTier(content: string): string {
|
|
|
108
121
|
return content.replace(/^(\s*service_tier\s*=\s*)["']priority["']\s*$/gm, '$1"fast"');
|
|
109
122
|
}
|
|
110
123
|
|
|
124
|
+
function ensureFastModeFeature(content: string): string {
|
|
125
|
+
const lines = content.split("\n");
|
|
126
|
+
const featuresStart = lines.findIndex(line => line.trim() === "[features]");
|
|
127
|
+
if (featuresStart === -1) {
|
|
128
|
+
return content.trimEnd() + "\n\n[features]\nfast_mode = true\n";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const nextTable = lines.findIndex((line, index) => index > featuresStart && /^\s*\[/.test(line));
|
|
132
|
+
const featuresEnd = nextTable === -1 ? lines.length : nextTable;
|
|
133
|
+
for (let i = featuresStart + 1; i < featuresEnd; i++) {
|
|
134
|
+
if (/^\s*fast_mode\s*=/.test(lines[i])) {
|
|
135
|
+
lines[i] = lines[i].replace(/^(\s*)fast_mode\s*=.*$/, "$1fast_mode = true");
|
|
136
|
+
return lines.join("\n");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let insertAt = featuresEnd;
|
|
141
|
+
while (insertAt > featuresStart + 1 && lines[insertAt - 1].trim() === "") insertAt--;
|
|
142
|
+
lines.splice(insertAt, 0, "fast_mode = true");
|
|
143
|
+
return lines.join("\n");
|
|
144
|
+
}
|
|
145
|
+
|
|
111
146
|
function stripDefaultCatalogPath(content: string): string {
|
|
112
147
|
return content
|
|
113
148
|
.split("\n")
|
|
@@ -125,10 +160,13 @@ function buildProfileFile(port: number, catalogPath: string): string {
|
|
|
125
160
|
'model_provider = "opencodex"',
|
|
126
161
|
`model_catalog_json = ${tomlString(catalogPath)}`,
|
|
127
162
|
"",
|
|
163
|
+
"[features]",
|
|
164
|
+
"fast_mode = true",
|
|
165
|
+
"",
|
|
128
166
|
].join("\n");
|
|
129
167
|
}
|
|
130
168
|
|
|
131
|
-
export async function injectCodexConfig(port: number,
|
|
169
|
+
export async function injectCodexConfig(port: number, config?: OcxConfig): Promise<{ success: boolean; message: string }> {
|
|
132
170
|
if (!existsSync(CODEX_CONFIG_PATH)) {
|
|
133
171
|
return { success: false, message: `Codex config not found at ${CODEX_CONFIG_PATH}. Is Codex installed?` };
|
|
134
172
|
}
|
|
@@ -143,7 +181,9 @@ export async function injectCodexConfig(port: number, _config?: OcxConfig): Prom
|
|
|
143
181
|
}
|
|
144
182
|
content = removeProfileSection(content);
|
|
145
183
|
content = stripExistingModelProvider(content);
|
|
184
|
+
content = stripRootContextWindowOverrides(content);
|
|
146
185
|
content = normalizeServiceTier(content);
|
|
186
|
+
content = ensureFastModeFeature(content);
|
|
147
187
|
|
|
148
188
|
const catalogPath = readRootModelCatalogPath(content) ?? DEFAULT_CATALOG_PATH;
|
|
149
189
|
content = setRootModelCatalogPath(content, catalogPath);
|
|
@@ -151,7 +191,7 @@ export async function injectCodexConfig(port: number, _config?: OcxConfig): Prom
|
|
|
151
191
|
// 1) Root key BEFORE the first table header (must be a global, not nested under a table).
|
|
152
192
|
content = setRootModelProvider(content);
|
|
153
193
|
// 2) Provider table appended at EOF (position-independent).
|
|
154
|
-
content = content.trimEnd() + "\n" + buildProviderTableBlock(port);
|
|
194
|
+
content = content.trimEnd() + "\n" + buildProviderTableBlock(port, websocketsEnabled(config ?? {}));
|
|
155
195
|
|
|
156
196
|
writeFileSync(CODEX_CONFIG_PATH, content, "utf-8");
|
|
157
197
|
writeFileSync(CODEX_PROFILE_PATH, buildProfileFile(port, catalogPath), "utf-8");
|
|
@@ -199,6 +239,7 @@ export function stripOpencodexConfig(content: string): string {
|
|
|
199
239
|
// Regex (not exact-string) removal so compact `model_provider="opencodex"` is stripped too —
|
|
200
240
|
// must match the detection regex above, or a detected line could survive un-removed.
|
|
201
241
|
out = out.split("\n").filter(l => !/^\s*model_provider\s*=\s*"opencodex"\s*$/.test(l)).join("\n");
|
|
242
|
+
out = stripRootContextWindowOverrides(out);
|
|
202
243
|
out = stripDefaultCatalogPath(out);
|
|
203
244
|
return out.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
204
245
|
}
|
|
@@ -215,6 +256,8 @@ export function removeCodexConfig(): { success: boolean; message: string } {
|
|
|
215
256
|
const had = hasOpencodexRouting(content);
|
|
216
257
|
if (had) {
|
|
217
258
|
atomicWriteFile(CODEX_CONFIG_PATH, stripOpencodexConfig(content));
|
|
259
|
+
} else if (stripRootContextWindowOverrides(content) !== content) {
|
|
260
|
+
atomicWriteFile(CODEX_CONFIG_PATH, stripOpencodexConfig(content));
|
|
218
261
|
}
|
|
219
262
|
if (existsSync(CODEX_PROFILE_PATH)) unlinkSync(CODEX_PROFILE_PATH);
|
|
220
263
|
return {
|
package/src/config.ts
CHANGED
|
@@ -57,6 +57,10 @@ export function saveConfig(config: OcxConfig): void {
|
|
|
57
57
|
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
export function websocketsEnabled(config: Pick<OcxConfig, "websockets">): boolean {
|
|
61
|
+
return config.websockets !== false;
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
export function getDefaultConfig(): OcxConfig {
|
|
61
65
|
// Fresh-install default: works out of the box with Codex's ChatGPT OAuth (no API key).
|
|
62
66
|
// gpt-* requests forward the caller's incoming OAuth headers to the ChatGPT backend.
|
|
@@ -72,6 +76,7 @@ export function getDefaultConfig(): OcxConfig {
|
|
|
72
76
|
},
|
|
73
77
|
defaultProvider: "openai",
|
|
74
78
|
subagentModels: [...DEFAULT_SUBAGENT_MODELS],
|
|
79
|
+
websockets: true,
|
|
75
80
|
};
|
|
76
81
|
}
|
|
77
82
|
|
package/src/debug.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Opt-in frame-drop visibility. The streaming path is intentionally quiet (no unconditional
|
|
2
|
+
// console output), so this no-ops unless OCX_DEBUG_FRAMES=1. Lets a malformed/chunk-split
|
|
3
|
+
// upstream frame be detected instead of silently truncating content.
|
|
4
|
+
const DEBUG_FRAMES = process.env.OCX_DEBUG_FRAMES === "1";
|
|
5
|
+
|
|
6
|
+
export function debugDroppedFrame(adapter: string, payload: string): void {
|
|
7
|
+
if (!DEBUG_FRAMES) return;
|
|
8
|
+
const preview = payload.length > 200 ? `${payload.slice(0, 200)}…` : payload;
|
|
9
|
+
console.error(`[ocx:frame-drop] ${adapter}: ${preview}`);
|
|
10
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface OcxErrorPayload {
|
|
2
|
+
message: string;
|
|
3
|
+
type: string;
|
|
4
|
+
code: string | null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function classifyError(status: number, type: string, message: string): OcxErrorPayload {
|
|
8
|
+
const text = message.toLowerCase();
|
|
9
|
+
if (
|
|
10
|
+
text.includes("context_length_exceeded") ||
|
|
11
|
+
text.includes("context window") ||
|
|
12
|
+
text.includes("context length") ||
|
|
13
|
+
text.includes("maximum context") ||
|
|
14
|
+
text.includes("too many tokens")
|
|
15
|
+
) {
|
|
16
|
+
return { message, type: "invalid_request_error", code: "context_length_exceeded" };
|
|
17
|
+
}
|
|
18
|
+
if (
|
|
19
|
+
text.includes("insufficient_quota") ||
|
|
20
|
+
text.includes("exceeded your current quota")
|
|
21
|
+
) {
|
|
22
|
+
return { message, type: "insufficient_quota", code: "insufficient_quota" };
|
|
23
|
+
}
|
|
24
|
+
if (status === 429 || text.includes("rate limit") || text.includes("too many requests")) {
|
|
25
|
+
return { message, type: "rate_limit_error", code: "rate_limit_exceeded" };
|
|
26
|
+
}
|
|
27
|
+
if (status === 401 || status === 403 || type === "authentication_error") {
|
|
28
|
+
return { message, type: "authentication_error", code: "invalid_api_key" };
|
|
29
|
+
}
|
|
30
|
+
if (
|
|
31
|
+
status === 503 ||
|
|
32
|
+
text.includes("overloaded") ||
|
|
33
|
+
text.includes("server is busy") ||
|
|
34
|
+
text.includes("temporarily unavailable")
|
|
35
|
+
) {
|
|
36
|
+
// Codex recognizes "server_is_overloaded" and applies retry-after backoff
|
|
37
|
+
// (responses.rs is_server_overloaded_error); generic "upstream_server_error" is not recognized.
|
|
38
|
+
return { message, type: "server_error", code: "server_is_overloaded" };
|
|
39
|
+
}
|
|
40
|
+
if (status >= 500) {
|
|
41
|
+
return { message, type: "server_error", code: "upstream_server_error" };
|
|
42
|
+
}
|
|
43
|
+
if (status === 400 || type === "invalid_request_error") {
|
|
44
|
+
return { message, type: "invalid_request_error", code: "invalid_request_error" };
|
|
45
|
+
}
|
|
46
|
+
return { message, type, code: type || null };
|
|
47
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Generated by scripts/generate-jawcode-metadata.ts. Do not edit by hand.
|
|
3
|
+
|
|
4
|
+
export interface JawcodeModelMetadata {
|
|
5
|
+
provider: string;
|
|
6
|
+
id: string;
|
|
7
|
+
contextWindow?: number;
|
|
8
|
+
maxTokens?: number;
|
|
9
|
+
input?: ("text" | "image")[];
|
|
10
|
+
reasoning?: boolean;
|
|
11
|
+
wireModelId?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const PROVIDER_ALIASES: Record<string, string> = {
|
|
15
|
+
"xai": "xai",
|
|
16
|
+
"anthropic": "anthropic",
|
|
17
|
+
"kimi": "moonshot",
|
|
18
|
+
"opencode-go": "opencode-go",
|
|
19
|
+
"openrouter": "openrouter",
|
|
20
|
+
"google": "google",
|
|
21
|
+
"gemini": "google",
|
|
22
|
+
"moonshot": "moonshot",
|
|
23
|
+
"minimax": "minimax",
|
|
24
|
+
"minimax-cn": "minimax"
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
type Row = readonly [id: string, contextWindow?: number, maxTokens?: number, input?: string, reasoning?: 0 | 1, wireModelId?: string];
|
|
28
|
+
const DATA: Record<string, readonly Row[]> = {
|
|
29
|
+
"anthropic": [["claude-3-5-sonnet-20240620",200000,8192,"text,image",0],["claude-3-5-sonnet-20241022",200000,8192,"text,image",0],["claude-3-haiku-20240307",200000,4096,"text,image",0],["claude-fable-5",1000000,128000,"text,image",1],["claude-haiku-4-5",200000,64000,"text,image",1],["claude-haiku-4-5-20251001",200000,64000,"text,image",1],["claude-opus-4-0",200000,32000,"text,image",1],["claude-opus-4-1",200000,32000,"text,image",1],["claude-opus-4-1-20250805",200000,32000,"text,image",1],["claude-opus-4-20250514",200000,32000,"text,image",1],["claude-opus-4-5",200000,64000,"text,image",1],["claude-opus-4-5-20251101",200000,64000,"text,image",1],["claude-opus-4-6",1000000,128000,"text,image",1],["claude-opus-4-6[1m]",1000000,128000,"text,image",1,"claude-opus-4-6"],["claude-opus-4-7",1000000,128000,"text,image",1],["claude-opus-4-7[1m]",1000000,128000,"text,image",1,"claude-opus-4-7"],["claude-opus-4-8",1000000,128000,"text,image",1],["claude-opus-4-8[1m]",1000000,128000,"text,image",1,"claude-opus-4-8"],["claude-sonnet-4-0",200000,64000,"text,image",1],["claude-sonnet-4-20250514",200000,64000,"text,image",1],["claude-sonnet-4-5",200000,64000,"text,image",1],["claude-sonnet-4-5-20250929",200000,64000,"text,image",1],["claude-sonnet-4-6",200000,64000,"text,image",1],["claude-sonnet-4-6[1m]",200000,64000,"text,image",1,"claude-sonnet-4-6"]],
|
|
30
|
+
"google": [["gemini-1.5-flash",1000000,8192,"text,image",0],["gemini-1.5-flash-8b",1000000,8192,"text,image",0],["gemini-1.5-pro",1000000,8192,"text,image",0],["gemini-2.0-flash",1048576,8192,"text,image",0],["gemini-2.0-flash-lite",1048576,8192,"text,image",0],["gemini-2.5-flash",1048576,65536,"text,image",1],["gemini-2.5-flash-lite",1048576,65536,"text,image",1],["gemini-2.5-flash-lite-preview-06-17",1048576,65536,"text,image",1],["gemini-2.5-flash-lite-preview-09-2025",1048576,65536,"text,image",1],["gemini-2.5-flash-preview-04-17",1048576,65536,"text,image",1],["gemini-2.5-flash-preview-05-20",1048576,65536,"text,image",1],["gemini-2.5-flash-preview-09-2025",1048576,65536,"text,image",1],["gemini-2.5-pro",1048576,65536,"text,image",1],["gemini-2.5-pro-preview-05-06",1048576,65536,"text,image",1],["gemini-2.5-pro-preview-06-05",1048576,65536,"text,image",1],["gemini-3-flash-preview",1048576,65536,"text,image",1],["gemini-3-pro-preview",1048576,65536,"text,image",1],["gemini-3.1-flash-lite",1048576,65536,"text,image",1],["gemini-3.1-flash-lite-preview",1048576,65536,"text,image",1],["gemini-3.1-pro-preview",1048576,65536,"text,image",1],["gemini-3.1-pro-preview-customtools",1048576,65536,"text,image",1],["gemini-3.5-flash",1048576,65536,"text,image",1],["gemini-flash-latest",1048576,65536,"text,image",1],["gemini-flash-lite-latest",1048576,65536,"text,image",1],["gemini-live-2.5-flash",128000,8000,"text,image",1],["gemini-live-2.5-flash-preview-native-audio",131072,65536,"text",1],["gemma-3-27b-it",131072,8192,"text,image",0],["gemma-4-26b",256000,8192,"text,image",1],["gemma-4-26b-a4b-it",262144,32768,"text,image",1],["gemma-4-26b-it",256000,8192,"text,image",1],["gemma-4-31b",256000,8192,"text,image",1],["gemma-4-31b-it",262144,32768,"text,image",1],["gemma-4-E2B-it",131072,8192,"text,image",1],["gemma-4-E4B-it",131072,8192,"text,image",1]],
|
|
31
|
+
"minimax": [["MiniMax-M2",196608,128000,"text",1],["MiniMax-M2.1",204800,131072,"text",1],["MiniMax-M2.5",204800,131072,"text",1],["MiniMax-M2.5-highspeed",204800,131072,"text",1],["MiniMax-M2.5-lightning",204800,32000,"text",1],["MiniMax-M2.7",204800,131072,"text",1],["MiniMax-M2.7-highspeed",204800,131072,"text",1],["minimax-m3",512000,128000,"text,image",1],["MiniMax-M3",512000,128000,"text,image",1]],
|
|
32
|
+
"moonshot": [["kimi-k2.5",262144,65536,"text,image",1]],
|
|
33
|
+
"opencode-go": [["deepseek-v4-flash",1000000,384000,"text",1],["deepseek-v4-pro",1000000,384000,"text",1],["glm-5",204800,131072,"text",1],["glm-5.1",200000,131072,"text",1],["glm-5.2",1000000,131072,"text",1],["hy3-preview",256000,64000,"text",1],["kimi-k2.5",262144,262144,"text,image",1],["kimi-k2.6",262144,262144,"text,image",1],["kimi-k2.7-code",262144,262144,"text,image",1],["mimo-v2-omni",262144,131072,"text,image",1],["mimo-v2-pro",1048576,131072,"text",1],["mimo-v2.5",1048576,131072,"text,image",1],["mimo-v2.5-pro",1048576,131072,"text",1],["minimax-m2.5",204800,131072,"text",1],["minimax-m2.7",204800,131072,"text",1],["minimax-m3",512000,128000,"text,image",1],["qwen3.5-plus",1000000,65536,"text,image",1],["qwen3.6-plus",1000000,65536,"text,image",1],["qwen3.7-max",1000000,65536,"text",1],["qwen3.7-plus",1000000,64000,"text,image",1]],
|
|
34
|
+
"openrouter": [["~anthropic/claude-fable-latest",1000000,128000,"text,image",1],["~anthropic/claude-haiku-latest",200000,64000,"text,image",1],["~anthropic/claude-opus-latest",1000000,128000,"text,image",1],["~anthropic/claude-sonnet-latest",1000000,128000,"text,image",1],["~google/gemini-flash-latest",1048576,65536,"text,image",1],["~google/gemini-pro-latest",1048576,65536,"text,image",1],["~moonshotai/kimi-latest",262144,262144,"text,image",1],["~openai/gpt-latest",1050000,128000,"text,image",1],["~openai/gpt-mini-latest",400000,128000,"text,image",1],["ai21/jamba-large-1.7",256000,4096,"text",0],["alibaba/tongyi-deepresearch-30b-a3b",131072,131072,"text",1],["allenai/olmo-3.1-32b-instruct",65536,16384,"text",0],["amazon/nova-2-lite-v1",1000000,65535,"text,image",1],["amazon/nova-lite-v1",300000,5120,"text,image",0],["amazon/nova-micro-v1",128000,5120,"text",0],["amazon/nova-premier-v1",1000000,32000,"text,image",0],["amazon/nova-pro-v1",300000,5120,"text,image",0],["anthropic/claude-3-haiku",200000,4096,"text,image",0],["anthropic/claude-3.5-haiku",200000,8192,"text,image",0],["anthropic/claude-3.5-sonnet",200000,8192,"text,image",0],["anthropic/claude-3.7-sonnet",200000,128000,"text,image",1],["anthropic/claude-3.7-sonnet:thinking",200000,64000,"text,image",1],["anthropic/claude-fable-5",1000000,128000,"text,image",1],["anthropic/claude-haiku-4.5",200000,64000,"text,image",0],["anthropic/claude-opus-4",200000,32000,"text,image",1],["anthropic/claude-opus-4.1",200000,32000,"text,image",1],["anthropic/claude-opus-4.5",200000,64000,"text,image",1],["anthropic/claude-opus-4.6",1000000,128000,"text,image",1],["anthropic/claude-opus-4.6-fast",1000000,128000,"text,image",1],["anthropic/claude-opus-4.7",1000000,128000,"text,image",1],["anthropic/claude-opus-4.7-fast",1000000,128000,"text,image",1],["anthropic/claude-opus-4.8",1000000,128000,"text,image",1],["anthropic/claude-opus-4.8-fast",1000000,128000,"text,image",1],["anthropic/claude-sonnet-4",1000000,64000,"text,image",1],["anthropic/claude-sonnet-4.5",1000000,64000,"text,image",1],["anthropic/claude-sonnet-4.6",1000000,128000,"text,image",1],["arcee-ai/trinity-large-preview",131000,8888,"text",0],["arcee-ai/trinity-large-preview:free",131000,8888,"text",0],["arcee-ai/trinity-large-thinking",262144,262144,"text",1],["arcee-ai/trinity-large-thinking:free",262144,80000,"text",1],["arcee-ai/trinity-mini",131072,131072,"text",1],["arcee-ai/trinity-mini:free",131072,8888,"text",1],["arcee-ai/virtuoso-large",131072,64000,"text",0],["auto",2000000,30000,"text,image",1],["baidu/cobuddy:free",131072,65536,"text",1],["baidu/ernie-4.5-21b-a3b",131072,8000,"text",0],["baidu/ernie-4.5-vl-28b-a3b",131072,8000,"text,image",1],["bytedance-seed/seed-1.6",262144,32768,"text,image",1],["bytedance-seed/seed-1.6-flash",262144,32768,"text,image",1],["bytedance-seed/seed-2.0-lite",262144,131072,"text,image",1],["bytedance-seed/seed-2.0-mini",262144,131072,"text,image",1],["cohere/command-r-08-2024",128000,4000,"text",0],["cohere/command-r-plus-08-2024",128000,4000,"text",0],["cohere/north-mini-code:free",256000,64000,"text",1],["deepseek/deepseek-chat",131072,16000,"text",0],["deepseek/deepseek-chat-v3-0324",163840,16384,"text",1],["deepseek/deepseek-chat-v3.1",163840,32768,"text",1],["deepseek/deepseek-r1",163840,16000,"text",1],["deepseek/deepseek-r1-0528",163840,32768,"text",1],["deepseek/deepseek-v3.1-terminus",163840,32768,"text",1],["deepseek/deepseek-v3.1-terminus:exacto",163840,8888,"text",1],["deepseek/deepseek-v3.2",131072,64000,"text",1],["deepseek/deepseek-v3.2-exp",163840,65536,"text",1],["deepseek/deepseek-v4-flash",1048576,65536,"text",1],["deepseek/deepseek-v4-flash:free",1048576,384000,"text",1],["deepseek/deepseek-v4-pro",1048576,384000,"text",1],["essentialai/rnj-1-instruct",32768,8888,"text",0],["google/gemini-2.0-flash-001",1048576,8192,"text,image",0],["google/gemini-2.0-flash-lite-001",1048576,8192,"text,image",0],["google/gemini-2.5-flash",1048576,65535,"text,image",1],["google/gemini-2.5-flash-lite",1048576,65535,"text,image",0],["google/gemini-2.5-flash-lite-preview-09-2025",1048576,65535,"text,image",1],["google/gemini-2.5-flash-preview-09-2025",1048576,65536,"text,image",1],["google/gemini-2.5-pro",1048576,65536,"text,image",1],["google/gemini-2.5-pro-preview",1048576,65536,"text,image",1],["google/gemini-2.5-pro-preview-05-06",1048576,65535,"text,image",1],["google/gemini-3-flash-preview",1048576,65535,"text,image",1],["google/gemini-3-pro-image",65536,32768,"text,image",1],["google/gemini-3-pro-preview",1048000,64000,"text,image",1],["google/gemini-3.1-flash-lite",1048576,65536,"text,image",1],["google/gemini-3.1-flash-lite-preview",1048576,65536,"text,image",0],["google/gemini-3.1-pro-preview",1048576,65536,"text,image",1],["google/gemini-3.1-pro-preview-customtools",1048756,65536,"text,image",1],["google/gemini-3.5-flash",1048576,65536,"text,image",1],["google/gemma-3-12b-it",131072,16384,"text,image",0],["google/gemma-3-27b-it",131072,16384,"text,image",1],["google/gemma-3-27b-it:free",131072,8192,"text,image",0],["google/gemma-4-26b-a4b-it",262144,8888,"text,image",1],["google/gemma-4-26b-a4b-it:free",262144,32768,"text,image",1],["google/gemma-4-31b-it",262144,262144,"text,image",1],["google/gemma-4-31b-it:free",262144,8192,"text,image",1],["ibm-granite/granite-4.1-8b",131072,131072,"text",0],["inception/mercury",128000,32000,"text",0],["inception/mercury-2",128000,50000,"text",1],["inception/mercury-coder",128000,32000,"text",0],["inclusionai/ling-2.6-1t",262144,32768,"text",0],["inclusionai/ling-2.6-1t:free",262144,32768,"text",0],["inclusionai/ling-2.6-flash",262144,32768,"text",0],["inclusionai/ling-2.6-flash:free",262144,32768,"text",0],["inclusionai/ring-2.6-1t",262144,65536,"text",1],["inclusionai/ring-2.6-1t:free",262144,65536,"text",1],["kwaipilot/kat-coder-pro",256000,128000,"text",0],["kwaipilot/kat-coder-pro-v2",256000,80000,"text",0],["liquid/lfm-2.5-1.2b-thinking:free",32768,8888,"text",1],["meituan/longcat-flash-chat",131072,131072,"text",0],["meta-llama/llama-3-8b-instruct",8192,16384,"text",0],["meta-llama/llama-3.1-405b-instruct",131000,8888,"text",0],["meta-llama/llama-3.1-70b-instruct",131072,16384,"text",0],["meta-llama/llama-3.1-8b-instruct",131072,16384,"text",0],["meta-llama/llama-3.3-70b-instruct",131072,16384,"text",0],["meta-llama/llama-3.3-70b-instruct:free",131072,8888,"text",0],["meta-llama/llama-4-maverick",1048576,16384,"text,image",0],["meta-llama/llama-4-scout",10000000,16384,"text,image",0],["minimax/minimax-m1",1000000,40000,"text",1],["minimax/minimax-m2",204800,196608,"text",1],["minimax/minimax-m2.1",204800,196608,"text",1],["minimax/minimax-m2.5",204800,196608,"text",1],["minimax/minimax-m2.5:free",262144,8192,"text",1],["minimax/minimax-m2.7",204800,131072,"text",1],["minimax/minimax-m3",1048576,512000,"text,image",1],["mistralai/codestral-2508",256000,8888,"text",0],["mistralai/devstral-2512",262144,8888,"text",0],["mistralai/devstral-medium",131072,8888,"text",0],["mistralai/devstral-small",131072,8888,"text",0],["mistralai/ministral-14b-2512",262144,8888,"text,image",0],["mistralai/ministral-3b-2512",131072,8888,"text,image",0],["mistralai/ministral-8b-2512",262144,8888,"text,image",0],["mistralai/mistral-large",128000,8888,"text",0],["mistralai/mistral-large-2407",131072,8888,"text",0],["mistralai/mistral-large-2411",131072,8888,"text",0],["mistralai/mistral-large-2512",262144,8888,"text,image",0],["mistralai/mistral-medium-3",131072,8888,"text,image",0],["mistralai/mistral-medium-3-5",262144,8888,"text,image",1],["mistralai/mistral-medium-3.1",131072,8888,"text,image",0],["mistralai/mistral-nemo",131072,8888,"text",0],["mistralai/mistral-saba",32768,8888,"text",0],["mistralai/mistral-small-24b-instruct-2501",32768,16384,"text",0],["mistralai/mistral-small-2603",262144,8888,"text,image",1],["mistralai/mistral-small-3.1-24b-instruct",131072,131072,"text,image",0],["mistralai/mistral-small-3.1-24b-instruct:free",128000,8888,"text,image",0],["mistralai/mistral-small-3.2-24b-instruct",128000,16384,"text,image",0],["mistralai/mistral-small-creative",32768,8888,"text",0],["mistralai/mixtral-8x22b-instruct",65536,13108,"text",0],["mistralai/mixtral-8x7b-instruct",32768,16384,"text",0],["mistralai/pixtral-large-2411",131072,8888,"text,image",0],["mistralai/voxtral-small-24b-2507",32000,8888,"text",0],["moonshotai/kimi-k2",131072,32768,"text",0],["moonshotai/kimi-k2-0905",262144,262144,"text",0],["moonshotai/kimi-k2-0905:exacto",262144,8888,"text",0],["moonshotai/kimi-k2-thinking",262144,262144,"text",1],["moonshotai/kimi-k2.5",262144,64000,"text,image",1],["moonshotai/kimi-k2.6",262144,262144,"text,image",1],["moonshotai/kimi-k2.6:free",262144,8888,"text,image",1],["moonshotai/kimi-k2.7-code",262144,262144,"text,image",1],["nex-agi/deepseek-v3.1-nex-n1",131072,163840,"text",0],["nex-agi/nex-n2-pro:free",262144,262144,"text,image",1],["nousresearch/deephermes-3-mistral-24b-preview",32768,32768,"text",1],["nousresearch/hermes-4-70b",131072,131072,"text",1],["nvidia/llama-3.1-nemotron-70b-instruct",131072,16384,"text",0],["nvidia/llama-3.3-nemotron-super-49b-v1.5",131072,16384,"text",1],["nvidia/nemotron-3-nano-30b-a3b",262144,228000,"text",1],["nvidia/nemotron-3-nano-30b-a3b:free",256000,8888,"text",1],["nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free",256000,65536,"text,image",1],["nvidia/nemotron-3-super-120b-a12b",1000000,262144,"text",1],["nvidia/nemotron-3-super-120b-a12b:free",1000000,262144,"text",1],["nvidia/nemotron-3-ultra-550b-a55b",1000000,16384,"text",1],["nvidia/nemotron-3-ultra-550b-a55b:free",1000000,65536,"text",1],["nvidia/nemotron-nano-12b-v2-vl:free",128000,128000,"text,image",1],["nvidia/nemotron-nano-9b-v2",131072,16384,"text",1],["nvidia/nemotron-nano-9b-v2:free",128000,8888,"text",1],["openai/gpt-3.5-turbo",16385,4096,"text",0],["openai/gpt-3.5-turbo-0613",4095,4096,"text",0],["openai/gpt-3.5-turbo-16k",16385,4096,"text",0],["openai/gpt-4",8191,8192,"text",0],["openai/gpt-4-0314",8191,4096,"text",0],["openai/gpt-4-1106-preview",128000,4096,"text",0],["openai/gpt-4-turbo",128000,4096,"text,image",0],["openai/gpt-4-turbo-preview",128000,4096,"text",0],["openai/gpt-4.1",1047576,8888,"text,image",0],["openai/gpt-4.1-mini",1047576,32768,"text,image",0],["openai/gpt-4.1-nano",1047576,32768,"text,image",0],["openai/gpt-4o",128000,16384,"text,image",0],["openai/gpt-4o-2024-05-13",128000,4096,"text,image",0],["openai/gpt-4o-2024-08-06",128000,16384,"text,image",0],["openai/gpt-4o-2024-11-20",128000,16384,"text,image",0],["openai/gpt-4o-audio-preview",128000,16384,"text",0],["openai/gpt-4o-mini",128000,16384,"text,image",0],["openai/gpt-4o-mini-2024-07-18",128000,16384,"text,image",0],["openai/gpt-4o:extended",128000,64000,"text,image",0],["openai/gpt-5",400000,128000,"text,image",1],["openai/gpt-5-codex",272000,128000,"text,image",1],["openai/gpt-5-image",400000,128000,"text,image",1],["openai/gpt-5-image-mini",400000,128000,"text,image",1],["openai/gpt-5-mini",400000,128000,"text,image",1],["openai/gpt-5-nano",400000,8888,"text,image",1],["openai/gpt-5-pro",400000,128000,"text,image",1],["openai/gpt-5.1",400000,128000,"text,image",1],["openai/gpt-5.1-chat",128000,32000,"text,image",0],["openai/gpt-5.1-codex",272000,128000,"text,image",1],["openai/gpt-5.1-codex-max",272000,128000,"text,image",1],["openai/gpt-5.1-codex-mini",272000,100000,"text,image",1],["openai/gpt-5.2",400000,128000,"text,image",1],["openai/gpt-5.2-chat",128000,16384,"text,image",0],["openai/gpt-5.2-codex",272000,128000,"text,image",1],["openai/gpt-5.2-pro",400000,128000,"text,image",1],["openai/gpt-5.3-chat",128000,16384,"text",0],["openai/gpt-5.3-codex",272000,128000,"text,image",1],["openai/gpt-5.4",1050000,128000,"text,image",1],["openai/gpt-5.4-mini",400000,128000,"text",0],["openai/gpt-5.4-nano",400000,128000,"text",0],["openai/gpt-5.4-pro",1050000,128000,"text,image",1],["openai/gpt-5.5",1050000,128000,"text,image",1],["openai/gpt-5.5-pro",1050000,128000,"text,image",1],["openai/gpt-audio",128000,16384,"text",0],["openai/gpt-audio-mini",128000,16384,"text",0],["openai/gpt-chat-latest",400000,128000,"text,image",0],["openai/gpt-oss-120b",131072,65536,"text",1],["openai/gpt-oss-120b:exacto",131072,8888,"text",1],["openai/gpt-oss-120b:free",131072,131072,"text",1],["openai/gpt-oss-20b",131072,65536,"text",1],["openai/gpt-oss-20b:free",131072,32768,"text",1],["openai/gpt-oss-safeguard-20b",131072,65536,"text",1],["openai/o1",200000,100000,"text,image",1],["openai/o3",200000,100000,"text,image",1],["openai/o3-deep-research",200000,100000,"text,image",1],["openai/o3-mini",200000,100000,"text",1],["openai/o3-mini-high",200000,100000,"text",1],["openai/o3-pro",200000,100000,"text,image",1],["openai/o4-mini",200000,100000,"text,image",1],["openai/o4-mini-deep-research",200000,100000,"text,image",1],["openai/o4-mini-high",200000,100000,"text,image",1],["openrouter/aurora-alpha",128000,50000,"text",1],["openrouter/auto",2000000,8888,"text,image",1],["openrouter/elephant-alpha",262144,32768,"text",0],["openrouter/free",200000,8888,"text,image",1],["openrouter/healer-alpha",262144,32000,"text,image",1],["openrouter/hunter-alpha",1048576,32000,"text",1],["openrouter/owl-alpha",1048756,262144,"text",0],["poolside/laguna-m.1",262144,32768,"text",1],["poolside/laguna-m.1:free",262144,32768,"text",1],["poolside/laguna-xs.2",262144,32768,"text",1],["poolside/laguna-xs.2:free",262144,32768,"text",1],["prime-intellect/intellect-3",131072,131072,"text",1],["qwen/qwen-2.5-72b-instruct",131072,16384,"text",0],["qwen/qwen-2.5-7b-instruct",131072,32768,"text",0],["qwen/qwen-max",32768,8192,"text",0],["qwen/qwen-plus",1000000,32768,"text",0],["qwen/qwen-plus-2025-07-28",1000000,32768,"text",0],["qwen/qwen-plus-2025-07-28:thinking",1000000,32768,"text",1],["qwen/qwen-turbo",131072,8192,"text",0],["qwen/qwen-vl-max",131072,32768,"text,image",0],["qwen/qwen3-14b",131702,40960,"text",1],["qwen/qwen3-235b-a22b",131072,8192,"text",1],["qwen/qwen3-235b-a22b-2507",262144,16384,"text",1],["qwen/qwen3-235b-a22b-thinking-2507",262144,262144,"text",1],["qwen/qwen3-30b-a3b",131072,16384,"text",1],["qwen/qwen3-30b-a3b-instruct-2507",131072,32000,"text",0],["qwen/qwen3-30b-a3b-thinking-2507",131072,131072,"text",1],["qwen/qwen3-32b",131072,16384,"text",1],["qwen/qwen3-4b",131072,8192,"text",1],["qwen/qwen3-4b:free",40960,8888,"text",1],["qwen/qwen3-8b",131072,8192,"text",1],["qwen/qwen3-coder",1048576,65536,"text",0],["qwen/qwen3-coder-30b-a3b-instruct",160000,32768,"text",0],["qwen/qwen3-coder-flash",1000000,65536,"text",0],["qwen/qwen3-coder-next",262144,262144,"text",0],["qwen/qwen3-coder-plus",1000000,65536,"text",0],["qwen/qwen3-coder:exacto",262144,65536,"text",0],["qwen/qwen3-coder:free",1048576,262000,"text",0],["qwen/qwen3-max",262144,32768,"text",1],["qwen/qwen3-max-thinking",262144,32768,"text",1],["qwen/qwen3-next-80b-a3b-instruct",262144,16384,"text",0],["qwen/qwen3-next-80b-a3b-instruct:free",262144,8888,"text",0],["qwen/qwen3-next-80b-a3b-thinking",262144,32768,"text",1],["qwen/qwen3-vl-235b-a22b-instruct",262144,16384,"text,image",0],["qwen/qwen3-vl-235b-a22b-thinking",131072,32768,"text,image",1],["qwen/qwen3-vl-30b-a3b-instruct",262144,32768,"text,image",0],["qwen/qwen3-vl-30b-a3b-thinking",131072,32768,"text,image",1],["qwen/qwen3-vl-32b-instruct",262144,32768,"text,image",0],["qwen/qwen3-vl-8b-instruct",256000,32768,"text,image",0],["qwen/qwen3-vl-8b-thinking",256000,32768,"text,image",1],["qwen/qwen3.5-122b-a10b",262144,262144,"text,image",1],["qwen/qwen3.5-27b",262144,65536,"text,image",1],["qwen/qwen3.5-35b-a3b",262144,262144,"text,image",1],["qwen/qwen3.5-397b-a17b",256000,8192,"text,image",1],["qwen/qwen3.5-9b",262144,262144,"text,image",1],["qwen/qwen3.5-flash-02-23",1000000,65536,"text,image",1],["qwen/qwen3.5-plus-02-15",1000000,65536,"text,image",1],["qwen/qwen3.5-plus-20260420",1000000,65536,"text,image",1],["qwen/qwen3.6-27b",262144,262140,"text,image",1],["qwen/qwen3.6-35b-a3b",262144,262144,"text,image",1],["qwen/qwen3.6-flash",1000000,65536,"text,image",1],["qwen/qwen3.6-max-preview",262144,65536,"text",1],["qwen/qwen3.6-plus",1000000,65536,"text",1],["qwen/qwen3.6-plus-preview:free",1000000,32000,"text",1],["qwen/qwen3.6-plus:free",1000000,65536,"text,image",1],["qwen/qwen3.7-max",1000000,65536,"text",1],["qwen/qwen3.7-plus",1000000,65536,"text,image",1],["qwen/qwq-32b",131072,131072,"text",1],["reka/reka-edge",16384,16384,"text,image",0],["rekaai/reka-edge",16384,16384,"text,image",0],["relace/relace-search",256000,128000,"text",0],["sao10k/l3-euryale-70b",8192,8192,"text",0],["sao10k/l3.1-euryale-70b",131072,16384,"text",0],["stepfun/step-3.5-flash",262144,16384,"text",0],["stepfun/step-3.5-flash:free",256000,256000,"text",1],["stepfun/step-3.7-flash",256000,256000,"text,image",1],["tencent/hy3-preview",262144,262144,"text",1],["tencent/hy3-preview:free",262144,262144,"text",1],["thedrummer/rocinante-12b",32768,32768,"text",0],["thedrummer/unslopnemo-12b",32768,32768,"text",0],["tngtech/deepseek-r1t2-chimera",163840,163840,"text",1],["tngtech/tng-r1t-chimera",163840,65536,"text",1],["upstage/solar-pro-3",128000,8888,"text",1],["upstage/solar-pro-3:free",128000,8888,"text",1],["x-ai/grok-3",131072,8888,"text",0],["x-ai/grok-3-beta",131072,8888,"text",0],["x-ai/grok-3-mini",131072,8888,"text",1],["x-ai/grok-3-mini-beta",131072,8888,"text",1],["x-ai/grok-4",256000,64000,"text,image",1],["x-ai/grok-4-fast",2000000,30000,"text,image",1],["x-ai/grok-4.1-fast",2000000,30000,"text,image",1],["x-ai/grok-4.20",2000000,8888,"text,image",1],["x-ai/grok-4.20-beta",2000000,8888,"text,image",1],["x-ai/grok-4.3",1000000,1000000,"text,image",1],["x-ai/grok-build-0.1",256000,256000,"text,image",1],["x-ai/grok-code-fast-1",256000,10000,"text",1],["xiaomi/mimo-v2-flash",262144,65536,"text",1],["xiaomi/mimo-v2-omni",262144,65536,"text,image",1],["xiaomi/mimo-v2-pro",1048576,131072,"text",1],["xiaomi/mimo-v2.5",1048576,131072,"text,image",1],["xiaomi/mimo-v2.5-pro",1048576,131072,"text",1],["z-ai/glm-4-32b",128000,8888,"text",0],["z-ai/glm-4.5",131072,98304,"text",1],["z-ai/glm-4.5-air",131072,98304,"text",1],["z-ai/glm-4.5-air:free",131072,96000,"text",1],["z-ai/glm-4.5v",65536,16384,"text,image",1],["z-ai/glm-4.6",202752,131072,"text",1],["z-ai/glm-4.6:exacto",204800,131072,"text",1],["z-ai/glm-4.6v",131072,32768,"text,image",1],["z-ai/glm-4.7",202752,131072,"text",1],["z-ai/glm-4.7-flash",202752,16384,"text",1],["z-ai/glm-5",202752,128000,"text",1],["z-ai/glm-5-turbo",262144,131072,"text",1],["z-ai/glm-5.1",202752,131072,"text",1],["z-ai/glm-5.2",1048576,131072,"text",1],["z-ai/glm-5v-turbo",202752,131072,"text,image",1]],
|
|
35
|
+
"xai": [["grok-2",131072,8192,"text",0],["grok-2-1212",131072,8192,"text",0],["grok-2-latest",131072,8192,"text",0],["grok-2-vision",8192,4096,"text,image",0],["grok-2-vision-1212",8192,4096,"text,image",0],["grok-2-vision-latest",8192,4096,"text,image",0],["grok-3",131072,8192,"text",0],["grok-3-fast",131072,8192,"text",0],["grok-3-fast-latest",131072,8192,"text",0],["grok-3-latest",131072,8192,"text",0],["grok-3-mini",131072,8192,"text",1],["grok-3-mini-fast",131072,8192,"text",1],["grok-3-mini-fast-latest",131072,8192,"text",1],["grok-3-mini-latest",131072,8192,"text",1],["grok-4",256000,64000,"text",1],["grok-4-1-fast",2000000,30000,"text,image",1],["grok-4-1-fast-non-reasoning",2000000,30000,"text,image",0],["grok-4-fast",2000000,30000,"text,image",1],["grok-4-fast-non-reasoning",2000000,30000,"text,image",0],["grok-4.20-0309-non-reasoning",1000000,30000,"text,image",0],["grok-4.20-0309-reasoning",1000000,30000,"text,image",1],["grok-4.20-beta-latest-non-reasoning",2000000,30000,"text,image",0],["grok-4.20-beta-latest-reasoning",2000000,30000,"text,image",1],["grok-4.20-multi-agent-beta-latest",2000000,30000,"text,image",1],["grok-4.3",1000000,30000,"text,image",1],["grok-beta",131072,4096,"text",0],["grok-build-0.1",256000,256000,"text,image",1],["grok-code-fast-1",256000,10000,"text",1],["grok-composer-2.5-fast",200000,64000,"text",1],["grok-vision-beta",8192,4096,"text,image",0]],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export function resolveJawcodeProvider(provider: string): string | undefined {
|
|
39
|
+
return PROVIDER_ALIASES[provider];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getJawcodeModelMetadata(provider: string, modelId: string): JawcodeModelMetadata | undefined {
|
|
43
|
+
const row = DATA[provider]?.find(r => r[0] === modelId);
|
|
44
|
+
if (!row) return undefined;
|
|
45
|
+
return rowToMetadata(provider, row);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getJawcodeModelMetadataCaseInsensitive(provider: string, modelId: string): JawcodeModelMetadata | undefined {
|
|
49
|
+
const lower = modelId.toLowerCase();
|
|
50
|
+
const row = DATA[provider]?.find(r => r[0].toLowerCase() === lower);
|
|
51
|
+
if (!row) return undefined;
|
|
52
|
+
return rowToMetadata(provider, row);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function listJawcodeModelMetadata(provider: string): JawcodeModelMetadata[] {
|
|
56
|
+
return (DATA[provider] ?? []).map(row => rowToMetadata(provider, row));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function rowToMetadata(provider: string, row: Row): JawcodeModelMetadata {
|
|
60
|
+
const [id, contextWindow, maxTokens, input, reasoning, wireModelId] = row;
|
|
61
|
+
return {
|
|
62
|
+
provider, id,
|
|
63
|
+
...(contextWindow !== undefined ? { contextWindow } : {}),
|
|
64
|
+
...(maxTokens !== undefined ? { maxTokens } : {}),
|
|
65
|
+
...(input ? { input: input.split(",") as ("text" | "image")[] } : {}),
|
|
66
|
+
...(reasoning !== undefined ? { reasoning: reasoning === 1 } : {}),
|
|
67
|
+
...(wireModelId !== undefined ? { wireModelId } : {}),
|
|
68
|
+
};
|
|
69
|
+
}
|
package/src/init.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as readline from "node:readline";
|
|
2
2
|
import { injectCodexConfig } from "./codex-inject";
|
|
3
3
|
import { getDefaultConfig, saveConfig } from "./config";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { enrichProviderFromCatalog } from "./oauth/key-providers";
|
|
5
|
+
import { deriveInitProviders } from "./providers/derive";
|
|
6
6
|
import type { OcxConfig, OcxProviderConfig } from "./types";
|
|
7
7
|
|
|
8
8
|
function createPrompt(): { ask(question: string): Promise<string>; close(): void } {
|
|
@@ -26,39 +26,12 @@ export interface InitProvider {
|
|
|
26
26
|
defaultModel?: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const OAUTH_LABELS: Record<string, string> = {
|
|
30
|
-
xai: "xAI (Grok)", anthropic: "Anthropic (Claude)", kimi: "Kimi (Moonshot)",
|
|
31
|
-
};
|
|
32
|
-
|
|
33
29
|
/**
|
|
34
|
-
* The full CLI provider menu,
|
|
35
|
-
*
|
|
36
|
-
* so `ocx init` reaches provider parity with the GUI. Exported for verification.
|
|
30
|
+
* The full CLI provider menu, derived from the canonical provider registry so `ocx init`,
|
|
31
|
+
* the GUI picker, key-login catalog, OAuth seeds, and metadata aliases cannot drift.
|
|
37
32
|
*/
|
|
38
33
|
export function buildInitProviders(): InitProvider[] {
|
|
39
|
-
|
|
40
|
-
// ChatGPT login (no key) — the default forward provider.
|
|
41
|
-
out.push({ id: "openai", label: "OpenAI — ChatGPT login (no key)", adapter: "openai-responses", baseUrl: "https://chatgpt.com/backend-api/codex", kind: "forward" });
|
|
42
|
-
// Real account logins (OAuth).
|
|
43
|
-
for (const id of Object.keys(OAUTH_PROVIDERS)) {
|
|
44
|
-
const pc = OAUTH_PROVIDERS[id].providerConfig;
|
|
45
|
-
out.push({ id, label: `${OAUTH_LABELS[id] ?? id} — account login`, adapter: pc.adapter, baseUrl: pc.baseUrl, kind: "oauth", defaultModel: pc.defaultModel });
|
|
46
|
-
}
|
|
47
|
-
// Key providers not in the catalog (native adapters / well-known endpoints).
|
|
48
|
-
out.push({ id: "openai-apikey", label: "OpenAI (API key)", adapter: "openai-responses", baseUrl: "https://api.openai.com/v1", kind: "key", dashboardUrl: "https://platform.openai.com/api-keys", defaultModel: "gpt-5.5" });
|
|
49
|
-
out.push({ id: "openrouter", label: "OpenRouter", adapter: "openai-chat", baseUrl: "https://openrouter.ai/api/v1", kind: "key", dashboardUrl: "https://openrouter.ai/keys" });
|
|
50
|
-
out.push({ id: "groq", label: "Groq", adapter: "openai-chat", baseUrl: "https://api.groq.com/openai/v1", kind: "key", dashboardUrl: "https://console.groq.com/keys" });
|
|
51
|
-
out.push({ id: "google", label: "Google Gemini", adapter: "google", baseUrl: "https://generativelanguage.googleapis.com", kind: "key", dashboardUrl: "https://aistudio.google.com/apikey", defaultModel: "gemini-3-pro" });
|
|
52
|
-
out.push({ id: "azure-openai", label: "Azure OpenAI", adapter: "azure", baseUrl: "https://{resource}.openai.azure.com/openai/deployments/{deployment}", kind: "key", dashboardUrl: "https://portal.azure.com" });
|
|
53
|
-
// The full API-key catalog (deepseek, mistral, kilo, minimax, … — same set the GUI shows).
|
|
54
|
-
for (const [id, p] of Object.entries(KEY_LOGIN_PROVIDERS)) {
|
|
55
|
-
out.push({ id, label: p.label, adapter: p.adapter, baseUrl: p.baseUrl, kind: "key", dashboardUrl: p.dashboardUrl, defaultModel: p.defaultModel });
|
|
56
|
-
}
|
|
57
|
-
// Local servers (usually no key).
|
|
58
|
-
out.push({ id: "ollama", label: "Ollama (local)", adapter: "openai-chat", baseUrl: "http://localhost:11434/v1", kind: "local" });
|
|
59
|
-
out.push({ id: "vllm", label: "vLLM (local)", adapter: "openai-chat", baseUrl: "http://localhost:8000/v1", kind: "local" });
|
|
60
|
-
out.push({ id: "lm-studio", label: "LM Studio (local)", adapter: "openai-chat", baseUrl: "http://localhost:1234/v1", kind: "local" });
|
|
61
|
-
return out;
|
|
34
|
+
return deriveInitProviders();
|
|
62
35
|
}
|
|
63
36
|
|
|
64
37
|
const KIND_HEADING: Record<InitKind, string> = {
|