kestrel.markets
Version:
A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.
46 lines (42 loc) • 2.56 kB
text/typescript
/**
* # cli/commands/_heavy — the lazy-load boundary (Kestrel CLI v1 §4)
*
* `loadHeavy` is the ONE gate every HEAVY handler passes through. It fails closed on a bun-less host
* (exit 4, `RUNTIME_UNAVAILABLE`) — never a raw resolution stack, never a half-degraded verb. HEAVY
* handlers call it at the point of use, so the bundled `dist/cli.js` only reaches for
* `bun:sqlite`/`Bun.CryptoHasher`/chdb when a HEAVY command is actually invoked; LIGHT commands never
* reach here and stay node-runnable with no bun/chdb.
*
* The guard is STRUCTURAL, not incidental (kestrel-mkn review B1). It used to rely purely on the
* dynamic `import()` FAULTING on a `bun:*` specifier — which silently fails to fire for any heavy verb
* whose module graph happens to load fine under node and only touches Bun through a LAZY global. That
* was exactly the `agent` verb: its `{op:catalog}` ran to completion under bun-less node, and
* `{op:openSession}` died with a raw `ReferenceError: Bun is not defined` on the protocol channel (exit
* 1) instead of the loud exit-4 refusal every other heavy verb raises. So the runtime is now checked
* BEFORE the import: no Bun ⇒ no heavy verb, whatever its import graph happens to look like.
*/
import { CliError, EXIT } from "../errors.ts";
function runtimeUnavailable(needsChdb: boolean): CliError {
return new CliError({
code: "RUNTIME_UNAVAILABLE",
exit: EXIT.RUNTIME_UNAVAILABLE,
message: "this command requires the Bun runtime" + (needsChdb ? " and the native chdb dependency" : ""),
hint: "no usable Bun runtime found — reinstall with optional dependencies enabled (the package bundles `bun`), set KESTREL_BUN=/path/to/bun, or install Bun (https://bun.sh)",
});
}
/**
* Load a heavy module, or fail closed. Refuses UP FRONT under a bun-less runtime (the published bin
* re-execs heavy verbs onto a bun, so reaching this on node means no bun exists anywhere), and maps any
* remaining load failure — a missing native chdb, say — onto the same loud exit-4 error.
*/
export async function loadHeavy<T>(imp: () => Promise<T>): Promise<T> {
// Fail closed BEFORE the import: a heavy verb must never half-run under node and surface its
// Bun-shaped fault as some unrelated domain error (a bogus `corrupt-content-root`, a raw TypeError).
if (process.versions.bun === undefined) throw runtimeUnavailable(false);
try {
return await imp();
} catch (e) {
const s = String(e instanceof Error ? e.message : e);
throw runtimeUnavailable(/chdb/i.test(s));
}
}