Skip to content

fix: Windows npm path for Gemini OAuth + feat: WORKING.md bootstrap (#46368, #46367)#46371

Closed
xuwei-xy wants to merge 1 commit intoopenclaw:mainfrom
xuwei-xy:batch4-pr46368-pr46367-v2
Closed

fix: Windows npm path for Gemini OAuth + feat: WORKING.md bootstrap (#46368, #46367)#46371
xuwei-xy wants to merge 1 commit intoopenclaw:mainfrom
xuwei-xy:batch4-pr46368-pr46367-v2

Conversation

@xuwei-xy
Copy link
Copy Markdown

@openclaw-barnacle openclaw-barnacle Bot added extensions: google-gemini-cli-auth Extension: google-gemini-cli-auth agents Agent runtime and tooling size: S labels Mar 14, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 14, 2026

Greptile Summary

This PR combines two fixes: a Windows-specific fallback for resolving Gemini CLI OAuth credentials via %APPDATA%\npm\node_modules when gemini is not on PATH, and opt-in loading of WORKING.md as a working-memory bootstrap file in agent workspaces.

  • oauth.ts: Clean and well-guarded — the APPDATA fallback is added both in extractGeminiCliCredentials (for the no-PATH case) and in resolveGeminiCliDirs (for the has-PATH case on Windows). No issues found.
  • oauth.test.ts: New test correctly exercises the fallback, but process.env.PATH is set to "/nonexistent" inside the try block and is never saved or restored in the finally block, leaking a broken PATH to all subsequent tests in the file.
  • workspace.ts: WORKING.md opt-in loading follows the same pattern as MEMORY.md and is straightforward. However, DEFAULT_WORKING_FILENAME was not added to MINIMAL_BOOTSTRAP_ALLOWLIST, so subagent and cron sessions will not receive this file — worth confirming whether this is intentional.
  • workspace.test.ts: Two new tests covering presence and absence of WORKING.md are correct and sufficient.

Confidence Score: 3/5

  • Mostly safe to merge; one test isolation bug (PATH leak) should be fixed before merging to avoid flaky downstream tests.
  • The production code changes are solid and well-scoped. The score is lowered primarily because of the process.env.PATH leak in the new test, which can silently break other tests in the same suite. There is also a design question around whether WORKING.md should be visible to subagent/cron sessions that should be clarified.
  • extensions/google-gemini-cli-auth/oauth.test.ts — PATH environment variable is not restored in the finally block.
Prompt To Fix All 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.

---

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

Comment on lines +192 to +237
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;
}
}
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Suggested change
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.

Comment thread src/agents/workspace.ts
Comment on lines +526 to +533
// 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
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 28, 2026

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close as superseded. The OAuth half targets the removed extensions/google-gemini-cli-auth path while the active Google OAuth implementation has since received separate merged Windows/bundled-install fixes, and the remaining no-PATH APPDATA fallback would need a fresh security-reviewed proposal against extensions/google. The WORKING.md half is superseded by the maintainer decision on #9386 to keep that optional working-memory behavior in hooks/plugins/ClawHub rather than core.

So I’m closing this here and keeping the remaining discussion on the canonical linked item.

Review details

Best possible solution:

Close this combined PR. Keep the separately merged active-provider OAuth fixes, leave WORKING.md working-memory injection to the documented hook/plugin path, and only revisit the APPDATA no-PATH fallback as a narrow Google-provider security-reviewed proposal if maintainers explicitly want that behavior.

Security review:

Security review needs attention: The diff broadens OAuth credential-source discovery and should not be merged without maintainer security review, especially because the branch is already superseded.

  • [medium] Review APPDATA OAuth credential trust boundary — extensions/google-gemini-cli-auth/oauth.ts:84
    The proposed fallback reads Gemini CLI OAuth constants from %APPDATA%\npm\node_modules\@google\gemini-cli even when no gemini executable was found on PATH. That selects a user-writable package tree not anchored to an executable OpenClaw discovered, so maintainers should validate it against the Gemini CLI OAuth trust model before accepting any replacement.
    Confidence: 0.8

What I checked:

Likely related people:

  • vincentkoc: Authored merged Gemini CLI personal OAuth support, co-authored adjacent Windows/bundled-install OAuth fixes, and closed the linked Windows OAuth issue as fixed. (role: recent Google OAuth maintainer; confidence: high; commits: f02e43518826, 9dd449045a79, ef5f47bd3925; files: extensions/google/oauth.credentials.ts, extensions/google/oauth.project.ts, extensions/google/oauth.test.ts)
  • hughcube: Authored the merged fix(google-gemini-cli-auth): fix Gemini CLI OAuth failures on Windows #40729 fix for Windows Gemini CLI OAuth failures in the active Google provider files. (role: Windows OAuth fix author; confidence: high; commits: 9dd449045a79; files: extensions/google/oauth.credentials.ts, extensions/google/oauth.project.ts, extensions/google/oauth.test.ts)
  • steipete: Recent history shows OAuth/path-helper and workspace-bootstrap maintenance, and the linked WORKING.md feature was closed by this maintainer as ClawHub/plugin work. (role: recent maintainer and product-scope decision owner; confidence: high; commits: b6970865b65a, 3db60f7eab84; files: extensions/google/oauth.credentials.ts, src/agents/workspace.ts, VISION.md)
  • Takhoffman: Authored a recent merged workspace bootstrap routing fix touching the same bootstrap/session behavior family. (role: workspace bootstrap routing maintainer; confidence: medium; commits: 62703d84308a; files: src/agents/workspace.ts, src/agents/workspace.test.ts)
  • Patrick-Erichsen: Authored a recent merged stale-bootstrap completion fix in the workspace bootstrap area. (role: recent workspace lifecycle maintainer; confidence: medium; commits: 137f5c3a8b3c; files: src/agents/workspace.ts, src/agents/workspace.test.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 9e5d6c70917d.

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

Labels

agents Agent runtime and tooling extensions: google-gemini-cli-auth Extension: google-gemini-cli-auth size: S

Projects

None yet

1 participant