mirror of
https://github.com/stablyai/orca.git
synced 2026-10-22 00:03:47 +00:00
* fix(orchestration): preserve active workers across updates * test(ssh): model absent legacy adoption * test(orchestration): align compatibility contracts * fix(windows): escape updater PowerShell booleans * fix(windows): restore stock uninstall process check * fix(orchestration): keep recovery off renderer startup barrier * fix(orchestration): harden legacy recovery migration * fix(orchestration): close recovery review gaps * fix(orchestration): complete legacy worker cutover recovery * fix(orchestration): preserve legacy workers across updates --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
144 lines
4.9 KiB
TypeScript
144 lines
4.9 KiB
TypeScript
import { randomUUID } from 'node:crypto'
|
|
import { readOrchestrationCompatibilityEvidence } from '../../shared/orchestration-compatibility-evidence'
|
|
import { ORCHESTRATION_CONTRACT_VERSION } from '../../shared/protocol-version'
|
|
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
|
|
import type { RpcResponse } from '../runtime/rpc/core'
|
|
import { RpcDispatcher } from '../runtime/rpc/dispatcher'
|
|
import type {
|
|
RemoteOrcaCliPostOutput,
|
|
RemoteOrcaCliRequest
|
|
} from './ssh-remote-cli-host-passthrough'
|
|
import { RemoteCliArgumentError, type ParsedRemoteCli } from './ssh-remote-cli-argument-error'
|
|
import { optionalRemoteCliString, resolveRemoteCliHandle } from './ssh-remote-cli-args'
|
|
|
|
export async function acknowledgeRemoteOrcaCliPostOutput(
|
|
runtime: OrcaRuntimeService,
|
|
args: {
|
|
postOutput: RemoteOrcaCliPostOutput
|
|
env: Record<string, string>
|
|
runtimeAuthority?: RemoteOrcaCliRequest['runtimeAuthority']
|
|
}
|
|
): Promise<void> {
|
|
const inheritedEvidence = readOrchestrationCompatibilityEvidence(args.env)
|
|
const orchestrationCompatibilityEvidence = args.runtimeAuthority
|
|
? { ...inheritedEvidence, host: args.runtimeAuthority }
|
|
: inheritedEvidence
|
|
const params =
|
|
args.postOutput.kind === 'legacy_check_ack'
|
|
? {
|
|
terminal: args.postOutput.terminal,
|
|
compatibilityAck: JSON.stringify({
|
|
messageIds: args.postOutput.messageIds,
|
|
types: args.postOutput.types
|
|
})
|
|
}
|
|
: {
|
|
terminal: args.postOutput.terminal,
|
|
compatibilityQuestionAck: JSON.stringify({
|
|
questionId: args.postOutput.questionId,
|
|
answerMessageId: args.postOutput.answerMessageId
|
|
})
|
|
}
|
|
const response = await new RpcDispatcher({ runtime }).dispatch({
|
|
id: `remote-cli-post-output-${randomUUID()}`,
|
|
authToken: 'remote-cli',
|
|
method: 'orchestration.check',
|
|
params,
|
|
orchestrationContractVersion: ORCHESTRATION_CONTRACT_VERSION,
|
|
compatibilityInvocationId: randomUUID(),
|
|
orchestrationCompatibilityEvidence
|
|
})
|
|
if (!response.ok) {
|
|
throw new Error(response.error.message)
|
|
}
|
|
}
|
|
|
|
export function parseRemoteOrcaCliPostOutput(value: unknown): RemoteOrcaCliPostOutput {
|
|
if (!isRecord(value) || typeof value.kind !== 'string' || typeof value.terminal !== 'string') {
|
|
throw invalidPostOutput()
|
|
}
|
|
if (
|
|
value.kind === 'legacy_check_ack' &&
|
|
Array.isArray(value.messageIds) &&
|
|
value.messageIds.every((id) => typeof id === 'string') &&
|
|
(value.types === undefined ||
|
|
(Array.isArray(value.types) && value.types.every((type) => typeof type === 'string')))
|
|
) {
|
|
return {
|
|
kind: value.kind,
|
|
terminal: value.terminal,
|
|
messageIds: value.messageIds,
|
|
...(value.types === undefined ? {} : { types: value.types })
|
|
}
|
|
}
|
|
if (
|
|
value.kind === 'legacy_question_ack' &&
|
|
typeof value.questionId === 'string' &&
|
|
typeof value.answerMessageId === 'string'
|
|
) {
|
|
return {
|
|
kind: value.kind,
|
|
terminal: value.terminal,
|
|
questionId: value.questionId,
|
|
answerMessageId: value.answerMessageId
|
|
}
|
|
}
|
|
throw invalidPostOutput()
|
|
}
|
|
|
|
export function getRemoteCliPostOutput(
|
|
parsed: ParsedRemoteCli,
|
|
env: Record<string, string>,
|
|
response: RpcResponse
|
|
): RemoteOrcaCliPostOutput | undefined {
|
|
if (!response.ok || !isRecord(response.result)) {
|
|
return undefined
|
|
}
|
|
const compatibility = response.result.legacyCompatibility
|
|
if (!isRecord(compatibility)) {
|
|
return undefined
|
|
}
|
|
const command = parsed.commandPath.join(' ')
|
|
if (
|
|
command === 'orchestration check' &&
|
|
Array.isArray(compatibility.ackMessageIds) &&
|
|
compatibility.ackMessageIds.length > 0 &&
|
|
compatibility.ackMessageIds.every((id) => typeof id === 'string')
|
|
) {
|
|
const types = optionalRemoteCliString(parsed.flags, 'types')
|
|
?.split(',')
|
|
.map((type) => type.trim())
|
|
.filter(Boolean)
|
|
return {
|
|
kind: 'legacy_check_ack',
|
|
terminal: resolveRemoteCliHandle(parsed.flags, env, 'terminal'),
|
|
messageIds: compatibility.ackMessageIds,
|
|
...(types ? { types } : {})
|
|
}
|
|
}
|
|
const acknowledgement = compatibility.answerAcknowledgement
|
|
if (
|
|
command === 'orchestration ask' &&
|
|
response.result.answer !== null &&
|
|
isRecord(acknowledgement) &&
|
|
typeof acknowledgement.questionId === 'string' &&
|
|
typeof acknowledgement.answerMessageId === 'string'
|
|
) {
|
|
return {
|
|
kind: 'legacy_question_ack',
|
|
terminal: resolveRemoteCliHandle(parsed.flags, env, 'from'),
|
|
questionId: acknowledgement.questionId,
|
|
answerMessageId: acknowledgement.answerMessageId
|
|
}
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
function invalidPostOutput(): RemoteCliArgumentError {
|
|
return new RemoteCliArgumentError('invalid_argument', 'Invalid SSH CLI post-output action.')
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
}
|