@ai-devkit/agent-manager 0.2.0 → 0.4.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/dist/adapters/ClaudeCodeAdapter.d.ts +44 -28
- package/dist/adapters/ClaudeCodeAdapter.d.ts.map +1 -1
- package/dist/adapters/ClaudeCodeAdapter.js +383 -234
- package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
- package/dist/adapters/CodexAdapter.d.ts.map +1 -1
- package/dist/adapters/CodexAdapter.js +1 -3
- package/dist/adapters/CodexAdapter.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/terminal/TtyWriter.d.ts +23 -0
- package/dist/terminal/TtyWriter.d.ts.map +1 -0
- package/dist/terminal/TtyWriter.js +106 -0
- package/dist/terminal/TtyWriter.js.map +1 -0
- package/dist/terminal/index.d.ts +1 -0
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +3 -1
- package/dist/terminal/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +1045 -82
- package/src/__tests__/adapters/CodexAdapter.test.ts +7 -1
- package/src/__tests__/terminal/TtyWriter.test.ts +154 -0
- package/src/adapters/ClaudeCodeAdapter.ts +498 -327
- package/src/adapters/CodexAdapter.ts +2 -13
- package/src/index.ts +1 -0
- package/src/terminal/TtyWriter.ts +112 -0
- package/src/terminal/index.ts +1 -0
|
@@ -208,7 +208,13 @@ describe('CodexAdapter', () => {
|
|
|
208
208
|
|
|
209
209
|
const agents = await adapter.detectAgents();
|
|
210
210
|
expect(agents).toHaveLength(1);
|
|
211
|
-
expect(agents[0]
|
|
211
|
+
expect(agents[0]).toMatchObject({
|
|
212
|
+
pid: 105,
|
|
213
|
+
name: 'repo-x',
|
|
214
|
+
summary: 'Codex process running',
|
|
215
|
+
projectPath: '/repo-x',
|
|
216
|
+
});
|
|
217
|
+
expect(agents[0].sessionId).toBe('pid-105');
|
|
212
218
|
});
|
|
213
219
|
|
|
214
220
|
it('should list process when session metadata is unavailable', async () => {
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { describe, it, expect, jest, beforeEach } from '@jest/globals';
|
|
2
|
+
import { TtyWriter } from '../../terminal/TtyWriter';
|
|
3
|
+
import { TerminalType } from '../../terminal/TerminalFocusManager';
|
|
4
|
+
import type { TerminalLocation } from '../../terminal/TerminalFocusManager';
|
|
5
|
+
import { execFile } from 'child_process';
|
|
6
|
+
|
|
7
|
+
jest.mock('child_process', () => {
|
|
8
|
+
const actual = jest.requireActual<typeof import('child_process')>('child_process');
|
|
9
|
+
return {
|
|
10
|
+
...actual,
|
|
11
|
+
execFile: jest.fn(),
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const mockedExecFile = execFile as unknown as jest.Mock;
|
|
16
|
+
|
|
17
|
+
function mockExecFileSuccess(stdout = '') {
|
|
18
|
+
mockedExecFile.mockImplementation((...args: unknown[]) => {
|
|
19
|
+
const cb = args[args.length - 1] as (err: Error | null, result: { stdout: string }, stderr: string) => void;
|
|
20
|
+
cb(null, { stdout }, '');
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function mockExecFileError(message: string) {
|
|
25
|
+
mockedExecFile.mockImplementation((...args: unknown[]) => {
|
|
26
|
+
const cb = args[args.length - 1] as (err: Error | null, result: null, stderr: string) => void;
|
|
27
|
+
cb(new Error(message), null, '');
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('TtyWriter', () => {
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
jest.clearAllMocks();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('tmux', () => {
|
|
37
|
+
const location: TerminalLocation = {
|
|
38
|
+
type: TerminalType.TMUX,
|
|
39
|
+
identifier: 'main:0.1',
|
|
40
|
+
tty: '/dev/ttys030',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
it('sends message via tmux send-keys', async () => {
|
|
44
|
+
mockExecFileSuccess();
|
|
45
|
+
|
|
46
|
+
await TtyWriter.send(location, 'continue');
|
|
47
|
+
|
|
48
|
+
expect(mockedExecFile).toHaveBeenCalledWith(
|
|
49
|
+
'tmux',
|
|
50
|
+
['send-keys', '-t', 'main:0.1', 'continue', 'Enter'],
|
|
51
|
+
expect.any(Function),
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('throws on tmux failure', async () => {
|
|
56
|
+
mockExecFileError('tmux not running');
|
|
57
|
+
|
|
58
|
+
await expect(TtyWriter.send(location, 'hello'))
|
|
59
|
+
.rejects.toThrow('tmux not running');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('iTerm2', () => {
|
|
64
|
+
const location: TerminalLocation = {
|
|
65
|
+
type: TerminalType.ITERM2,
|
|
66
|
+
identifier: '/dev/ttys030',
|
|
67
|
+
tty: '/dev/ttys030',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
it('sends message via osascript with execFile (no shell)', async () => {
|
|
71
|
+
mockExecFileSuccess('ok');
|
|
72
|
+
|
|
73
|
+
await TtyWriter.send(location, 'hello');
|
|
74
|
+
|
|
75
|
+
expect(mockedExecFile).toHaveBeenCalledWith(
|
|
76
|
+
'osascript',
|
|
77
|
+
['-e', expect.stringContaining('write text "hello"')],
|
|
78
|
+
expect.any(Function),
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('escapes special characters in message', async () => {
|
|
83
|
+
mockExecFileSuccess('ok');
|
|
84
|
+
|
|
85
|
+
await TtyWriter.send(location, 'say "hi" \\ there');
|
|
86
|
+
|
|
87
|
+
expect(mockedExecFile).toHaveBeenCalledWith(
|
|
88
|
+
'osascript',
|
|
89
|
+
['-e', expect.stringContaining('write text "say \\"hi\\" \\\\ there"')],
|
|
90
|
+
expect.any(Function),
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('throws when session not found', async () => {
|
|
95
|
+
mockExecFileSuccess('not_found');
|
|
96
|
+
|
|
97
|
+
await expect(TtyWriter.send(location, 'test'))
|
|
98
|
+
.rejects.toThrow('iTerm2 session not found');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('Terminal.app', () => {
|
|
103
|
+
const location: TerminalLocation = {
|
|
104
|
+
type: TerminalType.TERMINAL_APP,
|
|
105
|
+
identifier: '/dev/ttys030',
|
|
106
|
+
tty: '/dev/ttys030',
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
it('sends message via System Events keystroke (not do script)', async () => {
|
|
110
|
+
mockExecFileSuccess('ok');
|
|
111
|
+
|
|
112
|
+
await TtyWriter.send(location, 'hello');
|
|
113
|
+
|
|
114
|
+
const scriptArg = (mockedExecFile.mock.calls[0] as unknown[])[1] as string[];
|
|
115
|
+
const script = scriptArg[1];
|
|
116
|
+
// Must use keystroke, NOT do script
|
|
117
|
+
expect(script).toContain('keystroke "hello"');
|
|
118
|
+
expect(script).toContain('key code 36');
|
|
119
|
+
expect(script).not.toContain('do script');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('uses execFile to avoid shell injection', async () => {
|
|
123
|
+
mockExecFileSuccess('ok');
|
|
124
|
+
|
|
125
|
+
await TtyWriter.send(location, "don't stop");
|
|
126
|
+
|
|
127
|
+
expect(mockedExecFile).toHaveBeenCalledWith(
|
|
128
|
+
'osascript',
|
|
129
|
+
['-e', expect.any(String)],
|
|
130
|
+
expect.any(Function),
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('throws when tab not found', async () => {
|
|
135
|
+
mockExecFileSuccess('not_found');
|
|
136
|
+
|
|
137
|
+
await expect(TtyWriter.send(location, 'test'))
|
|
138
|
+
.rejects.toThrow('Terminal.app tab not found');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe('unsupported terminal', () => {
|
|
143
|
+
it('throws for unknown terminal type', async () => {
|
|
144
|
+
const location: TerminalLocation = {
|
|
145
|
+
type: TerminalType.UNKNOWN,
|
|
146
|
+
identifier: '',
|
|
147
|
+
tty: '/dev/ttys030',
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
await expect(TtyWriter.send(location, 'test'))
|
|
151
|
+
.rejects.toThrow('Cannot send input: unsupported terminal type');
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|