Files
orca/src/cli/index.ts
T
Jinwoo-H e0281999db refactor(ai-vault-search): consolidate the review fixes into one owner per rule
Second pass over the session-search delta. Keeps the staged-write, streaming
capture, consent-gate and SQL-side filtering designs; removes the layers that
had accumulated around them.

Store and schema (schema 10): row visibility is two SQL views instead of four
hand-written predicates; publish nulls messages.batch_id and drops the batch row,
so the messages view is `batch_id IS NULL` and a recycled batch id can no longer
hide published rows; `published` column and its index removed; WAL checkpoint
guard taken off every read path and sampled on the write loop only;
maintenance class split into three plain functions; cwd_key indexed and the
scope filter switched to a range seek with an EXPLAIN plan assertion.

Service and capture: one write shape (streamingCapture flag and the dead
array branch deleted); configure applies policy to the store once; one
shutdown path (dispose removed) with an ownership guard so a late close
cannot unregister a replacement service's sink; unverifiable sources are
returned with a caveat instead of filtered like deletions; a transient write
failure no longer pins the indexing badge at error; refresh lane reuses
stableInFlightKey; message channel handles concurrent checkpoints.

Query: relevance sort now groups by session before the candidate limit (the
must-fix was only applied to newest); one operator parser shared by panel and
backend with the apostrophe bug fixed; OR within a key, AND across keys; one
documented case rule; received enums tolerate unknown values; result type
derived from the schema; projection applied on the desktop IPC path; tokenizer
contract pinned against fts5vocab.

Remote and CLI: SSH and local ids rejected at the RPC boundary for all four
methods; method-name regex sniffing removed from both transports; CommandSpec
gained booleanFlags/repeatableFlags so args.ts carries no command vocabulary;
settings update no longer blocks or fails on scanner reconfiguration; relay
owner keeps its lease through caller cancellation and answers index-status on
an unreadable policy; one SESSION_SEARCH_METHODS record feeds every caller.

Renderer: coverage polling stops once the index settles and re-arms on focus;
search results feed the coverage store instead of re-fetching; the `updating`
state and header line are deleted in favour of the list's own loading state;
local-only notice only when a remote host is in scope; status-bar segment is
read-only and opens settings; ownerKey folded into the args key.

Providers: OpenCode capture resets (not clears) the parse deadline; capture
and preview reads degrade to a scan issue instead of dropping the session;
a consumer write failure is not counted as a worker death; response union
discriminated on `kind`; worker host split out of the client.

Every behavioural fix carries a test that fails when the fix is reverted.
2026-09-07 22:12:56 -04:00

239 lines
9.4 KiB
JavaScript

