@ai-devkit/agent-manager 0.26.4 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/dist/AgentManager.d.ts +5 -0
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +21 -1
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/AgentManager.test.js +112 -0
- package/dist/__tests__/AgentManager.test.js.map +1 -1
- package/dist/__tests__/adapters/CodexAdapter.test.js +199 -0
- package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
- package/dist/__tests__/database/DurableAgentsDatabase.test.js +100 -0
- package/dist/__tests__/database/DurableAgentsDatabase.test.js.map +1 -0
- package/dist/__tests__/print/ClaudePrintAgent.integration.test.js +7 -7
- package/dist/__tests__/print/ClaudePrintAgent.integration.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintAgentService.test.js +7 -7
- package/dist/__tests__/print/ClaudePrintAgentService.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintRunner.test.js +1 -1
- package/dist/__tests__/print/ClaudePrintRunner.test.js.map +1 -1
- package/dist/__tests__/print/DurableAgent.test.js +17 -0
- package/dist/__tests__/print/DurableAgent.test.js.map +1 -0
- package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js +186 -0
- package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js.map +1 -0
- package/dist/__tests__/print/{PrintAgentStore.test.js → DurableAgentRepository.test.js} +63 -110
- package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -0
- package/dist/__tests__/utils/AgentRegistry.test.js +67 -0
- package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
- package/dist/adapters/AgentAdapter.d.ts +2 -0
- package/dist/adapters/AgentAdapter.d.ts.map +1 -1
- package/dist/adapters/AgentAdapter.js.map +1 -1
- package/dist/adapters/CodexAdapter.d.ts +1 -0
- package/dist/adapters/CodexAdapter.d.ts.map +1 -1
- package/dist/adapters/CodexAdapter.js +16 -6
- package/dist/adapters/CodexAdapter.js.map +1 -1
- package/dist/database/connection.d.ts +1 -0
- package/dist/database/connection.d.ts.map +1 -1
- package/dist/database/connection.js +20 -5
- package/dist/database/connection.js.map +1 -1
- package/dist/database/migrations/002_pins.sql +1 -0
- package/dist/database/migrations/003_durable_agents.sql +43 -0
- package/dist/durable/ClaudeCliProbe.d.ts.map +1 -0
- package/dist/{print → durable}/ClaudeCliProbe.js +1 -1
- package/dist/durable/ClaudeCliProbe.js.map +1 -0
- package/dist/{print → durable}/ClaudePrintAgentService.d.ts +11 -11
- package/dist/durable/ClaudePrintAgentService.d.ts.map +1 -0
- package/dist/{print → durable}/ClaudePrintAgentService.js +12 -12
- package/dist/durable/ClaudePrintAgentService.js.map +1 -0
- package/dist/{print → durable}/ClaudePrintRunner.d.ts +3 -3
- package/dist/durable/ClaudePrintRunner.d.ts.map +1 -0
- package/dist/{print → durable}/ClaudePrintRunner.js +2 -2
- package/dist/durable/ClaudePrintRunner.js.map +1 -0
- package/dist/durable/DurableAgent.d.ts +61 -0
- package/dist/durable/DurableAgent.d.ts.map +1 -0
- package/dist/durable/DurableAgent.js +46 -0
- package/dist/durable/DurableAgent.js.map +1 -0
- package/dist/durable/DurableAgentRepository.d.ts +60 -0
- package/dist/durable/DurableAgentRepository.d.ts.map +1 -0
- package/dist/durable/DurableAgentRepository.js +325 -0
- package/dist/durable/DurableAgentRepository.js.map +1 -0
- package/dist/index.d.ts +12 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/utils/AgentRegistry.d.ts +5 -0
- package/dist/utils/AgentRegistry.d.ts.map +1 -1
- package/dist/utils/AgentRegistry.js +19 -2
- package/dist/utils/AgentRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/AgentManager.ts +21 -0
- package/src/__tests__/AgentManager.test.ts +117 -0
- package/src/__tests__/adapters/CodexAdapter.test.ts +132 -0
- package/src/__tests__/database/DurableAgentsDatabase.test.ts +77 -0
- package/src/__tests__/print/ClaudePrintAgent.integration.test.ts +6 -6
- package/src/__tests__/print/ClaudePrintAgentService.test.ts +7 -7
- package/src/__tests__/print/ClaudePrintRunner.test.ts +3 -3
- package/src/__tests__/print/{PrintAgent.test.ts → DurableAgent.test.ts} +6 -6
- package/src/__tests__/print/DurableAgentRepository.sqlite.test.ts +105 -0
- package/src/__tests__/print/DurableAgentRepository.test.ts +163 -0
- package/src/__tests__/utils/AgentRegistry.test.ts +68 -0
- package/src/adapters/AgentAdapter.ts +3 -0
- package/src/adapters/CodexAdapter.ts +14 -11
- package/src/database/connection.ts +18 -5
- package/src/database/migrations/002_pins.sql +1 -0
- package/src/database/migrations/003_durable_agents.sql +43 -0
- package/src/{print → durable}/ClaudeCliProbe.ts +1 -1
- package/src/{print → durable}/ClaudePrintAgentService.ts +21 -21
- package/src/{print → durable}/ClaudePrintRunner.ts +4 -4
- package/src/durable/DurableAgent.ts +91 -0
- package/src/durable/DurableAgentRepository.ts +307 -0
- package/src/index.ts +27 -26
- package/src/utils/AgentRegistry.ts +21 -0
- package/dist/__tests__/print/PrintAgent.test.js +0 -17
- package/dist/__tests__/print/PrintAgent.test.js.map +0 -1
- package/dist/__tests__/print/PrintAgentStore.test.js.map +0 -1
- package/dist/print/ClaudeCliProbe.d.ts.map +0 -1
- package/dist/print/ClaudeCliProbe.js.map +0 -1
- package/dist/print/ClaudePrintAgentService.d.ts.map +0 -1
- package/dist/print/ClaudePrintAgentService.js.map +0 -1
- package/dist/print/ClaudePrintRunner.d.ts.map +0 -1
- package/dist/print/ClaudePrintRunner.js.map +0 -1
- package/dist/print/PrintAgent.d.ts +0 -57
- package/dist/print/PrintAgent.d.ts.map +0 -1
- package/dist/print/PrintAgent.js +0 -42
- package/dist/print/PrintAgent.js.map +0 -1
- package/dist/print/PrintAgentStore.d.ts +0 -69
- package/dist/print/PrintAgentStore.d.ts.map +0 -1
- package/dist/print/PrintAgentStore.js +0 -484
- package/dist/print/PrintAgentStore.js.map +0 -1
- package/src/__tests__/print/PrintAgentStore.test.ts +0 -192
- package/src/print/PrintAgent.ts +0 -86
- package/src/print/PrintAgentStore.ts +0 -503
- /package/dist/{print → durable}/ClaudeCliProbe.d.ts +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/print/PrintAgentStore.ts"],"sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport { randomUUID } from 'crypto';\nimport { execFileSync } from 'child_process';\nimport type { PrintAgent, ProcessIdentity, PrintRunStatus, PrintSessionHealth } from './PrintAgent.js';\nimport {\n PrintAgentBusyError,\n PrintAgentNameConflictError,\n PrintAgentNotFoundError,\n PrintAgentStoreError,\n} from './PrintAgent.js';\n\ninterface PrintAgentStoreFile {\n version: 1;\n agents: PrintAgent[];\n}\n\nexport interface CreatePrintAgentInput {\n name: string;\n cwd: string;\n}\n\nexport interface PrintAgentStoreOptions {\n filePath?: string;\n lockTimeoutMs?: number;\n now?: () => Date;\n processInspector?: ProcessInspector;\n incompleteLockGraceMs?: number;\n mutationLockStaleMs?: number;\n}\n\nexport interface ProcessInspector {\n getIdentity(pid: number): ProcessIdentity | null;\n}\n\nexport interface PrintRunCompletion {\n status: PrintRunStatus;\n exitCode: number | null;\n summary: string;\n sessionHealth: PrintSessionHealth;\n}\n\nconst DEFAULT_FILE = path.join(os.homedir(), '.ai-devkit', 'print-agents.json');\n\nexport class PrintAgentStore {\n readonly filePath: string;\n private readonly lockPath: string;\n private readonly lockTimeoutMs: number;\n private readonly now: () => Date;\n private readonly processInspector: ProcessInspector;\n private readonly runLocksRoot: string;\n private readonly incompleteLockGraceMs: number;\n private readonly mutationLockStaleMs: number;\n\n constructor(options: PrintAgentStoreOptions = {}) {\n this.filePath = options.filePath ?? DEFAULT_FILE;\n this.lockPath = `${this.filePath}.lock`;\n this.lockTimeoutMs = options.lockTimeoutMs ?? 2000;\n this.now = options.now ?? (() => new Date());\n this.processInspector = options.processInspector ?? new LocalProcessInspector();\n this.runLocksRoot = path.join(path.dirname(this.filePath), 'print-agent-locks');\n this.incompleteLockGraceMs = options.incompleteLockGraceMs ?? 30_000;\n this.mutationLockStaleMs = options.mutationLockStaleMs ?? 30_000;\n }\n\n async create(input: CreatePrintAgentInput): Promise<PrintAgent> {\n const cwd = this.canonicalDirectory(input.cwd);\n return this.withMutationLock(async () => {\n const data = this.readFile();\n if (data.agents.some((agent) => agent.name.toLowerCase() === input.name.toLowerCase())) {\n throw new PrintAgentNameConflictError(input.name);\n }\n const timestamp = this.now().toISOString();\n let id = randomUUID();\n let providerSessionId = randomUUID();\n while (providerSessionId === id) providerSessionId = randomUUID();\n while (data.agents.some((agent) => agent.id === id)) id = randomUUID();\n const agent: PrintAgent = {\n id,\n name: input.name,\n provider: 'claude',\n mode: 'print',\n cwd,\n providerSessionId,\n state: 'ready',\n sessionHealth: 'uninitialized',\n createdAt: timestamp,\n updatedAt: timestamp,\n lastActiveAt: null,\n lastResult: null,\n activeRun: null,\n };\n data.agents.push(agent);\n this.writeFile(data);\n return structuredClone(agent);\n });\n }\n\n async list(): Promise<PrintAgent[]> {\n await this.reconcile();\n return this.listRaw();\n }\n\n async getById(id: string): Promise<PrintAgent | null> {\n return (await this.list()).find((agent) => agent.id === id) ?? null;\n }\n\n async reconcile(): Promise<void> {\n const running = this.listRaw().filter((agent) => agent.state === 'running' && agent.activeRun);\n for (const snapshot of running) {\n const lockPath = this.runLockPath(snapshot.id);\n const metadata = this.readRunLock(snapshot.id);\n if (metadata && this.isActive(metadata)) continue;\n if (!metadata && this.isYoungLock(lockPath)) continue;\n\n if (fs.existsSync(lockPath)) {\n const quarantine = `${lockPath}.stale-${randomUUID()}`;\n try {\n fs.renameSync(lockPath, quarantine);\n this.removeLockDirectory(quarantine);\n } catch {\n continue;\n }\n }\n const completedAt = this.now().toISOString();\n await this.updateAgent(snapshot.id, (current) => {\n if (current.state !== 'running' || current.activeRun?.token !== snapshot.activeRun?.token) return current;\n return {\n ...current,\n state: 'degraded',\n sessionHealth: 'unknown',\n activeRun: null,\n updatedAt: completedAt,\n lastActiveAt: completedAt,\n lastResult: {\n status: 'interrupted',\n completedAt,\n exitCode: null,\n summary: 'Previous print run was interrupted.',\n },\n };\n });\n }\n }\n\n async resolve(reference: string): Promise<PrintAgent | PrintAgent[] | null> {\n const agents = await this.list();\n const byId = agents.find((agent) => agent.id === reference);\n if (byId) return byId;\n const matches = agents.filter((agent) => agent.name.toLowerCase() === reference.toLowerCase());\n if (matches.length === 0) return null;\n return matches.length === 1 ? matches[0]! : matches;\n }\n\n async acquireRun(id: string): Promise<{ agent: PrintAgent; token: string }> {\n const existing = await this.getById(id);\n if (!existing) throw new PrintAgentNotFoundError(id);\n this.validateBoundCwd(existing.cwd);\n const runLock = this.runLockPath(id);\n let recoveredStale = false;\n\n for (;;) {\n this.ensureRunLocksRoot();\n this.assertNotSymlink(runLock);\n try {\n fs.mkdirSync(runLock, { mode: 0o700 });\n break;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw new PrintAgentStoreError(`Cannot acquire print-agent run lock: ${(error as Error).message}`);\n }\n const metadata = this.readRunLock(id);\n if (!metadata || this.isActive(metadata)) {\n throw new PrintAgentBusyError(id, existing.name);\n }\n const quarantine = `${runLock}.stale-${randomUUID()}`;\n try {\n fs.renameSync(runLock, quarantine);\n this.removeLockDirectory(quarantine);\n recoveredStale = true;\n } catch {\n // Another contender changed the lock. Retry and inspect the winner.\n }\n }\n }\n\n const owner = this.processInspector.getIdentity(process.pid);\n if (!owner) {\n this.removeLockDirectory(runLock);\n throw new PrintAgentStoreError('Cannot determine the current process identity.');\n }\n const token = randomUUID();\n const startedAt = this.now().toISOString();\n const activeRun = { token, owner, provider: null, startedAt };\n this.writeRunLock(id, activeRun);\n\n try {\n const agent = await this.updateAgent(id, (current) => ({\n ...current,\n state: 'running',\n activeRun,\n updatedAt: startedAt,\n ...(recoveredStale ? {\n sessionHealth: 'unknown' as const,\n lastResult: {\n status: 'interrupted' as const,\n completedAt: startedAt,\n exitCode: null,\n summary: 'Previous print run was interrupted.',\n },\n } : {}),\n }));\n return { agent, token };\n } catch (error) {\n this.removeOwnedRunLock(id, token);\n throw error;\n }\n }\n\n async recordProviderProcess(id: string, token: string, identity: ProcessIdentity): Promise<void> {\n const metadata = this.requireOwnedRun(id, token);\n const next = { ...metadata, provider: identity };\n this.writeRunLock(id, next);\n await this.updateAgent(id, (agent) => {\n if (agent.activeRun?.token !== token) throw new PrintAgentStoreError('Print run ownership changed.');\n return { ...agent, activeRun: next, updatedAt: this.now().toISOString() };\n });\n }\n\n async completeRun(id: string, token: string, result: PrintRunCompletion): Promise<PrintAgent> {\n this.requireOwnedRun(id, token);\n const completedAt = this.now().toISOString();\n const agent = await this.updateAgent(id, (current) => {\n if (current.activeRun?.token !== token) throw new PrintAgentStoreError('Print run ownership changed.');\n return {\n ...current,\n state: result.status === 'succeeded' ? 'ready' : 'degraded',\n sessionHealth: result.sessionHealth,\n activeRun: null,\n lastActiveAt: completedAt,\n updatedAt: completedAt,\n lastResult: {\n status: result.status,\n completedAt,\n exitCode: result.exitCode,\n summary: result.summary.slice(0, 4096),\n },\n };\n });\n this.removeOwnedRunLock(id, token);\n return agent;\n }\n\n private canonicalDirectory(input: string): string {\n try {\n const resolved = fs.realpathSync(input);\n if (!fs.statSync(resolved).isDirectory()) throw new Error('not a directory');\n return resolved;\n } catch {\n throw new PrintAgentStoreError(`Print agent cwd is not an existing directory: ${input}`);\n }\n }\n\n private validateBoundCwd(bound: string): void {\n try {\n const stat = fs.lstatSync(bound);\n if (!stat.isDirectory() || stat.isSymbolicLink() || fs.realpathSync(bound) !== bound) {\n throw new Error('binding changed');\n }\n } catch {\n throw new PrintAgentStoreError(`Print agent cwd binding is no longer safe: ${bound}`);\n }\n }\n\n private ensureSafeParent(): string {\n const parent = path.dirname(this.filePath);\n fs.mkdirSync(parent, { recursive: true, mode: 0o700 });\n const stat = fs.lstatSync(parent);\n if (!stat.isDirectory() || stat.isSymbolicLink()) {\n throw new PrintAgentStoreError(`Unsafe print-agent store directory: ${parent}`);\n }\n return parent;\n }\n\n private ensureRunLocksRoot(): void {\n this.ensureSafeParent();\n fs.mkdirSync(this.runLocksRoot, { recursive: true, mode: 0o700 });\n const stat = fs.lstatSync(this.runLocksRoot);\n if (!stat.isDirectory() || stat.isSymbolicLink()) {\n throw new PrintAgentStoreError(`Unsafe print-agent lock directory: ${this.runLocksRoot}`);\n }\n }\n\n private assertNotSymlink(target: string): void {\n try {\n if (fs.lstatSync(target).isSymbolicLink()) {\n throw new PrintAgentStoreError(`Unsafe symbolic link in print-agent storage: ${target}`);\n }\n } catch (error) {\n if (error instanceof PrintAgentStoreError) throw error;\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new PrintAgentStoreError(`Cannot inspect print-agent storage: ${target}`);\n }\n }\n }\n\n private readFile(): PrintAgentStoreFile {\n this.ensureSafeParent();\n this.assertNotSymlink(this.filePath);\n if (!fs.existsSync(this.filePath)) return { version: 1, agents: [] };\n try {\n const parsed = JSON.parse(fs.readFileSync(this.filePath, 'utf8')) as unknown;\n if (!this.isStoreFile(parsed)) throw new Error('invalid schema');\n return parsed;\n } catch {\n throw new PrintAgentStoreError(`Invalid print-agent store: ${this.filePath}`);\n }\n }\n\n private listRaw(): PrintAgent[] {\n return this.readFile().agents.map((agent) => structuredClone(agent));\n }\n\n private isStoreFile(value: unknown): value is PrintAgentStoreFile {\n if (!value || typeof value !== 'object') return false;\n const record = value as Record<string, unknown>;\n return record.version === 1 && Array.isArray(record.agents);\n }\n\n private writeFile(data: PrintAgentStoreFile): void {\n const parent = this.ensureSafeParent();\n this.assertNotSymlink(this.filePath);\n const temp = path.join(parent, `.print-agents-${process.pid}-${randomUUID()}.tmp`);\n this.assertNotSymlink(temp);\n let fd: number | undefined;\n try {\n fd = fs.openSync(temp, 'wx', 0o600);\n fs.writeFileSync(fd, JSON.stringify(data, null, 2), 'utf8');\n fs.fsyncSync(fd);\n fs.closeSync(fd);\n fd = undefined;\n fs.renameSync(temp, this.filePath);\n fs.chmodSync(this.filePath, 0o600);\n } catch (error) {\n if (fd !== undefined) fs.closeSync(fd);\n try { fs.unlinkSync(temp); } catch { /* best effort */ }\n if (error instanceof PrintAgentStoreError) throw error;\n throw new PrintAgentStoreError(`Failed to update print-agent store: ${(error as Error).message}`);\n }\n }\n\n private async withMutationLock<T>(operation: () => Promise<T>): Promise<T> {\n this.ensureSafeParent();\n const started = Date.now();\n for (;;) {\n this.assertNotSymlink(this.lockPath);\n try {\n fs.mkdirSync(this.lockPath, { mode: 0o700 });\n break;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw new PrintAgentStoreError(`Cannot acquire print-agent store lock: ${(error as Error).message}`);\n }\n if (!this.isYoungMutationLock()) {\n const quarantine = `${this.lockPath}.stale-${randomUUID()}`;\n try {\n fs.renameSync(this.lockPath, quarantine);\n fs.rmdirSync(quarantine);\n continue;\n } catch {\n // Another contender changed the lock. Retry until the bounded timeout.\n }\n }\n if (Date.now() - started >= this.lockTimeoutMs) {\n throw new PrintAgentStoreError('Timed out acquiring print-agent store lock.');\n }\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n }\n try {\n return await operation();\n } finally {\n try { fs.rmdirSync(this.lockPath); } catch { /* surfaced by later contention */ }\n }\n }\n\n private isYoungMutationLock(): boolean {\n try {\n this.assertNotSymlink(this.lockPath);\n const stat = fs.statSync(this.lockPath);\n return stat.isDirectory() && Date.now() - stat.mtimeMs < this.mutationLockStaleMs;\n } catch {\n return false;\n }\n }\n\n private async updateAgent(id: string, update: (agent: PrintAgent) => PrintAgent): Promise<PrintAgent> {\n return this.withMutationLock(async () => {\n const data = this.readFile();\n const index = data.agents.findIndex((agent) => agent.id === id);\n if (index < 0) throw new PrintAgentNotFoundError(id);\n const next = update(data.agents[index]!);\n data.agents[index] = next;\n this.writeFile(data);\n return structuredClone(next);\n });\n }\n\n private runLockPath(id: string): string {\n if (!/^[0-9a-f-]{36}$/i.test(id)) throw new PrintAgentStoreError('Invalid print-agent id.');\n return path.join(this.runLocksRoot, `${id}.lock`);\n }\n\n private readRunLock(id: string): import('./PrintAgent.js').PrintActiveRun | null {\n const ownerPath = path.join(this.runLockPath(id), 'owner.json');\n try {\n this.assertNotSymlink(ownerPath);\n const value = JSON.parse(fs.readFileSync(ownerPath, 'utf8')) as import('./PrintAgent.js').PrintActiveRun;\n if (!value || typeof value.token !== 'string' || !value.owner || typeof value.owner.pid !== 'number') return null;\n return value;\n } catch {\n return null;\n }\n }\n\n private writeRunLock(id: string, metadata: import('./PrintAgent.js').PrintActiveRun): void {\n const lockPath = this.runLockPath(id);\n const ownerPath = path.join(lockPath, 'owner.json');\n const tempPath = path.join(lockPath, `.owner-${randomUUID()}.tmp`);\n this.assertNotSymlink(lockPath);\n this.assertNotSymlink(ownerPath);\n fs.writeFileSync(tempPath, JSON.stringify(metadata), { encoding: 'utf8', mode: 0o600, flag: 'wx' });\n fs.renameSync(tempPath, ownerPath);\n }\n\n private requireOwnedRun(id: string, token: string): import('./PrintAgent.js').PrintActiveRun {\n const metadata = this.readRunLock(id);\n if (!metadata || metadata.token !== token) throw new PrintAgentStoreError('Print run ownership changed.');\n return metadata;\n }\n\n private isActive(metadata: import('./PrintAgent.js').PrintActiveRun): boolean {\n return this.sameProcess(metadata.owner) || (metadata.provider !== null && this.sameProcess(metadata.provider));\n }\n\n private sameProcess(expected: ProcessIdentity): boolean {\n const actual = this.processInspector.getIdentity(expected.pid);\n return actual !== null && actual.startedAt === expected.startedAt;\n }\n\n private isYoungLock(lockPath: string): boolean {\n try {\n this.assertNotSymlink(lockPath);\n return Date.now() - fs.statSync(lockPath).mtimeMs < this.incompleteLockGraceMs;\n } catch {\n return false;\n }\n }\n\n private removeOwnedRunLock(id: string, token: string): void {\n const metadata = this.readRunLock(id);\n if (!metadata || metadata.token !== token) return;\n this.removeLockDirectory(this.runLockPath(id));\n }\n\n private removeLockDirectory(lockPath: string): void {\n this.assertNotSymlink(lockPath);\n try {\n for (const name of fs.readdirSync(lockPath)) {\n const entry = path.join(lockPath, name);\n this.assertNotSymlink(entry);\n if (!fs.lstatSync(entry).isFile()) throw new PrintAgentStoreError(`Unsafe entry in print-agent lock: ${entry}`);\n fs.unlinkSync(entry);\n }\n fs.rmdirSync(lockPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n }\n}\n\nexport class LocalProcessInspector implements ProcessInspector {\n getIdentity(pid: number): ProcessIdentity | null {\n if (!Number.isInteger(pid) || pid <= 0) return null;\n try {\n if (process.platform === 'linux') {\n const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8');\n const close = stat.lastIndexOf(')');\n const fields = stat.slice(close + 2).split(' ');\n const startTicks = fields[19];\n if (!startTicks) return null;\n return { pid, startedAt: `linux:${startTicks}` };\n }\n const startedAt = execFileSync('ps', ['-o', 'lstart=', '-p', String(pid)], {\n encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n return startedAt ? { pid, startedAt } : null;\n } catch {\n return null;\n }\n }\n}\n"],"names":["fs","os","path","randomUUID","execFileSync","PrintAgentBusyError","PrintAgentNameConflictError","PrintAgentNotFoundError","PrintAgentStoreError","DEFAULT_FILE","join","homedir","PrintAgentStore","filePath","lockPath","lockTimeoutMs","now","processInspector","runLocksRoot","incompleteLockGraceMs","mutationLockStaleMs","options","Date","LocalProcessInspector","dirname","create","input","cwd","canonicalDirectory","withMutationLock","data","readFile","agents","some","agent","name","toLowerCase","timestamp","toISOString","id","providerSessionId","provider","mode","state","sessionHealth","createdAt","updatedAt","lastActiveAt","lastResult","activeRun","push","writeFile","structuredClone","list","reconcile","listRaw","getById","find","running","filter","snapshot","runLockPath","metadata","readRunLock","isActive","isYoungLock","existsSync","quarantine","renameSync","removeLockDirectory","completedAt","updateAgent","current","token","status","exitCode","summary","resolve","reference","byId","matches","length","acquireRun","existing","validateBoundCwd","runLock","recoveredStale","ensureRunLocksRoot","assertNotSymlink","mkdirSync","error","code","message","owner","getIdentity","process","pid","startedAt","writeRunLock","removeOwnedRunLock","recordProviderProcess","identity","requireOwnedRun","next","completeRun","result","slice","resolved","realpathSync","statSync","isDirectory","Error","bound","stat","lstatSync","isSymbolicLink","ensureSafeParent","parent","recursive","target","version","parsed","JSON","parse","readFileSync","isStoreFile","map","value","record","Array","isArray","temp","fd","openSync","writeFileSync","stringify","fsyncSync","closeSync","undefined","chmodSync","unlinkSync","operation","started","isYoungMutationLock","rmdirSync","Promise","setTimeout","mtimeMs","update","index","findIndex","test","ownerPath","tempPath","encoding","flag","sameProcess","expected","actual","readdirSync","entry","isFile","Number","isInteger","platform","close","lastIndexOf","fields","split","startTicks","String","stdio","trim"],"mappings":"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"}
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
-
|
|
6
|
-
const tempDirs: string[] = [];
|
|
7
|
-
|
|
8
|
-
afterEach(() => {
|
|
9
|
-
for (const dir of tempDirs.splice(0)) fs.rmSync(dir, { recursive: true, force: true });
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
async function loadStore(): Promise<any> {
|
|
13
|
-
const api = await import('../../index.js') as Record<string, unknown>;
|
|
14
|
-
expect(api).toHaveProperty('PrintAgentStore');
|
|
15
|
-
return api.PrintAgentStore;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function fixture(): { root: string; cwd: string; filePath: string } {
|
|
19
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'print-agent-store-'));
|
|
20
|
-
tempDirs.push(root);
|
|
21
|
-
const cwd = path.join(root, 'project');
|
|
22
|
-
fs.mkdirSync(cwd);
|
|
23
|
-
return { root, cwd, filePath: path.join(root, 'state', 'print-agents.json') };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
describe('PrintAgentStore create/list/resolve', () => {
|
|
27
|
-
it('creates distinct durable identities with a canonical cwd and lists them', async () => {
|
|
28
|
-
const PrintAgentStore = await loadStore();
|
|
29
|
-
const { cwd, filePath } = fixture();
|
|
30
|
-
const store = new PrintAgentStore({ filePath, now: () => new Date('2026-08-07T09:00:00Z') });
|
|
31
|
-
|
|
32
|
-
const agent = await store.create({ name: 'reviewer', cwd });
|
|
33
|
-
|
|
34
|
-
expect(agent).toMatchObject({
|
|
35
|
-
name: 'reviewer',
|
|
36
|
-
provider: 'claude',
|
|
37
|
-
mode: 'print',
|
|
38
|
-
cwd: fs.realpathSync(cwd),
|
|
39
|
-
state: 'ready',
|
|
40
|
-
sessionHealth: 'uninitialized',
|
|
41
|
-
activeRun: null,
|
|
42
|
-
});
|
|
43
|
-
expect(agent.id).toMatch(/^[0-9a-f-]{36}$/);
|
|
44
|
-
expect(agent.providerSessionId).toMatch(/^[0-9a-f-]{36}$/);
|
|
45
|
-
expect(agent.id).not.toBe(agent.providerSessionId);
|
|
46
|
-
expect(await store.list()).toEqual([agent]);
|
|
47
|
-
expect(fs.statSync(filePath).mode & 0o777).toBe(0o600);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('resolves exact ids and names and rejects duplicate names', async () => {
|
|
51
|
-
const PrintAgentStore = await loadStore();
|
|
52
|
-
const { cwd, filePath } = fixture();
|
|
53
|
-
const store = new PrintAgentStore({ filePath });
|
|
54
|
-
const agent = await store.create({ name: 'Reviewer', cwd });
|
|
55
|
-
|
|
56
|
-
expect(await store.resolve(agent.id)).toMatchObject({ id: agent.id });
|
|
57
|
-
expect(await store.resolve('reviewer')).toMatchObject({ id: agent.id });
|
|
58
|
-
expect(await store.resolve('view')).toBeNull();
|
|
59
|
-
await expect(store.create({ name: 'reviewer', cwd })).rejects.toMatchObject({
|
|
60
|
-
code: 'PRINT_AGENT_NAME_CONFLICT',
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('rejects missing cwd, malformed storage, and symlinked store targets', async () => {
|
|
65
|
-
const PrintAgentStore = await loadStore();
|
|
66
|
-
const { root, cwd, filePath } = fixture();
|
|
67
|
-
const store = new PrintAgentStore({ filePath });
|
|
68
|
-
|
|
69
|
-
await expect(store.create({ name: 'missing', cwd: path.join(root, 'missing') }))
|
|
70
|
-
.rejects.toMatchObject({ code: 'PRINT_AGENT_STORE' });
|
|
71
|
-
|
|
72
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
73
|
-
fs.writeFileSync(filePath, '{bad json', { mode: 0o600 });
|
|
74
|
-
await expect(store.list()).rejects.toMatchObject({ code: 'PRINT_AGENT_STORE' });
|
|
75
|
-
|
|
76
|
-
fs.rmSync(filePath);
|
|
77
|
-
const target = path.join(root, 'target.json');
|
|
78
|
-
fs.writeFileSync(target, JSON.stringify({ version: 1, agents: [] }));
|
|
79
|
-
fs.symlinkSync(target, filePath);
|
|
80
|
-
await expect(store.create({ name: 'unsafe', cwd })).rejects.toMatchObject({
|
|
81
|
-
code: 'PRINT_AGENT_STORE',
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('recovers an abandoned old mutation lock after a crash', async () => {
|
|
86
|
-
const PrintAgentStore = await loadStore();
|
|
87
|
-
const { cwd, filePath } = fixture();
|
|
88
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
89
|
-
const lockPath = `${filePath}.lock`;
|
|
90
|
-
fs.mkdirSync(lockPath);
|
|
91
|
-
const old = new Date(Date.now() - 60_000);
|
|
92
|
-
fs.utimesSync(lockPath, old, old);
|
|
93
|
-
const store = new PrintAgentStore({ filePath, mutationLockStaleMs: 10 });
|
|
94
|
-
|
|
95
|
-
await expect(store.create({ name: 'recovered', cwd })).resolves.toMatchObject({ name: 'recovered' });
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('PrintAgentStore run ownership', () => {
|
|
100
|
-
it('fails fast when another exact owner is live and completes only for its token', async () => {
|
|
101
|
-
const PrintAgentStore = await loadStore();
|
|
102
|
-
const { cwd, filePath } = fixture();
|
|
103
|
-
const live = new Map<number, string>([[process.pid, 'owner-start']]);
|
|
104
|
-
const processInspector = { getIdentity: (pid: number) => {
|
|
105
|
-
const startedAt = live.get(pid);
|
|
106
|
-
return startedAt ? { pid, startedAt } : null;
|
|
107
|
-
} };
|
|
108
|
-
const store = new PrintAgentStore({ filePath, processInspector });
|
|
109
|
-
const agent = await store.create({ name: 'runner', cwd });
|
|
110
|
-
|
|
111
|
-
const acquired = await store.acquireRun(agent.id);
|
|
112
|
-
await expect(store.acquireRun(agent.id)).rejects.toMatchObject({ code: 'PRINT_AGENT_BUSY' });
|
|
113
|
-
await expect(store.completeRun(agent.id, 'wrong-token', {
|
|
114
|
-
status: 'succeeded', exitCode: 0, summary: 'done', sessionHealth: 'healthy',
|
|
115
|
-
})).rejects.toMatchObject({ code: 'PRINT_AGENT_STORE' });
|
|
116
|
-
|
|
117
|
-
const completed = await store.completeRun(agent.id, acquired.token, {
|
|
118
|
-
status: 'succeeded', exitCode: 0, summary: 'done', sessionHealth: 'healthy',
|
|
119
|
-
});
|
|
120
|
-
expect(completed).toMatchObject({ state: 'ready', sessionHealth: 'healthy', activeRun: null });
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('retains busy for a live provider then recovers a dead run without signaling it', async () => {
|
|
124
|
-
const PrintAgentStore = await loadStore();
|
|
125
|
-
const { cwd, filePath } = fixture();
|
|
126
|
-
const live = new Map<number, string>([[process.pid, 'owner-start'], [4242, 'provider-start']]);
|
|
127
|
-
const processInspector = { getIdentity: (pid: number) => {
|
|
128
|
-
const startedAt = live.get(pid);
|
|
129
|
-
return startedAt ? { pid, startedAt } : null;
|
|
130
|
-
} };
|
|
131
|
-
const first = new PrintAgentStore({ filePath, processInspector });
|
|
132
|
-
const agent = await first.create({ name: 'recoverable', cwd });
|
|
133
|
-
const run = await first.acquireRun(agent.id);
|
|
134
|
-
await first.recordProviderProcess(agent.id, run.token, { pid: 4242, startedAt: 'provider-start' });
|
|
135
|
-
|
|
136
|
-
live.delete(process.pid);
|
|
137
|
-
await expect(first.acquireRun(agent.id)).rejects.toMatchObject({ code: 'PRINT_AGENT_BUSY' });
|
|
138
|
-
|
|
139
|
-
live.delete(4242);
|
|
140
|
-
live.set(process.pid, 'replacement-owner-start');
|
|
141
|
-
const recovered = await first.acquireRun(agent.id);
|
|
142
|
-
expect(recovered.agent).toMatchObject({
|
|
143
|
-
state: 'running',
|
|
144
|
-
lastResult: { status: 'interrupted' },
|
|
145
|
-
});
|
|
146
|
-
await first.completeRun(agent.id, recovered.token, {
|
|
147
|
-
status: 'failed', exitCode: 1, summary: 'failed', sessionHealth: 'unknown',
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('reconciles an old incomplete lock to degraded during list', async () => {
|
|
152
|
-
const PrintAgentStore = await loadStore();
|
|
153
|
-
const { root, cwd, filePath } = fixture();
|
|
154
|
-
const live = new Map<number, string>([[process.pid, 'owner-start']]);
|
|
155
|
-
const store = new PrintAgentStore({ filePath, incompleteLockGraceMs: 10, processInspector: {
|
|
156
|
-
getIdentity: (pid: number) => {
|
|
157
|
-
const startedAt = live.get(pid);
|
|
158
|
-
return startedAt ? { pid, startedAt } : null;
|
|
159
|
-
},
|
|
160
|
-
} });
|
|
161
|
-
const agent = await store.create({ name: 'crashed', cwd });
|
|
162
|
-
await store.acquireRun(agent.id);
|
|
163
|
-
const lockPath = path.join(root, 'state', 'print-agent-locks', `${agent.id}.lock`);
|
|
164
|
-
fs.unlinkSync(path.join(lockPath, 'owner.json'));
|
|
165
|
-
const old = new Date(Date.now() - 1000);
|
|
166
|
-
fs.utimesSync(lockPath, old, old);
|
|
167
|
-
live.clear();
|
|
168
|
-
|
|
169
|
-
const listed = await store.list();
|
|
170
|
-
|
|
171
|
-
expect(listed[0]).toMatchObject({
|
|
172
|
-
state: 'degraded',
|
|
173
|
-
sessionHealth: 'unknown',
|
|
174
|
-
activeRun: null,
|
|
175
|
-
lastResult: { status: 'interrupted' },
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('rejects send acquisition when the bound cwd is replaced by a symlink', async () => {
|
|
180
|
-
const PrintAgentStore = await loadStore();
|
|
181
|
-
const { root, cwd, filePath } = fixture();
|
|
182
|
-
const store = new PrintAgentStore({ filePath });
|
|
183
|
-
const agent = await store.create({ name: 'bound', cwd });
|
|
184
|
-
const moved = path.join(root, 'moved-project');
|
|
185
|
-
const other = path.join(root, 'other-project');
|
|
186
|
-
fs.renameSync(cwd, moved);
|
|
187
|
-
fs.mkdirSync(other);
|
|
188
|
-
fs.symlinkSync(other, cwd);
|
|
189
|
-
|
|
190
|
-
await expect(store.acquireRun(agent.id)).rejects.toMatchObject({ code: 'PRINT_AGENT_STORE' });
|
|
191
|
-
});
|
|
192
|
-
});
|
package/src/print/PrintAgent.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
export type PrintAgentState = 'ready' | 'running' | 'degraded';
|
|
2
|
-
export type PrintSessionHealth = 'uninitialized' | 'healthy' | 'unknown' | 'mismatch';
|
|
3
|
-
export type PrintRunStatus = 'succeeded' | 'failed' | 'interrupted';
|
|
4
|
-
|
|
5
|
-
export interface ProcessIdentity {
|
|
6
|
-
pid: number;
|
|
7
|
-
startedAt: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface PrintActiveRun {
|
|
11
|
-
token: string;
|
|
12
|
-
owner: ProcessIdentity;
|
|
13
|
-
provider: ProcessIdentity | null;
|
|
14
|
-
startedAt: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface PrintLastResult {
|
|
18
|
-
status: PrintRunStatus;
|
|
19
|
-
completedAt: string;
|
|
20
|
-
exitCode: number | null;
|
|
21
|
-
summary: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface PrintAgent {
|
|
25
|
-
id: string;
|
|
26
|
-
name: string;
|
|
27
|
-
provider: 'claude';
|
|
28
|
-
mode: 'print';
|
|
29
|
-
cwd: string;
|
|
30
|
-
providerSessionId: string;
|
|
31
|
-
state: PrintAgentState;
|
|
32
|
-
sessionHealth: PrintSessionHealth;
|
|
33
|
-
createdAt: string;
|
|
34
|
-
updatedAt: string;
|
|
35
|
-
lastActiveAt: string | null;
|
|
36
|
-
lastResult: PrintLastResult | null;
|
|
37
|
-
activeRun: PrintActiveRun | null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class PrintAgentError extends Error {
|
|
41
|
-
constructor(
|
|
42
|
-
message: string,
|
|
43
|
-
public readonly code: string,
|
|
44
|
-
) {
|
|
45
|
-
super(message);
|
|
46
|
-
this.name = 'PrintAgentError';
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export class PrintAgentBusyError extends PrintAgentError {
|
|
51
|
-
constructor(
|
|
52
|
-
public readonly agentId: string,
|
|
53
|
-
agentName: string,
|
|
54
|
-
) {
|
|
55
|
-
super(`Print agent "${agentName}" is busy.`, 'PRINT_AGENT_BUSY');
|
|
56
|
-
this.name = 'PrintAgentBusyError';
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export class PrintAgentNotFoundError extends PrintAgentError {
|
|
61
|
-
constructor(public readonly reference: string) {
|
|
62
|
-
super(`Print agent "${reference}" was not found.`, 'PRINT_AGENT_NOT_FOUND');
|
|
63
|
-
this.name = 'PrintAgentNotFoundError';
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export class PrintAgentStoreError extends PrintAgentError {
|
|
68
|
-
constructor(message: string) {
|
|
69
|
-
super(message, 'PRINT_AGENT_STORE');
|
|
70
|
-
this.name = 'PrintAgentStoreError';
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export class PrintAgentNameConflictError extends PrintAgentError {
|
|
75
|
-
constructor(public readonly agentName: string) {
|
|
76
|
-
super(`Print agent name "${agentName}" is already in use.`, 'PRINT_AGENT_NAME_CONFLICT');
|
|
77
|
-
this.name = 'PrintAgentNameConflictError';
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export class ClaudePrintError extends PrintAgentError {
|
|
82
|
-
constructor(message: string, code = 'CLAUDE_PRINT_FAILED') {
|
|
83
|
-
super(message, code);
|
|
84
|
-
this.name = 'ClaudePrintError';
|
|
85
|
-
}
|
|
86
|
-
}
|