fix(preflight): detect Grok installed in ~/.grok/bin (#25324)

Grok's installer defaults to ~/.grok/bin and only symlinks into
~/.local/bin when that dir is already on PATH. A GUI-launched Orca never
looked in ~/.grok/bin, so the Agents page reported Grok as not installed.

Append ~/.grok/bin after ~/.vite-plus/bin in the three lists that must
stay in step: the packaged PATH seed, the system install-dir lookup
fallback, and the POSIX/WSL guest fallback prelude.

Fixes #17590

Co-authored-by: brynnclaw <261708852+brynnclaw@users.noreply.github.com>
This commit is contained in:
Brynn Bendixen
2026-10-26 01:55:02 -07:00
committed by GitHub
co-authored by brynnclaw
parent 351705bd35
commit 963e8cc431
5 changed files with 24 additions and 15 deletions
+3 -2
View File
@@ -56,7 +56,7 @@ describe('patchPackagedProcessPath', () => {
}
})
it('prepends agent-CLI install dirs (~/.opencode/bin, ~/.vite-plus/bin) for packaged darwin runs', async () => {
it('seeds agent-CLI install dirs (~/.opencode/bin, ~/.vite-plus/bin, ~/.grok/bin) for packaged darwin runs', async () => {
const { app } = await import('electron')
const { patchPackagedProcessPath } = await import('./configure-process')
@@ -68,9 +68,10 @@ describe('patchPackagedProcessPath', () => {
patchPackagedProcessPath()
const segments = (process.env.PATH ?? '').split(':')
// #829: GUI PATH must discover the documented OpenCode/Pi fallback installs.
// #829/#17590: GUI PATH must discover the documented OpenCode/Pi/Grok fallback installs.
expect(segments).toContain(join('/Users/tester', '.opencode/bin'))
expect(segments).toContain(join('/Users/tester', '.vite-plus/bin'))
expect(segments).toContain(join('/Users/tester', '.grok/bin'))
expect(segments).toContain(join('/Users/tester', 'bin'))
})
+2 -1
View File
@@ -151,7 +151,8 @@ export function patchPackagedProcessPath(): void {
join(home, '.nix-profile/bin'),
// GUI PATH omits agent-specific install directories (#829).
join(home, '.opencode/bin'),
join(home, '.vite-plus/bin')
join(home, '.vite-plus/bin'),
join(home, '.grok/bin')
)
}
}
@@ -134,7 +134,8 @@ describe('agent CLI install-dir fallback', () => {
'/nix/var/nix/profiles/default/bin',
join(home, '.nix-profile', 'bin'),
join(home, '.opencode', 'bin'),
join(home, '.vite-plus', 'bin')
join(home, '.vite-plus', 'bin'),
join(home, '.grok', 'bin')
]
stage(...dirs.map((dir) => join(dir, 'opencode')))
for (const expected of dirs) {
@@ -193,7 +194,8 @@ describe('agent CLI install-dir fallback', () => {
'/nix/var/nix/profiles/default/bin',
join(home, '.nix-profile', 'bin'),
join(home, '.opencode', 'bin'),
join(home, '.vite-plus', 'bin')
join(home, '.vite-plus', 'bin'),
join(home, '.grok', 'bin')
]) {
expect(seeded).not.toContain(directory)
}
@@ -208,12 +210,14 @@ describe('agent CLI install-dir fallback', () => {
join('/usr/local/bin', 'codex'),
join(MOCK_HOME, '.opencode', 'bin', 'opencode'),
// Pi defaults to ~/.vite-plus/bin and is probed on every runtime (#829).
join(MOCK_HOME, '.vite-plus', 'bin', 'pi')
)
// All three come from the fallback: the stubbed PATH holds no system dir.
expect(detectCommandsInInstallDirs(['codex', 'opencode', 'pi', 'cursor-agent'])).toEqual(
new Set(['codex', 'opencode', 'pi'])
join(MOCK_HOME, '.vite-plus', 'bin', 'pi'),
// Grok's installer defaults to ~/.grok/bin (#17590).
join(MOCK_HOME, '.grok', 'bin', 'grok')
)
// All come from the fallback: the stubbed PATH holds no system dir.
expect(
detectCommandsInInstallDirs(['codex', 'opencode', 'pi', 'grok', 'cursor-agent'])
).toEqual(new Set(['codex', 'opencode', 'pi', 'grok']))
}
)
@@ -239,7 +243,8 @@ describe('agent CLI install-dir fallback', () => {
login,
join(home, '.nvm', 'versions', 'node', 'v24.0.0', 'bin'),
join(home, '.opencode', 'bin'),
join(home, '.vite-plus', 'bin')
join(home, '.vite-plus', 'bin'),
join(home, '.grok', 'bin')
]
const command = 'orca-fallback-fixture'
try {
+3 -2
View File
@@ -17,9 +17,10 @@ const POSIX_VERSION_MANAGER_BIN_DIRS = [
'"/home/linuxbrew/.linuxbrew/bin"',
'"/nix/var/nix/profiles/default/bin"',
'"$HOME/.nix-profile/bin"',
// Why both: the opencode and Pi installers' own defaults, which no version manager owns (#829).
// Installer-owned defaults no version manager covers: opencode, Pi (#829), Grok (#17590).
'"$HOME/.opencode/bin"',
'"$HOME/.vite-plus/bin"'
'"$HOME/.vite-plus/bin"',
'"$HOME/.grok/bin"'
].join(' ')
/** Append behind login PATH so stale fallback nvm installs cannot shadow the user’s selected binary. */
+3 -2
View File
@@ -3,7 +3,7 @@ import { join } from 'node:path'
/** Lookup-only fallback: prepending system dirs to inherited PATH would reorder the user’s binaries (#18234).
* Keep system-block order aligned with patchPackagedProcessPath/POSIX_VERSION_MANAGER_BIN_DIRS; cross-block precedence differs
* for ~/.local/bin, which precedes this fallback but follows seeded system dirs. Do not hoist system paths above version managers.
* Omit sbin/~/bin and off-darwin /opt/homebrew; Linuxbrew has its own prefix. Include installer-owned .opencode/.vite-plus bins (#829).
* Omit sbin/~/bin and off-darwin /opt/homebrew; Linuxbrew has its own prefix. Include installer-owned .opencode/.vite-plus (#829) and .grok (#17590) bins.
* Exception: a newly discovered CLI with sibling node may prepend that dir only to its child PATH for runtime pairing (#10932). */
export function getSystemCliInstallDirectories(
platform: NodeJS.Platform,
@@ -27,7 +27,8 @@ export function getSystemCliInstallDirectories(
'/nix/var/nix/profiles/default/bin',
join(homePath, '.nix-profile', 'bin'),
join(homePath, '.opencode', 'bin'),
join(homePath, '.vite-plus', 'bin')
join(homePath, '.vite-plus', 'bin'),
join(homePath, '.grok', 'bin')
)
return directories
}