#!/usr/bin/env node
import {
findCommandSpec,
isCommandGroup,
normalizeCommandPositionals,
parseArgs,
resolveHelpPath,
specPaths,
validateCommandAndFlags
} from './args'
import { readOrcaCliVersion } from './cli-version'
import { dispatch } from './dispatch'
import {
assertEnvironmentSelectorResolvable,
resolveHostFlagEnvironmentId
} from './execution-host-flag'
import { listSshTargets } from './host-selector-alternatives'
import { reportCliError } from './cli-error'
import { printHelp } from './help'
import type { RuntimeClient } from './runtime-client'
import { COMMAND_SPECS } from './specs'
import { resolveOrchestrationCliExecutable } from './runtime/orchestration-recovery-command'
export { COMMAND_SPECS } from './specs'
export { buildCurrentWorktreeSelector, normalizeWorktreeSelector } from './selectors'
const COMMAND_PATHS = COMMAND_SPECS.flatMap((spec) => specPaths(spec))
function shouldIgnoreRemoteSelection(commandPath: string[]): boolean {
return (
commandPath[0] === 'account' ||
commandPath[0] === 'artifacts' ||
commandPath[0] === 'environment' ||
// Why: `host list` answers "what can this machine target, and with what flag". Half of that
// answer (paired servers) is read from this machine's own pairing store and cannot be routed,
// so routing the other half produced one listing describing two machines at once.
commandPath[0] === 'host' ||
commandPath[0] === 'serve' ||
commandPath[0] === 'agent' ||
commandPath[0] === 'vm' ||
commandPath[0] === 'agent-context'
)
}
// Why: the RuntimeClient graph is 153 of the CLI's 199 eager modules (zod via
// shared/pairing, ws + tweetnacl via websocket-transport). Loading it here
// rather than at module scope means --help, `help <cmd>`, and command/flag
// errors — which all return before this call — never pay for it. Awaited
// before dispatch so `ctx.client` stays a synchronous getter.
async function loadRuntimeClientClass(): Promise<typeof RuntimeClient> {
return (await import('./runtime-client.js')).RuntimeClient
}
// Why: the SSH relay bridge executes this CLI on the Orca host while the
// caller's shell cwd lives on the remote machine (which cannot be chdir'd
// into). ORCA_CLI_CWD carries that remote cwd so cwd-based selectors like
// `--worktree active` resolve against the caller's directory.
function resolveInvocationCwd(): string {
const override = process.env.ORCA_CLI_CWD
return typeof override === 'string' && override.length > 0 ? override : process.cwd()
}
export async function main(
argv = process.argv.slice(2),
cwd = resolveInvocationCwd()
): Promise<void> {
// Why: version audits use the bundled launcher; Electron intercepts direct binary version flags.
if (argv.length === 1 && (argv[0] === '--version' || argv[0] === '-v')) {
const version = readOrcaCliVersion()
if (!version) {
process.stderr.write('Could not determine the Orca version for this build.\n')
process.exitCode = 1
return
}
process.stdout.write(`${version}\n`)
return
}
if (argv[0] === 'agent-teams-tmux') {
await runAgentTeamsTmuxShim(argv.slice(1))
return
}
if (argv[0] === 'claude-teams') {
await runClaudeTeams(argv.slice(1), cwd)
return
}
const parsed = normalizeCommandPositionals(
COMMAND_SPECS,
parseArgs(argv, COMMAND_PATHS, COMMAND_SPECS)
)
const helpPath = resolveHelpPath(parsed)
if (helpPath !== null) {
printHelp(COMMAND_SPECS, helpPath)
if (
helpPath.length > 0 &&
!findCommandSpec(COMMAND_SPECS, helpPath) &&
!isCommandGroup(helpPath)
) {
process.exitCode = 1
}
return
}
if (parsed.commandPath.length === 0) {
printHelp(COMMAND_SPECS, [])
return
}
const json = parsed.flags.has('json')
try {
// Why: CLI syntax and flag errors should be reported before any runtime
// lookup so users do not get misleading "Orca is not running" failures for
// simple command typos or unsupported flags.
validateCommandAndFlags(COMMAND_SPECS, parsed)
const RuntimeClientClass = await loadRuntimeClientClass()
const ignoreRemoteSelection = shouldIgnoreRemoteSelection(parsed.commandPath)
const pairingCode = ignoreRemoteSelection ? null : parsed.flags.get('pairing-code')
const environmentSelector = ignoreRemoteSelection ? null : parsed.flags.get('environment')
// Why: only the explicit flag is asserted eagerly. An ambient ORCA_ENVIRONMENT is background
// config, and failing local-only commands because of a stale one would be a regression; the
// explicit flag means the caller named that machine, so a bad name should fail immediately
// with the cross-kind hint rather than a bare store error at first use.
const listSshTargetsForSuggestion = async (): Promise<{ id: string; label: string }[]> =>
listSshTargets(new RuntimeClientClass(undefined, undefined, null, null))
if (typeof environmentSelector === 'string') {
await assertEnvironmentSelectorResolvable(environmentSelector, listSshTargetsForSuggestion)
}
// Why: --host runtime:<id> names a paired server, not a filter over this
// runtime's rows, so it has to pick the connection before the client exists.
// An ambient ORCA_ENVIRONMENT is checked for disagreement too — silently
// retargeting a mutation to another server is the bug this flag already had.
// An ambient pairing code cannot be resolved to an id to compare, so the
// explicit flag simply wins there.
const hostEnvironmentId =
ignoreRemoteSelection ||
(parsed.commandPath[0] === 'search' && parsed.flags.get('host') === 'all')
? null
: await resolveHostFlagEnvironmentId(parsed.flags, {
// Why: only consulted when the name missed, and against this machine's own runtime —
// SSH targets are registered there, not in the paired server we failed to find.
listSshTargets: listSshTargetsForSuggestion,
pairingCode: typeof pairingCode === 'string' ? pairingCode : null,
environmentSelector:
typeof environmentSelector === 'string'
? { value: environmentSelector, label: '--environment' }
: process.env.ORCA_ENVIRONMENT
? { value: process.env.ORCA_ENVIRONMENT, label: 'ORCA_ENVIRONMENT' }
: null
})
// Why: --host runtime:<name> is canonicalized to the environment's id so downstream host-id
// comparisons against stored rows still match; rewrite the flag once, here, rather than
// resolving the name again at every consumer.
if (hostEnvironmentId !== null) {
parsed.flags.set('host', `runtime:${hostEnvironmentId}`)
}
// Why: pass `null` (not `undefined`) when remote selection is suppressed
// so the RuntimeClient default parameter does not re-activate the
// ORCA_PAIRING_CODE / ORCA_ENVIRONMENT env-var fallback for commands
// that must run locally (environment / serve).
const suppressed = ignoreRemoteSelection ? null : undefined
// An explicit --host runtime:<id> outranks an ambient pairing code or environment.
const remotePairingCode =
hostEnvironmentId !== null ? null : typeof pairingCode === 'string' ? pairingCode : suppressed
const remoteEnvironment =
hostEnvironmentId ??
(typeof environmentSelector === 'string' ? environmentSelector : suppressed)
let client: RuntimeClient | undefined
await dispatch(parsed.commandPath, {
flags: parsed.flags,
// Why: local-only handlers must not resolve runtime metadata just to dispatch.
get client() {
client ??= new RuntimeClientClass(
undefined,
undefined,
remotePairingCode,
remoteEnvironment,
resolveOrchestrationCliExecutable(),
argv
)
return client
},
cwd,
json
})
} catch (error) {
const worktreeSelector = parsed.flags.get('worktree')
reportCliError(error, json, {
commandPath: parsed.commandPath,
...(typeof worktreeSelector === 'string' ? { worktreeSelector } : {})
})
process.exitCode = 1
}
}
async function runClaudeTeams(argv: string[], cwd: string): Promise<void> {
try {
// Why: everything after `orca claude-teams` belongs to Claude Code, not
// Orca's own flag parser, so new Claude flags work without Orca changes.
const client = new (await loadRuntimeClientClass())(undefined, undefined, null, null)
await dispatch(['claude-teams'], {
flags: new Map(),
client,
cwd,
json: false,
rawArgs: argv
})
} catch (error) {
reportCliError(error, false, { commandPath: ['claude-teams'] })
process.exitCode = 1
}
}
async function runAgentTeamsTmuxShim(argv: string[]): Promise<void> {
try {
const client = new (await loadRuntimeClientClass())(undefined, 10_000)
const response = await client.call<{
tmux: { stdout: string; stderr: string; exitCode: number }
}>(
'agentTeams.tmuxCompat',
{
teamId: process.env.ORCA_AGENT_TEAMS_TEAM_ID,
token: process.env.ORCA_AGENT_TEAMS_TOKEN,
envPane: process.env.TMUX_PANE,
cwd: process.cwd(),
argv
},
{ timeoutMs: 10_000 }
)
process.stdout.write(response.result.tmux.stdout)
process.stderr.write(response.result.tmux.stderr)
process.exitCode = response.result.tmux.exitCode
} catch (error) {
reportCliError(error, false, { commandPath: ['agent-teams-tmux'] })
process.exitCode = 1
}
}
if (require.main === module) {
void main()
}