mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
src/cli/index.ts was the only eager value-import of RuntimeClient, and five other eager modules imported just RuntimeClientError / RuntimeRpcFailureError from the runtime-client barrel -- dragging in client -> pairing -> zod -> ws -> e2ee on every invocation. Those error classes live in runtime/types.ts, which has zero children, so the five imports now point there and the client loads through the existing (already lazy by design) ctx.client getter. Eager modules 199 -> 46, with node_modules dropping 94 -> 0. `orca --help` 2.04x (59.6 -> 29.2 ms); the same for help, no-args, and both error paths, which return before constructing a client. Commands that DO construct one still gain 1.10-1.12x from not eagerly parsing the transport the local path never uses. Correction to an earlier note: websocket-transport alone is ~24 modules / ~8 ms, not the 107 / 28 ms once recorded -- that figure wrongly charged it for zod, which enters through shared/pairing on a different edge. Marginal cost, never isolated cost. Co-authored-by: Orca <help@stably.ai>
58 lines
1.9 KiB
TypeScript
58 lines
1.9 KiB
TypeScript
import type { RuntimeClient } from './runtime-client'
|
|
import { RuntimeClientError } from './runtime/types'
|
|
import { HANDLER_GROUPS, type HandlerGroup } from './handler-group-manifest'
|
|
|
|
export type HandlerContext = {
|
|
flags: Map<string, string | boolean>
|
|
client: RuntimeClient
|
|
cwd: string
|
|
json: boolean
|
|
rawArgs?: string[]
|
|
}
|
|
|
|
export type CommandHandler = (ctx: HandlerContext) => Promise<void>
|
|
|
|
// Why: routing only needs key→group, so every CLI invocation can skip the
|
|
// transitive module graph of the 24 groups it does not dispatch into.
|
|
function buildRoutes(groups: readonly HandlerGroup[]): Map<string, HandlerGroup> {
|
|
const table = new Map<string, HandlerGroup>()
|
|
for (const group of groups) {
|
|
for (const key of group.keys) {
|
|
const owner = table.get(key)
|
|
if (owner) {
|
|
throw new Error(
|
|
`Duplicate CLI handler registration for "${key}" (${owner.name} and ${group.name})`
|
|
)
|
|
}
|
|
table.set(key, group)
|
|
}
|
|
}
|
|
return table
|
|
}
|
|
|
|
const ROUTES = buildRoutes(HANDLER_GROUPS)
|
|
|
|
// Why: exposes only the canonical command keys (not the handler internals) so the
|
|
// registry-parity guard can check specs↔handlers without rebuilding the table.
|
|
export const HANDLER_COMMAND_KEYS: ReadonlySet<string> = new Set(ROUTES.keys())
|
|
|
|
export async function dispatch(commandPath: string[], ctx: HandlerContext): Promise<void> {
|
|
const key = commandPath.join(' ')
|
|
const group = ROUTES.get(key)
|
|
if (!group) {
|
|
throw new RuntimeClientError('invalid_argument', `Unknown command: ${key}`)
|
|
}
|
|
const handler = (await group.load())[key]
|
|
// Why: the manifest key list is verified against the real exports in CI, so a
|
|
// miss here means the group changed without the manifest — fail loudly.
|
|
if (!handler) {
|
|
throw new RuntimeClientError(
|
|
'invalid_argument',
|
|
`CLI handler group "${group.name}" does not export "${key}"`
|
|
)
|
|
}
|
|
await handler(ctx)
|
|
}
|
|
|
|
export { buildRoutes as buildHandlerRoutes }
|