@swarmify/agents-cli 1.10.3 → 1.10.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.
Files changed (129) hide show
  1. package/README.md +28 -0
  2. package/dist/commands/__tests__/sessions.test.d.ts +2 -0
  3. package/dist/commands/__tests__/sessions.test.d.ts.map +1 -0
  4. package/dist/commands/__tests__/sessions.test.js +170 -0
  5. package/dist/commands/__tests__/sessions.test.js.map +1 -0
  6. package/dist/commands/commands.d.ts.map +1 -1
  7. package/dist/commands/commands.js +74 -56
  8. package/dist/commands/commands.js.map +1 -1
  9. package/dist/commands/daemon.js +1 -1
  10. package/dist/commands/daemon.js.map +1 -1
  11. package/dist/commands/hooks.d.ts.map +1 -1
  12. package/dist/commands/hooks.js +67 -49
  13. package/dist/commands/hooks.js.map +1 -1
  14. package/dist/commands/mcp.d.ts.map +1 -1
  15. package/dist/commands/mcp.js +29 -18
  16. package/dist/commands/mcp.js.map +1 -1
  17. package/dist/commands/packages.d.ts.map +1 -1
  18. package/dist/commands/packages.js +3 -3
  19. package/dist/commands/packages.js.map +1 -1
  20. package/dist/commands/permissions.d.ts.map +1 -1
  21. package/dist/commands/permissions.js +71 -48
  22. package/dist/commands/permissions.js.map +1 -1
  23. package/dist/commands/plugins.js +8 -8
  24. package/dist/commands/plugins.js.map +1 -1
  25. package/dist/commands/pty.d.ts +20 -0
  26. package/dist/commands/pty.d.ts.map +1 -0
  27. package/dist/commands/pty.js +280 -0
  28. package/dist/commands/pty.js.map +1 -0
  29. package/dist/commands/pull.d.ts.map +1 -1
  30. package/dist/commands/pull.js +15 -14
  31. package/dist/commands/pull.js.map +1 -1
  32. package/dist/commands/push.d.ts.map +1 -1
  33. package/dist/commands/push.js +2 -2
  34. package/dist/commands/push.js.map +1 -1
  35. package/dist/commands/routines.d.ts.map +1 -1
  36. package/dist/commands/routines.js +14 -10
  37. package/dist/commands/routines.js.map +1 -1
  38. package/dist/commands/rules.d.ts.map +1 -1
  39. package/dist/commands/rules.js +65 -50
  40. package/dist/commands/rules.js.map +1 -1
  41. package/dist/commands/sessions.d.ts.map +1 -1
  42. package/dist/commands/sessions.js +219 -21
  43. package/dist/commands/sessions.js.map +1 -1
  44. package/dist/commands/sessions.test.d.ts +2 -0
  45. package/dist/commands/sessions.test.d.ts.map +1 -0
  46. package/dist/commands/sessions.test.js +53 -0
  47. package/dist/commands/sessions.test.js.map +1 -0
  48. package/dist/commands/skills.d.ts.map +1 -1
  49. package/dist/commands/skills.js +70 -46
  50. package/dist/commands/skills.js.map +1 -1
  51. package/dist/commands/subagents.d.ts.map +1 -1
  52. package/dist/commands/subagents.js +10 -4
  53. package/dist/commands/subagents.js.map +1 -1
  54. package/dist/commands/utils.d.ts +16 -0
  55. package/dist/commands/utils.d.ts.map +1 -1
  56. package/dist/commands/utils.js +48 -0
  57. package/dist/commands/utils.js.map +1 -1
  58. package/dist/commands/versions.d.ts.map +1 -1
  59. package/dist/commands/versions.js +144 -43
  60. package/dist/commands/versions.js.map +1 -1
  61. package/dist/commands/view.d.ts.map +1 -1
  62. package/dist/commands/view.js +97 -40
  63. package/dist/commands/view.js.map +1 -1
  64. package/dist/index.js +65 -42
  65. package/dist/index.js.map +1 -1
  66. package/dist/lib/agents.d.ts +4 -0
  67. package/dist/lib/agents.d.ts.map +1 -1
  68. package/dist/lib/agents.js +36 -1
  69. package/dist/lib/agents.js.map +1 -1
  70. package/dist/lib/daemon.d.ts.map +1 -1
  71. package/dist/lib/daemon.js +15 -7
  72. package/dist/lib/daemon.js.map +1 -1
  73. package/dist/lib/git.d.ts.map +1 -1
  74. package/dist/lib/git.js +11 -1
  75. package/dist/lib/git.js.map +1 -1
  76. package/dist/lib/pty-client.d.ts +22 -0
  77. package/dist/lib/pty-client.d.ts.map +1 -0
  78. package/dist/lib/pty-client.js +181 -0
  79. package/dist/lib/pty-client.js.map +1 -0
  80. package/dist/lib/pty-server.d.ts +16 -0
  81. package/dist/lib/pty-server.d.ts.map +1 -0
  82. package/dist/lib/pty-server.js +422 -0
  83. package/dist/lib/pty-server.js.map +1 -0
  84. package/dist/lib/runner.d.ts.map +1 -1
  85. package/dist/lib/runner.js +13 -9
  86. package/dist/lib/runner.js.map +1 -1
  87. package/dist/lib/sandbox.js +1 -1
  88. package/dist/lib/sandbox.js.map +1 -1
  89. package/dist/lib/session/discover.d.ts +18 -0
  90. package/dist/lib/session/discover.d.ts.map +1 -1
  91. package/dist/lib/session/discover.js +78 -67
  92. package/dist/lib/session/discover.js.map +1 -1
  93. package/dist/lib/session/parse.d.ts.map +1 -1
  94. package/dist/lib/session/parse.js +8 -3
  95. package/dist/lib/session/parse.js.map +1 -1
  96. package/dist/lib/shims.d.ts.map +1 -1
  97. package/dist/lib/shims.js +6 -1
  98. package/dist/lib/shims.js.map +1 -1
  99. package/dist/lib/state.d.ts +0 -1
  100. package/dist/lib/state.d.ts.map +1 -1
  101. package/dist/lib/state.js +3 -9
  102. package/dist/lib/state.js.map +1 -1
  103. package/dist/lib/types.d.ts +2 -5
  104. package/dist/lib/types.d.ts.map +1 -1
  105. package/dist/lib/types.js.map +1 -1
  106. package/dist/lib/usage.d.ts +29 -0
  107. package/dist/lib/usage.d.ts.map +1 -0
  108. package/dist/lib/usage.js +416 -0
  109. package/dist/lib/usage.js.map +1 -0
  110. package/dist/lib/versions.d.ts.map +1 -1
  111. package/dist/lib/versions.js +19 -8
  112. package/dist/lib/versions.js.map +1 -1
  113. package/package.json +3 -1
  114. package/dist/commands/cron.d.ts +0 -3
  115. package/dist/commands/cron.d.ts.map +0 -1
  116. package/dist/commands/cron.js +0 -457
  117. package/dist/commands/cron.js.map +0 -1
  118. package/dist/lib/cron.d.ts +0 -70
  119. package/dist/lib/cron.d.ts.map +0 -1
  120. package/dist/lib/cron.js +0 -325
  121. package/dist/lib/cron.js.map +0 -1
  122. package/dist/lib/drive-server.d.ts +0 -9
  123. package/dist/lib/drive-server.d.ts.map +0 -1
  124. package/dist/lib/drive-server.js +0 -217
  125. package/dist/lib/drive-server.js.map +0 -1
  126. package/dist/lib/drives.d.ts +0 -34
  127. package/dist/lib/drives.d.ts.map +0 -1
  128. package/dist/lib/drives.js +0 -267
  129. package/dist/lib/drives.js.map +0 -1
