mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 00:03:15 +00:00
The relay could only say "terminals are unavailable" and then list three remedies for four different faults, none of which the user could verify (#17830). Two things were destroying the evidence: - `loadPtyUncached` caught the load error into bare `catch {}` blocks (pty-handler.ts:539, :551) and returned null. The only cause anyone had was discarded on the spot. - node-pty's own loader walks three directories and rethrows only the LAST failure, so even an uncaught error arrives as `Cannot find module '../prebuilds/...'` — the GLIBC/ABI/arch sentence is already gone. The relay now keeps the load error, recovers the real dlopen message with an out-of-process load of the file node-pty would have opened, reads what node-gyp configured the binding for (`build/config.gypi`), captures the host's Node ABI, arch and glibc, and probes the toolchain only when nothing was compiled. Each fault gets its own message naming values the user can check: toolchain_missing, dependency_missing, abi_mismatch, arch_mismatch, libc_floor, shared_library_missing, load_crashed, and load_failed which quotes the loader verbatim. A probe that did not answer stays `unverifiable` and prescribes nothing. The classification is now also structured data on the error, so a client can repair the host instead of printing a paragraph: an additive, schema-validated `data` field on an existing JSON-RPC error, with `repairable` true only for a proved fault that recompiling on the host actually fixes. Reuses orcad's loader-message parsers and out-of-process probe rather than adding a second copy; `classifyLoaderMessage` moves to a shared module and gains architecture and missing-shared-library cases, which the orcad boot precondition picks up too.
74 lines
3.6 KiB
TypeScript
74 lines
3.6 KiB
TypeScript
export const TERMINAL_UNAVAILABLE_ERROR_CODE = 'terminal_unavailable' as const
|
|
|
|
export const TERMINAL_PTY_DEGRADATION_CAPABILITY = 'terminal.pty.v1' as const
|
|
|
|
export type RuntimeBrowserUnavailableReason =
|
|
| 'unconfigured'
|
|
| 'driver_missing'
|
|
| 'executable_not_found'
|
|
| 'executable_not_executable'
|
|
| 'electron_start_failed'
|
|
| 'chromium_start_failed'
|
|
| 'provider_unhealthy'
|
|
| 'desktop_window_unavailable'
|
|
| 'unknown'
|
|
|
|
/**
|
|
* Why this host cannot spawn PTYs. Members are opaque to clients: render `message`,
|
|
* never switch exhaustively. Dynamic-loader failures are proved out of process because
|
|
* an incompatible native binary can terminate the host before JavaScript can catch it.
|
|
*/
|
|
export type RuntimeTerminalUnavailableReason =
|
|
| 'dependency_missing'
|
|
| 'toolchain_missing'
|
|
| 'libc_floor'
|
|
| 'shared_library_missing'
|
|
| 'abi_mismatch'
|
|
| 'arch_mismatch'
|
|
| 'load_failed'
|
|
| 'load_crashed'
|
|
| 'spawn_helper_missing'
|
|
| 'unknown'
|
|
|
|
export type RuntimeDegradation = {
|
|
/**
|
|
* Open vocabulary. New codes ship without a protocol bump, so clients must render
|
|
* `message` and must not switch exhaustively on this or the `reason` field.
|
|
*/
|
|
code: 'browser_unavailable' | typeof TERMINAL_UNAVAILABLE_ERROR_CODE
|
|
capability: 'browser.headless.v1' | typeof TERMINAL_PTY_DEGRADATION_CAPABILITY
|
|
message: string
|
|
reason?: RuntimeBrowserUnavailableReason | RuntimeTerminalUnavailableReason
|
|
/** Underlying error text when the host has one. Diagnostic only; never load-bearing. */
|
|
detail?: string
|
|
}
|
|
|
|
const TERMINAL_UNAVAILABLE_MESSAGES: Record<RuntimeTerminalUnavailableReason, string> = {
|
|
dependency_missing:
|
|
'Terminals are unavailable on this host: node-pty has no native binary for this platform. Install or rebuild it, or deploy a build that ships a prebuilt binary for this platform.',
|
|
toolchain_missing:
|
|
'Terminals are unavailable on this host: node-pty has no prebuilt binary for Linux and this host is missing the C/C++ build tools needed to compile one. Install them, then reconnect.',
|
|
libc_floor:
|
|
"This host's node-pty binary was built against a newer C library than the host provides, so the dynamic loader refuses it. Rebuild node-pty on this host, or deploy a build whose prebuilt binary matches this platform's libc.",
|
|
shared_library_missing:
|
|
'Terminals are unavailable on this host: a shared library that node-pty links against is not installed, so the dynamic loader cannot open the binary. Install the named library, then reconnect.',
|
|
abi_mismatch:
|
|
"This host's node-pty binary was built for a different Node ABI than the running Node, so it cannot be loaded. Rebuild node-pty against this Node version.",
|
|
arch_mismatch:
|
|
"This host's node-pty binary was built for a different CPU architecture than the running Node, so the dynamic loader refuses it. Rebuild node-pty on this host, or deploy a build for this architecture.",
|
|
load_failed: 'Terminals are unavailable on this host: node-pty failed to load.',
|
|
load_crashed:
|
|
'Terminals are unavailable on this host: loading node-pty terminated the probe process, which means the binary is incompatible with this host rather than merely missing.',
|
|
spawn_helper_missing:
|
|
'node-pty loaded, but its spawn-helper executable is missing or not executable, so every terminal spawn would fail. Reinstall node-pty on this host.',
|
|
unknown: 'Terminals are unavailable on this host, and the cause could not be determined.'
|
|
}
|
|
|
|
export function terminalUnavailableMessage(
|
|
reason: RuntimeTerminalUnavailableReason,
|
|
detail?: string
|
|
): string {
|
|
const base = TERMINAL_UNAVAILABLE_MESSAGES[reason]
|
|
return detail ? `${base} (${detail})` : base
|
|
}
|