Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* gateway-stop-before-update.ts
|
||||
*
|
||||
* Windows-only helper for the update hand-off (#70337): stop every
|
||||
* separately-running messaging gateway BEFORE the venv-shim lock poll.
|
||||
*
|
||||
* Why not just tree-kill gateway.pid's PID:
|
||||
* - gateway.pid records the uv WORKER process, but the venv shim lock is
|
||||
* held by its parent LAUNCHER (venv\Scripts\python.exe). taskkill /T from
|
||||
* the worker PID does not reach parents, so the lock could survive.
|
||||
* - a single gateway.pid read misses multi-profile setups entirely.
|
||||
*
|
||||
* So we delegate to `hermes gateway stop --all`: the CLI discovers every
|
||||
* profile's gateway processes (launcher + worker) via find_gateway_pids,
|
||||
* drains in-flight agents (planned-stop marker -> resume_pending), and
|
||||
* force-kills survivors — the same logic `hermes update`'s
|
||||
* _pause_windows_gateways_for_update relies on.
|
||||
*
|
||||
* Pure + dependency-injected so the launcher/worker and multi-profile
|
||||
* behavior is assertable without booting Electron.
|
||||
*/
|
||||
|
||||
import { execFileSync, type ExecFileSyncOptionsWithStringEncoding } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
|
||||
export interface StopGatewayBeforeUpdateDeps {
|
||||
/** Defaults to process.platform === 'win32'; injectable for tests. */
|
||||
isWindows?: boolean
|
||||
/** Defaults to fs.existsSync; injectable for tests. */
|
||||
existsSync?: (p: string) => boolean
|
||||
/** Defaults to execFileSync from node:child_process; injectable for tests. */
|
||||
execFileSync?: (command: string, args: string[], options: ExecFileSyncOptionsWithStringEncoding) => Buffer | string
|
||||
/** Observability hook for tests. */
|
||||
spy?: (command: string, args: string[]) => void
|
||||
}
|
||||
|
||||
export const GATEWAY_STOP_TIMEOUT_MS = 20_000
|
||||
|
||||
/**
|
||||
* Best-effort stop of all-profile messaging gateways via the CLI.
|
||||
* Never throws: a wedged/absent CLI must not abort the update hand-off
|
||||
* (the shim-lock poll + the updater's venv-blocker scan still fail loudly
|
||||
* if the venv stays held). Returns true when the CLI ran (or was invoked
|
||||
* with the injected spy), false when skipped (non-Windows / missing CLI).
|
||||
*/
|
||||
export function stopGatewayBeforeUpdate(
|
||||
hermesCliPath: string,
|
||||
hermesHome: string,
|
||||
deps: StopGatewayBeforeUpdateDeps = {}
|
||||
): boolean {
|
||||
return runGatewayLifecycleCommand(hermesCliPath, ['gateway', 'stop', '--all'], deps)
|
||||
}
|
||||
|
||||
/**
|
||||
* Drain-semantics counterpart (#76057 review): `gateway stop --all` before
|
||||
* the lock gate takes gateways down even when the update later ABORTS
|
||||
* (venv-blocked by a user terminal, probe failure, updater spawn failure).
|
||||
* The updater's own pause machinery resumes what it pauses — the Desktop
|
||||
* must mirror that on its abort paths, or a failed update strands every
|
||||
* profile's gateway stopped. Best-effort, never throws.
|
||||
*/
|
||||
export function startGatewaysAfterUpdateAbort(hermesCliPath: string, deps: StopGatewayBeforeUpdateDeps = {}): boolean {
|
||||
return runGatewayLifecycleCommand(hermesCliPath, ['gateway', 'start', '--all'], deps)
|
||||
}
|
||||
|
||||
function runGatewayLifecycleCommand(hermesCliPath: string, args: string[], deps: StopGatewayBeforeUpdateDeps): boolean {
|
||||
const isWindows = deps.isWindows ?? process.platform === 'win32'
|
||||
|
||||
if (!isWindows) {
|
||||
return false
|
||||
}
|
||||
|
||||
const existsSync = deps.existsSync ?? fs.existsSync
|
||||
const exec = deps.execFileSync ?? execFileSync
|
||||
|
||||
if (deps.spy) {
|
||||
deps.spy(hermesCliPath, args)
|
||||
}
|
||||
|
||||
if (!existsSync(hermesCliPath)) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
exec(hermesCliPath, args, {
|
||||
timeout: GATEWAY_STOP_TIMEOUT_MS,
|
||||
windowsHide: true,
|
||||
stdio: 'ignore',
|
||||
encoding: 'utf8'
|
||||
})
|
||||
|
||||
return true
|
||||
} catch {
|
||||
// Best-effort (see header comment).
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user