Skip to content

Gemini OAuth: resolve npm global shim install layouts#26839

Closed
Phineas1500 wants to merge 1 commit intoopenclaw:mainfrom
Phineas1500:fix/gemini-cli-detect-npm-shims
Closed

Gemini OAuth: resolve npm global shim install layouts#26839
Phineas1500 wants to merge 1 commit intoopenclaw:mainfrom
Phineas1500:fix/gemini-cli-detect-npm-shims

Conversation

@Phineas1500
Copy link
Contributor

@Phineas1500 Phineas1500 commented Feb 25, 2026

Summary

  • make Gemini CLI OAuth credential discovery robust across npm global shim layouts
  • resolve candidate install roots from both resolved executable and PATH bin directory
  • add regression coverage for npm-shim installs where gemini is a wrapper entry

Why

Fixes onboarding/auth failures where Gemini CLI is installed and on PATH, but OpenClaw reports it as missing.

Fixes #9831

Testing

  • pnpm vitest run extensions/google-gemini-cli-auth/oauth.test.ts
  • pnpm test:fast

Greptile Summary

Robustifies Gemini CLI OAuth credential discovery to work across various npm global installation layouts (shims, symlinks, standard installs). The implementation now resolves multiple candidate install roots from both the resolved executable path and the PATH bin directory, fixing onboarding/auth failures where Gemini CLI is correctly installed and on PATH but OpenClaw couldn't locate its OAuth credentials.

Key changes:

  • Extracted new resolveGeminiCliDirs function that generates multiple candidate directories for credential discovery
  • Now checks both symlink targets (dirname(dirname(resolvedPath))) and npm shim layouts (binDir/node_modules/@google/gemini-cli)
  • Added deduplication logic to avoid checking the same path multiple times (with Windows case-insensitivity handling)
  • Added regression test coverage for npm global shim installs

The fix maintains backward compatibility with existing install layouts while expanding support for edge cases.

Confidence Score: 5/5

  • This PR is safe to merge with no identified risks.
  • The changes are narrowly scoped to credential discovery logic, maintain backward compatibility with existing layouts, and include comprehensive test coverage. The implementation properly handles edge cases (Windows case-insensitivity, path deduplication) and follows the repository's TypeScript and testing conventions.
  • No files require special attention

Last reviewed commit: ce779ef

Copilot AI review requested due to automatic review settings February 25, 2026 20:18
@openclaw-barnacle openclaw-barnacle bot added extensions: google-gemini-cli-auth Extension: google-gemini-cli-auth size: S labels Feb 25, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes Gemini CLI OAuth credential discovery more robust across npm global installation “shim” layouts, so OpenClaw can locate and extract the bundled OAuth client credentials even when gemini on PATH is a wrapper script.

Changes:

  • Expand credential discovery to consider multiple candidate Gemini CLI install roots derived from both the resolved executable path and the PATH bin directory.
  • Fall back to scanning for oauth2.js across those candidate roots when the known paths don’t match.
  • Add a regression test covering an npm global shim layout.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
extensions/google-gemini-cli-auth/oauth.ts Adds multi-root resolution logic for locating Gemini CLI’s bundled oauth2.js across shim layouts.
extensions/google-gemini-cli-auth/oauth.test.ts Adds a regression test for npm global shim installs to ensure credentials can still be extracted.

Comment on lines +133 to +140
const candidates = [
dirname(dirname(resolvedPath)),
join(dirname(resolvedPath), "node_modules", "@google", "gemini-cli"),
join(binDir, "node_modules", "@google", "gemini-cli"),
join(dirname(binDir), "node_modules", "@google", "gemini-cli"),
join(dirname(binDir), "lib", "node_modules", "@google", "gemini-cli"),
];

Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolveGeminiCliDirs tries dirname(dirname(resolvedPath)) first. When resolvedPath is an npm shim/wrapper (common for global installs, especially on Windows), this can be a very broad directory (e.g. %APPDATA%//usr/local). The code then falls back to findFile(candidate, "oauth2.js", 10) against that broad directory, which (a) can trigger a large synchronous filesystem traversal before the later correct candidates are tried, and (b) increases the chance of accidentally matching an unrelated oauth2.js elsewhere on disk. Reorder candidates to try the most specific/likely package roots first (e.g. binDir/.../@google/gemini-cli, .../lib/node_modules/...), and/or only include the dirname(dirname(resolvedPath)) candidate (and/or recursive search) when resolvedPath appears to be inside the actual @google/gemini-cli install (e.g. contains node_modules/@google/gemini-cli).

Suggested change
const candidates = [
dirname(dirname(resolvedPath)),
join(dirname(resolvedPath), "node_modules", "@google", "gemini-cli"),
join(binDir, "node_modules", "@google", "gemini-cli"),
join(dirname(binDir), "node_modules", "@google", "gemini-cli"),
join(dirname(binDir), "lib", "node_modules", "@google", "gemini-cli"),
];
const candidates: string[] = [
// Prefer specific, likely package roots first.
join(dirname(resolvedPath), "node_modules", "@google", "gemini-cli"),
join(binDir, "node_modules", "@google", "gemini-cli"),
join(dirname(binDir), "node_modules", "@google", "gemini-cli"),
join(dirname(binDir), "lib", "node_modules", "@google", "gemini-cli"),
];
// Only consider the broader parent-parent directory when it appears to be
// within an actual @google/gemini-cli installation, to avoid scanning very
// large, unrelated directories (for example, global npm shim locations).
const parentParent = dirname(dirname(resolvedPath));
const normalizedParentParent =
process.platform === "win32"
? parentParent.replace(/\\/g, "/").toLowerCase()
: parentParent;
if (/node_modules\/@google\/gemini-cli/i.test(normalizedParentParent)) {
candidates.push(parentParent);
}

Copilot uses AI. Check for mistakes.
Comment on lines +165 to +176
it("extracts credentials when PATH entry is an npm global shim", async () => {
installNpmShimLayout({ oauth2Exists: true, oauth2Content: FAKE_OAUTH2_CONTENT });

const { extractGeminiCliCredentials, clearCredentialsCache } = await import("./oauth.js");
clearCredentialsCache();
const result = extractGeminiCliCredentials();

expect(result).toEqual({
clientId: FAKE_CLIENT_ID,
clientSecret: FAKE_CLIENT_SECRET,
});
});
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The npm-shim regression test validates the happy path, but it won’t catch a performance regression where extractGeminiCliCredentials() still falls back to the recursive findFile() scan before hitting the shim candidate path. Consider asserting that readdirSync/mockReaddirSync is not called in this test (or making mockReaddirSync throw and asserting it wasn’t invoked), so the test enforces that known-path resolution succeeds without a deep directory walk.

Copilot uses AI. Check for mistakes.
@arosstale

This comment was marked as spam.

@vincentkoc
Copy link
Member

vincentkoc commented Feb 26, 2026

Thanks for the detailed work here. Closing this as a duplicate of #6045 to preserve credit to the original implementation by @ehgamemo. Your refactor and expanded coverage were useful for validating the root-cause family, but we’re keeping attribution anchored to the earlier thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: google-gemini-cli-auth Extension: google-gemini-cli-auth size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: openclaw config doesn't see gemini-cli installation

4 participants