agent-relay 0.1.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.
Files changed (143) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +22 -0
  3. package/PROTOCOL.md +319 -0
  4. package/README.md +791 -0
  5. package/dist/cli/index.d.ts +7 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +1591 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/daemon/connection.d.ts +60 -0
  10. package/dist/daemon/connection.d.ts.map +1 -0
  11. package/dist/daemon/connection.js +245 -0
  12. package/dist/daemon/connection.js.map +1 -0
  13. package/dist/daemon/index.d.ts +4 -0
  14. package/dist/daemon/index.d.ts.map +1 -0
  15. package/dist/daemon/index.js +4 -0
  16. package/dist/daemon/index.js.map +1 -0
  17. package/dist/daemon/router.d.ts +72 -0
  18. package/dist/daemon/router.d.ts.map +1 -0
  19. package/dist/daemon/router.js +183 -0
  20. package/dist/daemon/router.js.map +1 -0
  21. package/dist/daemon/server.d.ts +52 -0
  22. package/dist/daemon/server.d.ts.map +1 -0
  23. package/dist/daemon/server.js +186 -0
  24. package/dist/daemon/server.js.map +1 -0
  25. package/dist/dashboard/public/index.html +690 -0
  26. package/dist/dashboard/server.d.ts +2 -0
  27. package/dist/dashboard/server.d.ts.map +1 -0
  28. package/dist/dashboard/server.js +220 -0
  29. package/dist/dashboard/server.js.map +1 -0
  30. package/dist/games/index.d.ts +2 -0
  31. package/dist/games/index.d.ts.map +1 -0
  32. package/dist/games/index.js +2 -0
  33. package/dist/games/index.js.map +1 -0
  34. package/dist/games/tictactoe.d.ts +24 -0
  35. package/dist/games/tictactoe.d.ts.map +1 -0
  36. package/dist/games/tictactoe.js +160 -0
  37. package/dist/games/tictactoe.js.map +1 -0
  38. package/dist/hooks/inbox-check/hook.d.ts +28 -0
  39. package/dist/hooks/inbox-check/hook.d.ts.map +1 -0
  40. package/dist/hooks/inbox-check/hook.js +97 -0
  41. package/dist/hooks/inbox-check/hook.js.map +1 -0
  42. package/dist/hooks/inbox-check/index.d.ts +8 -0
  43. package/dist/hooks/inbox-check/index.d.ts.map +1 -0
  44. package/dist/hooks/inbox-check/index.js +8 -0
  45. package/dist/hooks/inbox-check/index.js.map +1 -0
  46. package/dist/hooks/inbox-check/types.d.ts +31 -0
  47. package/dist/hooks/inbox-check/types.d.ts.map +1 -0
  48. package/dist/hooks/inbox-check/types.js +5 -0
  49. package/dist/hooks/inbox-check/types.js.map +1 -0
  50. package/dist/hooks/inbox-check/utils.d.ts +44 -0
  51. package/dist/hooks/inbox-check/utils.d.ts.map +1 -0
  52. package/dist/hooks/inbox-check/utils.js +107 -0
  53. package/dist/hooks/inbox-check/utils.js.map +1 -0
  54. package/dist/index.d.ts +10 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +10 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/protocol/framing.d.ts +32 -0
  59. package/dist/protocol/framing.d.ts.map +1 -0
  60. package/dist/protocol/framing.js +71 -0
  61. package/dist/protocol/framing.js.map +1 -0
  62. package/dist/protocol/index.d.ts +3 -0
  63. package/dist/protocol/index.d.ts.map +1 -0
  64. package/dist/protocol/index.js +3 -0
  65. package/dist/protocol/index.js.map +1 -0
  66. package/dist/protocol/types.d.ts +104 -0
  67. package/dist/protocol/types.d.ts.map +1 -0
  68. package/dist/protocol/types.js +6 -0
  69. package/dist/protocol/types.js.map +1 -0
  70. package/dist/state/agent-state.d.ts +40 -0
  71. package/dist/state/agent-state.d.ts.map +1 -0
  72. package/dist/state/agent-state.js +120 -0
  73. package/dist/state/agent-state.js.map +1 -0
  74. package/dist/storage/adapter.d.ts +29 -0
  75. package/dist/storage/adapter.d.ts.map +1 -0
  76. package/dist/storage/adapter.js +2 -0
  77. package/dist/storage/adapter.js.map +1 -0
  78. package/dist/storage/sqlite-adapter.d.ts +15 -0
  79. package/dist/storage/sqlite-adapter.d.ts.map +1 -0
  80. package/dist/storage/sqlite-adapter.js +116 -0
  81. package/dist/storage/sqlite-adapter.js.map +1 -0
  82. package/dist/supervisor/inbox.d.ts +38 -0
  83. package/dist/supervisor/inbox.d.ts.map +1 -0
  84. package/dist/supervisor/inbox.js +162 -0
  85. package/dist/supervisor/inbox.js.map +1 -0
  86. package/dist/supervisor/index.d.ts +10 -0
  87. package/dist/supervisor/index.d.ts.map +1 -0
  88. package/dist/supervisor/index.js +10 -0
  89. package/dist/supervisor/index.js.map +1 -0
  90. package/dist/supervisor/spawner.d.ts +54 -0
  91. package/dist/supervisor/spawner.d.ts.map +1 -0
  92. package/dist/supervisor/spawner.js +282 -0
  93. package/dist/supervisor/spawner.js.map +1 -0
  94. package/dist/supervisor/state.d.ts +132 -0
  95. package/dist/supervisor/state.d.ts.map +1 -0
  96. package/dist/supervisor/state.js +465 -0
  97. package/dist/supervisor/state.js.map +1 -0
  98. package/dist/supervisor/supervisor.d.ts +67 -0
  99. package/dist/supervisor/supervisor.d.ts.map +1 -0
  100. package/dist/supervisor/supervisor.js +263 -0
  101. package/dist/supervisor/supervisor.js.map +1 -0
  102. package/dist/supervisor/types.d.ts +139 -0
  103. package/dist/supervisor/types.d.ts.map +1 -0
  104. package/dist/supervisor/types.js +12 -0
  105. package/dist/supervisor/types.js.map +1 -0
  106. package/dist/utils/index.d.ts +2 -0
  107. package/dist/utils/index.d.ts.map +1 -0
  108. package/dist/utils/index.js +2 -0
  109. package/dist/utils/index.js.map +1 -0
  110. package/dist/utils/name-generator.d.ts +17 -0
  111. package/dist/utils/name-generator.d.ts.map +1 -0
  112. package/dist/utils/name-generator.js +52 -0
  113. package/dist/utils/name-generator.js.map +1 -0
  114. package/dist/webhook/spawner.d.ts +79 -0
  115. package/dist/webhook/spawner.d.ts.map +1 -0
  116. package/dist/webhook/spawner.js +288 -0
  117. package/dist/webhook/spawner.js.map +1 -0
  118. package/dist/wrapper/client.d.ts +72 -0
  119. package/dist/wrapper/client.d.ts.map +1 -0
  120. package/dist/wrapper/client.js +306 -0
  121. package/dist/wrapper/client.js.map +1 -0
  122. package/dist/wrapper/inbox.d.ts +37 -0
  123. package/dist/wrapper/inbox.d.ts.map +1 -0
  124. package/dist/wrapper/inbox.js +73 -0
  125. package/dist/wrapper/inbox.js.map +1 -0
  126. package/dist/wrapper/index.d.ts +4 -0
  127. package/dist/wrapper/index.d.ts.map +1 -0
  128. package/dist/wrapper/index.js +7 -0
  129. package/dist/wrapper/index.js.map +1 -0
  130. package/dist/wrapper/parser.d.ts +94 -0
  131. package/dist/wrapper/parser.d.ts.map +1 -0
  132. package/dist/wrapper/parser.js +360 -0
  133. package/dist/wrapper/parser.js.map +1 -0
  134. package/dist/wrapper/pty-wrapper.d.ts +125 -0
  135. package/dist/wrapper/pty-wrapper.d.ts.map +1 -0
  136. package/dist/wrapper/pty-wrapper.js +494 -0
  137. package/dist/wrapper/pty-wrapper.js.map +1 -0
  138. package/dist/wrapper/tmux-wrapper.d.ts +131 -0
  139. package/dist/wrapper/tmux-wrapper.d.ts.map +1 -0
  140. package/dist/wrapper/tmux-wrapper.js +427 -0
  141. package/dist/wrapper/tmux-wrapper.js.map +1 -0
  142. package/install.sh +69 -0
  143. package/package.json +82 -0
