@koreanhole/opencodex 3.2.7 → 3.2.9

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/AGENTS_INSTALL.md CHANGED
@@ -90,7 +90,6 @@ bun run verify:upstream
90
90
  bun run typecheck
91
91
  bun test tests/e2e/cline-only.test.ts
92
92
  bun run test:e2e:gemini
93
- bun run test:e2e:gemini-oauth
94
93
  bun run test:e2e:gemini-tool
95
94
  bun run test:e2e:gemini-reasoning
96
95
  bun run test:e2e:codex-restore
@@ -99,6 +98,4 @@ bun run test:e2e:codex-restore
99
98
  Report whether each evidence path used a deterministic fixture or a real
100
99
  vendor binary/account. The Gemini command above is a hard live gate: missing
101
100
  binaries, authentication, a Flash Lite model, or cleanup evidence must fail.
102
- The OAuth gate additionally removes API-key/Vertex environment values and the
103
- vendor `.env`.
104
101
  Never report a skipped or synthetic run as a real login.
package/README.md CHANGED
@@ -160,7 +160,6 @@ bun run typecheck
160
160
  bun test tests
161
161
  bun run test:e2e
162
162
  bun run test:e2e:gemini
163
- bun run test:e2e:gemini-oauth
164
163
  bun run test:e2e:gemini-slow
165
164
  bun run test:e2e:gemini-parent-tool
166
165
  bun run privacy:scan
@@ -172,9 +171,6 @@ a real Codex client and a real authenticated Gemini CLI. It runs both the minima
172
171
  completion and a 12-second pre-event slow-start regression. It fails, not skips,
173
172
  on a missing binary, authentication, Flash Lite model, response marker, timeout,
174
173
  or child cleanup assertion. `test:e2e:gemini-slow` runs only the slow-start case.
175
- `test:e2e:gemini-oauth` strips API-key and Vertex environment values, removes the
176
- vendor `.env`, and requires a real OAuth-backed completion through Codex and the
177
- installed Gemini CLI.
178
174
 
179
175
  Optional overrides are `CLINE_BIN_PATH`, `GEMINI_BIN_PATH`, and
180
176
  `CODEX_BIN_PATH`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koreanhole/opencodex",
3
- "version": "3.2.7",
3
+ "version": "3.2.9",
4
4
  "description": "Full opencodex runtime with Cline JSON and Gemini CLI headless provider adapters",
5
5
  "type": "module",
6
6
  "main": "./bin/package-main.mjs",
@@ -31,7 +31,6 @@
31
31
  "test": "bun test tests",
32
32
  "test:e2e": "bun test tests/e2e/cline-only.test.ts",
33
33
  "test:e2e:gemini": "bun test ./tests/e2e/gemini-codex.live.ts -t \"full-stack:\"",
34
- "test:e2e:gemini-oauth": "bun test ./tests/e2e/gemini-codex.live.ts -t \"uses an existing Gemini CLI OAuth login without an API key\"",
35
34
  "test:e2e:gemini-slow": "bun test ./tests/e2e/gemini-codex.live.ts -t \"full-stack: a slow Gemini start\"",
36
35
  "test:e2e:gemini-tool": "bun test ./tests/e2e/gemini-codex.live.ts -t \"one short Gemini turn leaves the workspace unchanged\"",
37
36
  "test:e2e:gemini-multiturn": "bun test ./tests/e2e/gemini-codex.live.ts -t \"reuses a live Codex multi-turn session\"",
@@ -188,8 +188,8 @@ function isLegacyProviderRow(name: AdapterProviderId, value: unknown): boolean {
188
188
  return row?.adapter === name;
189
189
  }
190
190
 
