mirror of
https://github.com/stablyai/orca.git
synced 2026-11-01 09:37:33 +00:00
* fix(terminal): preserve preparation and discovery for delayed agent launches * refactor(agents): centralize launch intent recognition * refactor(agents): keep launch intent in one module apart from process recognition Move recognizeAgentLaunchCommand next to the sleep-prefix scanner so the strict process-table matcher is untouched, name the module away from the agent-launch funnel files, share one executable-name normalizer, and colocate the preparation tests with their modules.
101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import { parseWslUncPath, toLinuxPath } from '../../shared/wsl-paths'
|
|
import {
|
|
getOpenCodeCliCapabilities,
|
|
type OpenCodeCliCapabilities
|
|
} from '../../shared/opencode-cli-version'
|
|
import type { TuiAgent } from '../../shared/tui-agent'
|
|
import { resolveCommandOnLocalPath } from '../ipc/command-path-resolver'
|
|
import { runWslProcess } from '../wsl/wsl-runner'
|
|
import { probeOpenCodeCliVersion } from './opencode-cli-version'
|
|
import {
|
|
getAgentLaunchCommandToken,
|
|
getAgentLaunchExecutableName
|
|
} from '../../shared/agent-command-line-intent'
|
|
|
|
export function getOpenCodeLaunchExecutable(
|
|
command: string | undefined,
|
|
agent?: TuiAgent
|
|
): string | null {
|
|
const executable = getAgentLaunchCommandToken(command)
|
|
const name = getAgentLaunchExecutableName(command)
|
|
return agent === 'opencode' ||
|
|
agent === 'opencode2' ||
|
|
(!agent && (name === 'opencode' || name === 'opencode2'))
|
|
? executable || null
|
|
: null
|
|
}
|
|
|
|
export async function probeOpenCodeLaunchCapabilities(options: {
|
|
command: string | undefined
|
|
agent?: TuiAgent
|
|
env: NodeJS.ProcessEnv
|
|
cwd?: string
|
|
wsl?: { distro?: string }
|
|
hostIdentity?: string
|
|
resolveExecutable?: (executable: string) => Promise<string | null>
|
|
}): Promise<OpenCodeCliCapabilities | null> {
|
|
const executable = getOpenCodeLaunchExecutable(options.command, options.agent)
|
|
if (!executable) {
|
|
return null
|
|
}
|
|
if (options.wsl) {
|
|
// An empty carrier prevents the runner from restoring deleted ambient imports.
|
|
const guestEnv: Record<string, string> = { WSLENV: '' }
|
|
const wslEnv = options.env.WSLENV?.split(':')
|
|
.filter((token) => options.env[token.split('/')[0]] !== undefined)
|
|
.join(':')
|
|
if (wslEnv) {
|
|
guestEnv.WSLENV = wslEnv
|
|
for (const token of wslEnv.split(':')) {
|
|
const [key, flags = ''] = token.split('/')
|
|
if (!key || flags.includes('w') || ['PATH', 'HOME', 'TMP', 'TEMP'].includes(key)) {
|
|
continue
|
|
}
|
|
const value = options.env[key]
|
|
if (value !== undefined) {
|
|
guestEnv[key] = value
|
|
}
|
|
}
|
|
}
|
|
const wslPath = options.cwd ? parseWslUncPath(options.cwd) : null
|
|
const cwd = options.cwd ? (wslPath?.linuxPath ?? toLinuxPath(options.cwd)) : undefined
|
|
if (cwd !== undefined && !cwd.startsWith('/')) {
|
|
return getOpenCodeCliCapabilities(null)
|
|
}
|
|
const distro = wslPath?.distro ?? options.wsl.distro
|
|
return probeOpenCodeCliVersion({
|
|
executablePath: executable,
|
|
env: guestEnv,
|
|
cwd,
|
|
hostIdentity: `${options.hostIdentity ?? 'local'}:wsl:${distro ?? 'default'}`,
|
|
execute: async () => {
|
|
const result = await runWslProcess({
|
|
distro,
|
|
loginPath: 'preferred',
|
|
cwd,
|
|
program: executable,
|
|
args: ['--version'],
|
|
env: guestEnv,
|
|
timeoutMs: 5_000,
|
|
maxOutputBytes: 4_096
|
|
})
|
|
return result.environmentResolved ? result : { ...result, code: null }
|
|
}
|
|
})
|
|
}
|
|
const executablePath = options.resolveExecutable
|
|
? await options.resolveExecutable(executable)
|
|
: await resolveCommandOnLocalPath(executable, {
|
|
env: options.env,
|
|
cwd: options.cwd
|
|
})
|
|
return executablePath
|
|
? probeOpenCodeCliVersion({
|
|
executablePath,
|
|
env: options.env,
|
|
cwd: options.cwd,
|
|
hostIdentity: options.hostIdentity
|
|
})
|
|
: getOpenCodeCliCapabilities(null)
|
|
}
|