@@ -0,0 +1,131 @@
1
+ /**
2
+ * TmuxWrapper - Attach-based tmux wrapper
3
+ *
4
+ * Architecture:
5
+ * 1. Start agent in detached tmux session
6
+ * 2. Attach user to tmux (they see real terminal)
7
+ * 3. Background: poll capture-pane silently (no stdout writes)
8
+ * 4. Background: parse @relay commands, send to daemon
9
+ * 5. Background: inject messages via send-keys
10
+ *
11
+ * The key insight: user sees the REAL tmux session, not a proxy.
12
+ * We just do background parsing and injection.
13
+ */
14
+ export interface TmuxWrapperConfig {
15
+ name: string;
16
+ command: string;
17
+ args?: string[];
18
+ socketPath?: string;
19
+ cols?: number;
20
+ rows?: number;
21
+ cwd?: string;
22
+ env?: Record<string, string>;
23
+ /** Use file-based inbox in addition to injection */
24
+ useInbox?: boolean;
25
+ /** Custom inbox directory */
26
+ inboxDir?: string;
27
+ /** Polling interval for capture-pane (ms) */
28
+ pollInterval?: number;
29
+ /** Enable debug logging to stderr */
30
+ debug?: boolean;
31
+ /** Throttle debug logs (ms) */
32
+ debugLogIntervalMs?: number;
33
+ /** Idle time after last output before injecting (ms) */
34
+ idleBeforeInjectMs?: number;
35
+ /** Retry interval while waiting for idle window (ms) */
36
+ injectRetryMs?: number;
37
+ }
38
+ export declare class TmuxWrapper {
39
+ private config;
40
+ private sessionName;
41
+ private client;
42
+ private parser;
43
+ private inbox?;
44
+ private running;
45
+ private pollTimer?;
46
+ private attachProcess?;
47
+ private lastCapturedOutput;
48
+ private lastOutputTime;
49
+ private recentlySentMessages;
50
+ private sentMessageHashes;
51
+ private messageQueue;
52
+ private isInjecting;
53
+ private processedOutputLength;
54
+ private lastDebugLog;
55
+ constructor(config: TmuxWrapperConfig);
56
+ /**
57
+ * Log to stderr (safe - doesn't interfere with tmux display)
58
+ */
59
+ private logStderr;
60
+ /**
61
+ * Build the full command with proper quoting
62
+ * Args containing spaces need to be quoted
63
+ */
64
+ private buildCommand;
65
+ /**
66
+ * Check if tmux session exists
67
+ */
68
+ private sessionExists;
69
+ /**
70
+ * Start the wrapped agent process
71
+ */
72
+ start(): Promise<void>;
73
+ /**
74
+ * Wait for tmux session to be ready
75
+ */
76
+ private waitForSession;
77
+ /**
78
+ * Wait for shell prompt to appear (shell is ready for input)
79
+ */
80
+ private waitForShellReady;
81
+ /**
82
+ * Attach user to tmux session
83
+ * This spawns tmux attach and lets it take over stdin/stdout
84
+ */
85
+ private attachToSession;
86
+ /**
87
+ * Start silent polling for @relay commands
88
+ * Does NOT write to stdout - just parses and sends to daemon
89
+ */
90
+ private startSilentPolling;
91
+ /**
92
+ * Poll for @relay commands in output (silent)
93
+ */
94
+ private pollForRelayCommands;
95
+ /**
96
+ * Strip ANSI escape codes
97
+ */
98
+ private stripAnsi;
99
+ /**
100
+ * Send relay command to daemon
101
+ */
102
+ private sendRelayCommand;
103
+ /**
104
+ * Handle incoming message from relay
105
+ */
106
+ private handleIncomingMessage;
107
+ /**
108
+ * Check if we should inject a message
109
+ */
110
+ private checkForInjectionOpportunity;
111
+ /**
112
+ * Inject message via tmux send-keys
113
+ */
114
+ private injectNextMessage;
115
+ /**
116
+ * Send special keys to tmux
117
+ */
118
+ private sendKeys;
119
+ /**
120
+ * Send literal text to tmux
121
+ */
122
+ private sendKeysLiteral;
123
+ private sleep;
124
+ /**
125
+ * Stop and cleanup
126
+ */
127
+ stop(): void;
128
+ get isRunning(): boolean;
129
+ get name(): string;
130
+ }
131
+ //# sourceMappingURL=tmux-wrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-wrapper.d.ts","sourceRoot":"","sources":["../../src/wrapper/tmux-wrapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+BAA+B;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,KAAK,CAAC,CAAe;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,OAAO,CAAC,aAAa,CAAC,CAAe;IACrC,OAAO,CAAC,kBAAkB,CAAM;IAChC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,oBAAoB,CAAkC;IAC9D,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,YAAY,CAA6C;IACjE,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,YAAY,CAAK;gBAEb,MAAM,EAAE,iBAAiB;IA2CrC;;OAEG;IACH,OAAO,CAAC,SAAS;IAejB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAiBpB;;OAEG;YACW,aAAa;IAS3B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkE5B;;OAEG;YACW,cAAc;IAc5B;;OAEG;YACW,iBAAiB;IAkC/B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAyBvB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;YACW,oBAAoB;IAmClC;;OAEG;IACH,OAAO,CAAC,SAAS;IAKjB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAaxB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAe7B;;OAEG;IACH,OAAO,CAAC,4BAA4B;IAgBpC;;OAEG;YACW,iBAAiB;IAyC/B;;OAEG;YACW,QAAQ;IAItB;;OAEG;YACW,eAAe;IAM7B,OAAO,CAAC,KAAK;IAIb;;OAEG;IACH,IAAI,IAAI,IAAI;IAqBZ,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,427 @@
1
+ /**
2
+ * TmuxWrapper - Attach-based tmux wrapper
3
+ *
4
+ * Architecture:
5
+ * 1. Start agent in detached tmux session
6
+ * 2. Attach user to tmux (they see real terminal)
7
+ * 3. Background: poll capture-pane silently (no stdout writes)
8
+ * 4. Background: parse @relay commands, send to daemon
9
+ * 5. Background: inject messages via send-keys
10
+ *
11
+ * The key insight: user sees the REAL tmux session, not a proxy.
12
+ * We just do background parsing and injection.
13
+ */
14
+ import { exec, execSync, spawn } from 'node:child_process';
15
+ import { promisify } from 'node:util';
16
+ import { RelayClient } from './client.js';
17
+ import { OutputParser } from './parser.js';
18
+ import { InboxManager } from './inbox.js';
19
+ const execAsync = promisify(exec);
20
+ export class TmuxWrapper {
21
+ config;
22
+ sessionName;
23
+ client;
24
+ parser;
25
+ inbox;
26
+ running = false;
27
+ pollTimer;
28
+ attachProcess;
29
+ lastCapturedOutput = '';
30
+ lastOutputTime = 0;
31
+ recentlySentMessages = new Map();
32
+ sentMessageHashes = new Set(); // Permanent dedup
33
+ messageQueue = [];
34
+ isInjecting = false;
35
+ // Track processed output to avoid re-parsing
36
+ processedOutputLength = 0;
37
+ lastDebugLog = 0;
38
+ constructor(config) {
39
+ this.config = {
40
+ cols: process.stdout.columns || 120,
41
+ rows: process.stdout.rows || 40,
42
+ pollInterval: 200, // Slightly slower polling since we're not displaying
43
+ idleBeforeInjectMs: 1500,
44
+ injectRetryMs: 500,
45
+ debug: true,
46
+ debugLogIntervalMs: 0,
47
+ ...config,
48
+ };
49
+ // Generate unique session name
50
+ this.sessionName = `relay-${config.name}-${process.pid}`;
51
+ this.client = new RelayClient({
52
+ agentName: config.name,
53
+ socketPath: config.socketPath,
54
+ });
55
+ this.parser = new OutputParser();
56
+ // Initialize inbox if using file-based messaging
57
+ if (config.useInbox) {
58
+ this.inbox = new InboxManager({
59
+ agentName: config.name,
60
+ inboxDir: config.inboxDir,
61
+ });
62
+ }
63
+ // Handle incoming messages from relay
64
+ this.client.onMessage = (from, payload) => {
65
+ this.handleIncomingMessage(from, payload);
66
+ };
67
+ this.client.onStateChange = (state) => {
68
+ // Only log to stderr, never stdout (user is in tmux)
69
+ if (state === 'READY') {
70
+ this.logStderr(`Connected to relay daemon`);
71
+ }
72
+ };
73
+ }
74
+ /**
75
+ * Log to stderr (safe - doesn't interfere with tmux display)
76
+ */
77
+ logStderr(msg, force = false) {
78
+ if (!force && this.config.debug === false)
79
+ return;
80
+ const now = Date.now();
81
+ if (!force && this.config.debugLogIntervalMs && this.config.debugLogIntervalMs > 0) {
82
+ if (now - this.lastDebugLog < this.config.debugLogIntervalMs) {
83
+ return;
84
+ }
85
+ this.lastDebugLog = now;
86
+ }
87
+ // Prefix with newline to avoid corrupting tmux status line
88
+ process.stderr.write(`\r[relay:${this.config.name}] ${msg}\n`);
89
+ }
90
+ /**
91
+ * Build the full command with proper quoting
92
+ * Args containing spaces need to be quoted
93
+ */
94
+ buildCommand() {
95
+ if (!this.config.args || this.config.args.length === 0) {
96
+ return this.config.command;
97
+ }
98
+ // Quote any argument that contains spaces, quotes, or special chars
99
+ const quotedArgs = this.config.args.map(arg => {
100
+ if (arg.includes(' ') || arg.includes('"') || arg.includes("'") || arg.includes('$')) {
101
+ // Use double quotes and escape internal quotes
102
+ return `"${arg.replace(/"/g, '\\"')}"`;
103
+ }
104
+ return arg;
105
+ });
106
+ return `${this.config.command} ${quotedArgs.join(' ')}`;
107
+ }
108
+ /**
109
+ * Check if tmux session exists
110
+ */
111
+ async sessionExists() {
112
+ try {
113
+ await execAsync(`tmux has-session -t ${this.sessionName} 2>/dev/null`);
114
+ return true;
115
+ }
116
+ catch {
117
+ return false;
118
+ }
119
+ }
120
+ /**
121
+ * Start the wrapped agent process
122
+ */
123
+ async start() {
124
+ if (this.running)
125
+ return;
126
+ // Initialize inbox if enabled
127
+ if (this.inbox) {
128
+ this.inbox.init();
129
+ }
130
+ // Connect to relay daemon (in background, don't block)
131
+ this.client.connect().catch(() => {
132
+ // Silent - relay connection is optional
133
+ });
134
+ // Kill any existing session with this name
135
+ try {
136
+ execSync(`tmux kill-session -t ${this.sessionName} 2>/dev/null`);
137
+ }
138
+ catch {
139
+ // Session doesn't exist, that's fine
140
+ }
141
+ // Build the command - properly quote args that contain spaces
142
+ const fullCommand = this.buildCommand();
143
+ this.logStderr(`Command: ${fullCommand}`);
144
+ // Create tmux session
145
+ try {
146
+ execSync(`tmux new-session -d -s ${this.sessionName} -x ${this.config.cols} -y ${this.config.rows}`, {
147
+ cwd: this.config.cwd ?? process.cwd(),
148
+ stdio: 'pipe',
149
+ });
150
+ // Set environment variables
151
+ for (const [key, value] of Object.entries({
152
+ ...this.config.env,
153
+ AGENT_RELAY_NAME: this.config.name,
154
+ TERM: 'xterm-256color',
155
+ })) {
156
+ const escaped = value.replace(/"/g, '\\"');
157
+ execSync(`tmux setenv -t ${this.sessionName} ${key} "${escaped}"`);
158
+ }
159
+ // Wait for shell to be ready (look for prompt)
160
+ await this.waitForShellReady();
161
+ // Send the command to run
162
+ await this.sendKeysLiteral(fullCommand);
163
+ await this.sleep(100);
164
+ await this.sendKeys('Enter');
165
+ }
166
+ catch (err) {
167
+ throw new Error(`Failed to create tmux session: ${err.message}`);
168
+ }
169
+ // Wait for session to be ready
170
+ await this.waitForSession();
171
+ this.running = true;
172
+ // Start background polling (silent - no stdout writes)
173
+ this.startSilentPolling();
174
+ // Attach user to tmux session
175
+ // This takes over stdin/stdout - user sees the real terminal
176
+ this.attachToSession();
177
+ }
178
+ /**
179
+ * Wait for tmux session to be ready
180
+ */
181
+ async waitForSession(maxWaitMs = 5000) {
182
+ const startTime = Date.now();
183
+ while (Date.now() - startTime < maxWaitMs) {
184
+ if (await this.sessionExists()) {
185
+ await new Promise(r => setTimeout(r, 200));
186
+ return;
187
+ }
188
+ await new Promise(r => setTimeout(r, 100));
189
+ }
190
+ throw new Error('Timeout waiting for tmux session');
191
+ }
192
+ /**
193
+ * Wait for shell prompt to appear (shell is ready for input)
194
+ */
195
+ async waitForShellReady(maxWaitMs = 10000) {
196
+ const startTime = Date.now();
197
+ // Common prompt endings: $, %, >, ➜, #
198
+ const promptPatterns = /[$%>#➜]\s*$/;
199
+ this.logStderr('Waiting for shell to initialize...');
200
+ while (Date.now() - startTime < maxWaitMs) {
201
+ try {
202
+ const { stdout } = await execAsync(`tmux capture-pane -t ${this.sessionName} -p 2>/dev/null`);
203
+ // Check if the last non-empty line looks like a prompt
204
+ const lines = stdout.split('\n').filter(l => l.trim());
205
+ const lastLine = lines[lines.length - 1] || '';
206
+ if (promptPatterns.test(lastLine)) {
207
+ this.logStderr('Shell ready');
208
+ // Extra delay to ensure shell is fully ready
209
+ await this.sleep(200);
210
+ return;
211
+ }
212
+ }
213
+ catch {
214
+ // Session might not be ready yet
215
+ }
216
+ await this.sleep(200);
217
+ }
218
+ // Fallback: proceed anyway after timeout
219
+ this.logStderr('Shell ready timeout, proceeding anyway');
220
+ }
221
+ /**
222
+ * Attach user to tmux session
223
+ * This spawns tmux attach and lets it take over stdin/stdout
224
+ */
225
+ attachToSession() {
226
+ this.attachProcess = spawn('tmux', ['attach-session', '-t', this.sessionName], {
227
+ stdio: 'inherit', // User's terminal connects directly to tmux
228
+ });
229
+ this.attachProcess.on('exit', (code) => {
230
+ this.logStderr(`Session ended (code: ${code})`, true);
231
+ this.stop();
232
+ process.exit(code ?? 0);
233
+ });
234
+ this.attachProcess.on('error', (err) => {
235
+ this.logStderr(`Attach error: ${err.message}`, true);
236
+ this.stop();
237
+ process.exit(1);
238
+ });
239
+ // Handle signals
240
+ const cleanup = () => {
241
+ this.stop();
242
+ };
243
+ process.on('SIGINT', cleanup);
244
+ process.on('SIGTERM', cleanup);
245
+ }
246
+ /**
247
+ * Start silent polling for @relay commands
248
+ * Does NOT write to stdout - just parses and sends to daemon
249
+ */
250
+ startSilentPolling() {
251
+ this.pollTimer = setInterval(() => {
252
+ this.pollForRelayCommands().catch(() => {
253
+ // Ignore poll errors
254
+ });
255
+ }, this.config.pollInterval);
256
+ }
257
+ /**
258
+ * Poll for @relay commands in output (silent)
259
+ */
260
+ async pollForRelayCommands() {
261
+ if (!this.running)
262
+ return;
263
+ try {
264
+ // Capture scrollback
265
+ const { stdout } = await execAsync(`tmux capture-pane -t ${this.sessionName} -p -S - 2>/dev/null`);
266
+ // Always parse the FULL capture for @relay commands
267
+ // This handles terminal UIs that rewrite content in place
268
+ const cleanContent = this.stripAnsi(stdout);
269
+ const { commands } = this.parser.parse(cleanContent);
270
+ // Track last output time for injection timing
271
+ if (stdout.length !== this.processedOutputLength) {
272
+ this.lastOutputTime = Date.now();
273
+ this.processedOutputLength = stdout.length;
274
+ }
275
+ // Send any commands found (deduplication handles repeats)
276
+ for (const cmd of commands) {
277
+ this.sendRelayCommand(cmd);
278
+ }
279
+ // Also check for injection opportunity
280
+ this.checkForInjectionOpportunity();
281
+ }
282
+ catch (err) {
283
+ if (err.message?.includes('no such session')) {
284
+ this.stop();
285
+ }
286
+ }
287
+ }
288
+ /**
289
+ * Strip ANSI escape codes
290
+ */
291
+ stripAnsi(str) {
292
+ // eslint-disable-next-line no-control-regex
293
+ return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, '');
294
+ }
295
+ /**
296
+ * Send relay command to daemon
297
+ */
298
+ sendRelayCommand(cmd) {
299
+ const msgHash = `${cmd.to}:${cmd.body}`;
300
+ // Permanent dedup - never send the same message twice
301
+ if (this.sentMessageHashes.has(msgHash))
302
+ return;
303
+ const success = this.client.sendMessage(cmd.to, cmd.body, cmd.kind, cmd.data);
304
+ if (success) {
305
+ this.sentMessageHashes.add(msgHash);
306
+ this.logStderr(`→ ${cmd.to}: ${cmd.body.substring(0, 40)}...`);
307
+ }
308
+ }
309
+ /**
310
+ * Handle incoming message from relay
311
+ */
312
+ handleIncomingMessage(from, payload) {
313
+ this.logStderr(`← ${from}: ${payload.body.substring(0, 40)}...`);
314
+ // Queue for injection
315
+ this.messageQueue.push({ from, body: payload.body });
316
+ // Write to inbox if enabled
317
+ if (this.inbox) {
318
+ this.inbox.addMessage(from, payload.body);
319
+ }
320
+ // Try to inject
321
+ this.checkForInjectionOpportunity();
322
+ }
323
+ /**
324
+ * Check if we should inject a message
325
+ */
326
+ checkForInjectionOpportunity() {
327
+ if (this.messageQueue.length === 0)
328
+ return;
329
+ if (this.isInjecting)
330
+ return;
331
+ if (!this.running)
332
+ return;
333
+ // Wait for output to settle (agent might be busy)
334
+ const timeSinceOutput = Date.now() - this.lastOutputTime;
335
+ if (timeSinceOutput < (this.config.idleBeforeInjectMs ?? 1500)) {
336
+ const retryMs = this.config.injectRetryMs ?? 500;
337
+ setTimeout(() => this.checkForInjectionOpportunity(), retryMs);
338
+ return;
339
+ }
340
+ this.injectNextMessage();
341
+ }
342
+ /**
343
+ * Inject message via tmux send-keys
344
+ */
345
+ async injectNextMessage() {
346
+ const msg = this.messageQueue.shift();
347
+ if (!msg)
348
+ return;
349
+ this.isInjecting = true;
350
+ this.logStderr(`Injecting message from ${msg.from}`);
351
+ try {
352
+ let sanitizedBody = msg.body.replace(/[\r\n]+/g, ' ').trim();
353
+ // Truncate long messages to avoid display issues
354
+ const maxLen = 500;
355
+ if (sanitizedBody.length > maxLen) {
356
+ sanitizedBody = sanitizedBody.substring(0, maxLen) + '... [truncated]';
357
+ }
358
+ const injection = `Relay message from ${msg.from}: ${sanitizedBody}`;
359
+ // Clear any partial input
360
+ await this.sendKeys('Escape');
361
+ await this.sleep(30);
362
+ await this.sendKeys('C-u');
363
+ await this.sleep(30);
364
+ // Type the message
365
+ await this.sendKeysLiteral(injection);
366
+ await this.sleep(50);
367
+ // Submit
368
+ await this.sendKeys('Enter');
369
+ this.logStderr(`Injection complete`);
370
+ }
371
+ catch (err) {
372
+ this.logStderr(`Injection failed: ${err.message}`, true);
373
+ }
374
+ finally {
375
+ this.isInjecting = false;
376
+ if (this.messageQueue.length > 0) {
377
+ setTimeout(() => this.checkForInjectionOpportunity(), 1000);
378
+ }
379
+ }
380
+ }
381
+ /**
382
+ * Send special keys to tmux
383
+ */
384
+ async sendKeys(keys) {
385
+ await execAsync(`tmux send-keys -t ${this.sessionName} ${keys}`);
386
+ }
387
+ /**
388
+ * Send literal text to tmux
389
+ */
390
+ async sendKeysLiteral(text) {
391
+ // Escape for shell and use -l for literal
392
+ const escaped = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\$/g, '\\$');
393
+ await execAsync(`tmux send-keys -t ${this.sessionName} -l "${escaped}"`);
394
+ }
395
+ sleep(ms) {
396
+ return new Promise(r => setTimeout(r, ms));
397
+ }
398
+ /**
399
+ * Stop and cleanup
400
+ */
401
+ stop() {
402
+ if (!this.running)
403
+ return;
404
+ this.running = false;
405
+ // Stop polling
406
+ if (this.pollTimer) {
407
+ clearInterval(this.pollTimer);
408
+ this.pollTimer = undefined;
409
+ }
410
+ // Kill tmux session
411
+ try {
412
+ execSync(`tmux kill-session -t ${this.sessionName} 2>/dev/null`);
413
+ }
414
+ catch {
415
+ // Ignore
416
+ }
417
+ // Disconnect relay
418
+ this.client.destroy();
419
+ }
420
+ get isRunning() {
421
+ return this.running;
422
+ }
423
+ get name() {
424
+ return this.config.name;
425
+ }
426
+ }
427
+ //# sourceMappingURL=tmux-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-wrapper.js","sourceRoot":"","sources":["../../src/wrapper/tmux-wrapper.ts"],"names":[],"mappings":"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"}
package/install.sh ADDED
@@ -0,0 +1,69 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Agent Relay Installer
5
+ # Usage: curl -fsSL https://raw.githubusercontent.com/khaliqgant/agent-relay/main/install.sh | bash
6
+
7
+ REPO="khaliqgant/agent-relay"
8
+ INSTALL_DIR="${AGENT_RELAY_INSTALL_DIR:-$HOME/.agent-relay}"
9
+ BIN_DIR="${AGENT_RELAY_BIN_DIR:-$HOME/.local/bin}"
10
+
11
+ # Colors
12
+ RED='\033[0;31m'
13
+ GREEN='\033[0;32m'
14
+ YELLOW='\033[1;33m'
15
+ BLUE='\033[0;34m'
16
+ NC='\033[0m'
17
+
18
+ info() { echo -e "${BLUE}[info]${NC} $1"; }
19
+ success() { echo -e "${GREEN}[success]${NC} $1"; }
20
+ warn() { echo -e "${YELLOW}[warn]${NC} $1"; }
21
+ error() { echo -e "${RED}[error]${NC} $1"; exit 1; }
22
+
23
+ check_requirements() {
24
+ if ! command -v node &> /dev/null; then
25
+ error "Node.js is required. Please install Node.js 20+ first."
26
+ fi
27
+
28
+ NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
29
+ if [ "$NODE_VERSION" -lt 20 ]; then
30
+ error "Node.js 20+ required. Found: $(node -v)"
31
+ fi
32
+ info "Node.js $(node -v) detected"
33
+ }
34
+
35
+ install_source() {
36
+ info "Installing from source..."
37
+ mkdir -p "$INSTALL_DIR" "$BIN_DIR"
38
+
39
+ if command -v git &> /dev/null; then
40
+ [ -d "$INSTALL_DIR/.git" ] && cd "$INSTALL_DIR" && git pull || git clone "https://github.com/$REPO.git" "$INSTALL_DIR"
41
+ else
42
+ curl -fsSL "https://github.com/$REPO/archive/main.tar.gz" | tar -xz -C "$INSTALL_DIR" --strip-components=1
43
+ fi
44
+
45
+ cd "$INSTALL_DIR" && npm ci && npm run build
46
+ ln -sf "$INSTALL_DIR/dist/cli/index.js" "$BIN_DIR/agent-relay"
47
+ chmod +x "$BIN_DIR/agent-relay"
48
+
49
+ [[ ":$PATH:" != *":$BIN_DIR:"* ]] && warn "Add to PATH: export PATH=\"\$PATH:$BIN_DIR\""
50
+ success "Installed to $INSTALL_DIR"
51
+ }
52
+
53
+ main() {
54
+ echo -e "\n${YELLOW}⚡ Agent Relay${NC} Installer\n"
55
+ check_requirements
56
+ install_source
57
+ echo -e "\nQuick Start:"
58
+ echo -e " # Start the daemon"
59
+ echo -e " agent-relay start -f"
60
+ echo -e ""
61
+ echo -e " # Wrap an agent (tmux mode is default)"
62
+ echo -e " agent-relay wrap -n MyAgent \"claude\""
63
+ echo -e ""
64
+ echo -e " # Open the dashboard"
65
+ echo -e " agent-relay dashboard"
66
+ echo -e ""
67
+ }
68
+
69
+ main "$@"