@koreanhole/opencodex 3.2.5 → 3.2.7
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 +12 -5
- package/README.md +9 -2
- package/package.json +2 -1
- package/src/adapters/cli-agent/gemini/headless.ts +38 -4
package/AGENTS_INSTALL.md
CHANGED
|
@@ -26,7 +26,7 @@ To use Gemini instead, authenticate it through its official CLI and select the
|
|
|
26
26
|
provider:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
gemini #
|
|
29
|
+
gemini # complete the official OAuth login or configure an API key
|
|
30
30
|
ocx provider add gemini-cli --set-default
|
|
31
31
|
ocx start
|
|
32
32
|
ocx codex sync
|
|
@@ -52,10 +52,14 @@ or through the documented default path; unrelated Codex settings are not changed
|
|
|
52
52
|
mode-0600 request-scoped copy; remove that copy after the turn.
|
|
53
53
|
- Do not import or replay Gemini OAuth/API-key material; the Gemini CLI remains
|
|
54
54
|
the authentication authority.
|
|
55
|
-
-
|
|
56
|
-
|
|
57
|
-
OAuth
|
|
58
|
-
|
|
55
|
+
- Gemini headless turns support explicit Gemini Developer API, Vertex AI GenAI,
|
|
56
|
+
or the operator's existing Gemini CLI OAuth login. opencodex does not automate
|
|
57
|
+
OAuth login or copy OAuth tokens. OAuth reuse requires an account and tier
|
|
58
|
+
eligible for the installed CLI's service, and the operator remains responsible
|
|
59
|
+
for vendor terms.
|
|
60
|
+
- Explicit process-environment API-key, Vertex, or gateway configuration takes
|
|
61
|
+
precedence over automatic OAuth selection. An explicit
|
|
62
|
+
`GOOGLE_GENAI_USE_GCA=true` selects OAuth.
|
|
59
63
|
- The proxy writes under `$OPENCODEX_HOME` (default `~/.opencodex`) and may update
|
|
60
64
|
the active opencodex Codex model catalog selected by `model_catalog_json`.
|
|
61
65
|
|
|
@@ -86,6 +90,7 @@ bun run verify:upstream
|
|
|
86
90
|
bun run typecheck
|
|
87
91
|
bun test tests/e2e/cline-only.test.ts
|
|
88
92
|
bun run test:e2e:gemini
|
|
93
|
+
bun run test:e2e:gemini-oauth
|
|
89
94
|
bun run test:e2e:gemini-tool
|
|
90
95
|
bun run test:e2e:gemini-reasoning
|
|
91
96
|
bun run test:e2e:codex-restore
|
|
@@ -94,4 +99,6 @@ bun run test:e2e:codex-restore
|
|
|
94
99
|
Report whether each evidence path used a deterministic fixture or a real
|
|
95
100
|
vendor binary/account. The Gemini command above is a hard live gate: missing
|
|
96
101
|
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`.
|
|
97
104
|
Never report a skipped or synthetic run as a real login.
|
package/README.md
CHANGED
|
@@ -31,8 +31,11 @@ npm install -g @google/gemini-cli
|
|
|
31
31
|
gemini
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
Gemini headless turns support an explicit Gemini Developer API key, Vertex AI
|
|
35
|
+
GenAI configuration, or the operator's existing Gemini CLI OAuth login. opencodex
|
|
36
|
+
does not automate Google OAuth. OAuth reuse requires an account and tier eligible
|
|
37
|
+
for the installed Gemini CLI's service; explicit API-key or Vertex environment
|
|
38
|
+
configuration takes precedence over automatic OAuth selection.
|
|
36
39
|
|
|
37
40
|
## Start
|
|
38
41
|
|
|
@@ -157,6 +160,7 @@ bun run typecheck
|
|
|
157
160
|
bun test tests
|
|
158
161
|
bun run test:e2e
|
|
159
162
|
bun run test:e2e:gemini
|
|
163
|
+
bun run test:e2e:gemini-oauth
|
|
160
164
|
bun run test:e2e:gemini-slow
|
|
161
165
|
bun run test:e2e:gemini-parent-tool
|
|
162
166
|
bun run privacy:scan
|
|
@@ -168,6 +172,9 @@ a real Codex client and a real authenticated Gemini CLI. It runs both the minima
|
|
|
168
172
|
completion and a 12-second pre-event slow-start regression. It fails, not skips,
|
|
169
173
|
on a missing binary, authentication, Flash Lite model, response marker, timeout,
|
|
170
174
|
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.
|
|
171
178
|
|
|
172
179
|
Optional overrides are `CLINE_BIN_PATH`, `GEMINI_BIN_PATH`, and
|
|
173
180
|
`CODEX_BIN_PATH`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koreanhole/opencodex",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.7",
|
|
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,6 +31,7 @@
|
|
|
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\"",
|
|
34
35
|
"test:e2e:gemini-slow": "bun test ./tests/e2e/gemini-codex.live.ts -t \"full-stack: a slow Gemini start\"",
|
|
35
36
|
"test:e2e:gemini-tool": "bun test ./tests/e2e/gemini-codex.live.ts -t \"one short Gemini turn leaves the workspace unchanged\"",
|
|
36
37
|
"test:e2e:gemini-multiturn": "bun test ./tests/e2e/gemini-codex.live.ts -t \"reuses a live Codex multi-turn session\"",
|
|
@@ -60,6 +60,8 @@ const FORWARDED_ENV_KEYS = [
|
|
|
60
60
|
] as const;
|
|
61
61
|
|
|
62
62
|
const AUTH_FILE_NAMES = ["oauth_creds.json", "google_accounts.json", ".env"] as const;
|
|
63
|
+
const OAUTH_AUTH_FILE_NAMES = ["oauth_creds.json", "google_accounts.json"] as const;
|
|
64
|
+
type GeminiHeadlessAuth = "oauth" | "environment" | "default";
|
|
63
65
|
|
|
64
66
|
export interface GeminiHeadlessDeps extends CliAgentTurnDeps {
|
|
65
67
|
env?: NodeJS.ProcessEnv;
|
|
@@ -82,6 +84,10 @@ function nonEmpty(value: unknown): string | undefined {
|
|
|
82
84
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
function enabled(value: unknown): boolean {
|
|
88
|
+
return nonEmpty(value)?.toLowerCase() === "true";
|
|
89
|
+
}
|
|
90
|
+
|
|
85
91
|
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
86
92
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
87
93
|
? value as Record<string, unknown>
|
|
@@ -256,8 +262,12 @@ function geminiMcpSettings(
|
|
|
256
262
|
function geminiSystemSettings(
|
|
257
263
|
reasoning?: GeminiReasoningSelection,
|
|
258
264
|
mcpServers?: Record<string, unknown>,
|
|
265
|
+
auth: GeminiHeadlessAuth = "default",
|
|
259
266
|
): string {
|
|
260
267
|
const settings = JSON.parse(GEMINI_SYSTEM_SETTINGS) as Record<string, unknown>;
|
|
268
|
+
if (auth === "oauth") {
|
|
269
|
+
settings.security = { auth: { selectedType: "oauth-personal" } };
|
|
270
|
+
}
|
|
261
271
|
settings.tools = { core: mcpServers ? ["mcp_opencodex_*"] : [] };
|
|
262
272
|
settings.mcp = mcpServers
|
|
263
273
|
? { allowed: [GEMINI_MCP_SERVER_NAME], excluded: [] }
|
|
@@ -300,15 +310,34 @@ export async function stageGeminiHeadlessWorkspace(
|
|
|
300
310
|
const catalog = options.catalog ?? [];
|
|
301
311
|
const capturePath = catalog.length > 0 ? join(root, "tool-intents.ndjson") : undefined;
|
|
302
312
|
const mcpServers = geminiMcpSettings(catalog, options.mcp, catalogPath, capturePath);
|
|
313
|
+
const sourceEnv = options.sourceEnv ?? process.env;
|
|
314
|
+
const realHome = nonEmpty(sourceEnv.HOME) ?? nonEmpty(sourceEnv.USERPROFILE) ?? homedir();
|
|
315
|
+
let oauthAvailable = false;
|
|
316
|
+
try {
|
|
317
|
+
await lstat(join(realHome, ".gemini", "oauth_creds.json"));
|
|
318
|
+
oauthAvailable = true;
|
|
319
|
+
} catch {
|
|
320
|
+
// No existing vendor OAuth login to reuse.
|
|
321
|
+
}
|
|
322
|
+
const auth: GeminiHeadlessAuth = enabled(sourceEnv.GOOGLE_GENAI_USE_GCA)
|
|
323
|
+
? "oauth"
|
|
324
|
+
: nonEmpty(sourceEnv.GEMINI_API_KEY)
|
|
325
|
+
|| nonEmpty(sourceEnv.GOOGLE_API_KEY)
|
|
326
|
+
|| enabled(sourceEnv.GOOGLE_GENAI_USE_VERTEXAI)
|
|
327
|
+
|| nonEmpty(sourceEnv.GOOGLE_GEMINI_BASE_URL)
|
|
328
|
+
? "environment"
|
|
329
|
+
: oauthAvailable
|
|
330
|
+
? "oauth"
|
|
331
|
+
: "default";
|
|
303
332
|
const exclusive = options.root === undefined;
|
|
304
333
|
const writeOptions = { encoding: "utf8" as const, mode: 0o600, ...(exclusive ? { flag: "wx" as const } : {}) };
|
|
305
334
|
await mkdir(geminiConfigDir, { recursive: true, mode: 0o700 });
|
|
306
335
|
await Promise.all([
|
|
307
|
-
writeFile(settingsPath, geminiSystemSettings(reasoning, mcpServers), writeOptions),
|
|
336
|
+
writeFile(settingsPath, geminiSystemSettings(reasoning, mcpServers, auth), writeOptions),
|
|
308
337
|
writeFile(catalogPath, JSON.stringify(catalog), writeOptions),
|
|
309
338
|
...(capturePath ? [writeFile(capturePath, "", writeOptions)] : []),
|
|
310
339
|
]);
|
|
311
|
-
await linkGeminiAuth(geminiConfigDir,
|
|
340
|
+
await linkGeminiAuth(geminiConfigDir, sourceEnv, auth);
|
|
312
341
|
return {
|
|
313
342
|
root,
|
|
314
343
|
geminiHome,
|
|
@@ -319,10 +348,15 @@ export async function stageGeminiHeadlessWorkspace(
|
|
|
319
348
|
};
|
|
320
349
|
}
|
|
321
350
|
|
|
322
|
-
async function linkGeminiAuth(
|
|
351
|
+
async function linkGeminiAuth(
|
|
352
|
+
targetDir: string,
|
|
353
|
+
sourceEnv: NodeJS.ProcessEnv,
|
|
354
|
+
auth: GeminiHeadlessAuth,
|
|
355
|
+
): Promise<void> {
|
|
323
356
|
const realHome = nonEmpty(sourceEnv.HOME) ?? nonEmpty(sourceEnv.USERPROFILE) ?? homedir();
|
|
324
357
|
const realGemini = join(realHome, ".gemini");
|
|
325
|
-
|
|
358
|
+
const names = auth === "oauth" ? OAUTH_AUTH_FILE_NAMES : AUTH_FILE_NAMES;
|
|
359
|
+
await Promise.all(names.map(async name => {
|
|
326
360
|
const target = join(realGemini, name);
|
|
327
361
|
try {
|
|
328
362
|
await lstat(target);
|