@ai-devkit/agent-manager 0.13.0 → 0.15.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/.eslintrc.json +1 -1
- package/README.md +32 -0
- package/dist/AgentManager.d.ts +10 -12
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +6 -34
- package/dist/AgentManager.js.map +1 -1
- package/dist/adapters/AgentAdapter.js +2 -5
- package/dist/adapters/AgentAdapter.js.map +1 -1
- package/dist/adapters/ClaudeCodeAdapter.d.ts +1 -1
- package/dist/adapters/ClaudeCodeAdapter.d.ts.map +1 -1
- package/dist/adapters/ClaudeCodeAdapter.js +29 -63
- package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
- package/dist/adapters/CodexAdapter.d.ts +1 -1
- package/dist/adapters/CodexAdapter.d.ts.map +1 -1
- package/dist/adapters/CodexAdapter.js +32 -68
- package/dist/adapters/CodexAdapter.js.map +1 -1
- package/dist/adapters/GeminiCliAdapter.d.ts +1 -1
- package/dist/adapters/GeminiCliAdapter.d.ts.map +1 -1
- package/dist/adapters/GeminiCliAdapter.js +29 -65
- package/dist/adapters/GeminiCliAdapter.js.map +1 -1
- package/dist/adapters/OpenCodeAdapter.d.ts +1 -1
- package/dist/adapters/OpenCodeAdapter.d.ts.map +1 -1
- package/dist/adapters/OpenCodeAdapter.js +20 -59
- package/dist/adapters/OpenCodeAdapter.js.map +1 -1
- package/dist/adapters/index.d.ts +6 -6
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +5 -13
- package/dist/adapters/index.js.map +1 -1
- package/dist/index.d.ts +13 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -22
- package/dist/index.js.map +1 -1
- package/dist/terminal/TerminalFocusManager.js +13 -17
- package/dist/terminal/TerminalFocusManager.js.map +1 -1
- package/dist/terminal/TtyWriter.d.ts +1 -1
- package/dist/terminal/TtyWriter.d.ts.map +1 -1
- package/dist/terminal/TtyWriter.js +11 -15
- package/dist/terminal/TtyWriter.js.map +1 -1
- package/dist/terminal/index.d.ts +4 -4
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +3 -9
- package/dist/terminal/index.js.map +1 -1
- package/dist/utils/ClaudeSessionParser.d.ts +2 -2
- package/dist/utils/ClaudeSessionParser.d.ts.map +1 -1
- package/dist/utils/ClaudeSessionParser.js +11 -48
- package/dist/utils/ClaudeSessionParser.js.map +1 -1
- package/dist/utils/applescript.js +1 -4
- package/dist/utils/applescript.js.map +1 -1
- package/dist/utils/index.d.ts +6 -6
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -15
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/matching.d.ts +2 -2
- package/dist/utils/matching.d.ts.map +1 -1
- package/dist/utils/matching.js +3 -40
- package/dist/utils/matching.js.map +1 -1
- package/dist/utils/process.d.ts +1 -1
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +12 -52
- package/dist/utils/process.js.map +1 -1
- package/dist/utils/session.js +8 -49
- package/dist/utils/session.js.map +1 -1
- package/dist/utils/sortAgents.d.ts +4 -0
- package/dist/utils/sortAgents.d.ts.map +1 -0
- package/dist/utils/sortAgents.js +48 -0
- package/dist/utils/sortAgents.js.map +1 -0
- package/package.json +9 -8
- package/src/AgentManager.ts +13 -28
- package/src/__tests__/AgentManager.test.ts +5 -5
- package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +44 -42
- package/src/__tests__/adapters/CodexAdapter.test.ts +32 -30
- package/src/__tests__/adapters/GeminiCliAdapter.test.ts +19 -18
- package/src/__tests__/adapters/OpenCodeAdapter.test.ts +23 -21
- package/src/__tests__/terminal/TtyWriter.test.ts +10 -9
- package/src/__tests__/utils/ClaudeSessionParser.test.ts +2 -2
- package/src/__tests__/utils/matching.test.ts +4 -4
- package/src/__tests__/utils/process.test.ts +6 -5
- package/src/__tests__/utils/session.test.ts +8 -7
- package/src/adapters/ClaudeCodeAdapter.ts +8 -8
- package/src/adapters/CodexAdapter.ts +6 -6
- package/src/adapters/GeminiCliAdapter.ts +6 -6
- package/src/adapters/OpenCodeAdapter.ts +4 -4
- package/src/adapters/index.ts +6 -6
- package/src/index.ts +13 -11
- package/src/terminal/TerminalFocusManager.ts +2 -2
- package/src/terminal/TtyWriter.ts +3 -3
- package/src/terminal/index.ts +4 -4
- package/src/utils/ClaudeSessionParser.ts +2 -2
- package/src/utils/index.ts +6 -6
- package/src/utils/matching.ts +2 -2
- package/src/utils/process.ts +1 -1
- package/src/utils/sortAgents.ts +52 -0
- package/tsconfig.json +2 -2
- package/vitest.config.ts +20 -0
- package/jest.config.js +0 -21
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AgentStatus } from '../adapters/AgentAdapter.js';
|
|
2
|
+
const STATUS_PRIORITY = {
|
|
3
|
+
[AgentStatus.WAITING]: 0,
|
|
4
|
+
[AgentStatus.RUNNING]: 1,
|
|
5
|
+
[AgentStatus.IDLE]: 2,
|
|
6
|
+
[AgentStatus.UNKNOWN]: 3,
|
|
7
|
+
};
|
|
8
|
+
function compareByName(a, b) {
|
|
9
|
+
return a.name.localeCompare(b.name);
|
|
10
|
+
}
|
|
11
|
+
function compareByStatus(a, b) {
|
|
12
|
+
const pa = STATUS_PRIORITY[a.status] ?? 999;
|
|
13
|
+
const pb = STATUS_PRIORITY[b.status] ?? 999;
|
|
14
|
+
if (pa !== pb)
|
|
15
|
+
return pa - pb;
|
|
16
|
+
return compareByName(a, b);
|
|
17
|
+
}
|
|
18
|
+
function compareByPid(a, b) {
|
|
19
|
+
if (a.pid !== b.pid)
|
|
20
|
+
return a.pid - b.pid;
|
|
21
|
+
return compareByName(a, b);
|
|
22
|
+
}
|
|
23
|
+
function compareByLastActive(a, b) {
|
|
24
|
+
const ta = a.lastActive instanceof Date ? a.lastActive.getTime() : new Date(a.lastActive).getTime();
|
|
25
|
+
const tb = b.lastActive instanceof Date ? b.lastActive.getTime() : new Date(b.lastActive).getTime();
|
|
26
|
+
if (tb !== ta)
|
|
27
|
+
return tb - ta; // most recent first
|
|
28
|
+
return compareByName(a, b);
|
|
29
|
+
}
|
|
30
|
+
export function sortAgents(agents, by) {
|
|
31
|
+
const copy = agents.slice();
|
|
32
|
+
switch (by) {
|
|
33
|
+
case 'name':
|
|
34
|
+
copy.sort(compareByName);
|
|
35
|
+
break;
|
|
36
|
+
case 'status':
|
|
37
|
+
copy.sort(compareByStatus);
|
|
38
|
+
break;
|
|
39
|
+
case 'pid':
|
|
40
|
+
copy.sort(compareByPid);
|
|
41
|
+
break;
|
|
42
|
+
case 'lastActive':
|
|
43
|
+
copy.sort(compareByLastActive);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
return copy;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=sortAgents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortAgents.js","sourceRoot":"","sources":["../../src/utils/sortAgents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,6BAA6B,CAAC;AAI1E,MAAM,eAAe,GAAgC;IACjD,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;IACxB,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;IACxB,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;IACrB,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;CAC3B,CAAC;AAEF,SAAS,aAAa,CAAC,CAAY,EAAE,CAAY;IAC7C,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,eAAe,CAAC,CAAY,EAAE,CAAY;IAC/C,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;IAC5C,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;IAC5C,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IAC9B,OAAO,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,YAAY,CAAC,CAAY,EAAE,CAAY;IAC5C,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG;QAAE,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;IAC1C,OAAO,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,mBAAmB,CAAC,CAAY,EAAE,CAAY;IACnD,MAAM,EAAE,GAAG,CAAC,CAAC,UAAU,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;IACpG,MAAM,EAAE,GAAG,CAAC,CAAC,UAAU,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;IACpG,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,oBAAoB;IACnD,OAAO,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAmB,EAAE,EAAgB;IAC5D,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;IAC5B,QAAQ,EAAE,EAAE,CAAC;QACT,KAAK,MAAM;YACP,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACzB,MAAM;QACV,KAAK,QAAQ;YACT,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YAC3B,MAAM;QACV,KAAK,KAAK;YACN,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACxB,MAAM;QACV,KAAK,YAAY;YACb,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAC/B,MAAM;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-devkit/agent-manager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "Standalone agent detection and management utilities for AI DevKit",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
9
10
|
"types": "./dist/index.d.ts",
|
|
10
|
-
"
|
|
11
|
+
"import": "./dist/index.js"
|
|
11
12
|
}
|
|
12
13
|
},
|
|
13
14
|
"scripts": {
|
|
14
15
|
"build": "tsc",
|
|
15
|
-
"test": "
|
|
16
|
-
"test:
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"test:coverage": "vitest run --coverage",
|
|
17
19
|
"lint": "eslint src --ext .ts",
|
|
18
20
|
"typecheck": "tsc --noEmit",
|
|
19
21
|
"clean": "rm -rf dist"
|
|
@@ -31,14 +33,13 @@
|
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
33
35
|
"@types/better-sqlite3": "^7.6.11",
|
|
34
|
-
"@types/jest": "^30.0.0",
|
|
35
36
|
"@types/node": "^20.11.5",
|
|
36
37
|
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
|
37
38
|
"@typescript-eslint/parser": "^6.19.1",
|
|
39
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
38
40
|
"eslint": "^8.56.0",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"typescript": "^5.3.3"
|
|
41
|
+
"typescript": "^5.5.0",
|
|
42
|
+
"vitest": "^2.1.0"
|
|
42
43
|
},
|
|
43
44
|
"engines": {
|
|
44
45
|
"node": ">=20.20.0"
|
package/src/AgentManager.ts
CHANGED
|
@@ -10,8 +10,16 @@ import type {
|
|
|
10
10
|
AgentInfo,
|
|
11
11
|
SessionSummary,
|
|
12
12
|
ListSessionsOptions,
|
|
13
|
-
} from './adapters/AgentAdapter';
|
|
14
|
-
import {
|
|
13
|
+
} from './adapters/AgentAdapter.js';
|
|
14
|
+
import { sortAgents, type AgentSortKey } from './utils/sortAgents.js';
|
|
15
|
+
|
|
16
|
+
export interface ListAgentsOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Sort key for the returned list. Defaults to `status`, which orders
|
|
19
|
+
* waiting → running → idle → unknown, then by name for stability.
|
|
20
|
+
*/
|
|
21
|
+
sortBy?: AgentSortKey;
|
|
22
|
+
}
|
|
15
23
|
|
|
16
24
|
/**
|
|
17
25
|
* Agent Manager Class
|
|
@@ -108,7 +116,7 @@ export class AgentManager {
|
|
|
108
116
|
* });
|
|
109
117
|
* ```
|
|
110
118
|
*/
|
|
111
|
-
async listAgents(): Promise<AgentInfo[]> {
|
|
119
|
+
async listAgents(options?: ListAgentsOptions): Promise<AgentInfo[]> {
|
|
112
120
|
const allAgents: AgentInfo[] = [];
|
|
113
121
|
const errors: Array<{ type: string; error: Error }> = [];
|
|
114
122
|
|
|
@@ -142,8 +150,8 @@ export class AgentManager {
|
|
|
142
150
|
});
|
|
143
151
|
}
|
|
144
152
|
|
|
145
|
-
|
|
146
|
-
return
|
|
153
|
+
const sortKey: AgentSortKey = options?.sortBy ?? 'status';
|
|
154
|
+
return sortAgents(allAgents, sortKey);
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
/**
|
|
@@ -193,29 +201,6 @@ export class AgentManager {
|
|
|
193
201
|
return merged;
|
|
194
202
|
}
|
|
195
203
|
|
|
196
|
-
/**
|
|
197
|
-
* Sort agents by status priority
|
|
198
|
-
*
|
|
199
|
-
* Priority order: waiting > running > idle > unknown
|
|
200
|
-
* This ensures agents that need attention appear first.
|
|
201
|
-
*
|
|
202
|
-
* @param agents Array of agents to sort
|
|
203
|
-
* @returns Sorted array of agents
|
|
204
|
-
*/
|
|
205
|
-
private sortAgentsByStatus(agents: AgentInfo[]): AgentInfo[] {
|
|
206
|
-
const statusPriority: Record<AgentStatus, number> = {
|
|
207
|
-
[AgentStatus.WAITING]: 0,
|
|
208
|
-
[AgentStatus.RUNNING]: 1,
|
|
209
|
-
[AgentStatus.IDLE]: 2,
|
|
210
|
-
[AgentStatus.UNKNOWN]: 3,
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
return agents.sort((a, b) => {
|
|
214
|
-
const priorityA = statusPriority[a.status] ?? 999;
|
|
215
|
-
const priorityB = statusPriority[b.status] ?? 999;
|
|
216
|
-
return priorityA - priorityB;
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
204
|
|
|
220
205
|
/**
|
|
221
206
|
* Get count of registered adapters
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
* Tests for AgentManager
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
import { AgentManager } from '../AgentManager';
|
|
5
|
+
|
|
6
|
+
import { AgentManager } from '../AgentManager.js';
|
|
7
7
|
import type {
|
|
8
8
|
AgentAdapter,
|
|
9
9
|
AgentInfo,
|
|
10
10
|
AgentType,
|
|
11
11
|
ConversationMessage,
|
|
12
12
|
SessionSummary,
|
|
13
|
-
} from '../adapters/AgentAdapter';
|
|
14
|
-
import { AgentStatus } from '../adapters/AgentAdapter';
|
|
13
|
+
} from '../adapters/AgentAdapter.js';
|
|
14
|
+
import { AgentStatus } from '../adapters/AgentAdapter.js';
|
|
15
15
|
|
|
16
16
|
// Mock adapter for testing
|
|
17
17
|
class MockAdapter implements AgentAdapter {
|
|
@@ -333,7 +333,7 @@ describe('AgentManager', () => {
|
|
|
333
333
|
new MockAdapter('codex', [], false, [], true /* failListSessions */),
|
|
334
334
|
);
|
|
335
335
|
|
|
336
|
-
const consoleErrorSpy =
|
|
336
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
337
337
|
|
|
338
338
|
try {
|
|
339
339
|
const result = await manager.listSessions();
|
|
@@ -2,40 +2,42 @@
|
|
|
2
2
|
* Tests for ClaudeCodeAdapter
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { MockedFunction } from 'vitest';
|
|
5
6
|
import * as fs from 'fs';
|
|
6
7
|
import * as path from 'path';
|
|
7
|
-
|
|
8
|
-
import { ClaudeCodeAdapter } from '../../adapters/ClaudeCodeAdapter';
|
|
9
|
-
import type { ProcessInfo } from '../../adapters/AgentAdapter';
|
|
10
|
-
import { AgentStatus } from '../../adapters/AgentAdapter';
|
|
11
|
-
import { listAgentProcesses, enrichProcesses } from '../../utils/process';
|
|
12
|
-
import { batchGetSessionFileBirthtimes } from '../../utils/session';
|
|
13
|
-
import type { SessionFile } from '../../utils/session';
|
|
14
|
-
import { matchProcessesToSessions, generateAgentName } from '../../utils/matching';
|
|
15
|
-
import type { MatchResult } from '../../utils/matching';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
|
|
9
|
+
import { ClaudeCodeAdapter } from '../../adapters/ClaudeCodeAdapter.js';
|
|
10
|
+
import type { ProcessInfo } from '../../adapters/AgentAdapter.js';
|
|
11
|
+
import { AgentStatus } from '../../adapters/AgentAdapter.js';
|
|
12
|
+
import { listAgentProcesses, enrichProcesses } from '../../utils/process.js';
|
|
13
|
+
import { batchGetSessionFileBirthtimes } from '../../utils/session.js';
|
|
14
|
+
import type { SessionFile } from '../../utils/session.js';
|
|
15
|
+
import { matchProcessesToSessions, generateAgentName } from '../../utils/matching.js';
|
|
16
|
+
import type { MatchResult } from '../../utils/matching.js';
|
|
17
|
+
import * as os from 'os';
|
|
18
|
+
vi.mock('../../utils/process.js', () => ({
|
|
19
|
+
listAgentProcesses: vi.fn(),
|
|
20
|
+
enrichProcesses: vi.fn(),
|
|
19
21
|
}));
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
const actual =
|
|
23
|
+
vi.mock('../../utils/session.js', async () => {
|
|
24
|
+
const actual = await vi.importActual('../../utils/session') as typeof import('../../utils/session');
|
|
23
25
|
return {
|
|
24
26
|
...actual,
|
|
25
|
-
batchGetSessionFileBirthtimes:
|
|
27
|
+
batchGetSessionFileBirthtimes: vi.fn(),
|
|
26
28
|
};
|
|
27
29
|
});
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
matchProcessesToSessions:
|
|
31
|
-
generateAgentName:
|
|
31
|
+
vi.mock('../../utils/matching.js', () => ({
|
|
32
|
+
matchProcessesToSessions: vi.fn(),
|
|
33
|
+
generateAgentName: vi.fn(),
|
|
32
34
|
}));
|
|
33
35
|
|
|
34
|
-
const mockedListAgentProcesses = listAgentProcesses as
|
|
35
|
-
const mockedEnrichProcesses = enrichProcesses as
|
|
36
|
-
const mockedBatchGetSessionFileBirthtimes = batchGetSessionFileBirthtimes as
|
|
37
|
-
const mockedMatchProcessesToSessions = matchProcessesToSessions as
|
|
38
|
-
const mockedGenerateAgentName = generateAgentName as
|
|
36
|
+
const mockedListAgentProcesses = listAgentProcesses as MockedFunction<typeof listAgentProcesses>;
|
|
37
|
+
const mockedEnrichProcesses = enrichProcesses as MockedFunction<typeof enrichProcesses>;
|
|
38
|
+
const mockedBatchGetSessionFileBirthtimes = batchGetSessionFileBirthtimes as MockedFunction<typeof batchGetSessionFileBirthtimes>;
|
|
39
|
+
const mockedMatchProcessesToSessions = matchProcessesToSessions as MockedFunction<typeof matchProcessesToSessions>;
|
|
40
|
+
const mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;
|
|
39
41
|
describe('ClaudeCodeAdapter', () => {
|
|
40
42
|
let adapter: ClaudeCodeAdapter;
|
|
41
43
|
|
|
@@ -163,7 +165,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
163
165
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
164
166
|
|
|
165
167
|
// Set up projects dir with encoded directory name
|
|
166
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
168
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-test-'));
|
|
167
169
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
168
170
|
// Claude encodes /Users/test/my-project → -Users-test-my-project
|
|
169
171
|
const projDir = path.join(projectsDir, '-Users-test-my-project');
|
|
@@ -222,7 +224,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
222
224
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
223
225
|
|
|
224
226
|
// Set up projects dir with encoded directory names
|
|
225
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
227
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-test-'));
|
|
226
228
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
227
229
|
// /project-a → -project-a, /project-b → -project-b
|
|
228
230
|
const projDirA = path.join(projectsDir, '-project-a');
|
|
@@ -303,7 +305,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
303
305
|
mockedListAgentProcesses.mockReturnValue(processes);
|
|
304
306
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
305
307
|
|
|
306
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
308
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-resume-'));
|
|
307
309
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
308
310
|
const projDir = path.join(projectsDir, '-project-resumed');
|
|
309
311
|
fs.mkdirSync(projDir, { recursive: true });
|
|
@@ -367,7 +369,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
367
369
|
mockedListAgentProcesses.mockReturnValue(processes);
|
|
368
370
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
369
371
|
|
|
370
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
372
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-pid-test-'));
|
|
371
373
|
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
372
374
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
373
375
|
const projDir = path.join(projectsDir, '-project-direct');
|
|
@@ -416,7 +418,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
416
418
|
mockedListAgentProcesses.mockReturnValue(processes);
|
|
417
419
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
418
420
|
|
|
419
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
421
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-pid-wait-'));
|
|
420
422
|
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
421
423
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
422
424
|
const projDir = path.join(projectsDir, '-project-wait');
|
|
@@ -463,7 +465,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
463
465
|
mockedListAgentProcesses.mockReturnValue(processes);
|
|
464
466
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
465
467
|
|
|
466
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
468
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-pid-idle-'));
|
|
467
469
|
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
468
470
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
469
471
|
const projDir = path.join(projectsDir, '-project-idle');
|
|
@@ -508,7 +510,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
508
510
|
mockedListAgentProcesses.mockReturnValue(processes);
|
|
509
511
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
510
512
|
|
|
511
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
513
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-pid-nostat-'));
|
|
512
514
|
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
513
515
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
514
516
|
const projDir = path.join(projectsDir, '-project-legacy');
|
|
@@ -560,7 +562,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
560
562
|
mockedListAgentProcesses.mockReturnValue(processes);
|
|
561
563
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
562
564
|
|
|
563
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
565
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-resume-wait-'));
|
|
564
566
|
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
565
567
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
566
568
|
const projDir = path.join(projectsDir, '-project-resume-wait');
|
|
@@ -604,7 +606,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
604
606
|
mockedListAgentProcesses.mockReturnValue(processes);
|
|
605
607
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
606
608
|
|
|
607
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
609
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-gone-'));
|
|
608
610
|
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
609
611
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
610
612
|
const projDir = path.join(projectsDir, '-project-gone');
|
|
@@ -623,7 +625,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
623
625
|
(adapter as any).projectsDir = projectsDir;
|
|
624
626
|
|
|
625
627
|
// Simulate JSONL disappearing between existence check and read
|
|
626
|
-
|
|
628
|
+
vi.spyOn((adapter as any).parser, 'readSession').mockReturnValueOnce(null);
|
|
627
629
|
|
|
628
630
|
const agents = await adapter.detectAgents();
|
|
629
631
|
|
|
@@ -633,7 +635,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
633
635
|
expect(agents[0].status).toBe(AgentStatus.IDLE);
|
|
634
636
|
|
|
635
637
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
636
|
-
|
|
638
|
+
vi.restoreAllMocks();
|
|
637
639
|
});
|
|
638
640
|
|
|
639
641
|
it('should fall back to process-only when legacy-matched JSONL becomes unreadable', async () => {
|
|
@@ -644,7 +646,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
644
646
|
mockedListAgentProcesses.mockReturnValue(processes);
|
|
645
647
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
646
648
|
|
|
647
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
649
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-lgone-'));
|
|
648
650
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
649
651
|
const projDir = path.join(projectsDir, '-project-legacy-gone');
|
|
650
652
|
fs.mkdirSync(projDir, { recursive: true });
|
|
@@ -670,7 +672,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
670
672
|
]);
|
|
671
673
|
|
|
672
674
|
// Simulate JSONL disappearing between match and read
|
|
673
|
-
|
|
675
|
+
vi.spyOn((adapter as any).parser, 'readSession').mockReturnValueOnce(null);
|
|
674
676
|
|
|
675
677
|
const agents = await adapter.detectAgents();
|
|
676
678
|
|
|
@@ -679,7 +681,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
679
681
|
expect(agents[0].status).toBe(AgentStatus.IDLE);
|
|
680
682
|
|
|
681
683
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
682
|
-
|
|
684
|
+
vi.restoreAllMocks();
|
|
683
685
|
});
|
|
684
686
|
|
|
685
687
|
it('should mix direct PID-file matches and legacy matches across processes', async () => {
|
|
@@ -691,7 +693,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
691
693
|
mockedListAgentProcesses.mockReturnValue(processes);
|
|
692
694
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
693
695
|
|
|
694
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
696
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-mix-test-'));
|
|
695
697
|
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
696
698
|
const projectsDir = path.join(tmpDir, 'projects');
|
|
697
699
|
const projAlpha = path.join(projectsDir, '-project-alpha');
|
|
@@ -756,7 +758,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
756
758
|
let tmpDir: string;
|
|
757
759
|
|
|
758
760
|
beforeEach(() => {
|
|
759
|
-
tmpDir = fs.mkdtempSync(path.join(
|
|
761
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-test-'));
|
|
760
762
|
});
|
|
761
763
|
|
|
762
764
|
afterEach(() => {
|
|
@@ -1071,7 +1073,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
1071
1073
|
let tmpDir: string;
|
|
1072
1074
|
|
|
1073
1075
|
beforeEach(() => {
|
|
1074
|
-
tmpDir = fs.mkdtempSync(path.join(
|
|
1076
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-test-'));
|
|
1075
1077
|
});
|
|
1076
1078
|
|
|
1077
1079
|
afterEach(() => {
|
|
@@ -1419,7 +1421,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
1419
1421
|
let tmpDir: string;
|
|
1420
1422
|
|
|
1421
1423
|
beforeEach(() => {
|
|
1422
|
-
tmpDir = fs.mkdtempSync(path.join(
|
|
1424
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-conv-'));
|
|
1423
1425
|
});
|
|
1424
1426
|
|
|
1425
1427
|
afterEach(() => {
|
|
@@ -1594,7 +1596,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
1594
1596
|
let projectsDir: string;
|
|
1595
1597
|
|
|
1596
1598
|
beforeEach(() => {
|
|
1597
|
-
tmpDir = fs.mkdtempSync(path.join(
|
|
1599
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-list-'));
|
|
1598
1600
|
projectsDir = path.join(tmpDir, 'projects');
|
|
1599
1601
|
fs.mkdirSync(projectsDir, { recursive: true });
|
|
1600
1602
|
(adapter as any).projectsDir = projectsDir;
|
|
@@ -2,41 +2,43 @@
|
|
|
2
2
|
* Tests for CodexAdapter
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { MockedFunction } from 'vitest';
|
|
5
6
|
import * as fs from 'fs';
|
|
6
7
|
import * as path from 'path';
|
|
7
|
-
|
|
8
|
-
import { CodexAdapter } from '../../adapters/CodexAdapter';
|
|
9
|
-
import type { ProcessInfo } from '../../adapters/AgentAdapter';
|
|
10
|
-
import { AgentStatus } from '../../adapters/AgentAdapter';
|
|
11
|
-
import { listAgentProcesses, enrichProcesses } from '../../utils/process';
|
|
12
|
-
import { batchGetSessionFileBirthtimes } from '../../utils/session';
|
|
13
|
-
import type { SessionFile } from '../../utils/session';
|
|
14
|
-
import { matchProcessesToSessions, generateAgentName } from '../../utils/matching';
|
|
15
|
-
import type { MatchResult } from '../../utils/matching';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
|
|
9
|
+
import { CodexAdapter } from '../../adapters/CodexAdapter.js';
|
|
10
|
+
import type { ProcessInfo } from '../../adapters/AgentAdapter.js';
|
|
11
|
+
import { AgentStatus } from '../../adapters/AgentAdapter.js';
|
|
12
|
+
import { listAgentProcesses, enrichProcesses } from '../../utils/process.js';
|
|
13
|
+
import { batchGetSessionFileBirthtimes } from '../../utils/session.js';
|
|
14
|
+
import type { SessionFile } from '../../utils/session.js';
|
|
15
|
+
import { matchProcessesToSessions, generateAgentName } from '../../utils/matching.js';
|
|
16
|
+
import type { MatchResult } from '../../utils/matching.js';
|
|
17
|
+
import * as os from 'os';
|
|
18
|
+
|
|
19
|
+
vi.mock('../../utils/process.js', () => ({
|
|
20
|
+
listAgentProcesses: vi.fn(),
|
|
21
|
+
enrichProcesses: vi.fn(),
|
|
20
22
|
}));
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
const actual =
|
|
24
|
+
vi.mock('../../utils/session.js', async () => {
|
|
25
|
+
const actual = await vi.importActual('../../utils/session') as typeof import('../../utils/session');
|
|
24
26
|
return {
|
|
25
27
|
...actual,
|
|
26
|
-
batchGetSessionFileBirthtimes:
|
|
28
|
+
batchGetSessionFileBirthtimes: vi.fn(),
|
|
27
29
|
};
|
|
28
30
|
});
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
matchProcessesToSessions:
|
|
32
|
-
generateAgentName:
|
|
32
|
+
vi.mock('../../utils/matching.js', () => ({
|
|
33
|
+
matchProcessesToSessions: vi.fn(),
|
|
34
|
+
generateAgentName: vi.fn(),
|
|
33
35
|
}));
|
|
34
36
|
|
|
35
|
-
const mockedListAgentProcesses = listAgentProcesses as
|
|
36
|
-
const mockedEnrichProcesses = enrichProcesses as
|
|
37
|
-
const mockedBatchGetSessionFileBirthtimes = batchGetSessionFileBirthtimes as
|
|
38
|
-
const mockedMatchProcessesToSessions = matchProcessesToSessions as
|
|
39
|
-
const mockedGenerateAgentName = generateAgentName as
|
|
37
|
+
const mockedListAgentProcesses = listAgentProcesses as MockedFunction<typeof listAgentProcesses>;
|
|
38
|
+
const mockedEnrichProcesses = enrichProcesses as MockedFunction<typeof enrichProcesses>;
|
|
39
|
+
const mockedBatchGetSessionFileBirthtimes = batchGetSessionFileBirthtimes as MockedFunction<typeof batchGetSessionFileBirthtimes>;
|
|
40
|
+
const mockedMatchProcessesToSessions = matchProcessesToSessions as MockedFunction<typeof matchProcessesToSessions>;
|
|
41
|
+
const mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;
|
|
40
42
|
|
|
41
43
|
describe('CodexAdapter', () => {
|
|
42
44
|
let adapter: CodexAdapter;
|
|
@@ -136,7 +138,7 @@ describe('CodexAdapter', () => {
|
|
|
136
138
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
137
139
|
|
|
138
140
|
// Set up sessions dir with date directory
|
|
139
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
141
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-test-'));
|
|
140
142
|
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
141
143
|
const dateDir = path.join(sessionsDir, '2026', '03', '18');
|
|
142
144
|
fs.mkdirSync(dateDir, { recursive: true });
|
|
@@ -195,7 +197,7 @@ describe('CodexAdapter', () => {
|
|
|
195
197
|
mockedListAgentProcesses.mockReturnValue(processes);
|
|
196
198
|
mockedEnrichProcesses.mockReturnValue(processes);
|
|
197
199
|
|
|
198
|
-
const tmpDir = fs.mkdtempSync(path.join(
|
|
200
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-test-'));
|
|
199
201
|
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
200
202
|
const now = new Date();
|
|
201
203
|
const dateDir = path.join(
|
|
@@ -251,7 +253,7 @@ describe('CodexAdapter', () => {
|
|
|
251
253
|
let tmpDir: string;
|
|
252
254
|
|
|
253
255
|
beforeEach(() => {
|
|
254
|
-
tmpDir = fs.mkdtempSync(path.join(
|
|
256
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-test-'));
|
|
255
257
|
});
|
|
256
258
|
|
|
257
259
|
afterEach(() => {
|
|
@@ -399,7 +401,7 @@ describe('CodexAdapter', () => {
|
|
|
399
401
|
let tmpDir: string;
|
|
400
402
|
|
|
401
403
|
beforeEach(() => {
|
|
402
|
-
tmpDir = fs.mkdtempSync(path.join(
|
|
404
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-test-'));
|
|
403
405
|
});
|
|
404
406
|
|
|
405
407
|
afterEach(() => {
|
|
@@ -524,7 +526,7 @@ describe('CodexAdapter', () => {
|
|
|
524
526
|
let tmpDir: string;
|
|
525
527
|
|
|
526
528
|
beforeEach(() => {
|
|
527
|
-
tmpDir = fs.mkdtempSync(path.join(
|
|
529
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-conv-'));
|
|
528
530
|
});
|
|
529
531
|
|
|
530
532
|
afterEach(() => {
|
|
@@ -640,7 +642,7 @@ describe('CodexAdapter', () => {
|
|
|
640
642
|
let sessionsDir: string;
|
|
641
643
|
|
|
642
644
|
beforeEach(() => {
|
|
643
|
-
tmpDir = fs.mkdtempSync(path.join(
|
|
645
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-list-'));
|
|
644
646
|
sessionsDir = path.join(tmpDir, 'sessions');
|
|
645
647
|
fs.mkdirSync(sessionsDir, { recursive: true });
|
|
646
648
|
(adapter as any).codexSessionsDir = sessionsDir;
|
|
@@ -2,30 +2,32 @@
|
|
|
2
2
|
* Tests for GeminiCliAdapter
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { MockedFunction } from 'vitest';
|
|
5
6
|
import * as fs from 'fs';
|
|
6
7
|
import * as os from 'os';
|
|
7
8
|
import * as path from 'path';
|
|
8
|
-
|
|
9
|
-
import { GeminiCliAdapter } from '../../adapters/GeminiCliAdapter';
|
|
10
|
-
import type { ProcessInfo } from '../../adapters/AgentAdapter';
|
|
11
|
-
import { AgentStatus } from '../../adapters/AgentAdapter';
|
|
12
|
-
import { listAgentProcesses, enrichProcesses } from '../../utils/process';
|
|
13
|
-
import { matchProcessesToSessions, generateAgentName } from '../../utils/matching';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
|
|
10
|
+
import { GeminiCliAdapter } from '../../adapters/GeminiCliAdapter.js';
|
|
11
|
+
import type { ProcessInfo } from '../../adapters/AgentAdapter.js';
|
|
12
|
+
import { AgentStatus } from '../../adapters/AgentAdapter.js';
|
|
13
|
+
import { listAgentProcesses, enrichProcesses } from '../../utils/process.js';
|
|
14
|
+
import { matchProcessesToSessions, generateAgentName } from '../../utils/matching.js';
|
|
15
|
+
import * as crypto from 'crypto';
|
|
16
|
+
|
|
17
|
+
vi.mock('../../utils/process.js', () => ({
|
|
18
|
+
listAgentProcesses: vi.fn(),
|
|
19
|
+
enrichProcesses: vi.fn(),
|
|
18
20
|
}));
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
matchProcessesToSessions:
|
|
22
|
-
generateAgentName:
|
|
22
|
+
vi.mock('../../utils/matching.js', () => ({
|
|
23
|
+
matchProcessesToSessions: vi.fn(),
|
|
24
|
+
generateAgentName: vi.fn(),
|
|
23
25
|
}));
|
|
24
26
|
|
|
25
|
-
const mockedListAgentProcesses = listAgentProcesses as
|
|
26
|
-
const mockedEnrichProcesses = enrichProcesses as
|
|
27
|
-
const mockedMatchProcessesToSessions = matchProcessesToSessions as
|
|
28
|
-
const mockedGenerateAgentName = generateAgentName as
|
|
27
|
+
const mockedListAgentProcesses = listAgentProcesses as MockedFunction<typeof listAgentProcesses>;
|
|
28
|
+
const mockedEnrichProcesses = enrichProcesses as MockedFunction<typeof enrichProcesses>;
|
|
29
|
+
const mockedMatchProcessesToSessions = matchProcessesToSessions as MockedFunction<typeof matchProcessesToSessions>;
|
|
30
|
+
const mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;
|
|
29
31
|
|
|
30
32
|
describe('GeminiCliAdapter', () => {
|
|
31
33
|
let adapter: GeminiCliAdapter;
|
|
@@ -918,6 +920,5 @@ function writeSession(
|
|
|
918
920
|
*/
|
|
919
921
|
function hashProjectRoot(projectRoot: string): string {
|
|
920
922
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
921
|
-
const crypto = require('crypto');
|
|
922
923
|
return crypto.createHash('sha256').update(projectRoot).digest('hex');
|
|
923
924
|
}
|