mirror of
https://github.com/stablyai/orca.git
synced 2026-10-26 16:02:42 +00:00
123 lines
3.2 KiB
TypeScript
123 lines
3.2 KiB
TypeScript
import { createHash } from 'node:crypto'
|
|
import { OrchestrationError } from './orchestration-error'
|
|
|
|
const WORKER_OUTPUT_CURSOR_PREFIX = 'owr1_'
|
|
const WORKER_OUTPUT_CURSOR_MAX_LENGTH = 2_048
|
|
|
|
type WorkerOutputCursorPayload = {
|
|
v: 1
|
|
d: string
|
|
s: 'terminal' | 'transcript'
|
|
i: string
|
|
p: number
|
|
}
|
|
|
|
export type DecodedWorkerOutputCursor =
|
|
| {
|
|
source: 'terminal'
|
|
sourceIdentity: string | null
|
|
position: number
|
|
legacy: boolean
|
|
}
|
|
| {
|
|
source: 'transcript'
|
|
sourceIdentity: string
|
|
position: number
|
|
legacy: false
|
|
}
|
|
|
|
export function createWorkerOutputSourceIdentity(fields: readonly string[]): string {
|
|
return createHash('sha256').update(JSON.stringify(fields)).digest('base64url').slice(0, 32)
|
|
}
|
|
|
|
export function encodeWorkerOutputCursor(
|
|
dispatchId: string,
|
|
source: WorkerOutputCursorPayload['s'],
|
|
sourceIdentity: string,
|
|
position: number
|
|
): string {
|
|
const payload: WorkerOutputCursorPayload = {
|
|
v: 1,
|
|
d: dispatchId,
|
|
s: source,
|
|
i: sourceIdentity,
|
|
p: position
|
|
}
|
|
return `${WORKER_OUTPUT_CURSOR_PREFIX}${Buffer.from(JSON.stringify(payload)).toString('base64url')}`
|
|
}
|
|
|
|
export function decodeWorkerOutputCursor(
|
|
cursor: string | number | undefined,
|
|
dispatchId: string
|
|
): DecodedWorkerOutputCursor | null {
|
|
if (cursor === undefined) {
|
|
return null
|
|
}
|
|
if (typeof cursor === 'number') {
|
|
return decodeLegacyTerminalCursor(cursor)
|
|
}
|
|
if (/^\d+$/.test(cursor)) {
|
|
return decodeLegacyTerminalCursor(Number.parseInt(cursor, 10))
|
|
}
|
|
if (
|
|
cursor.length > WORKER_OUTPUT_CURSOR_MAX_LENGTH ||
|
|
!cursor.startsWith(WORKER_OUTPUT_CURSOR_PREFIX)
|
|
) {
|
|
throw invalidCursor()
|
|
}
|
|
let parsed: unknown
|
|
try {
|
|
parsed = JSON.parse(
|
|
Buffer.from(cursor.slice(WORKER_OUTPUT_CURSOR_PREFIX.length), 'base64url').toString('utf8')
|
|
)
|
|
} catch {
|
|
throw invalidCursor()
|
|
}
|
|
if (!isWorkerOutputCursorPayload(parsed)) {
|
|
throw invalidCursor()
|
|
}
|
|
if (parsed.d !== dispatchId) {
|
|
throw new OrchestrationError(
|
|
'cursor_dispatch_mismatch',
|
|
'The worker-read cursor belongs to a different Dispatch.'
|
|
)
|
|
}
|
|
return {
|
|
source: parsed.s,
|
|
sourceIdentity: parsed.i,
|
|
position: parsed.p,
|
|
legacy: false
|
|
}
|
|
}
|
|
|
|
function decodeLegacyTerminalCursor(position: number): DecodedWorkerOutputCursor {
|
|
if (!Number.isSafeInteger(position) || position < 0) {
|
|
throw invalidCursor()
|
|
}
|
|
return { source: 'terminal', sourceIdentity: null, position, legacy: true }
|
|
}
|
|
|
|
function isWorkerOutputCursorPayload(value: unknown): value is WorkerOutputCursorPayload {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
return false
|
|
}
|
|
const payload = value as Record<string, unknown>
|
|
return (
|
|
payload.v === 1 &&
|
|
typeof payload.d === 'string' &&
|
|
payload.d.length > 0 &&
|
|
payload.d.length <= 512 &&
|
|
(payload.s === 'terminal' || payload.s === 'transcript') &&
|
|
typeof payload.i === 'string' &&
|
|
payload.i.length > 0 &&
|
|
payload.i.length <= 128 &&
|
|
typeof payload.p === 'number' &&
|
|
Number.isSafeInteger(payload.p) &&
|
|
payload.p >= 0
|
|
)
|
|
}
|
|
|
|
function invalidCursor(): OrchestrationError {
|
|
return new OrchestrationError('cursor_invalid', 'The worker-read cursor is invalid.')
|
|
}
|