fix: Windows npm path for Gemini OAuth + feat: WORKING.md bootstrap (#46368, #46367)#46371
fix: Windows npm path for Gemini OAuth + feat: WORKING.md bootstrap (#46368, #46367)#46371xuwei-xy wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR combines two fixes: a Windows-specific fallback for resolving Gemini CLI OAuth credentials via
Confidence Score: 3/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/google-gemini-cli-auth/oauth.test.ts
Line: 192-237
Comment:
**`process.env.PATH` not restored in finally block**
The test saves and restores `process.platform` and `APPDATA`, but it modifies `process.env.PATH = "/nonexistent"` without saving the original value or restoring it in the `finally` block. This leaks the corrupted `PATH` into all subsequently running tests in the same test file, which can cause `findInPath` to silently return `null` for every subsequent test that depends on the real PATH.
```suggestion
it("falls back to APPDATA npm global path on Windows when gemini is not in PATH", async () => {
const originalPlatform = process.platform;
const originalAppdata = process.env.APPDATA;
const originalPath = process.env.PATH;
try {
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
process.env.APPDATA = join(rootDir, "fake", "AppData", "Roaming");
process.env.PATH = "/nonexistent";
```
And in the `finally` block, add:
```
} finally {
Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true });
if (originalAppdata !== undefined) {
process.env.APPDATA = originalAppdata;
} else {
delete process.env.APPDATA;
}
if (originalPath !== undefined) {
process.env.PATH = originalPath;
} else {
delete process.env.PATH;
}
}
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: src/agents/workspace.ts
Line: 526-533
Comment:
**WORKING.md excluded from subagent/cron sessions**
`DEFAULT_WORKING_FILENAME` is not added to `MINIMAL_BOOTSTRAP_ALLOWLIST` (line 555–561), so WORKING.md will be silently stripped by `filterBootstrapFilesForSession` when the session is a subagent or cron job. If WORKING.md is meant to convey the current active task context, subagents spawned during that task would lack this context, which could impair their effectiveness.
If the exclusion is intentional (e.g., WORKING.md is private to the main session), a short comment near `MINIMAL_BOOTSTRAP_ALLOWLIST` clarifying the design decision would help future readers. Otherwise, add `DEFAULT_WORKING_FILENAME` to that set.
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: ce79b76 |
| it("falls back to APPDATA npm global path on Windows when gemini is not in PATH", async () => { | ||
| const originalPlatform = process.platform; | ||
| const originalAppdata = process.env.APPDATA; | ||
| try { | ||
| Object.defineProperty(process, "platform", { value: "win32", configurable: true }); | ||
| process.env.APPDATA = join(rootDir, "fake", "AppData", "Roaming"); | ||
| process.env.PATH = "/nonexistent"; | ||
|
|
||
| const appdataOauth2Path = join( | ||
| process.env.APPDATA, | ||
| "npm", | ||
| "node_modules", | ||
| "@google", | ||
| "gemini-cli", | ||
| "node_modules", | ||
| "@google", | ||
| "gemini-cli-core", | ||
| "dist", | ||
| "src", | ||
| "code_assist", | ||
| "oauth2.js", | ||
| ); | ||
|
|
||
| mockExistsSync.mockImplementation((p: string) => { | ||
| const normalized = normalizePath(p); | ||
| if (normalized === normalizePath(appdataOauth2Path)) { | ||
| return true; | ||
| } | ||
| return false; | ||
| }); | ||
| mockReadFileSync.mockReturnValue(FAKE_OAUTH2_CONTENT); | ||
|
|
||
| const { extractGeminiCliCredentials, clearCredentialsCache } = await import("./oauth.js"); | ||
| clearCredentialsCache(); | ||
| const result = extractGeminiCliCredentials(); | ||
|
|
||
| expectFakeCliCredentials(result); | ||
| } finally { | ||
| Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true }); | ||
| if (originalAppdata !== undefined) { | ||
| process.env.APPDATA = originalAppdata; | ||
| } else { | ||
| delete process.env.APPDATA; | ||
| } | ||
| } | ||
| }); |
There was a problem hiding this comment.
process.env.PATH not restored in finally block
The test saves and restores process.platform and APPDATA, but it modifies process.env.PATH = "/nonexistent" without saving the original value or restoring it in the finally block. This leaks the corrupted PATH into all subsequently running tests in the same test file, which can cause findInPath to silently return null for every subsequent test that depends on the real PATH.
| it("falls back to APPDATA npm global path on Windows when gemini is not in PATH", async () => { | |
| const originalPlatform = process.platform; | |
| const originalAppdata = process.env.APPDATA; | |
| try { | |
| Object.defineProperty(process, "platform", { value: "win32", configurable: true }); | |
| process.env.APPDATA = join(rootDir, "fake", "AppData", "Roaming"); | |
| process.env.PATH = "/nonexistent"; | |
| const appdataOauth2Path = join( | |
| process.env.APPDATA, | |
| "npm", | |
| "node_modules", | |
| "@google", | |
| "gemini-cli", | |
| "node_modules", | |
| "@google", | |
| "gemini-cli-core", | |
| "dist", | |
| "src", | |
| "code_assist", | |
| "oauth2.js", | |
| ); | |
| mockExistsSync.mockImplementation((p: string) => { | |
| const normalized = normalizePath(p); | |
| if (normalized === normalizePath(appdataOauth2Path)) { | |
| return true; | |
| } | |
| return false; | |
| }); | |
| mockReadFileSync.mockReturnValue(FAKE_OAUTH2_CONTENT); | |
| const { extractGeminiCliCredentials, clearCredentialsCache } = await import("./oauth.js"); | |
| clearCredentialsCache(); | |
| const result = extractGeminiCliCredentials(); | |
| expectFakeCliCredentials(result); | |
| } finally { | |
| Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true }); | |
| if (originalAppdata !== undefined) { | |
| process.env.APPDATA = originalAppdata; | |
| } else { | |
| delete process.env.APPDATA; | |
| } | |
| } | |
| }); | |
| it("falls back to APPDATA npm global path on Windows when gemini is not in PATH", async () => { | |
| const originalPlatform = process.platform; | |
| const originalAppdata = process.env.APPDATA; | |
| const originalPath = process.env.PATH; | |
| try { | |
| Object.defineProperty(process, "platform", { value: "win32", configurable: true }); | |
| process.env.APPDATA = join(rootDir, "fake", "AppData", "Roaming"); | |
| process.env.PATH = "/nonexistent"; |
And in the finally block, add:
} finally {
Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true });
if (originalAppdata !== undefined) {
process.env.APPDATA = originalAppdata;
} else {
delete process.env.APPDATA;
}
if (originalPath !== undefined) {
process.env.PATH = originalPath;
} else {
delete process.env.PATH;
}
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/google-gemini-cli-auth/oauth.test.ts
Line: 192-237
Comment:
**`process.env.PATH` not restored in finally block**
The test saves and restores `process.platform` and `APPDATA`, but it modifies `process.env.PATH = "/nonexistent"` without saving the original value or restoring it in the `finally` block. This leaks the corrupted `PATH` into all subsequently running tests in the same test file, which can cause `findInPath` to silently return `null` for every subsequent test that depends on the real PATH.
```suggestion
it("falls back to APPDATA npm global path on Windows when gemini is not in PATH", async () => {
const originalPlatform = process.platform;
const originalAppdata = process.env.APPDATA;
const originalPath = process.env.PATH;
try {
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
process.env.APPDATA = join(rootDir, "fake", "AppData", "Roaming");
process.env.PATH = "/nonexistent";
```
And in the `finally` block, add:
```
} finally {
Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true });
if (originalAppdata !== undefined) {
process.env.APPDATA = originalAppdata;
} else {
delete process.env.APPDATA;
}
if (originalPath !== undefined) {
process.env.PATH = originalPath;
} else {
delete process.env.PATH;
}
}
```
How can I resolve this? If you propose a fix, please make it concise.| // WORKING.md is opt-in: only loaded when present in the workspace. | ||
| const workingPath = path.join(resolvedDir, DEFAULT_WORKING_FILENAME); | ||
| try { | ||
| await fs.access(workingPath); | ||
| entries.push({ name: DEFAULT_WORKING_FILENAME, filePath: workingPath }); | ||
| } catch { | ||
| // WORKING.md not present — skip | ||
| } |
There was a problem hiding this comment.
WORKING.md excluded from subagent/cron sessions
DEFAULT_WORKING_FILENAME is not added to MINIMAL_BOOTSTRAP_ALLOWLIST (line 555–561), so WORKING.md will be silently stripped by filterBootstrapFilesForSession when the session is a subagent or cron job. If WORKING.md is meant to convey the current active task context, subagents spawned during that task would lack this context, which could impair their effectiveness.
If the exclusion is intentional (e.g., WORKING.md is private to the main session), a short comment near MINIMAL_BOOTSTRAP_ALLOWLIST clarifying the design decision would help future readers. Otherwise, add DEFAULT_WORKING_FILENAME to that set.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/workspace.ts
Line: 526-533
Comment:
**WORKING.md excluded from subagent/cron sessions**
`DEFAULT_WORKING_FILENAME` is not added to `MINIMAL_BOOTSTRAP_ALLOWLIST` (line 555–561), so WORKING.md will be silently stripped by `filterBootstrapFilesForSession` when the session is a subagent or cron job. If WORKING.md is meant to convey the current active task context, subagents spawned during that task would lack this context, which could impair their effectiveness.
If the exclusion is intentional (e.g., WORKING.md is private to the main session), a short comment near `MINIMAL_BOOTSTRAP_ALLOWLIST` clarifying the design decision would help future readers. Otherwise, add `DEFAULT_WORKING_FILENAME` to that set.
How can I resolve this? If you propose a fix, please make it concise.
Combines fixes from upstream PRs:
Merged from openclaw/openclaw PRs.