mirror of
https://github.com/stablyai/orca.git
synced 2026-11-04 02:57:36 +00:00
* Require Codex 0.136.0 for native chats
* Add host-owned Codex install and update actions
* Bind Codex checks and maintenance to the selected installation
* Resolve Windows updater cleanup through system helpers
* Use one child environment for Codex checks and launch
* Expire Codex readiness and reconcile current host jobs
* Resolve Codex checks against the child command search path
* Restrict global npm repair to the selected Codex install layout
* Pin the host updater directory through Windows supervision
* Synchronize Codex maintenance fixtures on readiness
* Unify Codex notices and recover version-refused chats
* Route Codex recovery to the operation that remains
* Preserve Codex setup execution errors in the job log
* Preserve UTF-8 in Codex setup logs
* Remove unused Codex host installation detector
* Require host administration for Codex maintenance
* Show a missing Codex explanation once
* test(native-chat): give scripted Codex runtimes their installation fact; keep the registry out of the relay
Runtime tests that drive a scripted Codex app-server still ran the new
version check against whatever codex was on the machine's PATH: refused
on CI (no codex), and on a dev machine it ran the real codex --version.
A scripted transport now must state the Codex it stands for
(readCodexInstallation), asserted at install, and the tests get it from
one scriptedCodexTransport fixture.
The relay's Codex maintenance path reached @orca/windows-registry through
resolvePathEnvKey's re-export in windows-environment-path; import it from
its registry-free module instead.
Also update the catalog-probe test for the shared child environment.
* refactor(native-chat): drop the Update Codex action; keep Install for a missing Codex
A too-old Codex now gets only the plain message ("Codex X is too old for
chats. Update to 0.136.0 or newer.") with no button and no job. The
existing rechecks (window focus, the <=30 s expiry of a saved result)
retry the refused chat once the user has updated Codex themselves.
A missing Codex keeps the Install Codex button: `npm install -g
@openai/codex` with the npm on the chat's PATH, as a host-run job with
its log, then a recheck and recovery. A configured Command that is
missing gets the message only, since a global npm install would not
land at that path.
Removed: the `codex update` command and its 0.126 floor, manual
instructions, npm layout and launcher identity detection, the action
descriptor (install|update) and its titles, the legacy capability, and
the SSH relay maintenance path (structured chat never runs over SSH or
WSL), so the relay preflight handler is back to main's.
* refactor(native-chat): keep Install Codex in Settings only
When Codex isn't installed, main already hides Codex from the New tab menu,
so the in-chat Install button was almost never reachable. Settings → Agents
already shows "Codex isn't installed." with Install Codex.
- Chats and the new-workspace composer now only state "Codex isn't
installed." (no button, no install job from the chat).
- The Install action, its busy spinner and the host-busy subscription move
into the Settings Codex row, the only place that starts the job.
- The store no longer re-checks every other workspace context when a job
finishes. A refused chat recovers through the same re-check a too-old
Codex uses (window focus, or the <=30 s expiry of a saved result).
- The chat recovery key no longer includes the install job id.
- Tests: Settings drives the install; a new test proves a chat refused for
a missing Codex recovers through focus after a Settings install, not
through the job; the expiry recovery test covers missing and too old.
* refactor(codex): check chat compatibility when acquiring the child
* Restore workspace submit guard and simplify Codex maintenance
* Keep only the Codex chat minimum version
80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
import {
|
|
hasReachedAppVersion,
|
|
isPrereleaseAppVersion,
|
|
parseCliVersion
|
|
} from '../shared/app-version'
|
|
import { runProcess } from '@orca/process-host'
|
|
import type { ProcessSpec } from '@orca/process-host/process-spec'
|
|
|
|
/** One line naming why a probe said no; the binary's own words, bounded. */
|
|
function warnRefused(program: string, why: string, output = ''): void {
|
|
const said = output.trim().replace(/\s+/g, ' ').slice(0, 200)
|
|
console.warn(`[agent-cli-version] ${program} --version: ${why}${said ? `: ${said}` : ''}`)
|
|
}
|
|
|
|
/** Runs `<program> --version` on this host and asks `supports` about the version it prints. False
|
|
* when it cannot tell: a failed spawn, a non-zero exit, a timeout, or output with no version. */
|
|
export async function probeAgentCliVersion(
|
|
input: Pick<ProcessSpec, 'program' | 'cwd' | 'env'>,
|
|
supports: (version: string) => boolean
|
|
): Promise<boolean> {
|
|
const result = await readAgentCliVersion(input)
|
|
if (result.version === null) {
|
|
return false
|
|
}
|
|
if (!supports(result.version)) {
|
|
warnRefused(input.program, `${result.version} is not a supported release`)
|
|
return false
|
|
}
|
|
console.info(`[agent-cli-version] ${input.program} --version: ${result.version} is supported`)
|
|
return true
|
|
}
|
|
|
|
export async function readAgentCliVersion(
|
|
input: Pick<ProcessSpec, 'program' | 'cwd' | 'env'>,
|
|
parseVersion: typeof parseCliVersion = parseCliVersion
|
|
): Promise<{ version: string | null }> {
|
|
let result
|
|
try {
|
|
result = await runProcess({
|
|
...input,
|
|
args: ['--version'],
|
|
timeoutMs: 5_000,
|
|
maxOutputBytes: 4_096,
|
|
killOnOutputLimit: true
|
|
})
|
|
} catch (error) {
|
|
warnRefused(input.program, `did not start (${String(error)})`)
|
|
return { version: null }
|
|
}
|
|
const output = `${result.stdout}\n${result.stderr}`
|
|
if (result.timedOut || result.outputTruncated || result.code !== 0) {
|
|
const why = result.timedOut
|
|
? 'timed out'
|
|
: result.outputTruncated
|
|
? 'printed too much'
|
|
: `exited ${result.code ?? result.signal}`
|
|
warnRefused(input.program, why, output)
|
|
return { version: null }
|
|
}
|
|
const version = parseVersion(parseVersion === parseCliVersion ? result.stdout : output)
|
|
if (version === null) {
|
|
warnRefused(input.program, 'printed no version', output)
|
|
return { version: null }
|
|
}
|
|
return { version }
|
|
}
|
|
|
|
/** A stable release at or after `floor`, on any later major line too. */
|
|
export function isStableCliVersionFrom(version: string, floor: string): boolean {
|
|
return !isPrereleaseAppVersion(version) && hasReachedAppVersion(version, floor)
|
|
}
|
|
|
|
/** A stable release on `major`'s line, at or after `floor`. */
|
|
export function isStableCliVersionOnLine(
|
|
version: string,
|
|
line: { major: number; floor: string }
|
|
): boolean {
|
|
return version.startsWith(`${line.major}.`) && isStableCliVersionFrom(version, line.floor)
|
|
}
|