191
- function uniqueModels(metadata: AdapterModelMetadata | undefined): string[] {
192
- return [...new Set(["default", ...(metadata?.models ?? [])].filter(Boolean))];
191
+ function uniqueModels(name: AdapterProviderId, metadata: AdapterModelMetadata | undefined): string[] {
192
+ return [...new Set([...(name === "cline-cli" ? ["default"] : []), ...(metadata?.models ?? [])].filter(Boolean))];
193
193
  }
194
194
 
195
195
  export function buildManagedProviderRow(
@@ -208,7 +208,7 @@ export function buildManagedProviderRow(
208
208
  allowPrivateNetwork: true,
209
209
  liveModels: true,
210
210
  defaultModel: "default",
211
- models: uniqueModels(metadata),
211
+ models: uniqueModels(name, metadata),
212
212
  ...(metadata.modelDisplayNames ? { modelDisplayNames: metadata.modelDisplayNames } : {}),
213
213
  ...(metadata.modelContextWindows ? { modelContextWindows: metadata.modelContextWindows } : {}),
214
214
  ...(metadata.modelInputModalities ? { modelInputModalities: metadata.modelInputModalities } : {}),
@@ -204,6 +204,7 @@ export async function discoverGeminiLiveModels(options: GeminiLiveModelsOptions
204
204
  }
205
205
 
206
206
  const runtimeIds = runtime?.map(model => model.modelId) ?? [];
207
+ const runtimeAvailable = runtimeIds.length > 0;
207
208
  const ids = runtimeIds.length > 0 ? runtimeIds : Object.keys(metadata.capabilities);
208
209
  if (ids.length === 0) {
209
210
  failureCache.set(fingerprint, timestamp);
@@ -252,9 +253,11 @@ export async function discoverGeminiLiveModels(options: GeminiLiveModelsOptions
252
253
  loadSession: false,
253
254
  };
254
255
  });
255
- const modelInfoWithDefault = [defaultModelInfo(metadata, modelInfo), ...modelInfo.filter(model => model.id !== "default")];
256
+ const modelInfoWithDefault = runtimeAvailable
257
+ ? modelInfo
258
+ : [defaultModelInfo(metadata, modelInfo), ...modelInfo.filter(model => model.id !== "default")];
256
259
  const warning = metadata.warning
257
- ?? (runtimeError ? "Gemini runtime model discovery failed; using installed CLI metadata." : undefined);
260
+ ?? (runtimeAvailable ? undefined : "Gemini runtime model discovery failed; using installed CLI metadata.");
258
261
  cache.set(fingerprint, { models: ids, modelInfo: modelInfoWithDefault, source: "models", fetchedAt: timestamp, ...(warning ? { metadataWarning: warning } : {}) });
259
262
  failureCache.delete(fingerprint);
260
263
  return { ok: true, models: ids, modelInfo: modelInfoWithDefault, source: "models", fingerprint, ...(warning ? { metadataWarning: warning } : {}) };
@@ -178,12 +178,14 @@ function defaultModel(): OcxProviderModel {
178
178
  return { id: "default" };
179
179
  }
180
180
 
181
- function normalizedModels(models: readonly OcxProviderModel[]): OcxProviderModel[] {
181
+ function normalizedModels(models: readonly OcxProviderModel[], includeDefault = true): OcxProviderModel[] {
182
182
  const defaultInfo = models.find(model => model.id === "default");
183
- const out: OcxProviderModel[] = [{ ...defaultModel(), ...(defaultInfo ?? {}), id: "default" }];
184
- const seen = new Set<string>(["default"]);
183
+ const out: OcxProviderModel[] = includeDefault
184
+ ? [{ ...defaultModel(), ...(defaultInfo ?? {}), id: "default" }]
185
+ : [];
186
+ const seen = new Set<string>(includeDefault ? ["default"] : []);
185
187
  for (const model of models) {
186
- if (model.id === "default" || seen.has(model.id)) continue;
188
+ if ((includeDefault && model.id === "default") || seen.has(model.id)) continue;
187
189
  seen.add(model.id);
188
190
  out.push(model);
189
191
  }
@@ -207,7 +209,7 @@ export async function discoverCodexCatalogModels(options: CodexCatalogDiscoveryO
207
209
  if (!gemini.ok && gemini.reason !== "not_found" && env.OPENCODEX_GEMINI_ACP?.trim() !== "0") warnings.push(`Gemini: ${gemini.detail}`);
208
210
  if (gemini.ok && gemini.metadataWarning) warnings.push(`Gemini metadata: ${gemini.metadataWarning}`);
209
211
  const clineModels = normalizedModels(cline.ok ? cline.models.map(id => ({ id })) : []);
210
- const geminiModels = normalizedModels(gemini.ok ? gemini.modelInfo : []);
212
+ const geminiModels = normalizedModels(gemini.ok ? gemini.modelInfo : [], false);
211
213
  const geminiDefault = geminiModels.find(model => model.id === "default");
212
214
  const geminiContexts = geminiModels.flatMap(model => model.contextWindowTokens !== undefined ? [model.contextWindowTokens] : []);
213
215
  if (geminiDefault && geminiContexts.length > 0) {
@@ -118,7 +118,7 @@ async function discoverModels(
118
118
  }
119
119
  if (gemini) {
120
120
  models["gemini-cli"] = gemini.ok
121
- ? ["default", ...gemini.modelInfo.filter(model => model.id !== "default")]
121
+ ? gemini.modelInfo.map(model => model.id)
122
122
  : ["default"];
123
123
  }
124
124
  return models;
@@ -379,6 +379,16 @@ const PROVIDER_LABELS: Record<ProviderId, string> = {
379
379
  [GEMINI_PROVIDER_ID]: "Gemini CLI",
380
380
  };
381
381
 
382
+ function listedProviderEntries(
383
+ known: ProviderModels,
384
+ providerId: ProviderId,
385
+ ): readonly ProviderModelEntry[] {
386
+ const entries = known[providerId] ?? [];
387
+ if (providerId === GEMINI_PROVIDER_ID && entries.length > 0) return entries;
388
+ const defaultEntry = entries.find(entry => modelIdOf(entry) === "default") ?? "default";
389
+ return [defaultEntry, ...entries.filter(entry => modelIdOf(entry) !== "default")];
390
+ }
391
+
382
392
  export function modelList(
383
393
  known: string[] | ProviderModels,
384
394
  scope?: ProviderId,
@@ -394,8 +404,7 @@ export function modelList(
394
404
  }));
395
405
  }
396
406
  if (scope) {
397
- const defaultEntry = (known[scope] ?? []).find(entry => modelIdOf(entry) === "default") ?? "default";
398
- const entries = [defaultEntry, ...(known[scope] ?? []).filter(entry => modelIdOf(entry) !== "default")];
407
+ const entries = listedProviderEntries(known, scope);
399
408
  const rows: Array<Record<string, unknown>> = [];
400
409
  const seen = new Set<string>();
401
410
  for (const entry of entries) {
@@ -440,8 +449,7 @@ export function modelList(
440
449
  display_name: "Configured default provider",
441
450
  }];
442
451
  for (const providerId of [CLINE_PROVIDER_ID, GEMINI_PROVIDER_ID] as const) {
443
- const defaultEntry = (known[providerId] ?? []).find(entry => modelIdOf(entry) === "default") ?? "default";
444
- const entries = [defaultEntry, ...(known[providerId] ?? []).filter(entry => modelIdOf(entry) !== "default")];
452
+ const entries = listedProviderEntries(known, providerId);
445
453
  const seen = new Set<string>();
446
454
  for (const entry of entries) {
447
455
  const id = modelIdOf(entry);