mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 10:37:35 +00:00
* chore: shorten process-host comments * chore: audit CLI parsing and host-routing comments * chore: remove stale host-selector restriction comment * chore: shorten remaining CLI root comments * Shorten CLI handler comments * Shorten CLI runtime and command specification comments * Shorten top-level main process A–G comments --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
130 lines
4.4 KiB
TypeScript
130 lines
4.4 KiB
TypeScript
import { realpathSync } from 'node:fs'
|
||
import { homedir } from 'node:os'
|
||
import { resolve } from 'node:path'
|
||
import { markQoderWorkspaceTrusted } from './qoder/workspace-trust'
|
||
import {
|
||
type AgentTrustPreset,
|
||
markAntigravityWorkspaceTrusted,
|
||
markCodexProjectTrusted,
|
||
markCopilotFolderTrusted,
|
||
markCursorWorkspaceTrusted
|
||
} from './agent-trust-presets'
|
||
import { awaitAgentTrustWriteWithinDeadline } from './agent-trust-write-deadline'
|
||
import {
|
||
type ClaudeTrustConfigTarget,
|
||
grantClaudeWorkspaceTrust
|
||
} from './claude/claude-folder-trust-file'
|
||
import { isTooBroadToPreTrust } from '../shared/home-or-filesystem-root'
|
||
|
||
/** Resolve trust on the agent’s execution host, including WSL guests and SSH relays. */
|
||
export type WorkspaceTrustHost = {
|
||
/** Homes the agent may read trust under; with none known, an agent that inherits trust writes nothing. */
|
||
homes: readonly (string | null | undefined)[]
|
||
/** The home the launched agent resolves `~` to, where the per-user trust files live. */
|
||
agentHome: string
|
||
/** The config Claude reads on this host, or null when this host cannot tell. */
|
||
claudeConfig: () => ClaudeTrustConfigTarget | null
|
||
/** Every config.toml the launched Codex may read, in the hook installer's lock order. */
|
||
codexConfigFiles: () => readonly string[]
|
||
deadlineMs: number
|
||
}
|
||
|
||
/** The home an agent launched with `launchEnv` on this host resolves `~` to. */
|
||
export function launchedAgentHome(
|
||
launchEnv: Record<string, string | undefined> | undefined
|
||
): string {
|
||
return (process.platform === 'win32' ? launchEnv?.USERPROFILE : launchEnv?.HOME) || homedir()
|
||
}
|
||
|
||
/** Never prewrite home/root trust for agents whose trust lookup includes descendants. */
|
||
export const AGENT_TRUST_INHERITS_FROM_A_HOME: Record<AgentTrustPreset, boolean> = {
|
||
claude: true,
|
||
codex: false,
|
||
cursor: false,
|
||
copilot: true,
|
||
qoder: true,
|
||
'qoder-cn': true,
|
||
antigravity: false
|
||
}
|
||
|
||
/** Trust these agents’ starting folders; a workspace ancestor may not be a valid trust key. */
|
||
export const AGENT_TRUST_KEYED_BY_START_FOLDER: Record<AgentTrustPreset, boolean> = {
|
||
claude: false,
|
||
codex: true,
|
||
cursor: false,
|
||
copilot: false,
|
||
qoder: false,
|
||
'qoder-cn': false,
|
||
antigravity: false
|
||
}
|
||
|
||
// Claude stores resolved paths; resolve also collapses .. when realpath fails.
|
||
function withResolvedForm(path: string): string[] {
|
||
try {
|
||
return [path, resolve(path), realpathSync.native(path)]
|
||
} catch {
|
||
return [path, resolve(path)]
|
||
}
|
||
}
|
||
|
||
/** Compare raw and resolved forms because writers store realpaths. */
|
||
function wouldTrustAHome(storedPath: string, homes: readonly string[]): boolean {
|
||
const homeForms = homes.flatMap(withResolvedForm)
|
||
return withResolvedForm(storedPath).some((form) => isTooBroadToPreTrust(form, homeForms))
|
||
}
|
||
|
||
async function writePreset(
|
||
preset: AgentTrustPreset,
|
||
storedPath: string,
|
||
host: WorkspaceTrustHost
|
||
): Promise<void> {
|
||
switch (preset) {
|
||
case 'claude': {
|
||
const target = host.claudeConfig()
|
||
if (target) {
|
||
await grantClaudeWorkspaceTrust(target, storedPath)
|
||
}
|
||
return
|
||
}
|
||
case 'codex':
|
||
return markCodexProjectTrusted(storedPath, host.codexConfigFiles())
|
||
case 'cursor':
|
||
return markCursorWorkspaceTrusted(storedPath, host.agentHome)
|
||
case 'copilot':
|
||
return markCopilotFolderTrusted(storedPath, host.agentHome)
|
||
case 'qoder-cn':
|
||
return markQoderWorkspaceTrusted(storedPath, host.agentHome, '.qoder-cn')
|
||
case 'qoder':
|
||
return markQoderWorkspaceTrusted(storedPath, host.agentHome)
|
||
case 'antigravity':
|
||
return markAntigravityWorkspaceTrusted(storedPath, host.agentHome)
|
||
}
|
||
}
|
||
|
||
/** Reject inherited home trust; failures and deadline misses leave the agent to ask. */
|
||
export async function applyWorkspaceTrustOnThisHost(
|
||
preset: AgentTrustPreset,
|
||
workspacePath: string,
|
||
describeHost: () => WorkspaceTrustHost
|
||
): Promise<void> {
|
||
try {
|
||
const host = describeHost()
|
||
if (AGENT_TRUST_INHERITS_FROM_A_HOME[preset]) {
|
||
const homes = host.homes.filter((home): home is string => Boolean(home))
|
||
if (homes.length === 0 || wouldTrustAHome(workspacePath, homes)) {
|
||
return
|
||
}
|
||
}
|
||
await awaitAgentTrustWriteWithinDeadline(writePreset(preset, workspacePath, host), {
|
||
preset,
|
||
workspacePath,
|
||
deadlineMs: host.deadlineMs
|
||
})
|
||
} catch (error) {
|
||
console.warn(
|
||
`[agent-trust] ${preset} trust for ${workspacePath} failed; the agent will ask`,
|
||
error
|
||
)
|
||
}
|
||
}
|