mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-17 13:13:35 +08:00
fix(desktop): forward Windows PowerShell choice to agent sidecar
- serverRuntime injects CLAUDE_CODE_POWERSHELL_PATH from the user's chosen shell (readDesktopTerminalConfig + resolveDesktopTerminalShell) on Windows, so the agent PowerShellTool honors the same shell as the UI terminal. Best-effort: never blocks startup, never overrides an explicit env var, only forwards pwsh/powershell selections (not cmd/custom). Regression from the Tauri build. - document that notificationPermissionState reflects OS capability, not macOS authorization (no Electron API exists; the 'failed' lifecycle is the real signal) instead of calling the non-existent systemPreferences.getNotificationSettings Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f070d16d38
commit
7a882d1858
@ -23,6 +23,15 @@ export function validateNotificationOptions(value: unknown): value is DesktopNot
|
||||
&& (record.extra === undefined || (typeof record.extra === 'object' && record.extra !== null && !Array.isArray(record.extra)))
|
||||
}
|
||||
|
||||
// NOTE: Electron has no API to read the macOS user authorization status from the
|
||||
// main process (`systemPreferences.getNotificationSettings()` does not exist; the
|
||||
// only option is the native `macos-notification-state` module, which we avoid to
|
||||
// keep the cross-platform build dependency-free). `Notification.isSupported()`
|
||||
// reflects OS capability, NOT whether the user granted permission, so this can
|
||||
// report 'granted' even when the user denied notifications in System Settings.
|
||||
// The authoritative delivery signal is the 'failed' lifecycle event handled in
|
||||
// sendDesktopNotification(); on macOS, events only emit correctly for code-signed
|
||||
// builds (unsigned/ad-hoc builds emit 'failed').
|
||||
export function notificationPermissionState(
|
||||
NotificationClass: ElectronNotificationConstructor,
|
||||
): NotificationPermissionState {
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
formatStartupError,
|
||||
killSidecar,
|
||||
mergeProxyEnv,
|
||||
POWERSHELL_PATH_OVERRIDE_ENV,
|
||||
proxyUrlFromElectronProxyRules,
|
||||
pushStartupLog,
|
||||
reserveLocalPort,
|
||||
@ -12,8 +13,10 @@ import {
|
||||
SERVER_CONTROL_HOST,
|
||||
spawnSidecar,
|
||||
waitForServer,
|
||||
windowsPowerShellOverride,
|
||||
type SidecarChild,
|
||||
} from './sidecarManager'
|
||||
import { readDesktopTerminalConfig, resolveDesktopTerminalShell } from './terminal'
|
||||
|
||||
type ServerRuntimeOptions = {
|
||||
desktopRoot: string
|
||||
@ -157,17 +160,33 @@ export class ElectronServerRuntime {
|
||||
}
|
||||
|
||||
private async resolveSidecarBaseEnvOnce(): Promise<NodeJS.ProcessEnv> {
|
||||
if (!this.resolveSystemProxy) return process.env
|
||||
if (!this.resolveSystemProxy) return this.applyPowerShellOverride(process.env)
|
||||
|
||||
try {
|
||||
const rules = await this.resolveSystemProxy('https://auth.openai.com/')
|
||||
return mergeProxyEnv(
|
||||
return this.applyPowerShellOverride(mergeProxyEnv(
|
||||
process.env,
|
||||
proxyUrlFromElectronProxyRules(rules),
|
||||
)
|
||||
))
|
||||
} catch (error) {
|
||||
console.error('[desktop] failed to resolve system proxy for sidecars', error)
|
||||
return process.env
|
||||
return this.applyPowerShellOverride(process.env)
|
||||
}
|
||||
}
|
||||
|
||||
// On Windows, forward the user's chosen PowerShell to the agent sidecar so its
|
||||
// PowerShellTool honors the same shell as the UI terminal (regression from the
|
||||
// Tauri build, where this lived in src-tauri/src/lib.rs). Best-effort: never
|
||||
// block sidecar startup, and never override an explicitly set env var.
|
||||
private applyPowerShellOverride(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
||||
if (process.platform !== 'win32' || env[POWERSHELL_PATH_OVERRIDE_ENV]) return env
|
||||
try {
|
||||
const shell = resolveDesktopTerminalShell('win32', readDesktopTerminalConfig(env))
|
||||
const override = windowsPowerShellOverride(shell, 'win32')
|
||||
if (override) return { ...env, [POWERSHELL_PATH_OVERRIDE_ENV]: override }
|
||||
} catch {
|
||||
// Misconfigured custom shell etc. — fall through to the unmodified env.
|
||||
}
|
||||
return env
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
proxyUrlFromElectronProxyRules,
|
||||
pushStartupLog,
|
||||
resolveHostTriple,
|
||||
windowsPowerShellOverride,
|
||||
type SidecarChild,
|
||||
} from './sidecarManager'
|
||||
|
||||
@ -148,4 +149,17 @@ describe('Electron sidecar manager', () => {
|
||||
expect(spawnSyncFn).toHaveBeenCalledWith('taskkill', ['/F', '/T', '/PID', '777'], { stdio: 'ignore' })
|
||||
expect(spawnAsync).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('forwards a PowerShell shell choice to the sidecar only on Windows', () => {
|
||||
expect(windowsPowerShellOverride('pwsh.exe', 'win32')).toBe('pwsh.exe')
|
||||
expect(windowsPowerShellOverride('powershell.exe', 'win32')).toBe('powershell.exe')
|
||||
expect(windowsPowerShellOverride('C:\\tools\\PowerShell\\pwsh.exe', 'win32')).toBe('C:\\tools\\PowerShell\\pwsh.exe')
|
||||
// non-PowerShell selections must not be reported as a PowerShell override
|
||||
expect(windowsPowerShellOverride('cmd.exe', 'win32')).toBeNull()
|
||||
expect(windowsPowerShellOverride('C:\\bin\\bash.exe', 'win32')).toBeNull()
|
||||
expect(windowsPowerShellOverride(null, 'win32')).toBeNull()
|
||||
// never applies off Windows
|
||||
expect(windowsPowerShellOverride('pwsh', 'darwin')).toBeNull()
|
||||
expect(windowsPowerShellOverride('powershell.exe', 'linux')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@ -140,6 +140,28 @@ export function mergeProxyEnv(
|
||||
}
|
||||
}
|
||||
|
||||
// The agent's PowerShellTool reads this env var to honor the user's chosen shell
|
||||
// (mirrors src/utils/shell/powershellDetection.ts). Without it the agent would
|
||||
// re-autodetect PowerShell instead of using the shell the user picked in the UI.
|
||||
export const POWERSHELL_PATH_OVERRIDE_ENV = 'CLAUDE_CODE_POWERSHELL_PATH'
|
||||
|
||||
/**
|
||||
* Map a resolved Windows shell path to a PowerShell override for the sidecar env.
|
||||
* Returns the path only on Windows when it points at pwsh/powershell, so that a
|
||||
* cmd.exe or non-PowerShell custom shell selection does not get misreported as a
|
||||
* PowerShell override. Matches the consumer's isPowerShellExecutablePath check.
|
||||
*/
|
||||
export function windowsPowerShellOverride(
|
||||
shellPath: string | null | undefined,
|
||||
platform: NodeJS.Platform = process.platform,
|
||||
): string | null {
|
||||
if (platform !== 'win32') return null
|
||||
const trimmed = shellPath?.trim()
|
||||
if (!trimmed) return null
|
||||
const base = trimmed.split(/[\\/]/).pop()?.toLowerCase().replace(/\.exe$/, '')
|
||||
return base === 'pwsh' || base === 'powershell' ? trimmed : null
|
||||
}
|
||||
|
||||
export function buildSidecarEnv(baseEnv: NodeJS.ProcessEnv, h5DistDir: string): NodeJS.ProcessEnv {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
...baseEnv,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user