@ai-devkit/agent-manager 0.6.0 → 0.6.1
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TtyWriter.d.ts","sourceRoot":"","sources":["../../src/terminal/TtyWriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAa/D,qBAAa,SAAS;IAClB;;;;;;;;;;;;;;OAcG;WACU,IAAI,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;mBAgBxD,WAAW;
|
|
1
|
+
{"version":3,"file":"TtyWriter.d.ts","sourceRoot":"","sources":["../../src/terminal/TtyWriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAa/D,qBAAa,SAAS;IAClB;;;;;;;;;;;;;;OAcG;WACU,IAAI,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;mBAgBxD,WAAW;mBAWX,aAAa;mBAsCb,kBAAkB;CA6C1C"}
|
|
@@ -42,24 +42,46 @@ class TtyWriter {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
static async sendViaTmux(identifier, message) {
|
|
45
|
-
|
|
45
|
+
// Send text and Enter as two separate calls so that Enter arrives
|
|
46
|
+
// outside of bracketed paste mode. When the inner application (e.g.
|
|
47
|
+
// Claude Code) has bracketed paste enabled, tmux wraps the send-keys
|
|
48
|
+
// payload in paste brackets — if Enter is included, it gets swallowed
|
|
49
|
+
// as part of the paste instead of acting as a submit action.
|
|
50
|
+
await execFileAsync('tmux', ['send-keys', '-t', identifier, '-l', message]);
|
|
51
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
52
|
+
await execFileAsync('tmux', ['send-keys', '-t', identifier, 'Enter']);
|
|
46
53
|
}
|
|
47
54
|
static async sendViaITerm2(tty, message) {
|
|
48
55
|
const escaped = escapeAppleScript(message);
|
|
56
|
+
// Send text WITHOUT a trailing newline to avoid the newline being swallowed
|
|
57
|
+
// by bracketed paste mode. Then simulate pressing Return separately so that
|
|
58
|
+
// Claude Code (and other interactive TUIs) treat it as a real submit action.
|
|
49
59
|
const script = `
|
|
50
60
|
tell application "iTerm"
|
|
61
|
+
set targetSession to missing value
|
|
51
62
|
repeat with w in windows
|
|
52
63
|
repeat with t in tabs of w
|
|
53
64
|
repeat with s in sessions of t
|
|
54
65
|
if tty of s is "${tty}" then
|
|
55
|
-
|
|
56
|
-
|
|
66
|
+
set targetSession to s
|
|
67
|
+
exit repeat
|
|
57
68
|
end if
|
|
58
69
|
end repeat
|
|
70
|
+
if targetSession is not missing value then exit repeat
|
|
59
71
|
end repeat
|
|
72
|
+
if targetSession is not missing value then exit repeat
|
|
60
73
|
end repeat
|
|
74
|
+
if targetSession is missing value then return "not_found"
|
|
75
|
+
tell targetSession to write text "${escaped}" newline no
|
|
61
76
|
end tell
|
|
62
|
-
|
|
77
|
+
tell application "iTerm" to activate
|
|
78
|
+
delay 0.15
|
|
79
|
+
tell application "System Events"
|
|
80
|
+
tell process "iTerm2"
|
|
81
|
+
key code 36
|
|
82
|
+
end tell
|
|
83
|
+
end tell
|
|
84
|
+
return "ok"`;
|
|
63
85
|
const { stdout } = await execFileAsync('osascript', ['-e', script]);
|
|
64
86
|
if (stdout.trim() !== 'ok') {
|
|
65
87
|
throw new Error(`iTerm2 session not found for TTY ${tty}`);
|
|
@@ -70,6 +92,9 @@ return "not_found"`;
|
|
|
70
92
|
// Use System Events keystroke to type into the foreground process,
|
|
71
93
|
// NOT Terminal.app's "do script" which runs a new shell command.
|
|
72
94
|
// First activate Terminal and select the correct tab, then type via System Events.
|
|
95
|
+
// Send the text first, then wait for the paste/input to complete before pressing
|
|
96
|
+
// Return separately — this ensures interactive TUIs (like Claude Code) see the
|
|
97
|
+
// Return as a real submit action, not part of a bracketed paste.
|
|
73
98
|
const script = `
|
|
74
99
|
tell application "Terminal"
|
|
75
100
|
set targetFound to false
|
|
@@ -92,6 +117,11 @@ delay 0.1
|
|
|
92
117
|
tell application "System Events"
|
|
93
118
|
tell process "Terminal"
|
|
94
119
|
keystroke "${escaped}"
|
|
120
|
+
end tell
|
|
121
|
+
end tell
|
|
122
|
+
delay 0.15
|
|
123
|
+
tell application "System Events"
|
|
124
|
+
tell process "Terminal"
|
|
95
125
|
key code 36
|
|
96
126
|
end tell
|
|
97
127
|
end tell
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TtyWriter.js","sourceRoot":"","sources":["../../src/terminal/TtyWriter.ts"],"names":[],"mappings":";;;AAAA,iDAAyC;AACzC,+BAAiC;AAEjC,iEAAsD;AAEtD,MAAM,aAAa,GAAG,IAAA,gBAAS,EAAC,wBAAQ,CAAC,CAAC;AAE1C;;;GAGG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACnC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED,MAAa,SAAS;IAClB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAA0B,EAAE,OAAe;QACzD,QAAQ,QAAQ,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,mCAAY,CAAC,IAAI;gBAClB,OAAO,SAAS,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAC/D,KAAK,mCAAY,CAAC,MAAM;gBACpB,OAAO,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC1D,KAAK,mCAAY,CAAC,YAAY;gBAC1B,OAAO,SAAS,CAAC,kBAAkB,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC/D;gBACI,MAAM,IAAI,KAAK,CACX,iDAAiD,QAAQ,CAAC,IAAI,KAAK;oBACnE,wCAAwC,CAC3C,CAAC;QACV,CAAC;IACL,CAAC;IAEO,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,UAAkB,EAAE,OAAe;QAChE,MAAM,aAAa,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"TtyWriter.js","sourceRoot":"","sources":["../../src/terminal/TtyWriter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -40,16 +40,22 @@ describe('TtyWriter', () => {
|
|
|
40
40
|
tty: '/dev/ttys030',
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
it('sends message
|
|
43
|
+
it('sends message and Enter as separate tmux send-keys calls', async () => {
|
|
44
44
|
mockExecFileSuccess();
|
|
45
45
|
|
|
46
46
|
await TtyWriter.send(location, 'continue');
|
|
47
47
|
|
|
48
48
|
expect(mockedExecFile).toHaveBeenCalledWith(
|
|
49
49
|
'tmux',
|
|
50
|
-
['send-keys', '-t', 'main:0.1', '
|
|
50
|
+
['send-keys', '-t', 'main:0.1', '-l', 'continue'],
|
|
51
51
|
expect.any(Function),
|
|
52
52
|
);
|
|
53
|
+
expect(mockedExecFile).toHaveBeenCalledWith(
|
|
54
|
+
'tmux',
|
|
55
|
+
['send-keys', '-t', 'main:0.1', 'Enter'],
|
|
56
|
+
expect.any(Function),
|
|
57
|
+
);
|
|
58
|
+
expect(mockedExecFile).toHaveBeenCalledTimes(2);
|
|
53
59
|
});
|
|
54
60
|
|
|
55
61
|
it('throws on tmux failure', async () => {
|
|
@@ -74,9 +80,12 @@ describe('TtyWriter', () => {
|
|
|
74
80
|
|
|
75
81
|
expect(mockedExecFile).toHaveBeenCalledWith(
|
|
76
82
|
'osascript',
|
|
77
|
-
['-e', expect.stringContaining('write text "hello"')],
|
|
83
|
+
['-e', expect.stringContaining('write text "hello" newline no')],
|
|
78
84
|
expect.any(Function),
|
|
79
85
|
);
|
|
86
|
+
const scriptArg = (mockedExecFile.mock.calls[0] as unknown[])[1] as string[];
|
|
87
|
+
const script = scriptArg[1];
|
|
88
|
+
expect(script).toContain('key code 36');
|
|
80
89
|
});
|
|
81
90
|
|
|
82
91
|
it('escapes special characters in message', async () => {
|
|
@@ -86,7 +95,7 @@ describe('TtyWriter', () => {
|
|
|
86
95
|
|
|
87
96
|
expect(mockedExecFile).toHaveBeenCalledWith(
|
|
88
97
|
'osascript',
|
|
89
|
-
['-e', expect.stringContaining('write text "say \\"hi\\" \\\\ there"')],
|
|
98
|
+
['-e', expect.stringContaining('write text "say \\"hi\\" \\\\ there" newline no')],
|
|
90
99
|
expect.any(Function),
|
|
91
100
|
);
|
|
92
101
|
});
|
|
@@ -113,24 +122,11 @@ describe('TtyWriter', () => {
|
|
|
113
122
|
|
|
114
123
|
const scriptArg = (mockedExecFile.mock.calls[0] as unknown[])[1] as string[];
|
|
115
124
|
const script = scriptArg[1];
|
|
116
|
-
// Must use keystroke, NOT do script
|
|
117
125
|
expect(script).toContain('keystroke "hello"');
|
|
118
126
|
expect(script).toContain('key code 36');
|
|
119
127
|
expect(script).not.toContain('do script');
|
|
120
128
|
});
|
|
121
129
|
|
|
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
130
|
it('throws when tab not found', async () => {
|
|
135
131
|
mockExecFileSuccess('not_found');
|
|
136
132
|
|
|
@@ -46,25 +46,47 @@ export class TtyWriter {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
private static async sendViaTmux(identifier: string, message: string): Promise<void> {
|
|
49
|
-
|
|
49
|
+
// Send text and Enter as two separate calls so that Enter arrives
|
|
50
|
+
// outside of bracketed paste mode. When the inner application (e.g.
|
|
51
|
+
// Claude Code) has bracketed paste enabled, tmux wraps the send-keys
|
|
52
|
+
// payload in paste brackets — if Enter is included, it gets swallowed
|
|
53
|
+
// as part of the paste instead of acting as a submit action.
|
|
54
|
+
await execFileAsync('tmux', ['send-keys', '-t', identifier, '-l', message]);
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
56
|
+
await execFileAsync('tmux', ['send-keys', '-t', identifier, 'Enter']);
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
private static async sendViaITerm2(tty: string, message: string): Promise<void> {
|
|
53
60
|
const escaped = escapeAppleScript(message);
|
|
61
|
+
// Send text WITHOUT a trailing newline to avoid the newline being swallowed
|
|
62
|
+
// by bracketed paste mode. Then simulate pressing Return separately so that
|
|
63
|
+
// Claude Code (and other interactive TUIs) treat it as a real submit action.
|
|
54
64
|
const script = `
|
|
55
65
|
tell application "iTerm"
|
|
66
|
+
set targetSession to missing value
|
|
56
67
|
repeat with w in windows
|
|
57
68
|
repeat with t in tabs of w
|
|
58
69
|
repeat with s in sessions of t
|
|
59
70
|
if tty of s is "${tty}" then
|
|
60
|
-
|
|
61
|
-
|
|
71
|
+
set targetSession to s
|
|
72
|
+
exit repeat
|
|
62
73
|
end if
|
|
63
74
|
end repeat
|
|
75
|
+
if targetSession is not missing value then exit repeat
|
|
64
76
|
end repeat
|
|
77
|
+
if targetSession is not missing value then exit repeat
|
|
65
78
|
end repeat
|
|
79
|
+
if targetSession is missing value then return "not_found"
|
|
80
|
+
tell targetSession to write text "${escaped}" newline no
|
|
66
81
|
end tell
|
|
67
|
-
|
|
82
|
+
tell application "iTerm" to activate
|
|
83
|
+
delay 0.15
|
|
84
|
+
tell application "System Events"
|
|
85
|
+
tell process "iTerm2"
|
|
86
|
+
key code 36
|
|
87
|
+
end tell
|
|
88
|
+
end tell
|
|
89
|
+
return "ok"`;
|
|
68
90
|
|
|
69
91
|
const { stdout } = await execFileAsync('osascript', ['-e', script]);
|
|
70
92
|
if (stdout.trim() !== 'ok') {
|
|
@@ -77,6 +99,9 @@ return "not_found"`;
|
|
|
77
99
|
// Use System Events keystroke to type into the foreground process,
|
|
78
100
|
// NOT Terminal.app's "do script" which runs a new shell command.
|
|
79
101
|
// First activate Terminal and select the correct tab, then type via System Events.
|
|
102
|
+
// Send the text first, then wait for the paste/input to complete before pressing
|
|
103
|
+
// Return separately — this ensures interactive TUIs (like Claude Code) see the
|
|
104
|
+
// Return as a real submit action, not part of a bracketed paste.
|
|
80
105
|
const script = `
|
|
81
106
|
tell application "Terminal"
|
|
82
107
|
set targetFound to false
|
|
@@ -99,6 +124,11 @@ delay 0.1
|
|
|
99
124
|
tell application "System Events"
|
|
100
125
|
tell process "Terminal"
|
|
101
126
|
keystroke "${escaped}"
|
|
127
|
+
end tell
|
|
128
|
+
end tell
|
|
129
|
+
delay 0.15
|
|
130
|
+
tell application "System Events"
|
|
131
|
+
tell process "Terminal"
|
|
102
132
|
key code 36
|
|
103
133
|
end tell
|
|
104
134
|
end tell
|