Persist profile state in SQLite with background writes (#22612)

Migrate profile state to SQLite and move writes and backups into a background worker. Acknowledge terminal, SSH and automation changes only after durable saves. Preserve JSON import, recovery, rollback and compatibility exports.

Validate migration, worker failures, maintenance, cross-profile moves and terminal lifetime races with unit, integration and end-to-end coverage.
This commit is contained in:
OrcaWin
2026-09-25 22:47:33 -07:00
committed by GitHub
parent f0a3610928
commit 82412dab8b
490 changed files with 41290 additions and 3485 deletions
+3 -3
View File
@@ -255,7 +255,7 @@ function waitForRecipeJson(child: ReturnType<typeof spawnProcess>): Promise<numb
})
}
function getExecutableAppArgs(executable: string): string[] {
export function getExecutableAppArgs(executable: string): string[] {
const args = process.env.ORCA_APP_EXECUTABLE_NEEDS_APP_ROOT === '1' ? [resolveAppRoot()] : []
if (shouldDisableExtractedAppImageSandbox(executable)) {
args.push('--no-sandbox')
@@ -289,14 +289,14 @@ function getExecutableSpawnOptions(executable: string): Pick<SpawnOptions, 'shel
return process.platform === 'win32' && /\.(?:cmd|bat)$/i.test(executable) ? { shell: true } : {}
}
function resolveAppRoot(): string {
export function resolveAppRoot(): string {
// Why: dev-mode resource resolution in the Electron child may consult
// process.cwd(). Pin it to the app root so `orca serve` behaves the same
// regardless of the shell directory it was launched from.
return resolve(__dirname, '../../..')
}
function resolveForegroundOrcaExecutable(): string {
export function resolveForegroundOrcaExecutable(): string {
const overrideExecutable = process.env.ORCA_APP_EXECUTABLE
if (typeof overrideExecutable === 'string' && overrideExecutable.trim().length > 0) {
return overrideExecutable
@@ -0,0 +1,154 @@
import { realpathSync } from 'node:fs'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import {
PROFILE_STATE_RECOVERY_FLAG,
PROFILE_STATE_RECOVERY_RESULT_PREFIX
} from '../../shared/profile-state-recovery-command'
import {
canLaunchProfileStateRecovery,
launchProfileStateRecovery
} from './profile-state-recovery-launch'
const mocks = vi.hoisted(() => ({ run: vi.fn() }))
vi.mock('../../shared/child-process/run-process', () => ({ runProcess: mocks.run }))
vi.mock('./launch', () => ({
resolveForegroundOrcaExecutable: () => '/packaged/Orca',
resolveAppRoot: () => '/application',
getExecutableAppArgs: () => ['/application'],
stripElectronRunAsNode: (env: NodeJS.ProcessEnv) => {
const clean = { ...env }
delete clean.ELECTRON_RUN_AS_NODE
return clean
}
}))
const result = {
profileId: 'profile',
dataFile: '/root/orca-data.json',
databaseFile: '/root/profile-state.db',
exportPaths: [],
backups: [],
revision: 1,
quarantineDirectory: '/root/quarantine',
removedDatabaseFiles: [],
storage: 'json',
restoredPath: '/root/orca-data.json'
}
const request = { userDataPath: '.', selector: { kind: 'json', revision: 1 } } as const
beforeEach(() => {
mocks.run.mockReset().mockResolvedValue({
code: 0,
signal: null,
timedOut: false,
stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}${JSON.stringify({ ok: true, result })}\n`,
stderr: ''
})
})
afterEach(() => vi.unstubAllEnvs())
describe('profile-state recovery launch', () => {
it('preserves direct participation only for plain Node without an explicit Electron executable', () => {
vi.stubEnv('ELECTRON_RUN_AS_NODE', undefined)
vi.stubEnv('ORCA_APP_EXECUTABLE', undefined)
expect(canLaunchProfileStateRecovery()).toBe(false)
vi.stubEnv('ELECTRON_RUN_AS_NODE', '1')
expect(canLaunchProfileStateRecovery()).toBe(true)
vi.stubEnv('ELECTRON_RUN_AS_NODE', undefined)
vi.stubEnv('ORCA_APP_EXECUTABLE', '/explicit/Orca')
expect(canLaunchProfileStateRecovery()).toBe(true)
})
it('uses a foreground-safe serve request and binds the canonical recovery root', async () => {
vi.stubEnv('ELECTRON_RUN_AS_NODE', '1')
vi.stubEnv('ORCA_USER_DATA_PATH', '/stale/root')
expect(await launchProfileStateRecovery(request)).toEqual(result)
expect(mocks.run).toHaveBeenCalledWith(
expect.objectContaining({
program: '/packaged/Orca',
args: [
'/application',
'--serve',
PROFILE_STATE_RECOVERY_FLAG,
JSON.stringify({ ...request, userDataPath: realpathSync('.') })
],
env: expect.objectContaining({
ORCA_BACKGROUND_LAUNCH: '1',
ORCA_USER_DATA_PATH: realpathSync('.')
}),
timeoutMs: null
})
)
expect(mocks.run.mock.calls[0][0].env).not.toHaveProperty('ELECTRON_RUN_AS_NODE')
})
it('round-trips current JSON selection without requiring an invented revision', async () => {
const current = { ...result, revision: null }
mocks.run.mockResolvedValue({
code: 0,
signal: null,
timedOut: false,
stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}${JSON.stringify({ ok: true, result: current })}\n`,
stderr: ''
})
expect(
await launchProfileStateRecovery({ userDataPath: '.', selector: { kind: 'current-json' } })
).toEqual(current)
expect(mocks.run.mock.calls[0][0].args.at(-1)).toContain('"kind":"current-json"')
})
it('preserves a structured refusal from the lock owner', async () => {
mocks.run.mockResolvedValue({
code: 1,
stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}${JSON.stringify({ ok: false, code: 'invalid_argument', message: 'Backup unavailable' })}`
})
await expect(launchProfileStateRecovery(request)).rejects.toMatchObject({
code: 'invalid_argument',
message: 'Backup unavailable'
})
})
it.each([
{ code: 1 },
{ signal: 'SIGKILL' },
{ timedOut: true },
{ outputTruncated: true },
{ stdout: '' },
{ stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}{` },
{ stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}{"ok":true,"result":{}}` },
{
stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}{}\n${PROFILE_STATE_RECOVERY_RESULT_PREFIX}{}`
}
])('rejects incomplete or ambiguous child results %j', async (override) => {
const original = await mocks.run()
mocks.run.mockResolvedValue({ ...original, ...override })
await expect(launchProfileStateRecovery(request)).rejects.toMatchObject({
code: 'runtime_error'
})
})
it('propagates launch failure without retrying another recovery path', async () => {
mocks.run.mockRejectedValue(new Error('Executable unavailable'))
await expect(launchProfileStateRecovery(request)).rejects.toThrow('Executable unavailable')
expect(mocks.run).toHaveBeenCalledOnce()
})
it('retains bounded child diagnostics when recovery exits without a result', async () => {
mocks.run.mockResolvedValue({
code: null,
signal: 'SIGTRAP',
timedOut: false,
stdout: '',
stderr: `${'x'.repeat(5000)}\nsandbox unavailable\n`
})
await expect(launchProfileStateRecovery(request)).rejects.toMatchObject({
data: {
exitCode: null,
signal: 'SIGTRAP',
timedOut: false,
outputTruncated: false,
stderr: `${'x'.repeat(5000)}\nsandbox unavailable`.slice(-4096)
}
})
expect(mocks.run).toHaveBeenCalledOnce()
})
})
@@ -0,0 +1,78 @@
import { realpathSync } from 'node:fs'
import { runProcess } from '../../shared/child-process/run-process'
import {
PROFILE_STATE_RECOVERY_FLAG,
PROFILE_STATE_RECOVERY_RESULT_PREFIX,
profileStateRecoveryResponseSchema,
type ProfileStateRecoveryRequest,
type ProfileStateRollbackResult
} from '../../shared/profile-state-recovery-command'
import {
getExecutableAppArgs,
resolveAppRoot,
resolveForegroundOrcaExecutable,
stripElectronRunAsNode
} from './launch'
import { RuntimeClientError } from './types'
export function canLaunchProfileStateRecovery(): boolean {
return process.env.ELECTRON_RUN_AS_NODE === '1' || !!process.env.ORCA_APP_EXECUTABLE?.trim()
}
export async function launchProfileStateRecovery(
request: ProfileStateRecoveryRequest
): Promise<ProfileStateRollbackResult> {
const executable = resolveForegroundOrcaExecutable()
const userDataPath = realpathSync(request.userDataPath)
const response = await runProcess({
program: executable,
args: [
...getExecutableAppArgs(executable),
'--serve',
PROFILE_STATE_RECOVERY_FLAG,
JSON.stringify({ ...request, userDataPath })
],
cwd: resolveAppRoot(),
env: {
...stripElectronRunAsNode(process.env),
ORCA_BACKGROUND_LAUNCH: '1',
ORCA_USER_DATA_PATH: userDataPath
},
// Recovery may copy large backups; the lock owner must finish or be explicitly terminated.
timeoutMs: null
})
const lines = response.stdout
.split(/\r?\n/)
.filter((line) => line.startsWith(PROFILE_STATE_RECOVERY_RESULT_PREFIX))
if (!response.outputTruncated && lines.length === 1) {
let parsed: unknown
try {
parsed = JSON.parse(lines[0].slice(PROFILE_STATE_RECOVERY_RESULT_PREFIX.length))
} catch {
throw new RuntimeClientError(
'runtime_error',
'Orca recovery returned an invalid response. Inspect retained recovery artifacts before retrying.'
)
}
const result = profileStateRecoveryResponseSchema.safeParse(parsed)
if (result.success) {
if (!result.data.ok) {
throw new RuntimeClientError(result.data.code, result.data.message)
}
if (response.code === 0 && !response.signal && !response.timedOut) {
return result.data.result
}
}
}
throw new RuntimeClientError(
'runtime_error',
'Orca recovery did not complete successfully. Inspect retained recovery artifacts before retrying.',
{
exitCode: response.code,
signal: response.signal,
timedOut: response.timedOut,
outputTruncated: response.outputTruncated ?? false,
stderr: response.stderr.trim().slice(-4096)
}
)
}