@ai-devkit/agent-manager 0.26.3 → 0.26.4
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/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +7 -6
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/AgentManager.test.js +142 -4
- package/dist/__tests__/AgentManager.test.js.map +1 -1
- package/dist/__tests__/utils/AgentRegistry.test.js +140 -0
- package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
- package/dist/database/connection.d.ts +1 -1
- package/dist/database/connection.d.ts.map +1 -1
- package/dist/database/connection.js +1 -1
- package/dist/database/connection.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/AgentRegistry.d.ts +14 -1
- package/dist/utils/AgentRegistry.d.ts.map +1 -1
- package/dist/utils/AgentRegistry.js +65 -20
- package/dist/utils/AgentRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/AgentManager.ts +7 -4
- package/src/__tests__/AgentManager.test.ts +142 -4
- package/src/__tests__/utils/AgentRegistry.test.ts +119 -0
- package/src/database/connection.ts +4 -2
- package/src/index.ts +1 -1
- package/src/utils/AgentRegistry.ts +85 -17
|
@@ -25,6 +25,7 @@ describe('AgentRegistry', ()=>{
|
|
|
25
25
|
registry = new AgentRegistry(regPath);
|
|
26
26
|
});
|
|
27
27
|
afterEach(()=>{
|
|
28
|
+
vi.restoreAllMocks();
|
|
28
29
|
fs.rmSync(tmpDir, {
|
|
29
30
|
recursive: true,
|
|
30
31
|
force: true
|
|
@@ -101,6 +102,22 @@ describe('AgentRegistry', ()=>{
|
|
|
101
102
|
expect(registry.lookup(`ai-devkit-${process.pid}`)).toBeNull();
|
|
102
103
|
expect(registry.list()).toHaveLength(1);
|
|
103
104
|
});
|
|
105
|
+
it('preserves an existing name conflict when its probe fails with EPERM', ()=>{
|
|
106
|
+
registry.register(makeEntry({
|
|
107
|
+
name: 'claimed-name',
|
|
108
|
+
pid: process.pid
|
|
109
|
+
}));
|
|
110
|
+
vi.spyOn(process, 'kill').mockImplementation(()=>{
|
|
111
|
+
throw Object.assign(new Error('operation not permitted'), {
|
|
112
|
+
code: 'EPERM'
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
expect(()=>registry.register(makeEntry({
|
|
116
|
+
name: 'claimed-name',
|
|
117
|
+
pid: process.pid + 1
|
|
118
|
+
}))).toThrow();
|
|
119
|
+
expect(registry.lookup('claimed-name')?.pid).toBe(process.pid);
|
|
120
|
+
});
|
|
104
121
|
});
|
|
105
122
|
describe('registerBatch', ()=>{
|
|
106
123
|
it('is a no-op on empty array', ()=>{
|
|
@@ -161,6 +178,44 @@ describe('AgentRegistry', ()=>{
|
|
|
161
178
|
expect(registry.list()).toHaveLength(1);
|
|
162
179
|
expect(registry.lookup('custom-name')?.pid).toBe(process.pid);
|
|
163
180
|
});
|
|
181
|
+
it('cleans up a cross-type row when its pid has been reused', ()=>{
|
|
182
|
+
registry.register(makeEntry({
|
|
183
|
+
name: 'old-claude',
|
|
184
|
+
type: 'claude',
|
|
185
|
+
pid: process.pid
|
|
186
|
+
}));
|
|
187
|
+
registry.register(makeEntry({
|
|
188
|
+
name: 'new-codex',
|
|
189
|
+
type: 'codex',
|
|
190
|
+
pid: process.pid,
|
|
191
|
+
tmuxSession: ''
|
|
192
|
+
}));
|
|
193
|
+
expect(registry.lookup('old-claude')).toBeNull();
|
|
194
|
+
expect(registry.lookup('new-codex')).toMatchObject({
|
|
195
|
+
type: 'codex',
|
|
196
|
+
pid: process.pid
|
|
197
|
+
});
|
|
198
|
+
expect(registry.list()).toHaveLength(1);
|
|
199
|
+
});
|
|
200
|
+
it('rolls back the whole batch when a live name conflict rejects one entry', ()=>{
|
|
201
|
+
registry.register(makeEntry({
|
|
202
|
+
name: 'taken',
|
|
203
|
+
pid: process.pid
|
|
204
|
+
}));
|
|
205
|
+
expect(()=>registry.registerBatch([
|
|
206
|
+
makeEntry({
|
|
207
|
+
name: 'fresh',
|
|
208
|
+
pid: 999998
|
|
209
|
+
}),
|
|
210
|
+
makeEntry({
|
|
211
|
+
name: 'taken',
|
|
212
|
+
type: 'codex',
|
|
213
|
+
pid: 999997
|
|
214
|
+
})
|
|
215
|
+
])).toThrow(/UNIQUE constraint failed/);
|
|
216
|
+
expect(registry.lookup('fresh')).toBeNull();
|
|
217
|
+
expect(registry.lookup('taken')?.pid).toBe(process.pid);
|
|
218
|
+
});
|
|
164
219
|
});
|
|
165
220
|
describe('lookup', ()=>{
|
|
166
221
|
it('returns null when name not found', ()=>{
|
|
@@ -207,6 +262,28 @@ describe('AgentRegistry', ()=>{
|
|
|
207
262
|
pid: 999999
|
|
208
263
|
}))).toBe(false);
|
|
209
264
|
});
|
|
265
|
+
it('returns true when the process probe is forbidden with EPERM', ()=>{
|
|
266
|
+
vi.spyOn(process, 'kill').mockImplementation(()=>{
|
|
267
|
+
throw Object.assign(new Error('operation not permitted'), {
|
|
268
|
+
code: 'EPERM'
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
expect(registry.isAlive(makeEntry())).toBe(true);
|
|
272
|
+
});
|
|
273
|
+
it('returns false when the process probe reports ESRCH', ()=>{
|
|
274
|
+
vi.spyOn(process, 'kill').mockImplementation(()=>{
|
|
275
|
+
throw Object.assign(new Error('no such process'), {
|
|
276
|
+
code: 'ESRCH'
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
expect(registry.isAlive(makeEntry())).toBe(false);
|
|
280
|
+
});
|
|
281
|
+
it('returns true when the process probe fails without a definitive error code', ()=>{
|
|
282
|
+
vi.spyOn(process, 'kill').mockImplementation(()=>{
|
|
283
|
+
throw new Error('indeterminate probe failure');
|
|
284
|
+
});
|
|
285
|
+
expect(registry.isAlive(makeEntry())).toBe(true);
|
|
286
|
+
});
|
|
210
287
|
});
|
|
211
288
|
describe('prune', ()=>{
|
|
212
289
|
it('removes entries whose PIDs are dead', ()=>{
|
|
@@ -232,9 +309,55 @@ describe('AgentRegistry', ()=>{
|
|
|
232
309
|
const after = registry.list();
|
|
233
310
|
expect(after).toEqual(before);
|
|
234
311
|
});
|
|
312
|
+
it('preserves entries when liveness probing fails with EPERM', ()=>{
|
|
313
|
+
registry.register(makeEntry({
|
|
314
|
+
name: 'custom-name',
|
|
315
|
+
tmuxSession: 'tmux-custom'
|
|
316
|
+
}));
|
|
317
|
+
vi.spyOn(process, 'kill').mockImplementation(()=>{
|
|
318
|
+
throw Object.assign(new Error('operation not permitted'), {
|
|
319
|
+
code: 'EPERM'
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
registry.prune();
|
|
323
|
+
expect(registry.lookup('custom-name')).toMatchObject({
|
|
324
|
+
name: 'custom-name',
|
|
325
|
+
tmuxSession: 'tmux-custom'
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
it('removes entries when liveness probing fails with ESRCH', ()=>{
|
|
329
|
+
registry.register(makeEntry({
|
|
330
|
+
name: 'dead'
|
|
331
|
+
}));
|
|
332
|
+
vi.spyOn(process, 'kill').mockImplementation(()=>{
|
|
333
|
+
throw Object.assign(new Error('no such process'), {
|
|
334
|
+
code: 'ESRCH'
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
registry.prune();
|
|
338
|
+
expect(registry.lookup('dead')).toBeNull();
|
|
339
|
+
});
|
|
235
340
|
it('does nothing when file is missing', ()=>{
|
|
236
341
|
expect(()=>registry.prune()).not.toThrow();
|
|
237
342
|
});
|
|
343
|
+
it('keeps forced prune available before the passive cadence is due', ()=>{
|
|
344
|
+
let nowMs = Date.parse('2026-08-14T10:00:00.000Z');
|
|
345
|
+
const clocked = new AgentRegistry(regPath, {
|
|
346
|
+
now: ()=>new Date(nowMs),
|
|
347
|
+
pruneIntervalMs: 30_000
|
|
348
|
+
});
|
|
349
|
+
clocked.register(makeEntry({
|
|
350
|
+
name: 'forced',
|
|
351
|
+
pid: process.pid
|
|
352
|
+
}));
|
|
353
|
+
const alive = vi.spyOn(clocked, 'isAlive').mockReturnValue(true);
|
|
354
|
+
clocked.pruneIfDue();
|
|
355
|
+
alive.mockReturnValue(false);
|
|
356
|
+
nowMs += 1;
|
|
357
|
+
clocked.prune();
|
|
358
|
+
expect(alive).toHaveBeenCalledTimes(2);
|
|
359
|
+
expect(clocked.lookup('forced')).toBeNull();
|
|
360
|
+
});
|
|
238
361
|
});
|
|
239
362
|
describe('default()', ()=>{
|
|
240
363
|
it('returns a singleton instance', ()=>{
|
|
@@ -278,6 +401,23 @@ describe('AgentRegistry', ()=>{
|
|
|
278
401
|
}));
|
|
279
402
|
expect(()=>registry.rename('agent-a', 'agent-b')).toThrow(RenameConflictError);
|
|
280
403
|
});
|
|
404
|
+
it('throws RenameConflictError when the conflicting entry probe fails with EPERM', ()=>{
|
|
405
|
+
registry.register(makeEntry({
|
|
406
|
+
name: 'agent-a',
|
|
407
|
+
pid: process.pid
|
|
408
|
+
}));
|
|
409
|
+
registry.register(makeEntry({
|
|
410
|
+
name: 'agent-b',
|
|
411
|
+
pid: process.ppid
|
|
412
|
+
}));
|
|
413
|
+
vi.spyOn(process, 'kill').mockImplementation(()=>{
|
|
414
|
+
throw Object.assign(new Error('operation not permitted'), {
|
|
415
|
+
code: 'EPERM'
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
expect(()=>registry.rename('agent-a', 'agent-b')).toThrow(RenameConflictError);
|
|
419
|
+
expect(registry.lookup('agent-b')?.pid).toBe(process.ppid);
|
|
420
|
+
});
|
|
281
421
|
it('succeeds when new name exists only as a stale (dead) entry', ()=>{
|
|
282
422
|
registry.register(makeEntry({
|
|
283
423
|
name: 'agent-a',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/__tests__/utils/AgentRegistry.test.ts"],"sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport { AgentRegistry, RenameNotFoundError, RenameConflictError, type RegistryEntry } from '../../utils/AgentRegistry.js';\n\nfunction makeEntry(over: Partial<RegistryEntry> = {}): RegistryEntry {\n return {\n name: 'agent1',\n type: 'claude',\n pid: process.pid,\n tmuxSession: 'agent1',\n cwd: '/tmp',\n startedAt: '2026-05-30T00:00:00.000Z',\n sessionId: 'sid-1',\n sessionFilePath: '/tmp/session.jsonl',\n ...over,\n };\n}\n\ndescribe('AgentRegistry', () => {\n let tmpDir: string;\n let regPath: string;\n let registry: AgentRegistry;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-registry-'));\n regPath = path.join(tmpDir, 'nested', 'agents.json');\n registry = new AgentRegistry(regPath);\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n describe('register', () => {\n it('creates the SQLite database and parent directory if missing', () => {\n registry.register(makeEntry());\n expect(fs.existsSync(regPath.replace(/\\.json$/, '.db'))).toBe(true);\n expect(registry.list()[0].name).toBe('agent1');\n });\n\n it('appends a new entry when name is unique', () => {\n registry.register(makeEntry({ name: 'a' }));\n registry.register(makeEntry({ name: 'b', pid: process.ppid }));\n expect(registry.list()).toHaveLength(2);\n });\n\n it('upserts in place when type and pid already exist', () => {\n registry.register(makeEntry({ name: 'a', pid: process.pid }));\n registry.register(makeEntry({ name: 'fallback', pid: process.pid, tmuxSession: '' }));\n const all = registry.list();\n expect(all).toHaveLength(1);\n expect(all[0].pid).toBe(process.pid);\n expect(all[0].name).toBe('a');\n });\n\n it('does not write through the legacy fixed .tmp path', () => {\n registry.register(makeEntry());\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n\n it('persists session fields', () => {\n registry.register(makeEntry({ sessionId: 'sid-xyz', sessionFilePath: '/foo/bar.jsonl' }));\n const saved = registry.list()[0];\n expect(saved.sessionId).toBe('sid-xyz');\n expect(saved.sessionFilePath).toBe('/foo/bar.jsonl');\n });\n\n it('preserves existing tmuxSession when incoming is empty string', () => {\n registry.register(makeEntry({ name: 'a', tmuxSession: 'pinned' }));\n registry.register(makeEntry({ name: 'fallback', tmuxSession: '', pid: process.pid }));\n const saved = registry.lookup('a');\n expect(saved?.tmuxSession).toBe('pinned');\n expect(saved?.pid).toBe(process.pid);\n });\n\n it('lets a managed start entry replace a generated fallback for the same pid', () => {\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, tmuxSession: '' }));\n registry.register(makeEntry({ name: 'custom-name', tmuxSession: 'custom-name' }));\n expect(registry.lookup('custom-name')?.tmuxSession).toBe('custom-name');\n expect(registry.lookup(`ai-devkit-${process.pid}`)).toBeNull();\n expect(registry.list()).toHaveLength(1);\n });\n });\n\n describe('registerBatch', () => {\n it('is a no-op on empty array', () => {\n registry.registerBatch([]);\n expect(fs.existsSync(regPath)).toBe(false);\n });\n\n it('upserts multiple entries in a single batch', () => {\n registry.registerBatch([\n makeEntry({ name: 'a' }),\n makeEntry({ name: 'b', pid: process.pid + 1 }),\n makeEntry({ name: 'c', pid: process.pid + 2 }),\n ]);\n expect(registry.list()).toHaveLength(3);\n });\n\n it('applies the tmuxSession merge per entry', () => {\n registry.register(makeEntry({ name: 'a', tmuxSession: 'pinned' }));\n registry.registerBatch([\n makeEntry({ name: 'fallback', tmuxSession: '', pid: process.pid }),\n makeEntry({ name: 'b', tmuxSession: '', pid: process.pid + 1 }),\n ]);\n expect(registry.lookup('a')?.tmuxSession).toBe('pinned');\n expect(registry.lookup('a')?.pid).toBe(process.pid);\n expect(registry.lookup('b')?.tmuxSession).toBe('');\n });\n\n it('handles concurrent registry instances without duplicate pid rows', () => {\n const other = new AgentRegistry(regPath);\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, tmuxSession: '' }));\n other.register(makeEntry({ name: 'custom-name', tmuxSession: 'custom-name' }));\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, tmuxSession: '' }));\n\n expect(registry.list()).toHaveLength(1);\n expect(registry.lookup('custom-name')?.pid).toBe(process.pid);\n });\n });\n\n describe('lookup', () => {\n it('returns null when name not found', () => {\n expect(registry.lookup('missing')).toBeNull();\n });\n\n it('returns the entry when name matches', () => {\n registry.register(makeEntry({ name: 'a' }));\n expect(registry.lookup('a')?.name).toBe('a');\n });\n });\n\n describe('list', () => {\n it('returns empty array when database does not contain entries', () => {\n expect(registry.list()).toEqual([]);\n });\n\n it('ignores existing legacy agents.json entries', () => {\n const legacyEntry = makeEntry({ name: 'legacy', tmuxSession: 'legacy' });\n fs.mkdirSync(path.dirname(regPath), { recursive: true });\n fs.writeFileSync(regPath, JSON.stringify({ entries: [legacyEntry] }), 'utf8');\n\n const legacyRegistry = new AgentRegistry(regPath);\n\n expect(legacyRegistry.lookup('legacy')).toBeNull();\n expect(legacyRegistry.list()).toEqual([]);\n expect(fs.existsSync(regPath.replace(/\\.json$/, '.db'))).toBe(true);\n });\n });\n\n describe('isAlive', () => {\n it('returns true for the current process', () => {\n expect(registry.isAlive(makeEntry({ pid: process.pid }))).toBe(true);\n });\n\n it('returns false for a PID that does not exist', () => {\n expect(registry.isAlive(makeEntry({ pid: 999999 }))).toBe(false);\n });\n });\n\n describe('prune', () => {\n it('removes entries whose PIDs are dead', () => {\n registry.register(makeEntry({ name: 'alive', pid: process.pid }));\n registry.register(makeEntry({ name: 'dead', pid: 999999 }));\n registry.prune();\n const remaining = registry.list();\n expect(remaining).toHaveLength(1);\n expect(remaining[0].name).toBe('alive');\n });\n\n it('is a no-op when all entries are alive', () => {\n registry.register(makeEntry({ pid: process.pid }));\n const before = registry.list();\n registry.prune();\n const after = registry.list();\n expect(after).toEqual(before);\n });\n\n it('does nothing when file is missing', () => {\n expect(() => registry.prune()).not.toThrow();\n });\n });\n\n describe('default()', () => {\n it('returns a singleton instance', () => {\n expect(AgentRegistry.default()).toBe(AgentRegistry.default());\n });\n });\n\n describe('rename', () => {\n it('updates the name of an existing entry', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid }));\n registry.rename('old-name', 'new-name');\n expect(registry.lookup('new-name')?.name).toBe('new-name');\n expect(registry.lookup('old-name')).toBeNull();\n });\n\n it('preserves all other fields on the renamed entry', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid, tmuxSession: 'old-name', cwd: '/my/cwd' }));\n registry.rename('old-name', 'new-name');\n const entry = registry.lookup('new-name');\n expect(entry?.tmuxSession).toBe('old-name');\n expect(entry?.cwd).toBe('/my/cwd');\n expect(entry?.pid).toBe(process.pid);\n });\n\n it('throws RenameNotFoundError when current name does not exist', () => {\n expect(() => registry.rename('ghost', 'new-name')).toThrow(RenameNotFoundError);\n });\n\n it('throws RenameConflictError when new name is already in use by a live entry', () => {\n registry.register(makeEntry({ name: 'agent-a', pid: process.pid }));\n registry.register(makeEntry({ name: 'agent-b', pid: process.ppid }));\n expect(() => registry.rename('agent-a', 'agent-b')).toThrow(RenameConflictError);\n });\n\n it('succeeds when new name exists only as a stale (dead) entry', () => {\n registry.register(makeEntry({ name: 'agent-a', pid: process.pid }));\n registry.register(makeEntry({ name: 'agent-b', pid: 999999 }));\n expect(() => registry.rename('agent-a', 'agent-b')).not.toThrow();\n expect(registry.lookup('agent-b')?.pid).toBe(process.pid);\n });\n\n it('does not create the legacy fixed .tmp path on rename', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid }));\n registry.rename('old-name', 'new-name');\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n });\n});\n"],"names":["fs","os","path","AgentRegistry","RenameNotFoundError","RenameConflictError","makeEntry","over","name","type","pid","process","tmuxSession","cwd","startedAt","sessionId","sessionFilePath","describe","tmpDir","regPath","registry","beforeEach","mkdtempSync","join","tmpdir","afterEach","rmSync","recursive","force","it","register","expect","existsSync","replace","toBe","list","ppid","toHaveLength","all","saved","lookup","toBeNull","registerBatch","other","toEqual","legacyEntry","mkdirSync","dirname","writeFileSync","JSON","stringify","entries","legacyRegistry","isAlive","prune","remaining","before","after","not","toThrow","default","rename","entry"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/__tests__/utils/AgentRegistry.test.ts"],"sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport { AgentRegistry, RenameNotFoundError, RenameConflictError, type RegistryEntry } from '../../utils/AgentRegistry.js';\n\nfunction makeEntry(over: Partial<RegistryEntry> = {}): RegistryEntry {\n return {\n name: 'agent1',\n type: 'claude',\n pid: process.pid,\n tmuxSession: 'agent1',\n cwd: '/tmp',\n startedAt: '2026-05-30T00:00:00.000Z',\n sessionId: 'sid-1',\n sessionFilePath: '/tmp/session.jsonl',\n ...over,\n };\n}\n\ndescribe('AgentRegistry', () => {\n let tmpDir: string;\n let regPath: string;\n let registry: AgentRegistry;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-registry-'));\n regPath = path.join(tmpDir, 'nested', 'agents.json');\n registry = new AgentRegistry(regPath);\n });\n\n afterEach(() => {\n vi.restoreAllMocks();\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n describe('register', () => {\n it('creates the SQLite database and parent directory if missing', () => {\n registry.register(makeEntry());\n expect(fs.existsSync(regPath.replace(/\\.json$/, '.db'))).toBe(true);\n expect(registry.list()[0].name).toBe('agent1');\n });\n\n it('appends a new entry when name is unique', () => {\n registry.register(makeEntry({ name: 'a' }));\n registry.register(makeEntry({ name: 'b', pid: process.ppid }));\n expect(registry.list()).toHaveLength(2);\n });\n\n it('upserts in place when type and pid already exist', () => {\n registry.register(makeEntry({ name: 'a', pid: process.pid }));\n registry.register(makeEntry({ name: 'fallback', pid: process.pid, tmuxSession: '' }));\n const all = registry.list();\n expect(all).toHaveLength(1);\n expect(all[0].pid).toBe(process.pid);\n expect(all[0].name).toBe('a');\n });\n\n it('does not write through the legacy fixed .tmp path', () => {\n registry.register(makeEntry());\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n\n it('persists session fields', () => {\n registry.register(makeEntry({ sessionId: 'sid-xyz', sessionFilePath: '/foo/bar.jsonl' }));\n const saved = registry.list()[0];\n expect(saved.sessionId).toBe('sid-xyz');\n expect(saved.sessionFilePath).toBe('/foo/bar.jsonl');\n });\n\n it('preserves existing tmuxSession when incoming is empty string', () => {\n registry.register(makeEntry({ name: 'a', tmuxSession: 'pinned' }));\n registry.register(makeEntry({ name: 'fallback', tmuxSession: '', pid: process.pid }));\n const saved = registry.lookup('a');\n expect(saved?.tmuxSession).toBe('pinned');\n expect(saved?.pid).toBe(process.pid);\n });\n\n it('lets a managed start entry replace a generated fallback for the same pid', () => {\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, tmuxSession: '' }));\n registry.register(makeEntry({ name: 'custom-name', tmuxSession: 'custom-name' }));\n expect(registry.lookup('custom-name')?.tmuxSession).toBe('custom-name');\n expect(registry.lookup(`ai-devkit-${process.pid}`)).toBeNull();\n expect(registry.list()).toHaveLength(1);\n });\n\n it('preserves an existing name conflict when its probe fails with EPERM', () => {\n registry.register(makeEntry({ name: 'claimed-name', pid: process.pid }));\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('operation not permitted'), { code: 'EPERM' });\n });\n\n expect(() => registry.register(makeEntry({\n name: 'claimed-name',\n pid: process.pid + 1,\n }))).toThrow();\n expect(registry.lookup('claimed-name')?.pid).toBe(process.pid);\n });\n });\n\n describe('registerBatch', () => {\n it('is a no-op on empty array', () => {\n registry.registerBatch([]);\n expect(fs.existsSync(regPath)).toBe(false);\n });\n\n it('upserts multiple entries in a single batch', () => {\n registry.registerBatch([\n makeEntry({ name: 'a' }),\n makeEntry({ name: 'b', pid: process.pid + 1 }),\n makeEntry({ name: 'c', pid: process.pid + 2 }),\n ]);\n expect(registry.list()).toHaveLength(3);\n });\n\n it('applies the tmuxSession merge per entry', () => {\n registry.register(makeEntry({ name: 'a', tmuxSession: 'pinned' }));\n registry.registerBatch([\n makeEntry({ name: 'fallback', tmuxSession: '', pid: process.pid }),\n makeEntry({ name: 'b', tmuxSession: '', pid: process.pid + 1 }),\n ]);\n expect(registry.lookup('a')?.tmuxSession).toBe('pinned');\n expect(registry.lookup('a')?.pid).toBe(process.pid);\n expect(registry.lookup('b')?.tmuxSession).toBe('');\n });\n\n it('handles concurrent registry instances without duplicate pid rows', () => {\n const other = new AgentRegistry(regPath);\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, tmuxSession: '' }));\n other.register(makeEntry({ name: 'custom-name', tmuxSession: 'custom-name' }));\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, tmuxSession: '' }));\n\n expect(registry.list()).toHaveLength(1);\n expect(registry.lookup('custom-name')?.pid).toBe(process.pid);\n });\n\n it('cleans up a cross-type row when its pid has been reused', () => {\n registry.register(makeEntry({ name: 'old-claude', type: 'claude', pid: process.pid }));\n\n registry.register(makeEntry({\n name: 'new-codex',\n type: 'codex',\n pid: process.pid,\n tmuxSession: '',\n }));\n\n expect(registry.lookup('old-claude')).toBeNull();\n expect(registry.lookup('new-codex')).toMatchObject({ type: 'codex', pid: process.pid });\n expect(registry.list()).toHaveLength(1);\n });\n\n it('rolls back the whole batch when a live name conflict rejects one entry', () => {\n registry.register(makeEntry({ name: 'taken', pid: process.pid }));\n\n expect(() => registry.registerBatch([\n makeEntry({ name: 'fresh', pid: 999998 }),\n makeEntry({ name: 'taken', type: 'codex', pid: 999997 }),\n ])).toThrow(/UNIQUE constraint failed/);\n\n expect(registry.lookup('fresh')).toBeNull();\n expect(registry.lookup('taken')?.pid).toBe(process.pid);\n });\n });\n\n describe('lookup', () => {\n it('returns null when name not found', () => {\n expect(registry.lookup('missing')).toBeNull();\n });\n\n it('returns the entry when name matches', () => {\n registry.register(makeEntry({ name: 'a' }));\n expect(registry.lookup('a')?.name).toBe('a');\n });\n });\n\n describe('list', () => {\n it('returns empty array when database does not contain entries', () => {\n expect(registry.list()).toEqual([]);\n });\n\n it('ignores existing legacy agents.json entries', () => {\n const legacyEntry = makeEntry({ name: 'legacy', tmuxSession: 'legacy' });\n fs.mkdirSync(path.dirname(regPath), { recursive: true });\n fs.writeFileSync(regPath, JSON.stringify({ entries: [legacyEntry] }), 'utf8');\n\n const legacyRegistry = new AgentRegistry(regPath);\n\n expect(legacyRegistry.lookup('legacy')).toBeNull();\n expect(legacyRegistry.list()).toEqual([]);\n expect(fs.existsSync(regPath.replace(/\\.json$/, '.db'))).toBe(true);\n });\n });\n\n describe('isAlive', () => {\n it('returns true for the current process', () => {\n expect(registry.isAlive(makeEntry({ pid: process.pid }))).toBe(true);\n });\n\n it('returns false for a PID that does not exist', () => {\n expect(registry.isAlive(makeEntry({ pid: 999999 }))).toBe(false);\n });\n\n it('returns true when the process probe is forbidden with EPERM', () => {\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('operation not permitted'), { code: 'EPERM' });\n });\n\n expect(registry.isAlive(makeEntry())).toBe(true);\n });\n\n it('returns false when the process probe reports ESRCH', () => {\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('no such process'), { code: 'ESRCH' });\n });\n\n expect(registry.isAlive(makeEntry())).toBe(false);\n });\n\n it('returns true when the process probe fails without a definitive error code', () => {\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw new Error('indeterminate probe failure');\n });\n\n expect(registry.isAlive(makeEntry())).toBe(true);\n });\n });\n\n describe('prune', () => {\n it('removes entries whose PIDs are dead', () => {\n registry.register(makeEntry({ name: 'alive', pid: process.pid }));\n registry.register(makeEntry({ name: 'dead', pid: 999999 }));\n registry.prune();\n const remaining = registry.list();\n expect(remaining).toHaveLength(1);\n expect(remaining[0].name).toBe('alive');\n });\n\n it('is a no-op when all entries are alive', () => {\n registry.register(makeEntry({ pid: process.pid }));\n const before = registry.list();\n registry.prune();\n const after = registry.list();\n expect(after).toEqual(before);\n });\n\n it('preserves entries when liveness probing fails with EPERM', () => {\n registry.register(makeEntry({ name: 'custom-name', tmuxSession: 'tmux-custom' }));\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('operation not permitted'), { code: 'EPERM' });\n });\n\n registry.prune();\n\n expect(registry.lookup('custom-name')).toMatchObject({\n name: 'custom-name',\n tmuxSession: 'tmux-custom',\n });\n });\n\n it('removes entries when liveness probing fails with ESRCH', () => {\n registry.register(makeEntry({ name: 'dead' }));\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('no such process'), { code: 'ESRCH' });\n });\n\n registry.prune();\n\n expect(registry.lookup('dead')).toBeNull();\n });\n\n it('does nothing when file is missing', () => {\n expect(() => registry.prune()).not.toThrow();\n });\n\n it('keeps forced prune available before the passive cadence is due', () => {\n let nowMs = Date.parse('2026-08-14T10:00:00.000Z');\n const clocked = new AgentRegistry(regPath, {\n now: () => new Date(nowMs),\n pruneIntervalMs: 30_000,\n });\n clocked.register(makeEntry({ name: 'forced', pid: process.pid }));\n const alive = vi.spyOn(clocked, 'isAlive').mockReturnValue(true);\n clocked.pruneIfDue();\n alive.mockReturnValue(false);\n nowMs += 1;\n\n clocked.prune();\n\n expect(alive).toHaveBeenCalledTimes(2);\n expect(clocked.lookup('forced')).toBeNull();\n });\n });\n\n describe('default()', () => {\n it('returns a singleton instance', () => {\n expect(AgentRegistry.default()).toBe(AgentRegistry.default());\n });\n });\n\n describe('rename', () => {\n it('updates the name of an existing entry', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid }));\n registry.rename('old-name', 'new-name');\n expect(registry.lookup('new-name')?.name).toBe('new-name');\n expect(registry.lookup('old-name')).toBeNull();\n });\n\n it('preserves all other fields on the renamed entry', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid, tmuxSession: 'old-name', cwd: '/my/cwd' }));\n registry.rename('old-name', 'new-name');\n const entry = registry.lookup('new-name');\n expect(entry?.tmuxSession).toBe('old-name');\n expect(entry?.cwd).toBe('/my/cwd');\n expect(entry?.pid).toBe(process.pid);\n });\n\n it('throws RenameNotFoundError when current name does not exist', () => {\n expect(() => registry.rename('ghost', 'new-name')).toThrow(RenameNotFoundError);\n });\n\n it('throws RenameConflictError when new name is already in use by a live entry', () => {\n registry.register(makeEntry({ name: 'agent-a', pid: process.pid }));\n registry.register(makeEntry({ name: 'agent-b', pid: process.ppid }));\n expect(() => registry.rename('agent-a', 'agent-b')).toThrow(RenameConflictError);\n });\n\n it('throws RenameConflictError when the conflicting entry probe fails with EPERM', () => {\n registry.register(makeEntry({ name: 'agent-a', pid: process.pid }));\n registry.register(makeEntry({ name: 'agent-b', pid: process.ppid }));\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('operation not permitted'), { code: 'EPERM' });\n });\n\n expect(() => registry.rename('agent-a', 'agent-b')).toThrow(RenameConflictError);\n expect(registry.lookup('agent-b')?.pid).toBe(process.ppid);\n });\n\n it('succeeds when new name exists only as a stale (dead) entry', () => {\n registry.register(makeEntry({ name: 'agent-a', pid: process.pid }));\n registry.register(makeEntry({ name: 'agent-b', pid: 999999 }));\n expect(() => registry.rename('agent-a', 'agent-b')).not.toThrow();\n expect(registry.lookup('agent-b')?.pid).toBe(process.pid);\n });\n\n it('does not create the legacy fixed .tmp path on rename', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid }));\n registry.rename('old-name', 'new-name');\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n });\n});\n"],"names":["fs","os","path","AgentRegistry","RenameNotFoundError","RenameConflictError","makeEntry","over","name","type","pid","process","tmuxSession","cwd","startedAt","sessionId","sessionFilePath","describe","tmpDir","regPath","registry","beforeEach","mkdtempSync","join","tmpdir","afterEach","vi","restoreAllMocks","rmSync","recursive","force","it","register","expect","existsSync","replace","toBe","list","ppid","toHaveLength","all","saved","lookup","toBeNull","spyOn","mockImplementation","Object","assign","Error","code","toThrow","registerBatch","other","toMatchObject","toEqual","legacyEntry","mkdirSync","dirname","writeFileSync","JSON","stringify","entries","legacyRegistry","isAlive","prune","remaining","before","after","not","nowMs","Date","parse","clocked","now","pruneIntervalMs","alive","mockReturnValue","pruneIfDue","toHaveBeenCalledTimes","default","rename","entry"],"mappings":"AAAA,OAAOA,QAAQ,KAAK;AACpB,OAAOC,QAAQ,KAAK;AACpB,OAAOC,UAAU,OAAO;AACxB,SAASC,aAAa,EAAEC,mBAAmB,EAAEC,mBAAmB,QAA4B,+BAA+B;AAE3H,SAASC,UAAUC,OAA+B,CAAC,CAAC;IAChD,OAAO;QACHC,MAAM;QACNC,MAAM;QACNC,KAAKC,QAAQD,GAAG;QAChBE,aAAa;QACbC,KAAK;QACLC,WAAW;QACXC,WAAW;QACXC,iBAAiB;QACjB,GAAGT,IAAI;IACX;AACJ;AAEAU,SAAS,iBAAiB;IACtB,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAEJC,WAAW;QACPH,SAASlB,GAAGsB,WAAW,CAACpB,KAAKqB,IAAI,CAACtB,GAAGuB,MAAM,IAAI;QAC/CL,UAAUjB,KAAKqB,IAAI,CAACL,QAAQ,UAAU;QACtCE,WAAW,IAAIjB,cAAcgB;IACjC;IAEAM,UAAU;QACNC,GAAGC,eAAe;QAClB3B,GAAG4B,MAAM,CAACV,QAAQ;YAAEW,WAAW;YAAMC,OAAO;QAAK;IACrD;IAEAb,SAAS,YAAY;QACjBc,GAAG,+DAA+D;YAC9DX,SAASY,QAAQ,CAAC1B;YAClB2B,OAAOjC,GAAGkC,UAAU,CAACf,QAAQgB,OAAO,CAAC,WAAW,SAASC,IAAI,CAAC;YAC9DH,OAAOb,SAASiB,IAAI,EAAE,CAAC,EAAE,CAAC7B,IAAI,EAAE4B,IAAI,CAAC;QACzC;QAEAL,GAAG,2CAA2C;YAC1CX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;YAAI;YACxCY,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAKE,KAAKC,QAAQ2B,IAAI;YAAC;YAC3DL,OAAOb,SAASiB,IAAI,IAAIE,YAAY,CAAC;QACzC;QAEAR,GAAG,oDAAoD;YACnDX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAKE,KAAKC,QAAQD,GAAG;YAAC;YAC1DU,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAYE,KAAKC,QAAQD,GAAG;gBAAEE,aAAa;YAAG;YAClF,MAAM4B,MAAMpB,SAASiB,IAAI;YACzBJ,OAAOO,KAAKD,YAAY,CAAC;YACzBN,OAAOO,GAAG,CAAC,EAAE,CAAC9B,GAAG,EAAE0B,IAAI,CAACzB,QAAQD,GAAG;YACnCuB,OAAOO,GAAG,CAAC,EAAE,CAAChC,IAAI,EAAE4B,IAAI,CAAC;QAC7B;QAEAL,GAAG,qDAAqD;YACpDX,SAASY,QAAQ,CAAC1B;YAClB2B,OAAOjC,GAAGkC,UAAU,CAAC,GAAGf,QAAQ,IAAI,CAAC,GAAGiB,IAAI,CAAC;QACjD;QAEAL,GAAG,2BAA2B;YAC1BX,SAASY,QAAQ,CAAC1B,UAAU;gBAAES,WAAW;gBAAWC,iBAAiB;YAAiB;YACtF,MAAMyB,QAAQrB,SAASiB,IAAI,EAAE,CAAC,EAAE;YAChCJ,OAAOQ,MAAM1B,SAAS,EAAEqB,IAAI,CAAC;YAC7BH,OAAOQ,MAAMzB,eAAe,EAAEoB,IAAI,CAAC;QACvC;QAEAL,GAAG,gEAAgE;YAC/DX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAKI,aAAa;YAAS;YAC/DQ,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAYI,aAAa;gBAAIF,KAAKC,QAAQD,GAAG;YAAC;YAClF,MAAM+B,QAAQrB,SAASsB,MAAM,CAAC;YAC9BT,OAAOQ,OAAO7B,aAAawB,IAAI,CAAC;YAChCH,OAAOQ,OAAO/B,KAAK0B,IAAI,CAACzB,QAAQD,GAAG;QACvC;QAEAqB,GAAG,4EAA4E;YAC3EX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM,CAAC,UAAU,EAAEG,QAAQD,GAAG,EAAE;gBAAEE,aAAa;YAAG;YAChFQ,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAeI,aAAa;YAAc;YAC9EqB,OAAOb,SAASsB,MAAM,CAAC,gBAAgB9B,aAAawB,IAAI,CAAC;YACzDH,OAAOb,SAASsB,MAAM,CAAC,CAAC,UAAU,EAAE/B,QAAQD,GAAG,EAAE,GAAGiC,QAAQ;YAC5DV,OAAOb,SAASiB,IAAI,IAAIE,YAAY,CAAC;QACzC;QAEAR,GAAG,uEAAuE;YACtEX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAgBE,KAAKC,QAAQD,GAAG;YAAC;YACrEgB,GAAGkB,KAAK,CAACjC,SAAS,QAAQkC,kBAAkB,CAAC;gBACzC,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,4BAA4B;oBAAEC,MAAM;gBAAQ;YAC9E;YAEAhB,OAAO,IAAMb,SAASY,QAAQ,CAAC1B,UAAU;oBACrCE,MAAM;oBACNE,KAAKC,QAAQD,GAAG,GAAG;gBACvB,KAAKwC,OAAO;YACZjB,OAAOb,SAASsB,MAAM,CAAC,iBAAiBhC,KAAK0B,IAAI,CAACzB,QAAQD,GAAG;QACjE;IACJ;IAEAO,SAAS,iBAAiB;QACtBc,GAAG,6BAA6B;YAC5BX,SAAS+B,aAAa,CAAC,EAAE;YACzBlB,OAAOjC,GAAGkC,UAAU,CAACf,UAAUiB,IAAI,CAAC;QACxC;QAEAL,GAAG,8CAA8C;YAC7CX,SAAS+B,aAAa,CAAC;gBACnB7C,UAAU;oBAAEE,MAAM;gBAAI;gBACtBF,UAAU;oBAAEE,MAAM;oBAAKE,KAAKC,QAAQD,GAAG,GAAG;gBAAE;gBAC5CJ,UAAU;oBAAEE,MAAM;oBAAKE,KAAKC,QAAQD,GAAG,GAAG;gBAAE;aAC/C;YACDuB,OAAOb,SAASiB,IAAI,IAAIE,YAAY,CAAC;QACzC;QAEAR,GAAG,2CAA2C;YAC1CX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAKI,aAAa;YAAS;YAC/DQ,SAAS+B,aAAa,CAAC;gBACnB7C,UAAU;oBAAEE,MAAM;oBAAYI,aAAa;oBAAIF,KAAKC,QAAQD,GAAG;gBAAC;gBAChEJ,UAAU;oBAAEE,MAAM;oBAAKI,aAAa;oBAAIF,KAAKC,QAAQD,GAAG,GAAG;gBAAE;aAChE;YACDuB,OAAOb,SAASsB,MAAM,CAAC,MAAM9B,aAAawB,IAAI,CAAC;YAC/CH,OAAOb,SAASsB,MAAM,CAAC,MAAMhC,KAAK0B,IAAI,CAACzB,QAAQD,GAAG;YAClDuB,OAAOb,SAASsB,MAAM,CAAC,MAAM9B,aAAawB,IAAI,CAAC;QACnD;QAEAL,GAAG,oEAAoE;YACnE,MAAMqB,QAAQ,IAAIjD,cAAcgB;YAChCC,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM,CAAC,UAAU,EAAEG,QAAQD,GAAG,EAAE;gBAAEE,aAAa;YAAG;YAChFwC,MAAMpB,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAeI,aAAa;YAAc;YAC3EQ,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM,CAAC,UAAU,EAAEG,QAAQD,GAAG,EAAE;gBAAEE,aAAa;YAAG;YAEhFqB,OAAOb,SAASiB,IAAI,IAAIE,YAAY,CAAC;YACrCN,OAAOb,SAASsB,MAAM,CAAC,gBAAgBhC,KAAK0B,IAAI,CAACzB,QAAQD,GAAG;QAChE;QAEAqB,GAAG,2DAA2D;YAC1DX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAcC,MAAM;gBAAUC,KAAKC,QAAQD,GAAG;YAAC;YAEnFU,SAASY,QAAQ,CAAC1B,UAAU;gBACxBE,MAAM;gBACNC,MAAM;gBACNC,KAAKC,QAAQD,GAAG;gBAChBE,aAAa;YACjB;YAEAqB,OAAOb,SAASsB,MAAM,CAAC,eAAeC,QAAQ;YAC9CV,OAAOb,SAASsB,MAAM,CAAC,cAAcW,aAAa,CAAC;gBAAE5C,MAAM;gBAASC,KAAKC,QAAQD,GAAG;YAAC;YACrFuB,OAAOb,SAASiB,IAAI,IAAIE,YAAY,CAAC;QACzC;QAEAR,GAAG,0EAA0E;YACzEX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAASE,KAAKC,QAAQD,GAAG;YAAC;YAE9DuB,OAAO,IAAMb,SAAS+B,aAAa,CAAC;oBAChC7C,UAAU;wBAAEE,MAAM;wBAASE,KAAK;oBAAO;oBACvCJ,UAAU;wBAAEE,MAAM;wBAASC,MAAM;wBAASC,KAAK;oBAAO;iBACzD,GAAGwC,OAAO,CAAC;YAEZjB,OAAOb,SAASsB,MAAM,CAAC,UAAUC,QAAQ;YACzCV,OAAOb,SAASsB,MAAM,CAAC,UAAUhC,KAAK0B,IAAI,CAACzB,QAAQD,GAAG;QAC1D;IACJ;IAEAO,SAAS,UAAU;QACfc,GAAG,oCAAoC;YACnCE,OAAOb,SAASsB,MAAM,CAAC,YAAYC,QAAQ;QAC/C;QAEAZ,GAAG,uCAAuC;YACtCX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;YAAI;YACxCyB,OAAOb,SAASsB,MAAM,CAAC,MAAMlC,MAAM4B,IAAI,CAAC;QAC5C;IACJ;IAEAnB,SAAS,QAAQ;QACbc,GAAG,8DAA8D;YAC7DE,OAAOb,SAASiB,IAAI,IAAIiB,OAAO,CAAC,EAAE;QACtC;QAEAvB,GAAG,+CAA+C;YAC9C,MAAMwB,cAAcjD,UAAU;gBAAEE,MAAM;gBAAUI,aAAa;YAAS;YACtEZ,GAAGwD,SAAS,CAACtD,KAAKuD,OAAO,CAACtC,UAAU;gBAAEU,WAAW;YAAK;YACtD7B,GAAG0D,aAAa,CAACvC,SAASwC,KAAKC,SAAS,CAAC;gBAAEC,SAAS;oBAACN;iBAAY;YAAC,IAAI;YAEtE,MAAMO,iBAAiB,IAAI3D,cAAcgB;YAEzCc,OAAO6B,eAAepB,MAAM,CAAC,WAAWC,QAAQ;YAChDV,OAAO6B,eAAezB,IAAI,IAAIiB,OAAO,CAAC,EAAE;YACxCrB,OAAOjC,GAAGkC,UAAU,CAACf,QAAQgB,OAAO,CAAC,WAAW,SAASC,IAAI,CAAC;QAClE;IACJ;IAEAnB,SAAS,WAAW;QAChBc,GAAG,wCAAwC;YACvCE,OAAOb,SAAS2C,OAAO,CAACzD,UAAU;gBAAEI,KAAKC,QAAQD,GAAG;YAAC,KAAK0B,IAAI,CAAC;QACnE;QAEAL,GAAG,+CAA+C;YAC9CE,OAAOb,SAAS2C,OAAO,CAACzD,UAAU;gBAAEI,KAAK;YAAO,KAAK0B,IAAI,CAAC;QAC9D;QAEAL,GAAG,+DAA+D;YAC9DL,GAAGkB,KAAK,CAACjC,SAAS,QAAQkC,kBAAkB,CAAC;gBACzC,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,4BAA4B;oBAAEC,MAAM;gBAAQ;YAC9E;YAEAhB,OAAOb,SAAS2C,OAAO,CAACzD,cAAc8B,IAAI,CAAC;QAC/C;QAEAL,GAAG,sDAAsD;YACrDL,GAAGkB,KAAK,CAACjC,SAAS,QAAQkC,kBAAkB,CAAC;gBACzC,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,oBAAoB;oBAAEC,MAAM;gBAAQ;YACtE;YAEAhB,OAAOb,SAAS2C,OAAO,CAACzD,cAAc8B,IAAI,CAAC;QAC/C;QAEAL,GAAG,6EAA6E;YAC5EL,GAAGkB,KAAK,CAACjC,SAAS,QAAQkC,kBAAkB,CAAC;gBACzC,MAAM,IAAIG,MAAM;YACpB;YAEAf,OAAOb,SAAS2C,OAAO,CAACzD,cAAc8B,IAAI,CAAC;QAC/C;IACJ;IAEAnB,SAAS,SAAS;QACdc,GAAG,uCAAuC;YACtCX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAASE,KAAKC,QAAQD,GAAG;YAAC;YAC9DU,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAQE,KAAK;YAAO;YACxDU,SAAS4C,KAAK;YACd,MAAMC,YAAY7C,SAASiB,IAAI;YAC/BJ,OAAOgC,WAAW1B,YAAY,CAAC;YAC/BN,OAAOgC,SAAS,CAAC,EAAE,CAACzD,IAAI,EAAE4B,IAAI,CAAC;QACnC;QAEAL,GAAG,yCAAyC;YACxCX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEI,KAAKC,QAAQD,GAAG;YAAC;YAC/C,MAAMwD,SAAS9C,SAASiB,IAAI;YAC5BjB,SAAS4C,KAAK;YACd,MAAMG,QAAQ/C,SAASiB,IAAI;YAC3BJ,OAAOkC,OAAOb,OAAO,CAACY;QAC1B;QAEAnC,GAAG,4DAA4D;YAC3DX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAeI,aAAa;YAAc;YAC9Ec,GAAGkB,KAAK,CAACjC,SAAS,QAAQkC,kBAAkB,CAAC;gBACzC,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,4BAA4B;oBAAEC,MAAM;gBAAQ;YAC9E;YAEA7B,SAAS4C,KAAK;YAEd/B,OAAOb,SAASsB,MAAM,CAAC,gBAAgBW,aAAa,CAAC;gBACjD7C,MAAM;gBACNI,aAAa;YACjB;QACJ;QAEAmB,GAAG,0DAA0D;YACzDX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;YAAO;YAC3CkB,GAAGkB,KAAK,CAACjC,SAAS,QAAQkC,kBAAkB,CAAC;gBACzC,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,oBAAoB;oBAAEC,MAAM;gBAAQ;YACtE;YAEA7B,SAAS4C,KAAK;YAEd/B,OAAOb,SAASsB,MAAM,CAAC,SAASC,QAAQ;QAC5C;QAEAZ,GAAG,qCAAqC;YACpCE,OAAO,IAAMb,SAAS4C,KAAK,IAAII,GAAG,CAAClB,OAAO;QAC9C;QAEAnB,GAAG,kEAAkE;YACjE,IAAIsC,QAAQC,KAAKC,KAAK,CAAC;YACvB,MAAMC,UAAU,IAAIrE,cAAcgB,SAAS;gBACvCsD,KAAK,IAAM,IAAIH,KAAKD;gBACpBK,iBAAiB;YACrB;YACAF,QAAQxC,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAUE,KAAKC,QAAQD,GAAG;YAAC;YAC9D,MAAMiE,QAAQjD,GAAGkB,KAAK,CAAC4B,SAAS,WAAWI,eAAe,CAAC;YAC3DJ,QAAQK,UAAU;YAClBF,MAAMC,eAAe,CAAC;YACtBP,SAAS;YAETG,QAAQR,KAAK;YAEb/B,OAAO0C,OAAOG,qBAAqB,CAAC;YACpC7C,OAAOuC,QAAQ9B,MAAM,CAAC,WAAWC,QAAQ;QAC7C;IACJ;IAEA1B,SAAS,aAAa;QAClBc,GAAG,gCAAgC;YAC/BE,OAAO9B,cAAc4E,OAAO,IAAI3C,IAAI,CAACjC,cAAc4E,OAAO;QAC9D;IACJ;IAEA9D,SAAS,UAAU;QACfc,GAAG,yCAAyC;YACxCX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAYE,KAAKC,QAAQD,GAAG;YAAC;YACjEU,SAAS4D,MAAM,CAAC,YAAY;YAC5B/C,OAAOb,SAASsB,MAAM,CAAC,aAAalC,MAAM4B,IAAI,CAAC;YAC/CH,OAAOb,SAASsB,MAAM,CAAC,aAAaC,QAAQ;QAChD;QAEAZ,GAAG,mDAAmD;YAClDX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAYE,KAAKC,QAAQD,GAAG;gBAAEE,aAAa;gBAAYC,KAAK;YAAU;YAC1GO,SAAS4D,MAAM,CAAC,YAAY;YAC5B,MAAMC,QAAQ7D,SAASsB,MAAM,CAAC;YAC9BT,OAAOgD,OAAOrE,aAAawB,IAAI,CAAC;YAChCH,OAAOgD,OAAOpE,KAAKuB,IAAI,CAAC;YACxBH,OAAOgD,OAAOvE,KAAK0B,IAAI,CAACzB,QAAQD,GAAG;QACvC;QAEAqB,GAAG,+DAA+D;YAC9DE,OAAO,IAAMb,SAAS4D,MAAM,CAAC,SAAS,aAAa9B,OAAO,CAAC9C;QAC/D;QAEA2B,GAAG,8EAA8E;YAC7EX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQD,GAAG;YAAC;YAChEU,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQ2B,IAAI;YAAC;YACjEL,OAAO,IAAMb,SAAS4D,MAAM,CAAC,WAAW,YAAY9B,OAAO,CAAC7C;QAChE;QAEA0B,GAAG,gFAAgF;YAC/EX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQD,GAAG;YAAC;YAChEU,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQ2B,IAAI;YAAC;YACjEZ,GAAGkB,KAAK,CAACjC,SAAS,QAAQkC,kBAAkB,CAAC;gBACzC,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,4BAA4B;oBAAEC,MAAM;gBAAQ;YAC9E;YAEAhB,OAAO,IAAMb,SAAS4D,MAAM,CAAC,WAAW,YAAY9B,OAAO,CAAC7C;YAC5D4B,OAAOb,SAASsB,MAAM,CAAC,YAAYhC,KAAK0B,IAAI,CAACzB,QAAQ2B,IAAI;QAC7D;QAEAP,GAAG,8DAA8D;YAC7DX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQD,GAAG;YAAC;YAChEU,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAWE,KAAK;YAAO;YAC3DuB,OAAO,IAAMb,SAAS4D,MAAM,CAAC,WAAW,YAAYZ,GAAG,CAAClB,OAAO;YAC/DjB,OAAOb,SAASsB,MAAM,CAAC,YAAYhC,KAAK0B,IAAI,CAACzB,QAAQD,GAAG;QAC5D;QAEAqB,GAAG,wDAAwD;YACvDX,SAASY,QAAQ,CAAC1B,UAAU;gBAAEE,MAAM;gBAAYE,KAAKC,QAAQD,GAAG;YAAC;YACjEU,SAAS4D,MAAM,CAAC,YAAY;YAC5B/C,OAAOjC,GAAGkC,UAAU,CAAC,GAAGf,QAAQ,IAAI,CAAC,GAAGiB,IAAI,CAAC;QACjD;IACJ;AACJ"}
|
|
@@ -2,7 +2,7 @@ import Database from 'better-sqlite3';
|
|
|
2
2
|
export declare const DEFAULT_AGENT_REGISTRY_DB_PATH: string;
|
|
3
3
|
export interface DatabaseOptions {
|
|
4
4
|
dbPath?: string;
|
|
5
|
-
verbose?: boolean;
|
|
5
|
+
verbose?: boolean | ((message: string) => void);
|
|
6
6
|
readonly?: boolean;
|
|
7
7
|
}
|
|
8
8
|
export declare function resolveAgentRegistryDbPath(filePath?: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/database/connection.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAMtC,eAAO,MAAM,8BAA8B,QAA6C,CAAC;AAEzF,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/database/connection.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAMtC,eAAO,MAAM,8BAA8B,QAA6C,CAAC;AAEzF,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAGpE;AAED,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,GAAE,eAAoB;IAezC,OAAO,CAAC,SAAS;IAQjB,IAAI,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAEhC;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,CAAC,EAAE;IAIlD,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,CAAC,GAAG,SAAS;IAI/D,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,QAAQ,CAAC,SAAS;IAIhE,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAI9B,KAAK,IAAI,IAAI;CAKhB"}
|
|
@@ -18,7 +18,7 @@ export class DatabaseConnection {
|
|
|
18
18
|
});
|
|
19
19
|
this.db = new Database(this.dbPath, {
|
|
20
20
|
readonly: options.readonly ?? false,
|
|
21
|
-
verbose: options.verbose ? console.log : undefined
|
|
21
|
+
verbose: typeof options.verbose === 'function' ? options.verbose : options.verbose ? console.log : undefined
|
|
22
22
|
});
|
|
23
23
|
this.configure();
|
|
24
24
|
initializeSchema(this);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/database/connection.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { mkdirSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { homedir } from 'os';\nimport { initializeSchema } from './schema.js';\n\nexport const DEFAULT_AGENT_REGISTRY_DB_PATH = join(homedir(), '.ai-devkit', 'agents.db');\n\nexport interface DatabaseOptions {\n dbPath?: string;\n verbose?: boolean;\n readonly?: boolean;\n}\n\nexport function resolveAgentRegistryDbPath(filePath?: string): string {\n if (!filePath) return DEFAULT_AGENT_REGISTRY_DB_PATH;\n return filePath.endsWith('.json') ? filePath.replace(/\\.json$/, '.db') : filePath;\n}\n\nexport class DatabaseConnection {\n private db: Database.Database;\n private readonly dbPath: string;\n\n constructor(options: DatabaseOptions = {}) {\n this.dbPath = options.dbPath ?? DEFAULT_AGENT_REGISTRY_DB_PATH;\n mkdirSync(dirname(this.dbPath), { recursive: true });\n\n this.db = new Database(this.dbPath, {\n readonly: options.readonly ?? false,\n verbose: options.verbose ? console.log : undefined,\n });\n\n this.configure();\n initializeSchema(this);\n }\n\n private configure(): void {\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('foreign_keys = ON');\n this.db.pragma('synchronous = NORMAL');\n this.db.pragma('busy_timeout = 5000');\n this.db.pragma('mmap_size = 268435456');\n }\n\n get instance(): Database.Database {\n return this.db;\n }\n\n get path(): string {\n return this.dbPath;\n }\n\n query<T>(sql: string, params: unknown[] = []): T[] {\n return this.db.prepare(sql).all(...params) as T[];\n }\n\n queryOne<T>(sql: string, params: unknown[] = []): T | undefined {\n return this.db.prepare(sql).get(...params) as T | undefined;\n }\n\n execute(sql: string, params: unknown[] = []): Database.RunResult {\n return this.db.prepare(sql).run(...params);\n }\n\n transaction<T>(fn: () => T): T {\n return this.db.transaction(fn)();\n }\n\n close(): void {\n if (this.db.open) {\n this.db.close();\n }\n }\n}\n"],"names":["Database","mkdirSync","dirname","join","homedir","initializeSchema","DEFAULT_AGENT_REGISTRY_DB_PATH","resolveAgentRegistryDbPath","filePath","endsWith","replace","DatabaseConnection","db","dbPath","options","recursive","readonly","verbose","console","log","undefined","configure","pragma","instance","path","query","sql","params","prepare","all","queryOne","get","execute","run","transaction","fn","close","open"],"mappings":"AAAA,OAAOA,cAAc,iBAAiB;AACtC,SAASC,SAAS,QAAQ,KAAK;AAC/B,SAASC,OAAO,EAAEC,IAAI,QAAQ,OAAO;AACrC,SAASC,OAAO,QAAQ,KAAK;AAC7B,SAASC,gBAAgB,QAAQ,cAAc;AAE/C,OAAO,MAAMC,iCAAiCH,KAAKC,WAAW,cAAc,aAAa;AAQzF,OAAO,SAASG,2BAA2BC,QAAiB;IACxD,IAAI,CAACA,UAAU,OAAOF;IACtB,OAAOE,SAASC,QAAQ,CAAC,WAAWD,SAASE,OAAO,CAAC,WAAW,SAASF;AAC7E;AAEA,OAAO,MAAMG;IACDC,GAAsB;IACbC,OAAe;IAEhC,YAAYC,UAA2B,CAAC,CAAC,CAAE;QACvC,IAAI,CAACD,MAAM,GAAGC,QAAQD,MAAM,IAAIP;QAChCL,UAAUC,QAAQ,IAAI,CAACW,MAAM,GAAG;YAAEE,WAAW;QAAK;QAElD,IAAI,CAACH,EAAE,GAAG,IAAIZ,SAAS,IAAI,CAACa,MAAM,EAAE;YAChCG,UAAUF,QAAQE,QAAQ,IAAI;YAC9BC,
|
|
1
|
+
{"version":3,"sources":["../../src/database/connection.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { mkdirSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { homedir } from 'os';\nimport { initializeSchema } from './schema.js';\n\nexport const DEFAULT_AGENT_REGISTRY_DB_PATH = join(homedir(), '.ai-devkit', 'agents.db');\n\nexport interface DatabaseOptions {\n dbPath?: string;\n verbose?: boolean | ((message: string) => void);\n readonly?: boolean;\n}\n\nexport function resolveAgentRegistryDbPath(filePath?: string): string {\n if (!filePath) return DEFAULT_AGENT_REGISTRY_DB_PATH;\n return filePath.endsWith('.json') ? filePath.replace(/\\.json$/, '.db') : filePath;\n}\n\nexport class DatabaseConnection {\n private db: Database.Database;\n private readonly dbPath: string;\n\n constructor(options: DatabaseOptions = {}) {\n this.dbPath = options.dbPath ?? DEFAULT_AGENT_REGISTRY_DB_PATH;\n mkdirSync(dirname(this.dbPath), { recursive: true });\n\n this.db = new Database(this.dbPath, {\n readonly: options.readonly ?? false,\n verbose: typeof options.verbose === 'function'\n ? options.verbose\n : options.verbose ? console.log : undefined,\n });\n\n this.configure();\n initializeSchema(this);\n }\n\n private configure(): void {\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('foreign_keys = ON');\n this.db.pragma('synchronous = NORMAL');\n this.db.pragma('busy_timeout = 5000');\n this.db.pragma('mmap_size = 268435456');\n }\n\n get instance(): Database.Database {\n return this.db;\n }\n\n get path(): string {\n return this.dbPath;\n }\n\n query<T>(sql: string, params: unknown[] = []): T[] {\n return this.db.prepare(sql).all(...params) as T[];\n }\n\n queryOne<T>(sql: string, params: unknown[] = []): T | undefined {\n return this.db.prepare(sql).get(...params) as T | undefined;\n }\n\n execute(sql: string, params: unknown[] = []): Database.RunResult {\n return this.db.prepare(sql).run(...params);\n }\n\n transaction<T>(fn: () => T): T {\n return this.db.transaction(fn)();\n }\n\n close(): void {\n if (this.db.open) {\n this.db.close();\n }\n }\n}\n"],"names":["Database","mkdirSync","dirname","join","homedir","initializeSchema","DEFAULT_AGENT_REGISTRY_DB_PATH","resolveAgentRegistryDbPath","filePath","endsWith","replace","DatabaseConnection","db","dbPath","options","recursive","readonly","verbose","console","log","undefined","configure","pragma","instance","path","query","sql","params","prepare","all","queryOne","get","execute","run","transaction","fn","close","open"],"mappings":"AAAA,OAAOA,cAAc,iBAAiB;AACtC,SAASC,SAAS,QAAQ,KAAK;AAC/B,SAASC,OAAO,EAAEC,IAAI,QAAQ,OAAO;AACrC,SAASC,OAAO,QAAQ,KAAK;AAC7B,SAASC,gBAAgB,QAAQ,cAAc;AAE/C,OAAO,MAAMC,iCAAiCH,KAAKC,WAAW,cAAc,aAAa;AAQzF,OAAO,SAASG,2BAA2BC,QAAiB;IACxD,IAAI,CAACA,UAAU,OAAOF;IACtB,OAAOE,SAASC,QAAQ,CAAC,WAAWD,SAASE,OAAO,CAAC,WAAW,SAASF;AAC7E;AAEA,OAAO,MAAMG;IACDC,GAAsB;IACbC,OAAe;IAEhC,YAAYC,UAA2B,CAAC,CAAC,CAAE;QACvC,IAAI,CAACD,MAAM,GAAGC,QAAQD,MAAM,IAAIP;QAChCL,UAAUC,QAAQ,IAAI,CAACW,MAAM,GAAG;YAAEE,WAAW;QAAK;QAElD,IAAI,CAACH,EAAE,GAAG,IAAIZ,SAAS,IAAI,CAACa,MAAM,EAAE;YAChCG,UAAUF,QAAQE,QAAQ,IAAI;YAC9BC,SAAS,OAAOH,QAAQG,OAAO,KAAK,aAC9BH,QAAQG,OAAO,GACfH,QAAQG,OAAO,GAAGC,QAAQC,GAAG,GAAGC;QAC1C;QAEA,IAAI,CAACC,SAAS;QACdhB,iBAAiB,IAAI;IACzB;IAEQgB,YAAkB;QACtB,IAAI,CAACT,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;IACnB;IAEA,IAAIC,WAA8B;QAC9B,OAAO,IAAI,CAACX,EAAE;IAClB;IAEA,IAAIY,OAAe;QACf,OAAO,IAAI,CAACX,MAAM;IACtB;IAEAY,MAASC,GAAW,EAAEC,SAAoB,EAAE,EAAO;QAC/C,OAAO,IAAI,CAACf,EAAE,CAACgB,OAAO,CAACF,KAAKG,GAAG,IAAIF;IACvC;IAEAG,SAAYJ,GAAW,EAAEC,SAAoB,EAAE,EAAiB;QAC5D,OAAO,IAAI,CAACf,EAAE,CAACgB,OAAO,CAACF,KAAKK,GAAG,IAAIJ;IACvC;IAEAK,QAAQN,GAAW,EAAEC,SAAoB,EAAE,EAAsB;QAC7D,OAAO,IAAI,CAACf,EAAE,CAACgB,OAAO,CAACF,KAAKO,GAAG,IAAIN;IACvC;IAEAO,YAAeC,EAAW,EAAK;QAC3B,OAAO,IAAI,CAACvB,EAAE,CAACsB,WAAW,CAACC;IAC/B;IAEAC,QAAc;QACV,IAAI,IAAI,CAACxB,EAAE,CAACyB,IAAI,EAAE;YACd,IAAI,CAACzB,EAAE,CAACwB,KAAK;QACjB;IACJ;AACJ"}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export { captureProcessSnapshot, executableBasename, filterByProcessNames } from
|
|
|
16
16
|
export type { AgentSortKey } from './utils/sortAgents.js';
|
|
17
17
|
export type { ListAgentsOptions } from './AgentManager.js';
|
|
18
18
|
export { AgentRegistry, RenameNotFoundError, RenameConflictError } from './utils/AgentRegistry.js';
|
|
19
|
-
export type { RegistryEntry } from './utils/AgentRegistry.js';
|
|
19
|
+
export type { AgentRegistryOptions, RegistryEntry } from './utils/AgentRegistry.js';
|
|
20
20
|
export { TmuxManager } from './terminal/TmuxManager.js';
|
|
21
21
|
export { AGENTS } from './utils/agents.js';
|
|
22
22
|
export type { AgentConfig, StartableAgentType } from './utils/agents.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,YAAY,EACR,YAAY,EACZ,SAAS,EACT,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACxF,YAAY,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACtG,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACnG,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,YAAY,EACR,YAAY,EACZ,SAAS,EACT,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACxF,YAAY,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACtG,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACnG,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEzE,YAAY,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE3G,OAAO,EACH,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,2BAA2B,EAC3B,gBAAgB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACR,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,GAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,YAAY,EACR,qBAAqB,EACrB,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EACR,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,YAAY,EACR,8BAA8B,EAC9B,qBAAqB,GACxB,MAAM,oCAAoC,CAAC"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { AgentManager } from './AgentManager.js';\n\nexport { ClaudeCodeAdapter } from './adapters/ClaudeCodeAdapter.js';\nexport { CodexAdapter } from './adapters/CodexAdapter.js';\nexport { CopilotAdapter } from './adapters/CopilotAdapter.js';\nexport { GeminiCliAdapter } from './adapters/GeminiCliAdapter.js';\nexport { GrokCliAdapter } from './adapters/GrokCliAdapter.js';\nexport { OpenCodeAdapter } from './adapters/OpenCodeAdapter.js';\nexport { PiAdapter } from './adapters/PiAdapter.js';\nexport { AgentStatus } from './adapters/AgentAdapter.js';\nexport type {\n AgentAdapter,\n AgentType,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from './adapters/AgentAdapter.js';\n\nexport { TerminalFocusManager, TerminalType } from './terminal/TerminalFocusManager.js';\nexport type { TerminalLocation } from './terminal/TerminalFocusManager.js';\nexport { TtyWriter } from './terminal/TtyWriter.js';\n\nexport { getProcessTty } from './utils/process.js';\nexport { captureProcessSnapshot, executableBasename, filterByProcessNames } from './utils/process.js';\nexport type { AgentSortKey } from './utils/sortAgents.js';\nexport type { ListAgentsOptions } from './AgentManager.js';\n\nexport { AgentRegistry, RenameNotFoundError, RenameConflictError } from './utils/AgentRegistry.js';\nexport type { RegistryEntry } from './utils/AgentRegistry.js';\nexport { TmuxManager } from './terminal/TmuxManager.js';\nexport { AGENTS } from './utils/agents.js';\nexport type { AgentConfig, StartableAgentType } from './utils/agents.js';\n\nexport type { AgentRequest } from './utils/agent-requests.js';\nexport { getAgentRequestPath, readLatestAgentRequest, writeAgentRequest } from './utils/agent-requests.js';\n\nexport {\n PrintAgentError,\n PrintAgentBusyError,\n PrintAgentNotFoundError,\n PrintAgentStoreError,\n PrintAgentNameConflictError,\n ClaudePrintError,\n} from './print/PrintAgent.js';\nexport type {\n PrintAgent,\n PrintAgentState,\n PrintSessionHealth,\n PrintRunStatus,\n PrintActiveRun,\n PrintLastResult,\n ProcessIdentity,\n} from './print/PrintAgent.js';\nexport { PrintAgentStore } from './print/PrintAgentStore.js';\nexport { LocalProcessInspector } from './print/PrintAgentStore.js';\nexport type {\n CreatePrintAgentInput,\n PrintAgentStoreOptions,\n ProcessInspector,\n PrintRunCompletion,\n} from './print/PrintAgentStore.js';\nexport { ClaudeCliProbe } from './print/ClaudeCliProbe.js';\nexport type { ClaudeCliProbeOptions } from './print/ClaudeCliProbe.js';\nexport { ClaudePrintRunner } from './print/ClaudePrintRunner.js';\nexport type {\n ClaudePrintRunnerOptions,\n ClaudePrintRunRequest,\n ClaudePrintRunResult,\n} from './print/ClaudePrintRunner.js';\nexport { ClaudePrintAgentService } from './print/ClaudePrintAgentService.js';\nexport type {\n ClaudePrintAgentServiceOptions,\n ClaudePrintSendResult,\n} from './print/ClaudePrintAgentService.js';\n"],"names":["AgentManager","ClaudeCodeAdapter","CodexAdapter","CopilotAdapter","GeminiCliAdapter","GrokCliAdapter","OpenCodeAdapter","PiAdapter","AgentStatus","TerminalFocusManager","TerminalType","TtyWriter","getProcessTty","captureProcessSnapshot","executableBasename","filterByProcessNames","AgentRegistry","RenameNotFoundError","RenameConflictError","TmuxManager","AGENTS","getAgentRequestPath","readLatestAgentRequest","writeAgentRequest","PrintAgentError","PrintAgentBusyError","PrintAgentNotFoundError","PrintAgentStoreError","PrintAgentNameConflictError","ClaudePrintError","PrintAgentStore","LocalProcessInspector","ClaudeCliProbe","ClaudePrintRunner","ClaudePrintAgentService"],"mappings":"AAAA,SAASA,YAAY,QAAQ,oBAAoB;AAEjD,SAASC,iBAAiB,QAAQ,kCAAkC;AACpE,SAASC,YAAY,QAAQ,6BAA6B;AAC1D,SAASC,cAAc,QAAQ,+BAA+B;AAC9D,SAASC,gBAAgB,QAAQ,iCAAiC;AAClE,SAASC,cAAc,QAAQ,+BAA+B;AAC9D,SAASC,eAAe,QAAQ,gCAAgC;AAChE,SAASC,SAAS,QAAQ,0BAA0B;AACpD,SAASC,WAAW,QAAQ,6BAA6B;AAYzD,SAASC,oBAAoB,EAAEC,YAAY,QAAQ,qCAAqC;AAExF,SAASC,SAAS,QAAQ,0BAA0B;AAEpD,SAASC,aAAa,QAAQ,qBAAqB;AACnD,SAASC,sBAAsB,EAAEC,kBAAkB,EAAEC,oBAAoB,QAAQ,qBAAqB;AAItG,SAASC,aAAa,EAAEC,mBAAmB,EAAEC,mBAAmB,QAAQ,2BAA2B;AAEnG,SAASC,WAAW,QAAQ,4BAA4B;AACxD,SAASC,MAAM,QAAQ,oBAAoB;AAI3C,SAASC,mBAAmB,EAAEC,sBAAsB,EAAEC,iBAAiB,QAAQ,4BAA4B;AAE3G,SACIC,eAAe,EACfC,mBAAmB,EACnBC,uBAAuB,EACvBC,oBAAoB,EACpBC,2BAA2B,EAC3BC,gBAAgB,QACb,wBAAwB;AAU/B,SAASC,eAAe,QAAQ,6BAA6B;AAC7D,SAASC,qBAAqB,QAAQ,6BAA6B;AAOnE,SAASC,cAAc,QAAQ,4BAA4B;AAE3D,SAASC,iBAAiB,QAAQ,+BAA+B;AAMjE,SAASC,uBAAuB,QAAQ,qCAAqC"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { AgentManager } from './AgentManager.js';\n\nexport { ClaudeCodeAdapter } from './adapters/ClaudeCodeAdapter.js';\nexport { CodexAdapter } from './adapters/CodexAdapter.js';\nexport { CopilotAdapter } from './adapters/CopilotAdapter.js';\nexport { GeminiCliAdapter } from './adapters/GeminiCliAdapter.js';\nexport { GrokCliAdapter } from './adapters/GrokCliAdapter.js';\nexport { OpenCodeAdapter } from './adapters/OpenCodeAdapter.js';\nexport { PiAdapter } from './adapters/PiAdapter.js';\nexport { AgentStatus } from './adapters/AgentAdapter.js';\nexport type {\n AgentAdapter,\n AgentType,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from './adapters/AgentAdapter.js';\n\nexport { TerminalFocusManager, TerminalType } from './terminal/TerminalFocusManager.js';\nexport type { TerminalLocation } from './terminal/TerminalFocusManager.js';\nexport { TtyWriter } from './terminal/TtyWriter.js';\n\nexport { getProcessTty } from './utils/process.js';\nexport { captureProcessSnapshot, executableBasename, filterByProcessNames } from './utils/process.js';\nexport type { AgentSortKey } from './utils/sortAgents.js';\nexport type { ListAgentsOptions } from './AgentManager.js';\n\nexport { AgentRegistry, RenameNotFoundError, RenameConflictError } from './utils/AgentRegistry.js';\nexport type { AgentRegistryOptions, RegistryEntry } from './utils/AgentRegistry.js';\nexport { TmuxManager } from './terminal/TmuxManager.js';\nexport { AGENTS } from './utils/agents.js';\nexport type { AgentConfig, StartableAgentType } from './utils/agents.js';\n\nexport type { AgentRequest } from './utils/agent-requests.js';\nexport { getAgentRequestPath, readLatestAgentRequest, writeAgentRequest } from './utils/agent-requests.js';\n\nexport {\n PrintAgentError,\n PrintAgentBusyError,\n PrintAgentNotFoundError,\n PrintAgentStoreError,\n PrintAgentNameConflictError,\n ClaudePrintError,\n} from './print/PrintAgent.js';\nexport type {\n PrintAgent,\n PrintAgentState,\n PrintSessionHealth,\n PrintRunStatus,\n PrintActiveRun,\n PrintLastResult,\n ProcessIdentity,\n} from './print/PrintAgent.js';\nexport { PrintAgentStore } from './print/PrintAgentStore.js';\nexport { LocalProcessInspector } from './print/PrintAgentStore.js';\nexport type {\n CreatePrintAgentInput,\n PrintAgentStoreOptions,\n ProcessInspector,\n PrintRunCompletion,\n} from './print/PrintAgentStore.js';\nexport { ClaudeCliProbe } from './print/ClaudeCliProbe.js';\nexport type { ClaudeCliProbeOptions } from './print/ClaudeCliProbe.js';\nexport { ClaudePrintRunner } from './print/ClaudePrintRunner.js';\nexport type {\n ClaudePrintRunnerOptions,\n ClaudePrintRunRequest,\n ClaudePrintRunResult,\n} from './print/ClaudePrintRunner.js';\nexport { ClaudePrintAgentService } from './print/ClaudePrintAgentService.js';\nexport type {\n ClaudePrintAgentServiceOptions,\n ClaudePrintSendResult,\n} from './print/ClaudePrintAgentService.js';\n"],"names":["AgentManager","ClaudeCodeAdapter","CodexAdapter","CopilotAdapter","GeminiCliAdapter","GrokCliAdapter","OpenCodeAdapter","PiAdapter","AgentStatus","TerminalFocusManager","TerminalType","TtyWriter","getProcessTty","captureProcessSnapshot","executableBasename","filterByProcessNames","AgentRegistry","RenameNotFoundError","RenameConflictError","TmuxManager","AGENTS","getAgentRequestPath","readLatestAgentRequest","writeAgentRequest","PrintAgentError","PrintAgentBusyError","PrintAgentNotFoundError","PrintAgentStoreError","PrintAgentNameConflictError","ClaudePrintError","PrintAgentStore","LocalProcessInspector","ClaudeCliProbe","ClaudePrintRunner","ClaudePrintAgentService"],"mappings":"AAAA,SAASA,YAAY,QAAQ,oBAAoB;AAEjD,SAASC,iBAAiB,QAAQ,kCAAkC;AACpE,SAASC,YAAY,QAAQ,6BAA6B;AAC1D,SAASC,cAAc,QAAQ,+BAA+B;AAC9D,SAASC,gBAAgB,QAAQ,iCAAiC;AAClE,SAASC,cAAc,QAAQ,+BAA+B;AAC9D,SAASC,eAAe,QAAQ,gCAAgC;AAChE,SAASC,SAAS,QAAQ,0BAA0B;AACpD,SAASC,WAAW,QAAQ,6BAA6B;AAYzD,SAASC,oBAAoB,EAAEC,YAAY,QAAQ,qCAAqC;AAExF,SAASC,SAAS,QAAQ,0BAA0B;AAEpD,SAASC,aAAa,QAAQ,qBAAqB;AACnD,SAASC,sBAAsB,EAAEC,kBAAkB,EAAEC,oBAAoB,QAAQ,qBAAqB;AAItG,SAASC,aAAa,EAAEC,mBAAmB,EAAEC,mBAAmB,QAAQ,2BAA2B;AAEnG,SAASC,WAAW,QAAQ,4BAA4B;AACxD,SAASC,MAAM,QAAQ,oBAAoB;AAI3C,SAASC,mBAAmB,EAAEC,sBAAsB,EAAEC,iBAAiB,QAAQ,4BAA4B;AAE3G,SACIC,eAAe,EACfC,mBAAmB,EACnBC,uBAAuB,EACvBC,oBAAoB,EACpBC,2BAA2B,EAC3BC,gBAAgB,QACb,wBAAwB;AAU/B,SAASC,eAAe,QAAQ,6BAA6B;AAC7D,SAASC,qBAAqB,QAAQ,6BAA6B;AAOnE,SAASC,cAAc,QAAQ,4BAA4B;AAE3D,SAASC,iBAAiB,QAAQ,+BAA+B;AAMjE,SAASC,uBAAuB,QAAQ,qCAAqC"}
|
|
@@ -17,19 +17,32 @@ export interface RegistryEntry {
|
|
|
17
17
|
sessionId: string;
|
|
18
18
|
sessionFilePath: string;
|
|
19
19
|
}
|
|
20
|
+
export interface AgentRegistryOptions {
|
|
21
|
+
now?: () => Date;
|
|
22
|
+
pruneIntervalMs?: number;
|
|
23
|
+
onDatabaseOperation?: (sql: string) => void;
|
|
24
|
+
}
|
|
20
25
|
export declare class AgentRegistry {
|
|
21
26
|
private db;
|
|
22
|
-
|
|
27
|
+
private readonly now;
|
|
28
|
+
private readonly pruneIntervalMs;
|
|
29
|
+
private lastPrunedAt;
|
|
30
|
+
constructor(filePath?: string, options?: AgentRegistryOptions);
|
|
23
31
|
static default(): AgentRegistry;
|
|
24
32
|
private rowToEntry;
|
|
25
33
|
private mergeEntry;
|
|
26
34
|
private findByIdentity;
|
|
27
35
|
private findByName;
|
|
36
|
+
private findPidConflicts;
|
|
37
|
+
private entriesEqual;
|
|
28
38
|
private deleteNameConflict;
|
|
29
39
|
private insertOrUpdate;
|
|
40
|
+
private needsWrite;
|
|
30
41
|
private save;
|
|
31
42
|
isAlive(entry: RegistryEntry): boolean;
|
|
43
|
+
private pruneAt;
|
|
32
44
|
prune(): void;
|
|
45
|
+
pruneIfDue(): void;
|
|
33
46
|
register(entry: RegistryEntry): void;
|
|
34
47
|
registerBatch(entries: RegistryEntry[]): void;
|
|
35
48
|
rename(currentName: string, newName: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentRegistry.d.ts","sourceRoot":"","sources":["../../src/utils/AgentRegistry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAM7D,qBAAa,mBAAoB,SAAQ,KAAK;IACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"AgentRegistry.d.ts","sourceRoot":"","sources":["../../src/utils/AgentRegistry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAM7D,qBAAa,mBAAoB,SAAQ,KAAK;IACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CAC3B;AAmBD,MAAM,WAAW,oBAAoB;IACjC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,EAAE,CAAqB;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,YAAY,CAAqB;gBAE7B,QAAQ,GAAE,MAA8B,EAAE,OAAO,GAAE,oBAAyB;IASxF,MAAM,CAAC,OAAO,IAAI,aAAa;IAO/B,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,IAAI;IAeZ,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO;IAYtC,OAAO,CAAC,OAAO;IAaf,KAAK,IAAI,IAAI;IAIb,UAAU,IAAI,IAAI;IAOlB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAIpC,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI;IAU7C,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAqBlD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAI1C,IAAI,IAAI,aAAa,EAAE;CAI1B"}
|
|
@@ -16,12 +16,19 @@ export class RenameConflictError extends Error {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
const DEFAULT_REGISTRY_PATH = path.join(os.homedir(), '.ai-devkit', 'agents.json');
|
|
19
|
+
const DEFAULT_PRUNE_INTERVAL_MS = 30_000;
|
|
19
20
|
let defaultInstance = null;
|
|
20
21
|
export class AgentRegistry {
|
|
21
22
|
db;
|
|
22
|
-
|
|
23
|
+
now;
|
|
24
|
+
pruneIntervalMs;
|
|
25
|
+
lastPrunedAt;
|
|
26
|
+
constructor(filePath = DEFAULT_REGISTRY_PATH, options = {}){
|
|
27
|
+
this.now = options.now ?? (()=>new Date());
|
|
28
|
+
this.pruneIntervalMs = options.pruneIntervalMs ?? DEFAULT_PRUNE_INTERVAL_MS;
|
|
23
29
|
this.db = new DatabaseConnection({
|
|
24
|
-
dbPath: resolveAgentRegistryDbPath(filePath)
|
|
30
|
+
dbPath: resolveAgentRegistryDbPath(filePath),
|
|
31
|
+
verbose: options.onDatabaseOperation
|
|
25
32
|
});
|
|
26
33
|
}
|
|
27
34
|
static default() {
|
|
@@ -68,6 +75,15 @@ export class AgentRegistry {
|
|
|
68
75
|
]);
|
|
69
76
|
return row ? this.rowToEntry(row) : undefined;
|
|
70
77
|
}
|
|
78
|
+
findPidConflicts(type, pid) {
|
|
79
|
+
return this.db.query('SELECT * FROM agents WHERE pid = ? AND type <> ?', [
|
|
80
|
+
pid,
|
|
81
|
+
type
|
|
82
|
+
]).map((row)=>this.rowToEntry(row));
|
|
83
|
+
}
|
|
84
|
+
entriesEqual(left, right) {
|
|
85
|
+
return left.name === right.name && left.type === right.type && left.pid === right.pid && left.tmuxSession === right.tmuxSession && left.cwd === right.cwd && left.startedAt === right.startedAt && left.sessionId === right.sessionId && left.sessionFilePath === right.sessionFilePath;
|
|
86
|
+
}
|
|
71
87
|
deleteNameConflict(name, type, pid) {
|
|
72
88
|
const conflict = this.findByName(name);
|
|
73
89
|
if (!conflict) return;
|
|
@@ -97,32 +113,61 @@ export class AgentRegistry {
|
|
|
97
113
|
updated_at = excluded.updated_at
|
|
98
114
|
`).run({
|
|
99
115
|
...entry,
|
|
100
|
-
updatedAt:
|
|
116
|
+
updatedAt: this.now().toISOString()
|
|
101
117
|
});
|
|
102
118
|
}
|
|
103
|
-
|
|
104
|
-
this.
|
|
105
|
-
this.
|
|
119
|
+
needsWrite(incoming) {
|
|
120
|
+
const existing = this.findByIdentity(incoming.type, incoming.pid);
|
|
121
|
+
const merged = this.mergeEntry(incoming, existing);
|
|
122
|
+
return !existing || !this.entriesEqual(merged, existing) || this.findPidConflicts(incoming.type, incoming.pid).length > 0;
|
|
123
|
+
}
|
|
124
|
+
save(incoming) {
|
|
125
|
+
const existing = this.findByIdentity(incoming.type, incoming.pid);
|
|
126
|
+
const merged = this.mergeEntry(incoming, existing);
|
|
127
|
+
const pidConflicts = this.findPidConflicts(incoming.type, incoming.pid);
|
|
128
|
+
if (existing && this.entriesEqual(merged, existing) && pidConflicts.length === 0) return;
|
|
129
|
+
for (const conflict of pidConflicts){
|
|
130
|
+
this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [
|
|
131
|
+
conflict.type,
|
|
132
|
+
conflict.pid
|
|
133
|
+
]);
|
|
134
|
+
}
|
|
135
|
+
if (existing && this.entriesEqual(merged, existing)) return;
|
|
136
|
+
this.deleteNameConflict(merged.name, merged.type, merged.pid);
|
|
137
|
+
this.insertOrUpdate(merged);
|
|
106
138
|
}
|
|
107
139
|
isAlive(entry) {
|
|
108
140
|
try {
|
|
109
141
|
process.kill(entry.pid, 0);
|
|
110
142
|
return true;
|
|
111
|
-
} catch
|
|
112
|
-
|
|
143
|
+
} catch (error) {
|
|
144
|
+
const code = error && typeof error === 'object' && 'code' in error ? error.code : undefined;
|
|
145
|
+
return code !== 'ESRCH';
|
|
113
146
|
}
|
|
114
147
|
}
|
|
115
|
-
|
|
148
|
+
pruneAt(nowMs) {
|
|
116
149
|
const entries = this.list();
|
|
117
150
|
const stale = entries.filter((e)=>!this.isAlive(e));
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
151
|
+
if (stale.length > 0) {
|
|
152
|
+
this.db.transaction(()=>{
|
|
153
|
+
for (const entry of stale){
|
|
154
|
+
this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [
|
|
155
|
+
entry.type,
|
|
156
|
+
entry.pid
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
this.lastPrunedAt = nowMs;
|
|
162
|
+
}
|
|
163
|
+
prune() {
|
|
164
|
+
this.pruneAt(this.now().getTime());
|
|
165
|
+
}
|
|
166
|
+
pruneIfDue() {
|
|
167
|
+
const nowMs = this.now().getTime();
|
|
168
|
+
const elapsed = this.lastPrunedAt === undefined ? undefined : nowMs - this.lastPrunedAt;
|
|
169
|
+
if (elapsed !== undefined && elapsed >= 0 && elapsed < this.pruneIntervalMs) return;
|
|
170
|
+
this.pruneAt(nowMs);
|
|
126
171
|
}
|
|
127
172
|
register(entry) {
|
|
128
173
|
this.registerBatch([
|
|
@@ -131,10 +176,10 @@ export class AgentRegistry {
|
|
|
131
176
|
}
|
|
132
177
|
registerBatch(entries) {
|
|
133
178
|
if (entries.length === 0) return;
|
|
179
|
+
if (!entries.some((entry)=>this.needsWrite(entry))) return;
|
|
134
180
|
this.db.transaction(()=>{
|
|
135
181
|
for (const incoming of entries){
|
|
136
|
-
|
|
137
|
-
this.save(this.mergeEntry(incoming, existing));
|
|
182
|
+
this.save(incoming);
|
|
138
183
|
}
|
|
139
184
|
});
|
|
140
185
|
}
|
|
@@ -156,7 +201,7 @@ export class AgentRegistry {
|
|
|
156
201
|
}
|
|
157
202
|
this.db.execute('UPDATE agents SET name = ?, updated_at = ? WHERE type = ? AND pid = ?', [
|
|
158
203
|
newName,
|
|
159
|
-
|
|
204
|
+
this.now().toISOString(),
|
|
160
205
|
existing.type,
|
|
161
206
|
existing.pid
|
|
162
207
|
]);
|