fix(codex): make WSL auth drain transaction recoverable

This commit is contained in:
Brennan Benson
2026-08-27 05:14:36 -07:00
parent f1df01023a
commit 2969ba5ca2
4 changed files with 317 additions and 104 deletions
@@ -1,6 +1,14 @@
import { execFileSync } from 'node:child_process'
import { createHash } from 'node:crypto'
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs'
import {
chmodSync,
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync
} from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
@@ -11,8 +19,26 @@ const SOURCE = '{"tokens":{"expires_at":2000}}\n'
const TARGET = '{"tokens":{"expires_at":1000}}\n'
const NEWER = '{"tokens":{"expires_at":3000}}\n'
const sha256 = (value: string) => createHash('sha256').update(value).digest('hex')
type ApplyOutcome = {
firstStatus?: number
marker: boolean
quarantine?: string | null
source: string | null
status: number
target: string
}
function runApply(rewriteAfterHashCall?: number) {
function runApply(
options: {
deleteSource?: boolean
crashBeforeCommit?: boolean
promoteAuth?: boolean
retryAfterCrash?: boolean
rewriteAfterHashCall?: number
rewriteBeforeDelete?: boolean
rewriteSourceBeforeDelete?: boolean
} = {}
): ApplyOutcome {
const root = mkdtempSync(join(tmpdir(), 'orca-drain-race-'))
const legacy = join(root, 'legacy')
const target = join(root, 'target')
@@ -38,55 +64,139 @@ if(process.env.REWRITE && n===Number(process.env.REWRITE)) fs.writeFileSync(proc
`
)
chmodSync(shim, 0o755)
let status = 0
try {
execFileSync(
'/bin/sh',
[
'-c',
_internals.applyLegacyAuthScript,
'sh',
legacy,
join(root, 'active'),
marker,
target,
sha256(SOURCE),
sha256(TARGET),
'0',
'1',
'missing'
],
{
const mvShim = join(bin, 'mv')
writeFileSync(
mvShim,
`#!/usr/bin/env node
const { spawnSync } = require('node:child_process')
const fs = require('node:fs')
const args = process.argv.slice(2)
const retiresSource = args.at(-2)?.endsWith('/legacy/auth.json')
if (process.env.REWRITE_BEFORE_DELETE === '1' && retiresSource) {
const replacement = process.env.TARGET + '.replacement'
fs.writeFileSync(replacement, process.env.BYTES)
fs.renameSync(replacement, process.env.TARGET)
}
if (process.env.REWRITE_SOURCE_BEFORE_DELETE === '1' && retiresSource) {
const source = args.at(-2)
const replacement = source + '.replacement'
fs.writeFileSync(replacement, process.env.BYTES)
fs.renameSync(replacement, source)
}
const result = spawnSync('/bin/mv', args, { stdio: 'inherit' })
if (result.status === 0 && process.env.CRASH_BEFORE_COMMIT === '1' && retiresSource) {
process.kill(process.ppid, 'SIGKILL')
}
process.exit(result.status ?? 1)
`
)
chmodSync(mvShim, 0o755)
const applyArgs = [
'-c',
_internals.applyLegacyAuthScript,
'sh',
legacy,
join(root, 'active'),
marker,
target,
sha256(SOURCE),
sha256(TARGET),
options.promoteAuth ? '1' : '0',
options.deleteSource === false ? '0' : '1',
'missing'
]
const apply = (crashBeforeCommit: boolean): number => {
try {
execFileSync('/bin/sh', applyArgs, {
env: {
...process.env,
PATH: `${bin}:${process.env.PATH ?? ''}`,
COUNTER: counter,
REWRITE: rewriteAfterHashCall ? String(rewriteAfterHashCall) : '',
CRASH_BEFORE_COMMIT: crashBeforeCommit ? '1' : '',
REWRITE: options.rewriteAfterHashCall ? String(options.rewriteAfterHashCall) : '',
REWRITE_BEFORE_DELETE: options.rewriteBeforeDelete ? '1' : '',
REWRITE_SOURCE_BEFORE_DELETE: options.rewriteSourceBeforeDelete ? '1' : '',
TARGET: targetPath,
BYTES: NEWER
},
stdio: 'ignore'
}
)
} catch (error) {
status = (error as { status?: number }).status ?? -1
})
return 0
} catch (error) {
return (error as { status?: number }).status ?? -1
}
}
return {
const firstStatus = apply(Boolean(options.crashBeforeCommit))
let status = firstStatus
if (options.retryAfterCrash) {
execFileSync(
'/bin/sh',
['-c', _internals.inspectLegacyAuthScript, 'sh', legacy, join(root, 'active'), marker],
{ env: { ...process.env, PATH: `${bin}:${process.env.PATH ?? ''}` }, stdio: 'ignore' }
)
writeFileSync(counter, '0')
status = apply(false)
}
const outcome = {
status,
source: existsSync(sourcePath) ? readFileSync(sourcePath, 'utf8') : null,
target: readFileSync(targetPath, 'utf8'),
marker: existsSync(marker)
}
const quarantine = `${marker}.orca-drain-live-source`
const quarantineContents = existsSync(quarantine) ? readFileSync(quarantine, 'utf8') : null
rmSync(root, { recursive: true, force: true })
return {
...outcome,
...(options.retryAfterCrash ? { firstStatus } : {}),
...(options.rewriteSourceBeforeDelete ? { quarantine: quarantineContents } : {})
}
}
describe.skipIf(isWindows)('legacy WSL auth drain race guard', () => {
it('retires an unchanged source', () =>
expect(runApply()).toEqual({ status: 0, source: null, target: TARGET, marker: true }))
it('promotes the verified source before retiring it', () =>
expect(runApply({ promoteAuth: true })).toEqual({
status: 0,
source: null,
target: SOURCE,
marker: true
}))
it('retains the source and marker while a legacy pane is live', () =>
expect(runApply({ deleteSource: false, promoteAuth: true })).toEqual({
status: 0,
source: SOURCE,
target: SOURCE,
marker: false
}))
it('retains source when destination is rewritten during validation', () => {
const outcome = runApply(3)
const outcome = runApply({ rewriteAfterHashCall: 3 })
expect(outcome.status).toBe(45)
expect(outcome.source).toBe(SOURCE)
expect(outcome.target).toBe(NEWER)
expect(outcome.marker).toBe(false)
})
it('retains source when destination is rewritten immediately before deletion', () => {
const outcome = runApply({ rewriteBeforeDelete: true })
expect(outcome.status).not.toBe(0)
expect(outcome.source).toBe(SOURCE)
expect(outcome.target).toBe(NEWER)
expect(outcome.marker).toBe(false)
})
it('restores verified source bytes when the source path is atomically replaced', () => {
const outcome = runApply({ rewriteSourceBeforeDelete: true })
expect(outcome.status).toBe(40)
expect(outcome.source).toBe(SOURCE)
expect(outcome.quarantine).toBe(NEWER)
expect(outcome.marker).toBe(false)
})
it('recovers and retries after interruption before the completion marker', () => {
const outcome = runApply({ crashBeforeCommit: true, retryAfterCrash: true })
expect(outcome.firstStatus).not.toBe(0)
expect(outcome.status).toBe(0)
expect(outcome.source).toBeNull()
expect(outcome.target).toBe(TARGET)
expect(outcome.marker).toBe(true)
})
})
@@ -0,0 +1,140 @@
export const MARKER_PRESENT_EXIT = 20
export const SOURCE_AUTH_ABSENT_EXIT = 21
const RESOLVE_LEGACY_HOME_SCRIPT = `
legacy_home="$1"
legacy_home_resolved=0
if [ -e "$1" ] || [ -L "$1" ]; then legacy_home=$(readlink -f -- "$1") || exit 30; legacy_home_resolved=1; fi
if [ -e "$2" ] || [ -L "$2" ]; then active_home=$(readlink -f -- "$2") || exit 31; if [ "$legacy_home_resolved" = 1 ]; then [ "$active_home" = "$legacy_home" ] || exit 32; else legacy_home="$active_home"; legacy_home_resolved=1; fi; fi
`
export const INSPECT_LEGACY_AUTH_SCRIPT = `
set -eu
source_recovery_auth="$3.orca-drain-source"
source_quarantine_auth="$3.orca-drain-live-source"
destination_recovery_auth="$3.orca-drain-destination"
if [ -e "$3" ] || [ -L "$3" ]; then
[ -f "$3" ] && [ ! -L "$3" ] || exit 46
if [ -f "$destination_recovery_auth" ] && [ ! -L "$destination_recovery_auth" ]; then chmod 600 "$destination_recovery_auth"; fi
rm -f -- "$source_recovery_auth" "$source_quarantine_auth" "$destination_recovery_auth"
exit ${MARKER_PRESENT_EXIT}
fi
[ ! -e "$destination_recovery_auth" ] && [ ! -L "$destination_recovery_auth" ] || { [ -f "$destination_recovery_auth" ] && [ ! -L "$destination_recovery_auth" ] || exit 46; chmod 600 "$destination_recovery_auth"; }
${RESOLVE_LEGACY_HOME_SCRIPT}
source_auth="$legacy_home/auth.json"
if [ ! -e "$source_auth" ] && [ ! -L "$source_auth" ]; then
if [ -f "$source_recovery_auth" ] && [ ! -L "$source_recovery_auth" ]; then
mv -- "$source_recovery_auth" "$source_auth"; chmod 600 "$source_auth"
elif [ -f "$source_quarantine_auth" ] && [ ! -L "$source_quarantine_auth" ]; then
mv -- "$source_quarantine_auth" "$source_auth"; chmod 600 "$source_auth"
elif [ -e "$source_quarantine_auth" ] || [ -L "$source_quarantine_auth" ] || [ -e "$source_recovery_auth" ] || [ -L "$source_recovery_auth" ]; then
exit 46
fi
fi
[ -f "$source_auth" ] || exit ${SOURCE_AUTH_ABSENT_EXIT}
encode_file() { base64 < "$1" | tr -d '\\n'; }
encode_file "$source_auth"; printf '\\n'
source_credentials="$legacy_home/.credentials.json"
if [ -f "$source_credentials" ]; then printf 'present\\n'; encode_file "$source_credentials"; printf '\\n'; elif [ ! -e "$source_credentials" ] && [ ! -L "$source_credentials" ]; then printf 'missing\\n\\n'; else exit 44; fi
`
// Freeze a verified destination snapshot before source quarantine so the
// marker commits only while both credential copies remain recoverable.
export const APPLY_LEGACY_AUTH_SCRIPT = `
set -eu
source_recovery_auth="$3.orca-drain-source"
source_quarantine_auth="$3.orca-drain-live-source"
destination_recovery_auth="$3.orca-drain-destination"
if [ -e "$3" ] || [ -L "$3" ]; then
[ -f "$3" ] && [ ! -L "$3" ] || exit 46
if [ -f "$destination_recovery_auth" ] && [ ! -L "$destination_recovery_auth" ]; then chmod 600 "$destination_recovery_auth"; fi
rm -f -- "$source_recovery_auth" "$source_quarantine_auth" "$destination_recovery_auth"
exit 0
fi
${RESOLVE_LEGACY_HOME_SCRIPT}
target_home=$(readlink -f -- "$4") || exit 33
[ "$legacy_home" != "$target_home" ] || exit 34
source_auth="$legacy_home/auth.json"; target_auth="$target_home/auth.json"
[ -f "$source_auth" ] || exit 35; [ -f "$target_auth" ] || exit 36
hash_file() { sha256sum -- "$1" | cut -d ' ' -f 1; }
if [ -e "$source_quarantine_auth" ] || [ -L "$source_quarantine_auth" ]; then
[ -f "$source_quarantine_auth" ] && [ ! -L "$source_quarantine_auth" ] || exit 46
[ "$(hash_file "$source_auth")" = "$5" ] || exit 37
rm -- "$source_quarantine_auth"
fi
if [ -e "$source_recovery_auth" ] || [ -L "$source_recovery_auth" ]; then
[ -f "$source_recovery_auth" ] && [ ! -L "$source_recovery_auth" ] || exit 46
[ "$(hash_file "$source_auth")" = "$5" ] || exit 37
rm -- "$source_recovery_auth"
fi
if [ -e "$destination_recovery_auth" ] || [ -L "$destination_recovery_auth" ]; then
[ -f "$destination_recovery_auth" ] && [ ! -L "$destination_recovery_auth" ] || exit 46
if [ "$target_auth" -ef "$destination_recovery_auth" ]; then chmod 600 "$target_auth"; fi
rm -- "$destination_recovery_auth"
fi
[ "$(hash_file "$source_auth")" = "$5" ] || exit 37
[ "$(hash_file "$target_auth")" = "$6" ] || exit 38
umask 077
temporary_auth="$target_auth.orca-drain-$$"; temporary_credentials="$target_home/.credentials.json.orca-drain-$$"; destination_pin="$target_auth.orca-drain-pin-$$"; temporary_destination_snapshot="$target_auth.orca-drain-snapshot-$$"; temporary_source_snapshot="$source_auth.orca-drain-source-$$"; temporary_marker="$3.orca-drain-$$"
drain_marker="$3"; expected_source_hash="$5"
cleanup() {
if [ ! -f "$drain_marker" ] && [ ! -e "$source_auth" ] && [ ! -L "$source_auth" ]; then
if [ -f "$source_recovery_auth" ] && [ ! -L "$source_recovery_auth" ] && [ "$(hash_file "$source_recovery_auth")" = "$expected_source_hash" ]; then mv -- "$source_recovery_auth" "$source_auth" || :; chmod 600 "$source_auth" || :
elif [ -f "$source_quarantine_auth" ] && [ ! -L "$source_quarantine_auth" ] && [ "$(hash_file "$source_quarantine_auth")" = "$expected_source_hash" ]; then mv -- "$source_quarantine_auth" "$source_auth" || :; chmod 600 "$source_auth" || :; fi
fi
if [ -f "$destination_recovery_auth" ] && [ ! -L "$destination_recovery_auth" ]; then
if [ "$target_auth" -ef "$destination_recovery_auth" ]; then chmod 600 "$target_auth" || :; fi
[ ! -f "$drain_marker" ] || rm -f -- "$destination_recovery_auth"
fi
[ ! -f "$drain_marker" ] || rm -f -- "$source_recovery_auth" "$source_quarantine_auth"
rm -f -- "$temporary_auth" "$temporary_credentials" "$destination_pin" "$temporary_destination_snapshot" "$temporary_source_snapshot" "$temporary_marker"
}
trap cleanup EXIT HUP INT TERM
ln -- "$target_auth" "$destination_pin"
[ "$(hash_file "$destination_pin")" = "$6" ] || exit 38
[ "$target_auth" -ef "$destination_pin" ] || exit 38
source_credentials="$legacy_home/.credentials.json"; target_credentials="$target_home/.credentials.json"
if [ -f "$source_credentials" ] && [ ! -e "$target_credentials" ] && [ ! -L "$target_credentials" ]; then
[ "$9" != missing ] || exit 43; [ "$(hash_file "$source_credentials")" = "$9" ] || exit 43
cp -- "$source_credentials" "$temporary_credentials"; chmod 600 "$temporary_credentials"; [ "$(hash_file "$temporary_credentials")" = "$9" ] || exit 43; [ "$(hash_file "$source_credentials")" = "$9" ] || exit 43; mv -n -- "$temporary_credentials" "$target_credentials"; [ -f "$target_credentials" ] && [ ! -L "$target_credentials" ] && [ "$(hash_file "$target_credentials")" = "$9" ] || exit 43
elif [ "$9" = missing ] && [ ! -e "$target_credentials" ] && [ ! -L "$target_credentials" ]; then
[ ! -e "$source_credentials" ] && [ ! -L "$source_credentials" ] || exit 43
fi
if [ "$7" = 1 ]; then
cp -- "$source_auth" "$temporary_auth"; chmod 600 "$temporary_auth"; [ "$(hash_file "$temporary_auth")" = "$5" ] || exit 42
[ "$(hash_file "$destination_pin")" = "$6" ] || exit 39; [ "$target_auth" -ef "$destination_pin" ] || exit 39
mv -f -- "$temporary_auth" "$target_auth"
[ "$(hash_file "$destination_pin")" = "$6" ] || exit 39
rm -- "$destination_pin"; ln -- "$target_auth" "$destination_pin"
fi
expected_target_hash="$6"; [ "$7" != 1 ] || expected_target_hash="$5"
[ "$(hash_file "$destination_pin")" = "$expected_target_hash" ] || exit 45
[ "$target_auth" -ef "$destination_pin" ] || exit 45
if [ "$8" != 1 ]; then exit 0; fi
cp -- "$target_auth" "$temporary_destination_snapshot"; chmod 400 "$temporary_destination_snapshot"; [ ! -w "$temporary_destination_snapshot" ] || exit 45; [ "$(hash_file "$temporary_destination_snapshot")" = "$expected_target_hash" ] || exit 45
cp -- "$source_auth" "$temporary_source_snapshot"; chmod 400 "$temporary_source_snapshot"; [ ! -w "$temporary_source_snapshot" ] || exit 40; [ "$(hash_file "$temporary_source_snapshot")" = "$5" ] || exit 40
mv -f -- "$temporary_source_snapshot" "$source_recovery_auth"; [ "$(hash_file "$source_recovery_auth")" = "$5" ] || exit 40
ln -- "$temporary_destination_snapshot" "$destination_recovery_auth"; [ "$temporary_destination_snapshot" -ef "$destination_recovery_auth" ] || exit 45
[ "$(hash_file "$destination_pin")" = "$expected_target_hash" ] || exit 45; [ "$target_auth" -ef "$destination_pin" ] || exit 45
mv -f -- "$temporary_destination_snapshot" "$target_auth"
[ "$(hash_file "$destination_pin")" = "$expected_target_hash" ] || exit 45
[ ! -w "$target_auth" ] || exit 45
[ "$(hash_file "$target_auth")" = "$expected_target_hash" ] || exit 45
[ "$target_auth" -ef "$destination_recovery_auth" ] || exit 45
[ "$(hash_file "$source_auth")" = "$5" ] || exit 40
mv -- "$source_auth" "$source_quarantine_auth"; chmod 400 "$source_quarantine_auth"; [ ! -w "$source_quarantine_auth" ] || exit 40
[ "$(hash_file "$source_quarantine_auth")" = "$5" ] || exit 40
[ "$(hash_file "$target_auth")" = "$expected_target_hash" ] || exit 45
[ "$target_auth" -ef "$destination_recovery_auth" ] || exit 45
printf '%s\\n' '{"completed":true}' > "$temporary_marker"; chmod 600 "$temporary_marker"; mv -f -- "$temporary_marker" "$3"
chmod 600 "$destination_recovery_auth"
rm -- "$source_recovery_auth" "$source_quarantine_auth" "$destination_recovery_auth"
`
export const FINALIZE_ABSENT_AUTH_SCRIPT = `
set -eu
[ ! -f "$3" ] || exit 0
${RESOLVE_LEGACY_HOME_SCRIPT}
[ ! -e "$legacy_home/auth.json" ] && [ ! -L "$legacy_home/auth.json" ] || exit 41
umask 077; marker_parent=\${3%/*}; mkdir -p -- "$marker_parent"; temporary_marker="$3.orca-drain-$$"; trap 'rm -f -- "$temporary_marker"' EXIT HUP INT TERM; printf '%s\\n' '{"completed":true}' > "$temporary_marker"; chmod 600 "$temporary_marker"; mv -f -- "$temporary_marker" "$3"
`
@@ -70,6 +70,20 @@ describe('legacy WSL runtime auth drain', () => {
expect(runWslProcessMock.mock.calls[1][0].args.slice(-3)).toEqual(['0', '1', 'missing'])
})
it('does not expose captured auth output when the guest apply fails', async () => {
runWslProcessMock
.mockResolvedValueOnce(result(0, inspection(SOURCE)))
.mockResolvedValueOnce(result(45, 'secret-auth-output'))
const operation = drainLegacyWslRuntimeAuth({
distro: 'Ubuntu',
guestHomeLinuxPath: '/home/alice',
legacyPanePresent: false,
resolveDestination: () => ({ authContents: STALE, linuxHomePath: '/home/alice/.codex' })
})
await expect(operation).rejects.toThrow('Legacy WSL auth drain apply failed (exit 45)')
await expect(operation).rejects.not.toThrow('secret-auth-output')
})
it('coalesces concurrent launches and allows idempotent retries', async () => {
let release!: (value: ReturnType<typeof result>) => void
runWslProcessMock.mockImplementationOnce(
@@ -89,12 +103,11 @@ describe('legacy WSL runtime auth drain', () => {
await Promise.resolve()
expect(runWslProcessMock).toHaveBeenCalledTimes(1)
release(result(0, inspection(SOURCE)))
await Promise.resolve()
await Promise.resolve()
runWslProcessMock.mockResolvedValueOnce(result(0, inspection(SOURCE)))
startLegacyWslRuntimeAuthDrain(options)
await Promise.resolve()
expect(runWslProcessMock).toHaveBeenCalledTimes(1)
await vi.waitFor(() => {
startLegacyWslRuntimeAuthDrain(options)
expect(runWslProcessMock).toHaveBeenCalledTimes(2)
})
})
it('is idempotent when the completion marker is already present', async () => {
@@ -110,4 +123,17 @@ describe('legacy WSL runtime auth drain', () => {
).resolves.toBe('complete')
expect(resolveDestination).not.toHaveBeenCalled()
})
it('does not share completion across distinct guest homes in one distro', async () => {
runWslProcessMock.mockResolvedValue(result(20))
const options = {
distro: 'Ubuntu',
legacyPanePresent: false,
resolveDestination: vi.fn()
}
startLegacyWslRuntimeAuthDrain({ ...options, guestHomeLinuxPath: '/home/alice' })
await vi.waitFor(() => expect(runWslProcessMock).toHaveBeenCalledTimes(1))
startLegacyWslRuntimeAuthDrain({ ...options, guestHomeLinuxPath: '/home/bob' })
await vi.waitFor(() => expect(runWslProcessMock).toHaveBeenCalledTimes(2))
})
})
@@ -3,10 +3,15 @@ import { posix as pathPosix } from 'node:path'
import { WSL_CODEX_RUNTIME_HOME_SEGMENTS } from '../pty/codex-home-wsl-env'
import { runWslProcess } from '../wsl/wsl-runner'
import { compareCodexAuthFreshness, codexAuthIsFresher } from './codex-auth-identity'
import {
APPLY_LEGACY_AUTH_SCRIPT,
FINALIZE_ABSENT_AUTH_SCRIPT,
INSPECT_LEGACY_AUTH_SCRIPT,
MARKER_PRESENT_EXIT,
SOURCE_AUTH_ABSENT_EXIT
} from './legacy-wsl-runtime-auth-drain-scripts'
const DRAIN_MARKER_NAME = 'direct-home-auth-drain-v1.json'
const MARKER_PRESENT_EXIT = 20
const SOURCE_AUTH_ABSENT_EXIT = 21
export type LegacyWslRuntimeAuthDestination = { authContents: string; linuxHomePath: string }
type Inspection = {
@@ -26,7 +31,7 @@ const inFlight = new Map<string, Promise<void>>()
const complete = new Set<string>()
export function startLegacyWslRuntimeAuthDrain(options: LegacyWslRuntimeAuthDrainOptions): void {
const key = options.distro.trim().toLowerCase()
const key = `${options.distro.trim().toLowerCase()}\0${pathPosix.normalize(options.guestHomeLinuxPath)}`
if (complete.has(key) || inFlight.has(key)) {
return
}
@@ -174,74 +179,6 @@ function sha256(contents: string): string {
return createHash('sha256').update(contents).digest('hex')
}
const RESOLVE_LEGACY_HOME_SCRIPT = `
legacy_home="$1"
legacy_home_resolved=0
if [ -e "$1" ] || [ -L "$1" ]; then legacy_home=$(readlink -f -- "$1") || exit 30; legacy_home_resolved=1; fi
if [ -e "$2" ] || [ -L "$2" ]; then active_home=$(readlink -f -- "$2") || exit 31; if [ "$legacy_home_resolved" = 1 ]; then [ "$active_home" = "$legacy_home" ] || exit 32; else legacy_home="$active_home"; legacy_home_resolved=1; fi; fi
`
const INSPECT_LEGACY_AUTH_SCRIPT = `
set -eu
[ ! -f "$3" ] || exit ${MARKER_PRESENT_EXIT}
${RESOLVE_LEGACY_HOME_SCRIPT}
source_auth="$legacy_home/auth.json"
[ -f "$source_auth" ] || exit ${SOURCE_AUTH_ABSENT_EXIT}
encode_file() { base64 < "$1" | tr -d '\\n'; }
encode_file "$source_auth"; printf '\\n'
source_credentials="$legacy_home/.credentials.json"
if [ -f "$source_credentials" ]; then printf 'present\\n'; encode_file "$source_credentials"; printf '\\n'; elif [ ! -e "$source_credentials" ] && [ ! -L "$source_credentials" ]; then printf 'missing\\n\\n'; else exit 44; fi
`
// The destination pin is a hard link to the exact inode being validated. A
// concurrent in-place rewrite changes the pin; an atomic replacement breaks
// -ef. Either case aborts before source retirement, preserving credentials.
const APPLY_LEGACY_AUTH_SCRIPT = `
set -eu
[ ! -f "$3" ] || exit 0
${RESOLVE_LEGACY_HOME_SCRIPT}
target_home=$(readlink -f -- "$4") || exit 33
[ "$legacy_home" != "$target_home" ] || exit 34
source_auth="$legacy_home/auth.json"; target_auth="$target_home/auth.json"
[ -f "$source_auth" ] || exit 35; [ -f "$target_auth" ] || exit 36
hash_file() { sha256sum -- "$1" | cut -d ' ' -f 1; }
[ "$(hash_file "$source_auth")" = "$5" ] || exit 37
[ "$(hash_file "$target_auth")" = "$6" ] || exit 38
umask 077
temporary_auth="$target_auth.orca-drain-$$"; temporary_credentials="$target_home/.credentials.json.orca-drain-$$"; destination_pin="$target_auth.orca-drain-pin-$$"; temporary_marker="$3.orca-drain-$$"
cleanup() { rm -f -- "$temporary_auth" "$temporary_credentials" "$destination_pin" "$temporary_marker"; }
trap cleanup EXIT HUP INT TERM
source_credentials="$legacy_home/.credentials.json"; target_credentials="$target_home/.credentials.json"
if [ -f "$source_credentials" ] && [ ! -e "$target_credentials" ] && [ ! -L "$target_credentials" ]; then
[ "$9" != missing ] || exit 43; [ "$(hash_file "$source_credentials")" = "$9" ] || exit 43
cp -- "$source_credentials" "$temporary_credentials"; chmod 600 "$temporary_credentials"; [ "$(hash_file "$temporary_credentials")" = "$9" ] || exit 43; [ "$(hash_file "$source_credentials")" = "$9" ] || exit 43; mv -n -- "$temporary_credentials" "$target_credentials"
elif [ "$9" = missing ] && [ ! -e "$target_credentials" ] && [ ! -L "$target_credentials" ]; then
[ ! -e "$source_credentials" ] && [ ! -L "$source_credentials" ] || exit 43
fi
if [ "$7" = 1 ]; then
cp -- "$source_auth" "$temporary_auth"; chmod 600 "$temporary_auth"; [ "$(hash_file "$temporary_auth")" = "$5" ] || exit 42
[ "$(hash_file "$target_auth")" = "$6" ] || exit 39
mv -f -- "$temporary_auth" "$target_auth"
fi
ln -- "$target_auth" "$destination_pin"
expected_target_hash="$6"; [ "$7" != 1 ] || expected_target_hash="$5"
[ "$(hash_file "$destination_pin")" = "$expected_target_hash" ] || exit 45
[ "$target_auth" -ef "$destination_pin" ] || exit 45
if [ "$8" = 1 ]; then
[ "$(hash_file "$destination_pin")" = "$expected_target_hash" ] || exit 45
[ "$target_auth" -ef "$destination_pin" ] || exit 45
[ "$(hash_file "$source_auth")" = "$5" ] || exit 40
rm -- "$source_auth"
printf '%s\\n' '{"completed":true}' > "$temporary_marker"; chmod 600 "$temporary_marker"; mv -f -- "$temporary_marker" "$3"
fi
`
const FINALIZE_ABSENT_AUTH_SCRIPT = `
set -eu
[ ! -f "$3" ] || exit 0
${RESOLVE_LEGACY_HOME_SCRIPT}
[ ! -e "$legacy_home/auth.json" ] && [ ! -L "$legacy_home/auth.json" ] || exit 41
umask 077; marker_parent=\${3%/*}; mkdir -p -- "$marker_parent"; temporary_marker="$3.orca-drain-$$"; trap 'rm -f -- "$temporary_marker"' EXIT HUP INT TERM; printf '%s\\n' '{"completed":true}' > "$temporary_marker"; chmod 600 "$temporary_marker"; mv -f -- "$temporary_marker" "$3"
`
export const _internals = {
applyLegacyAuthScript: APPLY_LEGACY_AUTH_SCRIPT,
inspectLegacyAuthScript: INSPECT_LEGACY_AUTH_SCRIPT,