mirror of
https://github.com/stablyai/orca.git
synced 2026-10-04 16:02:08 +00:00
* feat(ai-vault): read OpenCode history with the pinned Node instead of Bun SSH hosts whose Node lacks node:sqlite (or its backup(), which 22.13-22.15 omit) now get the pinned Node in the shared ~/.orca-remote/runtimes/node-<sha> store orcad uses: POSIX hosts receive the official archive and extract and hash-verify it on the host; Windows hosts receive the verified node.exe the client extracted, promoted by host Node with the same hash check. WSL distros use the same layout and checks under ~/.cache/orca/runtimes/. The Bun release pin table and its materializer are deleted. Old relays keep reading their vault-sqlite/<sha>/bun references; nothing deletes those files. An unconfirmed runtime upload now keeps its stage instead of removing it. * refactor(sqlite): drop the Bun SQLite adapter; node:sqlite is the only backend Nothing outside Electron runs on Bun any more (design D4), so SyncDatabase loses its Bun branch, and bun-sqlite-database, bun-sqlite-statement and bun-readonly-wal go, with the relay's bun:sqlite external. The profile-state backup worker admits Electron or an entry that exists, and startup errors name the pinned Node. The D7 cross-runtime gate still runs Bun 1.4.2, now reaching Bun's SQLite through its node:sqlite. * test(native-chat): drop the Bun SQLite driver case now that node:sqlite is the only backend --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
130 lines
5.2 KiB
JavaScript
130 lines
5.2 KiB
JavaScript
// R3 gate (docs/reference/node-runtime-design.html, D7.1): swapping the process that hosts orcad
|
|
// or the daemon is not a daemon protocol change, so one PR must not do both. A bump riding along
|
|
// with a runtime swap would strand every live terminal on upgrade and rollback at once.
|
|
// Usage: node check-runtime-launcher-protocol-ratchet.mjs --base <ref>
|
|
import { execFileSync } from 'node:child_process'
|
|
import { readFileSync } from 'node:fs'
|
|
import { join, resolve } from 'node:path'
|
|
import { pathToFileURL } from 'node:url'
|
|
import { DAEMON_PROTOCOL_SOURCE_PATH, parseDaemonProtocolFacts } from './daemon-protocol-facts.mjs'
|
|
|
|
/** Files that decide which runtime executable launches orcad or the terminal daemon. */
|
|
export const RUNTIME_LAUNCHER_PATHS = [
|
|
// orcad handoff to its bundled runtime, and the pinned runtimes it can hand off to.
|
|
'src/main/orcad/orcad-bundled-runtime.ts',
|
|
'src/shared/node-runtime-pin.ts',
|
|
'src/main/ssh/pinned-runtime-materializer.ts',
|
|
'src/main/ssh/runtime-archive-download.ts',
|
|
'src/main/ssh/orcad-remote-node-runtime.ts',
|
|
// orcad slot layout: which runtime file a packaged slot carries.
|
|
'src/shared/orcad-artifacts.ts',
|
|
'config/scripts/build-orcad.mjs',
|
|
'config/scripts/build-orcad-node.mjs',
|
|
'config/scripts/build-orcad-template.mjs',
|
|
// Remote slot runtime selection.
|
|
'src/main/ssh/orcad-remote-runtime.ts',
|
|
// Terminal daemon host launch: which executable the daemon child is forked from.
|
|
'src/main/daemon/daemon-launched-child.ts',
|
|
'src/main/daemon/daemon-launched-child-spawn.ts',
|
|
'src/main/daemon/daemon-out-of-process-launcher.ts',
|
|
'src/main/daemon/daemon-host-relocation.ts',
|
|
'src/main/daemon/daemon-host-manifest.ts'
|
|
]
|
|
|
|
export const RUNTIME_PROTOCOL_OVERRIDE_ENV = 'ORCA_ALLOW_RUNTIME_LAUNCHER_PROTOCOL_BUMP'
|
|
export const RUNTIME_PROTOCOL_OVERRIDE_LABEL = 'allow-runtime-launcher-protocol-bump'
|
|
|
|
function git(repoRoot, args) {
|
|
return execFileSync('git', args, {
|
|
cwd: repoRoot,
|
|
encoding: 'utf8',
|
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
maxBuffer: 16 * 1024 * 1024
|
|
})
|
|
}
|
|
|
|
/**
|
|
* @param {{ changedFiles: string[], baseProtocolVersion: number, candidateProtocolVersion: number, overridden: boolean }} input
|
|
*/
|
|
export function assessRuntimeLauncherProtocolRatchet({
|
|
changedFiles,
|
|
baseProtocolVersion,
|
|
candidateProtocolVersion,
|
|
overridden
|
|
}) {
|
|
const launcherChanges = RUNTIME_LAUNCHER_PATHS.filter((path) => changedFiles.includes(path))
|
|
const protocolChanged = baseProtocolVersion !== candidateProtocolVersion
|
|
const violated = protocolChanged && launcherChanges.length > 0
|
|
const lines = [
|
|
`daemon PROTOCOL_VERSION: base ${baseProtocolVersion}, candidate ${candidateProtocolVersion}`,
|
|
launcherChanges.length > 0
|
|
? `runtime launcher files changed: ${launcherChanges.join(', ')}`
|
|
: 'runtime launcher files changed: none'
|
|
]
|
|
if (!violated) {
|
|
lines.push('OK: this change does not both swap a runtime launcher and bump the daemon protocol')
|
|
} else if (overridden) {
|
|
lines.push(
|
|
`OVERRIDDEN: launcher change and protocol bump together, allowed by ${RUNTIME_PROTOCOL_OVERRIDE_ENV}`
|
|
)
|
|
} else {
|
|
lines.push(
|
|
'FAIL: split the protocol bump and the runtime launcher change into separate PRs (D7.1 R3), ' +
|
|
`or apply the "${RUNTIME_PROTOCOL_OVERRIDE_LABEL}" label and re-run the workflow with a new push.`
|
|
)
|
|
}
|
|
return { ok: !violated || overridden, violated, lines }
|
|
}
|
|
|
|
export function isOverrideEnabled(env = process.env) {
|
|
return ['1', 'true'].includes(env[RUNTIME_PROTOCOL_OVERRIDE_ENV]?.trim().toLowerCase() ?? '')
|
|
}
|
|
|
|
export function checkRuntimeLauncherProtocolRatchet({ repoRoot, base, env = process.env }) {
|
|
// Why diff against the working tree: CI's tree is HEAD, and a local run also sees unstaged edits.
|
|
const changedFiles = git(repoRoot, ['diff', '--name-only', '--no-renames', base, '--'])
|
|
.split('\n')
|
|
.map((line) => line.trim())
|
|
.filter(Boolean)
|
|
const baseFacts = parseDaemonProtocolFacts(
|
|
git(repoRoot, ['show', `${base}:${DAEMON_PROTOCOL_SOURCE_PATH}`]),
|
|
`${base}:${DAEMON_PROTOCOL_SOURCE_PATH}`
|
|
)
|
|
const candidateFacts = parseDaemonProtocolFacts(
|
|
readFileSync(join(repoRoot, DAEMON_PROTOCOL_SOURCE_PATH), 'utf8'),
|
|
DAEMON_PROTOCOL_SOURCE_PATH
|
|
)
|
|
return assessRuntimeLauncherProtocolRatchet({
|
|
changedFiles,
|
|
baseProtocolVersion: baseFacts.protocolVersion,
|
|
candidateProtocolVersion: candidateFacts.protocolVersion,
|
|
overridden: isOverrideEnabled(env)
|
|
})
|
|
}
|
|
|
|
function parseArgs(argv) {
|
|
const index = argv.indexOf('--base')
|
|
const value = index === -1 ? undefined : argv[index + 1]
|
|
if (!value || value.startsWith('--')) {
|
|
throw new Error('--base <ref> is required (the pull request diff base)')
|
|
}
|
|
return { base: value }
|
|
}
|
|
|
|
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
const repoRoot = resolve(import.meta.dirname, '..', '..')
|
|
try {
|
|
const { base } = parseArgs(process.argv.slice(2))
|
|
const result = checkRuntimeLauncherProtocolRatchet({ repoRoot, base })
|
|
for (const line of result.lines) {
|
|
console.log(line)
|
|
}
|
|
if (!result.ok) {
|
|
process.exitCode = 1
|
|
}
|
|
} catch (error) {
|
|
console.error(`runtime launcher protocol ratchet failed: ${error.message}`)
|
|
process.exitCode = 1
|
|
}
|
|
}
|