mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
Gemini OAuth: resolve npm global shim install layouts (#27585)
* Changelog: credit session path fixes * test(gemini-oauth): cover npm global shim credential discovery * fix(gemini-oauth): resolve npm global shim install roots
This commit is contained in:
@@ -96,6 +96,41 @@ describe("extractGeminiCliCredentials", () => {
|
||||
return layout;
|
||||
}
|
||||
|
||||
function installNpmShimLayout(params: { oauth2Exists?: boolean; oauth2Content?: string }) {
|
||||
const binDir = join(rootDir, "fake", "npm-bin");
|
||||
const geminiPath = join(binDir, "gemini");
|
||||
const resolvedPath = geminiPath;
|
||||
const oauth2Path = join(
|
||||
binDir,
|
||||
"node_modules",
|
||||
"@google",
|
||||
"gemini-cli",
|
||||
"node_modules",
|
||||
"@google",
|
||||
"gemini-cli-core",
|
||||
"dist",
|
||||
"src",
|
||||
"code_assist",
|
||||
"oauth2.js",
|
||||
);
|
||||
process.env.PATH = binDir;
|
||||
|
||||
mockExistsSync.mockImplementation((p: string) => {
|
||||
const normalized = normalizePath(p);
|
||||
if (normalized === normalizePath(geminiPath)) {
|
||||
return true;
|
||||
}
|
||||
if (params.oauth2Exists && normalized === normalizePath(oauth2Path)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
mockRealpathSync.mockReturnValue(resolvedPath);
|
||||
if (params.oauth2Content !== undefined) {
|
||||
mockReadFileSync.mockReturnValue(params.oauth2Content);
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.clearAllMocks();
|
||||
originalPath = process.env.PATH;
|
||||
@@ -127,6 +162,19 @@ describe("extractGeminiCliCredentials", () => {
|
||||
});
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns null when oauth2.js cannot be found", async () => {
|
||||
installGeminiLayout({ oauth2Exists: false, readdir: [] });
|
||||
|
||||
|
||||
@@ -71,41 +71,45 @@ export function extractGeminiCliCredentials(): { clientId: string; clientSecret:
|
||||
}
|
||||
|
||||
const resolvedPath = realpathSync(geminiPath);
|
||||
const geminiCliDir = dirname(dirname(resolvedPath));
|
||||
|
||||
const searchPaths = [
|
||||
join(
|
||||
geminiCliDir,
|
||||
"node_modules",
|
||||
"@google",
|
||||
"gemini-cli-core",
|
||||
"dist",
|
||||
"src",
|
||||
"code_assist",
|
||||
"oauth2.js",
|
||||
),
|
||||
join(
|
||||
geminiCliDir,
|
||||
"node_modules",
|
||||
"@google",
|
||||
"gemini-cli-core",
|
||||
"dist",
|
||||
"code_assist",
|
||||
"oauth2.js",
|
||||
),
|
||||
];
|
||||
const geminiCliDirs = resolveGeminiCliDirs(geminiPath, resolvedPath);
|
||||
|
||||
let content: string | null = null;
|
||||
for (const p of searchPaths) {
|
||||
if (existsSync(p)) {
|
||||
content = readFileSync(p, "utf8");
|
||||
for (const geminiCliDir of geminiCliDirs) {
|
||||
const searchPaths = [
|
||||
join(
|
||||
geminiCliDir,
|
||||
"node_modules",
|
||||
"@google",
|
||||
"gemini-cli-core",
|
||||
"dist",
|
||||
"src",
|
||||
"code_assist",
|
||||
"oauth2.js",
|
||||
),
|
||||
join(
|
||||
geminiCliDir,
|
||||
"node_modules",
|
||||
"@google",
|
||||
"gemini-cli-core",
|
||||
"dist",
|
||||
"code_assist",
|
||||
"oauth2.js",
|
||||
),
|
||||
];
|
||||
|
||||
for (const p of searchPaths) {
|
||||
if (existsSync(p)) {
|
||||
content = readFileSync(p, "utf8");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (content) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!content) {
|
||||
const found = findFile(geminiCliDir, "oauth2.js", 10);
|
||||
if (found) {
|
||||
content = readFileSync(found, "utf8");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!content) {
|
||||
@@ -124,6 +128,30 @@ export function extractGeminiCliCredentials(): { clientId: string; clientSecret:
|
||||
return null;
|
||||
}
|
||||
|
||||
function resolveGeminiCliDirs(geminiPath: string, resolvedPath: string): string[] {
|
||||
const binDir = dirname(geminiPath);
|
||||
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 deduped: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const candidate of candidates) {
|
||||
const key =
|
||||
process.platform === "win32" ? candidate.replace(/\\/g, "/").toLowerCase() : candidate;
|
||||
if (seen.has(key)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(key);
|
||||
deduped.push(candidate);
|
||||
}
|
||||
return deduped;
|
||||
}
|
||||
|
||||
function findInPath(name: string): string | null {
|
||||
const exts = process.platform === "win32" ? [".cmd", ".bat", ".exe", ""] : [""];
|
||||
for (const dir of (process.env.PATH ?? "").split(delimiter)) {
|
||||
|
||||
Reference in New Issue
Block a user