@@ -0,0 +1,22 @@
1
+ /**
2
+ * PTY Client
3
+ *
4
+ * Thin client that connects to the PTY sidecar server over unix socket.
5
+ * Each call opens a connection, sends a JSON request, reads the JSON response, and closes.
6
+ */
7
+ export interface PtyResponse {
8
+ ok: boolean;
9
+ error?: string;
10
+ [key: string]: any;
11
+ }
12
+ /**
13
+ * Send a request to the PTY server and return the response.
14
+ * Auto-starts the server if not running.
15
+ */
16
+ export declare function ptyRequest(action: string, id?: string, params?: Record<string, any>): Promise<PtyResponse>;
17
+ /**
18
+ * Parse escape sequences in user input strings.
19
+ * Handles: \n \r \t \e \xHH \\
20
+ */
21
+ export declare function unescapeInput(input: string): string;
22
+ //# sourceMappingURL=pty-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-client.d.ts","sourceRoot":"","sources":["../../src/lib/pty-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAQhH;AAiJD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAgBnD"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * PTY Client
3
+ *
4
+ * Thin client that connects to the PTY sidecar server over unix socket.
5
+ * Each call opens a connection, sends a JSON request, reads the JSON response, and closes.
6
+ */
7
+ import * as net from 'net';
8
+ import * as fs from 'fs';
9
+ import { spawn, execSync } from 'child_process';
10
+ import { fileURLToPath } from 'url';
11
+ import * as path from 'path';
12
+ import { getSocketPath, isPtyServerRunning } from './pty-server.js';
13
+ const CONNECT_TIMEOUT_MS = 5000;
14
+ const RESPONSE_TIMEOUT_MS = 30000;
15
+ /**
16
+ * Send a request to the PTY server and return the response.
17
+ * Auto-starts the server if not running.
18
+ */
19
+ export async function ptyRequest(action, id, params) {
20
+ await ensureServer();
21
+ const req = { action };
22
+ if (id)
23
+ req.id = id;
24
+ if (params)
25
+ req.params = params;
26
+ return sendRequest(req);
27
+ }
28
+ /**
29
+ * Ensure the PTY server is running. Start it if not.
30
+ */
31
+ async function ensureServer() {
32
+ if (isPtyServerRunning())
33
+ return;
34
+ // Find the entry point to spawn the server
35
+ const { bin, args } = getServerSpawnArgs();
36
+ const logPath = path.join(path.dirname(getSocketPath()), 'pty.log');
37
+ const logFd = fs.openSync(logPath, 'a');
38
+ const child = spawn(bin, args, {
39
+ stdio: ['ignore', logFd, logFd],
40
+ detached: true,
41
+ });
42
+ child.unref();
43
+ fs.closeSync(logFd);
44
+ // Wait for socket to appear
45
+ const socketPath = getSocketPath();
46
+ const deadline = Date.now() + 5000;
47
+ while (Date.now() < deadline) {
48
+ if (fs.existsSync(socketPath)) {
49
+ // Verify we can connect
50
+ try {
51
+ await sendRequest({ action: 'ping' });
52
+ return;
53
+ }
54
+ catch {
55
+ // Not ready yet
56
+ }
57
+ }
58
+ await new Promise(r => setTimeout(r, 100));
59
+ }
60
+ throw new Error('PTY server failed to start within 5 seconds. Check ~/.agents/pty.log');
61
+ }
62
+ function getServerSpawnArgs() {
63
+ // Prefer the dist/index.js from the same installation as this code.
64
+ // This avoids version mismatch when a globally installed `agents` is older.
65
+ try {
66
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
67
+ const distIndex = path.join(__dirname, '..', 'index.js');
68
+ if (fs.existsSync(distIndex)) {
69
+ return { bin: process.execPath, args: [distIndex, 'pty', '_server'] };
70
+ }
71
+ }
72
+ catch { }
73
+ // Fallback: use the globally installed agents command
74
+ try {
75
+ const agentsBin = execSync('which agents', { encoding: 'utf-8' }).trim();
76
+ if (agentsBin) {
77
+ return { bin: agentsBin, args: ['pty', '_server'] };
78
+ }
79
+ }
80
+ catch { }
81
+ return { bin: 'agents', args: ['pty', '_server'] };
82
+ }
83
+ /**
84
+ * Send a JSON request over the unix socket and return the parsed response.
85
+ */
86
+ function sendRequest(req) {
87
+ return new Promise((resolve, reject) => {
88
+ const socketPath = getSocketPath();
89
+ if (!fs.existsSync(socketPath)) {
90
+ reject(new Error('PTY server socket not found. Is the server running?'));
91
+ return;
92
+ }
93
+ const conn = net.createConnection({ path: socketPath });
94
+ let data = '';
95
+ let settled = false;
96
+ const connectTimeout = setTimeout(() => {
97
+ if (!settled) {
98
+ settled = true;
99
+ conn.destroy();
100
+ reject(new Error('Connection to PTY server timed out'));
101
+ }
102
+ }, CONNECT_TIMEOUT_MS);
103
+ const responseTimeout = setTimeout(() => {
104
+ if (!settled) {
105
+ settled = true;
106
+ conn.destroy();
107
+ reject(new Error('PTY server response timed out'));
108
+ }
109
+ }, RESPONSE_TIMEOUT_MS);
110
+ conn.on('connect', () => {
111
+ clearTimeout(connectTimeout);
112
+ conn.write(JSON.stringify(req) + '\n');
113
+ });
114
+ conn.on('data', (chunk) => {
115
+ data += chunk.toString();
116
+ const nlIndex = data.indexOf('\n');
117
+ if (nlIndex !== -1) {
118
+ if (!settled) {
119
+ settled = true;
120
+ clearTimeout(connectTimeout);
121
+ clearTimeout(responseTimeout);
122
+ try {
123
+ resolve(JSON.parse(data.slice(0, nlIndex)));
124
+ }
125
+ catch (err) {
126
+ reject(new Error(`Invalid JSON from PTY server: ${data.slice(0, 200)}`));
127
+ }
128
+ }
129
+ conn.end();
130
+ }
131
+ });
132
+ conn.on('error', (err) => {
133
+ if (!settled) {
134
+ settled = true;
135
+ clearTimeout(connectTimeout);
136
+ clearTimeout(responseTimeout);
137
+ reject(new Error(`PTY server connection error: ${err.message}`));
138
+ }
139
+ });
140
+ conn.on('end', () => {
141
+ if (!settled) {
142
+ settled = true;
143
+ clearTimeout(connectTimeout);
144
+ clearTimeout(responseTimeout);
145
+ if (data.trim()) {
146
+ try {
147
+ resolve(JSON.parse(data.trim()));
148
+ }
149
+ catch {
150
+ reject(new Error('PTY server closed connection with invalid response'));
151
+ }
152
+ }
153
+ else {
154
+ reject(new Error('PTY server closed connection without response'));
155
+ }
156
+ }
157
+ });
158
+ });
159
+ }
160
+ /**
161
+ * Parse escape sequences in user input strings.
162
+ * Handles: \n \r \t \e \xHH \\
163
+ */
164
+ export function unescapeInput(input) {
165
+ return input.replace(/\\(n|r|t|e|\\|x[0-9a-fA-F]{2})/g, (match, seq) => {
166
+ switch (seq) {
167
+ case 'n': return '\n';
168
+ case 'r': return '\r';
169
+ case 't': return '\t';
170
+ case 'e': return '\x1b';
171
+ case '\\': return '\\';
172
+ default:
173
+ // \xHH
174
+ if (seq.startsWith('x')) {
175
+ return String.fromCharCode(parseInt(seq.slice(1), 16));
176
+ }
177
+ return match;
178
+ }
179
+ });
180
+ }
181
+ //# sourceMappingURL=pty-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-client.js","sourceRoot":"","sources":["../../src/lib/pty-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * PTY Sidecar Server
3
+ *
4
+ * Lightweight unix socket server that manages persistent PTY sessions.
5
+ * Started as a detached process by `agents pty` commands. Sessions survive
6
+ * across multiple CLI invocations. Each session holds a real PTY (via node-pty)
7
+ * and a headless terminal emulator (via @xterm/headless) for screen rendering.
8
+ *
9
+ * Protocol: newline-delimited JSON over ~/.agents/pty.sock
10
+ */
11
+ export declare function getSocketPath(): string;
12
+ export declare function getPtyPidPath(): string;
13
+ export declare function getPtyLogPath(): string;
14
+ export declare function isPtyServerRunning(): boolean;
15
+ export declare function runPtyServer(): Promise<void>;
16
+ //# sourceMappingURL=pty-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-server.d.ts","sourceRoot":"","sources":["../../src/lib/pty-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAwCH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAY5C;AAcD,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAqYlD"}
@@ -0,0 +1,422 @@
1
+ /**
2
+ * PTY Sidecar Server
3
+ *
4
+ * Lightweight unix socket server that manages persistent PTY sessions.
5
+ * Started as a detached process by `agents pty` commands. Sessions survive
6
+ * across multiple CLI invocations. Each session holds a real PTY (via node-pty)
7
+ * and a headless terminal emulator (via @xterm/headless) for screen rendering.
8
+ *
9
+ * Protocol: newline-delimited JSON over ~/.agents/pty.sock
10
+ */
11
+ import * as net from 'net';
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ import * as crypto from 'crypto';
15
+ import { fileURLToPath } from 'url';
16
+ import { getAgentsDir } from './state.js';
17
+ // --- Constants ---
18
+ const SENTINEL = '__AGENTS_PTY_DONE__';
19
+ const SOCKET_NAME = 'pty.sock';
20
+ const PID_FILE = 'pty.pid';
21
+ const LOG_FILE = 'pty.log';
22
+ const SESSION_IDLE_MS = 30 * 60 * 1000; // 30 min
23
+ const SERVER_IDLE_MS = 60 * 60 * 1000; // 1 hour
24
+ // --- Path helpers ---
25
+ export function getSocketPath() {
26
+ return path.join(getAgentsDir(), SOCKET_NAME);
27
+ }
28
+ export function getPtyPidPath() {
29
+ return path.join(getAgentsDir(), PID_FILE);
30
+ }
31
+ export function getPtyLogPath() {
32
+ return path.join(getAgentsDir(), LOG_FILE);
33
+ }
34
+ export function isPtyServerRunning() {
35
+ const pidPath = getPtyPidPath();
36
+ if (!fs.existsSync(pidPath))
37
+ return false;
38
+ try {
39
+ const pid = parseInt(fs.readFileSync(pidPath, 'utf-8').trim(), 10);
40
+ if (isNaN(pid))
41
+ return false;
42
+ process.kill(pid, 0);
43
+ return true;
44
+ }
45
+ catch {
46
+ try {
47
+ fs.unlinkSync(pidPath);
48
+ }
49
+ catch { }
50
+ return false;
51
+ }
52
+ }
53
+ // --- Logging ---
54
+ function log(level, message) {
55
+ const ts = new Date().toISOString();
56
+ const line = `[${ts}] [${level}] ${message}\n`;
57
+ try {
58
+ fs.appendFileSync(getPtyLogPath(), line, 'utf-8');
59
+ }
60
+ catch { }
61
+ }
62
+ // --- Server ---
63
+ export async function runPtyServer() {
64
+ // Dynamic imports for optional native deps
65
+ let nodePty;
66
+ let XtermTerminal;
67
+ try {
68
+ nodePty = await import('node-pty');
69
+ // Handle ESM default export
70
+ if (nodePty.default?.spawn)
71
+ nodePty = nodePty.default;
72
+ // Ensure spawn-helper is executable (bun install doesn't set +x on prebuilds)
73
+ try {
74
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
75
+ const ptyBase = path.resolve(__dirname, '..', '..', 'node_modules', 'node-pty');
76
+ const helpers = [
77
+ path.join(ptyBase, 'prebuilds', `${process.platform}-${process.arch}`, 'spawn-helper'),
78
+ path.join(ptyBase, 'build', 'Release', 'spawn-helper'),
79
+ ];
80
+ for (const h of helpers) {
81
+ if (fs.existsSync(h)) {
82
+ fs.chmodSync(h, 0o755);
83
+ }
84
+ }
85
+ }
86
+ catch { }
87
+ }
88
+ catch (err) {
89
+ console.error('node-pty is required for PTY support.');
90
+ console.error('Install: cd ' + getAgentsDir() + '/../agents-cli && bun add node-pty');
91
+ process.exit(1);
92
+ }
93
+ try {
94
+ const xterm = await import('@xterm/headless');
95
+ // Handle ESM default export wrapping
96
+ XtermTerminal = xterm.Terminal || xterm.default?.Terminal;
97
+ }
98
+ catch {
99
+ console.error('@xterm/headless is required for PTY support.');
100
+ console.error('Install: cd ' + getAgentsDir() + '/../agents-cli && bun add @xterm/headless');
101
+ process.exit(1);
102
+ }
103
+ const sessions = new Map();
104
+ const socketPath = getSocketPath();
105
+ // Remove stale socket
106
+ if (fs.existsSync(socketPath)) {
107
+ try {
108
+ fs.unlinkSync(socketPath);
109
+ }
110
+ catch { }
111
+ }
112
+ let lastActivityTime = Date.now();
113
+ function generateId() {
114
+ return crypto.randomBytes(4).toString('hex');
115
+ }
116
+ function killSession(session) {
117
+ if (!session.exited) {
118
+ try {
119
+ session.pty.kill();
120
+ }
121
+ catch { }
122
+ session.exited = true;
123
+ }
124
+ if (session.terminal) {
125
+ try {
126
+ session.terminal.dispose();
127
+ }
128
+ catch { }
129
+ }
130
+ }
131
+ function getScreenLines(session) {
132
+ const lines = [];
133
+ const buf = session.terminal.buffer.active;
134
+ for (let y = 0; y < session.rows; y++) {
135
+ const line = buf.getLine(y);
136
+ const text = line ? line.translateToString(true) : '';
137
+ // Strip lines containing the sentinel pattern
138
+ if (text.includes(SENTINEL))
139
+ continue;
140
+ lines.push(text);
141
+ }
142
+ // Trim trailing empty lines
143
+ while (lines.length > 0 && lines[lines.length - 1] === '') {
144
+ lines.pop();
145
+ }
146
+ return lines;
147
+ }
148
+ // Session idle cleanup + server auto-exit
149
+ const cleanupInterval = setInterval(() => {
150
+ const now = Date.now();
151
+ for (const [id, session] of sessions) {
152
+ if (now - session.lastActivity > SESSION_IDLE_MS) {
153
+ log('INFO', `Cleaning up idle session ${id}`);
154
+ killSession(session);
155
+ sessions.delete(id);
156
+ }
157
+ }
158
+ if (sessions.size === 0 && now - lastActivityTime > SERVER_IDLE_MS) {
159
+ log('INFO', 'No sessions, server idle timeout reached. Shutting down.');
160
+ shutdown();
161
+ }
162
+ }, 60_000);
163
+ // --- Request handlers ---
164
+ async function handleRequest(req) {
165
+ lastActivityTime = Date.now();
166
+ switch (req.action) {
167
+ case 'start': {
168
+ const rows = req.params?.rows || 24;
169
+ const cols = req.params?.cols || 120;
170
+ const shell = req.params?.shell || process.env.SHELL || 'zsh';
171
+ const cwd = req.params?.cwd || process.env.HOME || '/';
172
+ const id = generateId();
173
+ let ptyProcess;
174
+ try {
175
+ ptyProcess = nodePty.spawn(shell, [], {
176
+ name: 'xterm-256color',
177
+ cols,
178
+ rows,
179
+ cwd,
180
+ env: { ...process.env },
181
+ });
182
+ }
183
+ catch (err) {
184
+ return { ok: false, error: `Failed to spawn PTY: ${err.message}` };
185
+ }
186
+ const terminal = new XtermTerminal({ rows, cols, allowProposedApi: true });
187
+ const session = {
188
+ id,
189
+ pty: ptyProcess,
190
+ terminal,
191
+ rows,
192
+ cols,
193
+ shell,
194
+ cwd,
195
+ pid: ptyProcess.pid,
196
+ startedAt: Date.now(),
197
+ lastActivity: Date.now(),
198
+ pendingOutput: '',
199
+ appActive: false,
200
+ activeCommand: '',
201
+ exited: false,
202
+ exitCode: null,
203
+ };
204
+ ptyProcess.onData((data) => {
205
+ session.pendingOutput += data;
206
+ terminal.write(data);
207
+ session.lastActivity = Date.now();
208
+ // Check for sentinel to detect command completion
209
+ if (session.appActive && session.pendingOutput.includes(SENTINEL + ':')) {
210
+ session.appActive = false;
211
+ session.activeCommand = '';
212
+ }
213
+ });
214
+ ptyProcess.onExit(({ exitCode }) => {
215
+ session.exited = true;
216
+ session.exitCode = exitCode;
217
+ session.appActive = false;
218
+ });
219
+ // Wait for shell to initialize, then clear init output
220
+ await new Promise(r => setTimeout(r, 300));
221
+ session.pendingOutput = '';
222
+ sessions.set(id, session);
223
+ log('INFO', `Session started: ${id} (pid=${ptyProcess.pid}, shell=${shell}, ${cols}x${rows})`);
224
+ return { ok: true, id, pid: ptyProcess.pid, rows, cols, shell };
225
+ }
226
+ case 'exec': {
227
+ const session = sessions.get(req.id);
228
+ if (!session)
229
+ return { ok: false, error: `Session not found: ${req.id}` };
230
+ if (session.exited)
231
+ return { ok: false, error: 'Session has exited' };
232
+ if (session.appActive) {
233
+ return { ok: false, error: `Command already active: ${session.activeCommand}. Use write to interact or signal to interrupt.` };
234
+ }
235
+ const command = req.params?.command;
236
+ if (!command)
237
+ return { ok: false, error: 'command is required' };
238
+ session.appActive = true;
239
+ session.activeCommand = command;
240
+ session.pendingOutput = '';
241
+ session.pty.write(`${command}; echo "${SENTINEL}:$?"\n`);
242
+ session.lastActivity = Date.now();
243
+ return { ok: true, submitted: true };
244
+ }
245
+ case 'read': {
246
+ const session = sessions.get(req.id);
247
+ if (!session)
248
+ return { ok: false, error: `Session not found: ${req.id}` };
249
+ const waitMs = Math.min(Math.max(req.params?.ms || 100, 50), 5000);
250
+ // Wait for output to accumulate
251
+ if (session.pendingOutput.length === 0) {
252
+ await new Promise(r => setTimeout(r, waitMs));
253
+ }
254
+ const output = session.pendingOutput;
255
+ session.pendingOutput = '';
256
+ // Strip sentinel lines from output
257
+ const cleaned = output
258
+ .split('\n')
259
+ .filter(line => !line.includes(SENTINEL))
260
+ .join('\n');
261
+ return {
262
+ ok: true,
263
+ output: cleaned,
264
+ bytes: output.length,
265
+ app_active: session.appActive,
266
+ active_command: session.activeCommand || undefined,
267
+ exited: session.exited,
268
+ exit_code: session.exitCode,
269
+ };
270
+ }
271
+ case 'write': {
272
+ const session = sessions.get(req.id);
273
+ if (!session)
274
+ return { ok: false, error: `Session not found: ${req.id}` };
275
+ if (session.exited)
276
+ return { ok: false, error: 'Session has exited' };
277
+ let input = req.params?.input ?? '';
278
+ if (input === '')
279
+ input = '\n';
280
+ session.pty.write(input);
281
+ session.lastActivity = Date.now();
282
+ return { ok: true };
283
+ }
284
+ case 'screen': {
285
+ const session = sessions.get(req.id);
286
+ if (!session)
287
+ return { ok: false, error: `Session not found: ${req.id}` };
288
+ const lines = getScreenLines(session);
289
+ const buf = session.terminal.buffer.active;
290
+ return {
291
+ ok: true,
292
+ screen: lines.join('\n'),
293
+ rows: session.rows,
294
+ cols: session.cols,
295
+ cursor: { x: buf.cursorX, y: buf.cursorY },
296
+ app_active: session.appActive,
297
+ active_command: session.activeCommand || undefined,
298
+ exited: session.exited,
299
+ };
300
+ }
301
+ case 'signal': {
302
+ const session = sessions.get(req.id);
303
+ if (!session)
304
+ return { ok: false, error: `Session not found: ${req.id}` };
305
+ if (session.exited)
306
+ return { ok: false, error: 'Session has exited' };
307
+ const sig = (req.params?.signal || 'INT').toUpperCase();
308
+ if (!['INT', 'TERM', 'KILL', 'HUP'].includes(sig)) {
309
+ return { ok: false, error: `Unsupported signal: ${sig}` };
310
+ }
311
+ try {
312
+ // node-pty kill accepts signal number; use process.kill for named signals
313
+ process.kill(session.pid, `SIG${sig}`);
314
+ }
315
+ catch (err) {
316
+ return { ok: false, error: `Failed to send signal: ${err.message}` };
317
+ }
318
+ return { ok: true };
319
+ }
320
+ case 'resize': {
321
+ const session = sessions.get(req.id);
322
+ if (!session)
323
+ return { ok: false, error: `Session not found: ${req.id}` };
324
+ if (session.exited)
325
+ return { ok: false, error: 'Session has exited' };
326
+ const rows = req.params?.rows || session.rows;
327
+ const cols = req.params?.cols || session.cols;
328
+ session.pty.resize(cols, rows);
329
+ session.terminal.resize(cols, rows);
330
+ session.rows = rows;
331
+ session.cols = cols;
332
+ return { ok: true, rows, cols };
333
+ }
334
+ case 'list': {
335
+ const list = [];
336
+ for (const [, session] of sessions) {
337
+ list.push({
338
+ id: session.id,
339
+ pid: session.pid,
340
+ shell: session.shell,
341
+ cwd: session.cwd,
342
+ rows: session.rows,
343
+ cols: session.cols,
344
+ started_at: session.startedAt,
345
+ last_activity: session.lastActivity,
346
+ app_active: session.appActive,
347
+ active_command: session.activeCommand || undefined,
348
+ exited: session.exited,
349
+ exit_code: session.exitCode,
350
+ });
351
+ }
352
+ return { ok: true, sessions: list };
353
+ }
354
+ case 'stop': {
355
+ const session = sessions.get(req.id);
356
+ if (!session)
357
+ return { ok: false, error: `Session not found: ${req.id}` };
358
+ killSession(session);
359
+ sessions.delete(req.id);
360
+ log('INFO', `Session stopped: ${req.id}`);
361
+ return { ok: true };
362
+ }
363
+ case 'ping': {
364
+ return { ok: true, sessions: sessions.size, pid: process.pid };
365
+ }
366
+ default:
367
+ return { ok: false, error: `Unknown action: ${req.action}` };
368
+ }
369
+ }
370
+ // --- Socket server ---
371
+ const server = net.createServer((conn) => {
372
+ let buf = '';
373
+ conn.on('data', async (chunk) => {
374
+ buf += chunk.toString();
375
+ const nlIndex = buf.indexOf('\n');
376
+ if (nlIndex === -1)
377
+ return;
378
+ const line = buf.slice(0, nlIndex);
379
+ buf = '';
380
+ try {
381
+ const req = JSON.parse(line);
382
+ const res = await handleRequest(req);
383
+ conn.write(JSON.stringify(res) + '\n');
384
+ }
385
+ catch (err) {
386
+ conn.write(JSON.stringify({ ok: false, error: err.message || String(err) }) + '\n');
387
+ }
388
+ conn.end();
389
+ });
390
+ conn.on('error', () => { });
391
+ });
392
+ await new Promise((resolve) => {
393
+ server.listen(socketPath, () => resolve());
394
+ });
395
+ // Write PID
396
+ fs.writeFileSync(getPtyPidPath(), String(process.pid), 'utf-8');
397
+ log('INFO', `PTY server started (PID: ${process.pid}, socket: ${socketPath})`);
398
+ // Shutdown handler
399
+ function shutdown() {
400
+ log('INFO', 'PTY server shutting down');
401
+ for (const session of sessions.values()) {
402
+ killSession(session);
403
+ }
404
+ sessions.clear();
405
+ clearInterval(cleanupInterval);
406
+ server.close();
407
+ try {
408
+ fs.unlinkSync(socketPath);
409
+ }
410
+ catch { }
411
+ try {
412
+ fs.unlinkSync(getPtyPidPath());
413
+ }
414
+ catch { }
415
+ process.exit(0);
416
+ }
417
+ process.on('SIGTERM', shutdown);
418
+ process.on('SIGINT', shutdown);
419
+ // Keep alive
420
+ await new Promise(() => { });
421
+ }
422
+ //# sourceMappingURL=pty-server.js.map