fix: Windows platform improvements — re-enable Chroma, fix DB race, simplify env isolation

1. ProcessManager: Migrate spawnDaemon() from WMIC to PowerShell Start-Process
   - WMIC deprecated in Windows 11, PowerShell inherits env vars properly
   - Use -WindowStyle Hidden to prevent console popups
   - Fix redundant backslash escaping in PowerShell $_ variables

2. ChromaSync: Re-enable vector search on Windows
   - Remove overly defensive platform check that disabled all semantic search
   - Worker daemon starts with -WindowStyle Hidden; child processes inherit
   - MCP SDK's StdioClientTransport uses shell:false, no new console created

3. worker-service: Unified DB-ready gate middleware
   - Replace single-endpoint /api/sessions/init wait with global middleware
   - Hold all DB-dependent requests until database is initialized (30s timeout)
   - Whitelist static assets, /health, and viewer page for immediate response
   - Separate dbReadyPromise (DB only) from initializationComplete (full init)
   - Fixes "Database not initialized" errors on /stream, /summarize, /init

4. EnvManager: Switch from allowlist to blocklist for subprocess env
   - Only strip ANTHROPIC_API_KEY to prevent Issue #733 billing hijack
   - Pass through all other vars (ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL, etc.)
   - Simpler, less fragile than maintaining an exhaustive system vars allowlist
This commit is contained in:
xingyu
2026-02-07 16:17:18 +08:00
parent dac989c697
commit e4e1d3fb92
3 changed files with 49 additions and 78 deletions
+13 -15
View File
@@ -101,7 +101,7 @@ export async function getChildProcesses(parentPid: number): Promise<number[]> {
try {
// PowerShell Get-Process instead of WMIC (deprecated in Windows 11)
const cmd = `powershell -NoProfile -NonInteractive -Command "Get-Process | Where-Object { \\$_.ParentProcessId -eq ${parentPid} } | Select-Object -ExpandProperty Id"`;
const cmd = `powershell -NoProfile -NonInteractive -Command "Get-Process | Where-Object { $_.ParentProcessId -eq ${parentPid} } | Select-Object -ExpandProperty Id"`;
const { stdout } = await execAsync(cmd, { timeout: HOOK_TIMEOUTS.POWERSHELL_COMMAND });
// PowerShell outputs just numbers (one per line), simpler than WMIC's "ProcessId=1234" format
return stdout
@@ -226,10 +226,10 @@ export async function cleanupOrphanedProcesses(): Promise<void> {
if (isWindows) {
// Windows: Use PowerShell Get-CimInstance with JSON output for age filtering
const patternConditions = ORPHAN_PROCESS_PATTERNS
.map(p => `\\$_.CommandLine -like '*${p}*'`)
.map(p => `$_.CommandLine -like '*${p}*'`)
.join(' -or ');
const cmd = `powershell -NoProfile -NonInteractive -Command "Get-CimInstance Win32_Process | Where-Object { (${patternConditions}) -and \\$_.ProcessId -ne ${currentPid} } | Select-Object ProcessId, CreationDate | ConvertTo-Json"`;
const cmd = `powershell -NoProfile -NonInteractive -Command "Get-CimInstance Win32_Process | Where-Object { (${patternConditions}) -and $_.ProcessId -ne ${currentPid} } | Select-Object ProcessId, CreationDate | ConvertTo-Json"`;
const { stdout } = await execAsync(cmd, { timeout: HOOK_TIMEOUTS.POWERSHELL_COMMAND });
if (!stdout.trim() || stdout.trim() === 'null') {
@@ -339,9 +339,9 @@ export async function cleanupOrphanedProcesses(): Promise<void> {
* Spawn a detached daemon process
* Returns the child PID or undefined if spawn failed
*
* On Windows, uses WMIC to spawn a truly independent process that
* survives parent exit without console popups. WMIC creates processes
* that are not associated with the parent's console.
* On Windows, uses PowerShell Start-Process with -WindowStyle Hidden to spawn
* a truly independent process without console popups. Unlike WMIC, PowerShell
* inherits environment variables from the parent process.
*
* On Unix, uses standard detached spawn.
*
@@ -361,21 +361,19 @@ export function spawnDaemon(
};
if (isWindows) {
// Use WMIC to spawn a process that's independent of the parent console
// This avoids the console popup that occurs with detached: true
// Paths must be individually quoted for WMIC when they contain spaces
// Use PowerShell Start-Process to spawn a hidden, independent process
// Unlike WMIC, PowerShell inherits environment variables from parent
// -WindowStyle Hidden prevents console popup
const execPath = process.execPath;
const script = scriptPath;
// WMIC command format: wmic process call create "\"path1\" \"path2\" args"
const command = `wmic process call create "\\"${execPath}\\" \\"${script}\\" --daemon"`;
const psCommand = `Start-Process -FilePath '${execPath}' -ArgumentList '${script}','--daemon' -WindowStyle Hidden`;
try {
execSync(command, {
execSync(`powershell -NoProfile -Command "${psCommand}"`, {
stdio: 'ignore',
windowsHide: true
windowsHide: true,
env
});
// WMIC returns immediately, we can't get the spawned PID easily
// Worker will write its own PID file after listen()
return 0;
} catch {
return undefined;