Main no longer opens desktop business SQLite. A single utility-process
storage owner holds the Assistant, Knowledge, attachment, document-result,
remote-binding and native model-ledger databases and runs whole domain
transactions; Main and Runtime callers use explicit async domain operations.
- Remove the Main readWithFallback path and the supervision worker's
separate business write connection; manifests are scanned read-only and
committed by the sole writer.
- Remote events are ACKed only after commit; process loss reports
unconfirmed writes instead of replaying them.
- Use native utilityProcess messaging with one in-flight bound (32) and
ordered waiting instead of capacity rejection and retries.
- Start the storage process before the window so startup overlaps.
- Keep short tool output in memory and store full output once in owned
storage; paging, branching, restart and deletion reuse that copy.
- Put required runtime launch files under goodbuddy-prefixed owned temp
directories and reclaim confirmed stale ones; stop writing duplicate
parsed.md.
- Add lint guards for SQLite ownership and architecture, concurrency
acceptance fixtures, and maintenance/architecture documentation.
Validation: typecheck and lint pass. Alternating baseline (edf019aa) vs
candidate runs show no regression on single-session, C01, C02, C04 and a
5-minute C06; Main p99 drops from ~21 ms to ~16.7 ms under concurrency.
Fresh portable copy (1.75M events) upgrade content unchanged; real-model
output paging passes with 4 external requests. Full npm test runs had
1-9 different timing-sensitive failures per run that pass when rerun.
Not covered: C03 review publication, C07 source body, app-level C05.
186 lines
5.8 KiB
TypeScript
186 lines
5.8 KiB
TypeScript
import { randomUUID } from 'node:crypto'
|
|
import {
|
|
lstat,
|
|
mkdir,
|
|
open,
|
|
readdir,
|
|
readFile,
|
|
rename,
|
|
rm
|
|
} from 'node:fs/promises'
|
|
import { dirname, resolve } from 'node:path'
|
|
|
|
const OWNER_MARKER = '.goodbuddy-managed-python'
|
|
const OWNER_VALUE = 'GoodBuddy managed Python root\n'
|
|
const versionPattern = /^python-(\d+\.\d+\.\d+)$/u
|
|
const stagingPattern = /^\.managed-python-stage-[0-9a-f-]{36}$/u
|
|
const backupPattern = /^\.managed-python-backup-[0-9a-f-]{36}$/u
|
|
const downloadPattern = /^\.managed-python-download-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u
|
|
|
|
function ensureNotAborted(signal?: AbortSignal): void {
|
|
if (signal?.aborted) {
|
|
throw signal.reason instanceof Error
|
|
? signal.reason
|
|
: new DOMException('The operation was aborted', 'AbortError')
|
|
}
|
|
}
|
|
|
|
function child(root: string, name: string): string {
|
|
const path = resolve(root, name)
|
|
if (dirname(path) !== resolve(root)) {
|
|
throw new Error('Managed Python path escaped its owned root')
|
|
}
|
|
return path
|
|
}
|
|
|
|
function versionName(version: string): string {
|
|
if (!/^\d+\.\d+\.\d+$/u.test(version)) {
|
|
throw new Error('Managed Python version is invalid')
|
|
}
|
|
return `python-${version}`
|
|
}
|
|
|
|
async function ensureOwnedRoot(root: string): Promise<void> {
|
|
await mkdir(root, { recursive: true })
|
|
const marker = child(root, OWNER_MARKER)
|
|
try {
|
|
const value = await readFile(marker, 'utf8')
|
|
if (value !== OWNER_VALUE) {
|
|
throw new Error('Managed Python root has an invalid ownership marker')
|
|
}
|
|
} catch (error) {
|
|
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
throw error
|
|
}
|
|
const entries = await readdir(root)
|
|
if (entries.length !== 0) {
|
|
throw new Error('Refusing to adopt a non-empty Managed Python root', {
|
|
cause: error
|
|
})
|
|
}
|
|
const handle = await open(marker, 'wx')
|
|
try {
|
|
await handle.writeFile(OWNER_VALUE)
|
|
await handle.sync()
|
|
} finally {
|
|
await handle.close()
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function cleanupManagedPythonOperations(root: string): Promise<void> {
|
|
await ensureOwnedRoot(root)
|
|
for (const entry of await readdir(root, { withFileTypes: true })) {
|
|
const operationDirectory =
|
|
(stagingPattern.test(entry.name) || backupPattern.test(entry.name)) && entry.isDirectory()
|
|
const partialDownload = downloadPattern.test(entry.name) && entry.isFile()
|
|
if (
|
|
(operationDirectory || partialDownload) &&
|
|
!entry.isSymbolicLink()
|
|
) {
|
|
await rm(child(root, entry.name), { recursive: true, force: true })
|
|
}
|
|
}
|
|
}
|
|
|
|
async function pruneVersions(
|
|
root: string,
|
|
activeName: string,
|
|
keepVersions: number
|
|
): Promise<void> {
|
|
const candidates: { name: string; mtimeMs: number }[] = []
|
|
for (const entry of await readdir(root, { withFileTypes: true })) {
|
|
if (
|
|
entry.name !== activeName &&
|
|
versionPattern.test(entry.name) &&
|
|
entry.isDirectory() &&
|
|
!entry.isSymbolicLink()
|
|
) {
|
|
const status = await lstat(child(root, entry.name))
|
|
candidates.push({ name: entry.name, mtimeMs: status.mtimeMs })
|
|
}
|
|
}
|
|
candidates.sort((left, right) => right.mtimeMs - left.mtimeMs)
|
|
for (const candidate of candidates.slice(Math.max(0, keepVersions - 1))) {
|
|
await rm(child(root, candidate.name), { recursive: true, force: true })
|
|
}
|
|
}
|
|
|
|
export async function installManagedPython(options: {
|
|
rootDirectory: string
|
|
version: string
|
|
stage: (stagingDirectory: string) => Promise<void>
|
|
validate: (stagingDirectory: string) => Promise<void>
|
|
signal?: AbortSignal
|
|
keepVersions?: number
|
|
}): Promise<string> {
|
|
const root = resolve(options.rootDirectory)
|
|
const keepVersions = options.keepVersions ?? 1
|
|
if (!Number.isInteger(keepVersions) || keepVersions < 1 || keepVersions > 3) {
|
|
throw new Error('Managed Python retained version count must be between 1 and 3')
|
|
}
|
|
await ensureOwnedRoot(root)
|
|
ensureNotAborted(options.signal)
|
|
const destinationName = versionName(options.version)
|
|
const destination = child(root, destinationName)
|
|
const staging = child(root, `.managed-python-stage-${randomUUID()}`)
|
|
const backup = child(root, `.managed-python-backup-${randomUUID()}`)
|
|
await mkdir(staging)
|
|
let movedExisting = false
|
|
try {
|
|
await options.stage(staging)
|
|
ensureNotAborted(options.signal)
|
|
await options.validate(staging)
|
|
ensureNotAborted(options.signal)
|
|
try {
|
|
const status = await lstat(destination)
|
|
if (!status.isDirectory() || status.isSymbolicLink()) {
|
|
throw new Error('Managed Python destination is not an owned directory')
|
|
}
|
|
await rename(destination, backup)
|
|
movedExisting = true
|
|
} catch (error) {
|
|
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
throw error
|
|
}
|
|
}
|
|
try {
|
|
await rename(staging, destination)
|
|
} catch (error) {
|
|
if (movedExisting) {
|
|
await rename(backup, destination).catch(() => undefined)
|
|
}
|
|
throw error
|
|
}
|
|
if (movedExisting) {
|
|
await rm(backup, { recursive: true, force: true })
|
|
}
|
|
await pruneVersions(root, destinationName, keepVersions)
|
|
return destination
|
|
} catch (error) {
|
|
await rm(staging, { recursive: true, force: true }).catch(() => undefined)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export async function removeManagedPython(options: {
|
|
rootDirectory: string
|
|
version: string
|
|
}): Promise<void> {
|
|
const root = resolve(options.rootDirectory)
|
|
await ensureOwnedRoot(root)
|
|
const destination = child(root, versionName(options.version))
|
|
try {
|
|
const status = await lstat(destination)
|
|
if (!status.isDirectory() || status.isSymbolicLink()) {
|
|
throw new Error('Refusing to remove a non-directory Managed Python path')
|
|
}
|
|
} catch (error) {
|
|
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
return
|
|
}
|
|
throw error
|
|
}
|
|
await rm(destination, { recursive: true, force: true })
|
|
}
|