@kadi.build/core 0.8.0 → 0.11.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/README.md +424 -1
- package/agent.json +19 -0
- package/dist/agent-json.d.ts +231 -0
- package/dist/agent-json.d.ts.map +1 -0
- package/dist/agent-json.js +554 -0
- package/dist/agent-json.js.map +1 -0
- package/dist/client.d.ts +41 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +102 -43
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/process-manager.d.ts +235 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +647 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/stdio-framing.d.ts +88 -0
- package/dist/stdio-framing.d.ts.map +1 -0
- package/dist/stdio-framing.js +194 -0
- package/dist/stdio-framing.js.map +1 -0
- package/dist/transports/stdio.d.ts.map +1 -1
- package/dist/transports/stdio.js +3 -181
- package/dist/transports/stdio.js.map +1 -1
- package/dist/types.d.ts +274 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +107 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +212 -0
- package/dist/utils.js.map +1 -0
- package/package.json +3 -1
- package/scripts/symlink.mjs +131 -0
- package/src/agent-json.ts +655 -0
- package/src/client.ts +120 -46
- package/src/errors.ts +15 -0
- package/src/index.ts +32 -0
- package/src/process-manager.ts +821 -0
- package/src/stdio-framing.ts +227 -0
- package/src/transports/stdio.ts +4 -221
- package/src/types.ts +291 -23
- package/src/utils.ts +246 -0
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProcessManager for kadi-core
|
|
3
|
+
*
|
|
4
|
+
* Manages background child processes with three execution modes:
|
|
5
|
+
*
|
|
6
|
+
* - **headless**: Fire-and-forget. No communication channel.
|
|
7
|
+
* - **piped**: stdout/stderr are streamed back and buffered.
|
|
8
|
+
* - **bridge**: Full JSON-RPC stdio bridge (Content-Length framing).
|
|
9
|
+
*
|
|
10
|
+
* Primary use case: Running long tasks (builds, deploys, inference) in the
|
|
11
|
+
* background so the main agent's event loop stays free for broker heartbeats.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { ProcessManager } from '@kadi.build/core';
|
|
16
|
+
*
|
|
17
|
+
* const pm = new ProcessManager();
|
|
18
|
+
*
|
|
19
|
+
* // Headless build
|
|
20
|
+
* const build = await pm.spawn('build', {
|
|
21
|
+
* command: 'docker', args: ['build', '.'], mode: 'headless',
|
|
22
|
+
* });
|
|
23
|
+
* const status = pm.getStatus('build');
|
|
24
|
+
*
|
|
25
|
+
* // Piped deploy with live output
|
|
26
|
+
* const deploy = await pm.spawn('deploy', {
|
|
27
|
+
* command: 'kadi', args: ['deploy'], mode: 'piped',
|
|
28
|
+
* });
|
|
29
|
+
* deploy.on('stdout', (data) => console.log(data));
|
|
30
|
+
*
|
|
31
|
+
* // Bridge mode for interactive workers
|
|
32
|
+
* const worker = await pm.spawn('worker', {
|
|
33
|
+
* command: 'python3', args: ['worker.py'], mode: 'bridge',
|
|
34
|
+
* });
|
|
35
|
+
* const result = await worker.request('run-inference', { prompt: 'hello' });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
import { spawn } from 'child_process';
|
|
39
|
+
import { EventEmitter } from 'events';
|
|
40
|
+
import { KadiError } from './errors.js';
|
|
41
|
+
import { StdioMessageReader, StdioMessageWriter } from './stdio-framing.js';
|
|
42
|
+
import * as protocol from './protocol.js';
|
|
43
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
44
|
+
// CONSTANTS
|
|
45
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
46
|
+
const DEFAULT_KILL_GRACE_PERIOD = 5000;
|
|
47
|
+
const DEFAULT_MAX_OUTPUT_BUFFER = 10 * 1024 * 1024; // 10 MB
|
|
48
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
49
|
+
// MANAGED PROCESS
|
|
50
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
51
|
+
/**
|
|
52
|
+
* A handle to a spawned background process.
|
|
53
|
+
*
|
|
54
|
+
* Provides event subscription, lifecycle management, and (in bridge mode)
|
|
55
|
+
* JSON-RPC request/response communication.
|
|
56
|
+
*/
|
|
57
|
+
export class ManagedProcess extends EventEmitter {
|
|
58
|
+
/** Unique identifier */
|
|
59
|
+
id;
|
|
60
|
+
/** OS process ID */
|
|
61
|
+
pid;
|
|
62
|
+
/** Execution mode */
|
|
63
|
+
mode;
|
|
64
|
+
/** Internal state */
|
|
65
|
+
_state = 'running';
|
|
66
|
+
_exitCode = null;
|
|
67
|
+
_signal = null;
|
|
68
|
+
_startedAt;
|
|
69
|
+
_endedAt = null;
|
|
70
|
+
/** Command info for getInfo() */
|
|
71
|
+
_command;
|
|
72
|
+
_args;
|
|
73
|
+
_cwd;
|
|
74
|
+
/** Output buffering (piped mode) */
|
|
75
|
+
_stdout = '';
|
|
76
|
+
_stderr = '';
|
|
77
|
+
_maxOutputBuffer;
|
|
78
|
+
/** Bridge mode internals */
|
|
79
|
+
_reader = null;
|
|
80
|
+
_writer = null;
|
|
81
|
+
_bridgeIdCounter = 1;
|
|
82
|
+
/** The underlying child process */
|
|
83
|
+
_proc;
|
|
84
|
+
/** Timeout timer for auto-kill */
|
|
85
|
+
_timeoutTimer = null;
|
|
86
|
+
/** Kill grace period */
|
|
87
|
+
_killGracePeriod;
|
|
88
|
+
/** Waiters for the exit event */
|
|
89
|
+
_exitWaiters = [];
|
|
90
|
+
constructor(id, proc, options) {
|
|
91
|
+
super();
|
|
92
|
+
this.id = id;
|
|
93
|
+
this._proc = proc;
|
|
94
|
+
this.pid = proc.pid;
|
|
95
|
+
this.mode = options.mode;
|
|
96
|
+
this._command = options.command;
|
|
97
|
+
this._args = options.args ?? [];
|
|
98
|
+
this._cwd = options.cwd ?? process.cwd();
|
|
99
|
+
this._startedAt = new Date();
|
|
100
|
+
this._killGracePeriod = options.killGracePeriod ?? DEFAULT_KILL_GRACE_PERIOD;
|
|
101
|
+
this._maxOutputBuffer = options.maxOutputBuffer ?? DEFAULT_MAX_OUTPUT_BUFFER;
|
|
102
|
+
// Set up auto-kill timeout
|
|
103
|
+
if (options.timeout && options.timeout > 0) {
|
|
104
|
+
this._timeoutTimer = setTimeout(() => {
|
|
105
|
+
this.kill().catch(() => { });
|
|
106
|
+
}, options.timeout);
|
|
107
|
+
}
|
|
108
|
+
// Set up process exit handler
|
|
109
|
+
proc.on('exit', (code, signal) => {
|
|
110
|
+
this.handleExit(code, signal);
|
|
111
|
+
});
|
|
112
|
+
proc.on('error', (error) => {
|
|
113
|
+
if (this._state === 'running') {
|
|
114
|
+
this._state = 'errored';
|
|
115
|
+
this._endedAt = new Date();
|
|
116
|
+
this.emit('error', error);
|
|
117
|
+
this.resolveExitWaiters();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// Set up output streaming based on mode
|
|
121
|
+
if (options.mode === 'piped') {
|
|
122
|
+
this.setupPipedMode(proc);
|
|
123
|
+
}
|
|
124
|
+
else if (options.mode === 'bridge') {
|
|
125
|
+
this.setupBridgeMode(proc, options);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// ─────────────────────────────────────────────────────────────────
|
|
129
|
+
// STATE GETTERS
|
|
130
|
+
// ─────────────────────────────────────────────────────────────────
|
|
131
|
+
/** Current lifecycle state */
|
|
132
|
+
get state() { return this._state; }
|
|
133
|
+
/** Whether the process is still running */
|
|
134
|
+
get isRunning() { return this._state === 'running'; }
|
|
135
|
+
// ─────────────────────────────────────────────────────────────────
|
|
136
|
+
// MODE SETUP
|
|
137
|
+
// ─────────────────────────────────────────────────────────────────
|
|
138
|
+
setupPipedMode(proc) {
|
|
139
|
+
if (proc.stdout) {
|
|
140
|
+
proc.stdout.on('data', (chunk) => {
|
|
141
|
+
const str = chunk.toString('utf-8');
|
|
142
|
+
this.appendOutput('stdout', str);
|
|
143
|
+
this.emit('stdout', str);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (proc.stderr) {
|
|
147
|
+
proc.stderr.on('data', (chunk) => {
|
|
148
|
+
const str = chunk.toString('utf-8');
|
|
149
|
+
this.appendOutput('stderr', str);
|
|
150
|
+
this.emit('stderr', str);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
setupBridgeMode(proc, _options) {
|
|
155
|
+
if (!proc.stdin || !proc.stdout) {
|
|
156
|
+
throw new KadiError('Failed to get stdio streams for bridge mode', 'PROCESS_BRIDGE_ERROR', { processId: this.id });
|
|
157
|
+
}
|
|
158
|
+
this._reader = new StdioMessageReader(proc.stdout);
|
|
159
|
+
this._writer = new StdioMessageWriter(proc.stdin);
|
|
160
|
+
// Route notifications to event emitter
|
|
161
|
+
this._reader.setNotificationHandler((notification) => {
|
|
162
|
+
this.emit('notification', notification.method, notification.params);
|
|
163
|
+
});
|
|
164
|
+
// Also capture stderr for debugging
|
|
165
|
+
if (proc.stderr) {
|
|
166
|
+
proc.stderr.on('data', (chunk) => {
|
|
167
|
+
const str = chunk.toString('utf-8');
|
|
168
|
+
this.appendOutput('stderr', str);
|
|
169
|
+
this.emit('stderr', str);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// ─────────────────────────────────────────────────────────────────
|
|
174
|
+
// OUTPUT BUFFERING
|
|
175
|
+
// ─────────────────────────────────────────────────────────────────
|
|
176
|
+
appendOutput(stream, data) {
|
|
177
|
+
if (stream === 'stdout') {
|
|
178
|
+
this._stdout += data;
|
|
179
|
+
if (this._stdout.length > this._maxOutputBuffer) {
|
|
180
|
+
// Ring buffer: keep the most recent data
|
|
181
|
+
this._stdout = this._stdout.slice(-this._maxOutputBuffer);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
this._stderr += data;
|
|
186
|
+
if (this._stderr.length > this._maxOutputBuffer) {
|
|
187
|
+
this._stderr = this._stderr.slice(-this._maxOutputBuffer);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get all buffered output.
|
|
193
|
+
* Only available in piped and bridge modes.
|
|
194
|
+
*/
|
|
195
|
+
getOutput() {
|
|
196
|
+
return { stdout: this._stdout, stderr: this._stderr };
|
|
197
|
+
}
|
|
198
|
+
// ─────────────────────────────────────────────────────────────────
|
|
199
|
+
// BRIDGE MODE API
|
|
200
|
+
// ─────────────────────────────────────────────────────────────────
|
|
201
|
+
/**
|
|
202
|
+
* Send a JSON-RPC request and await the response.
|
|
203
|
+
* Only available in bridge mode.
|
|
204
|
+
*
|
|
205
|
+
* @param method - The RPC method to invoke
|
|
206
|
+
* @param params - Parameters to send
|
|
207
|
+
* @param timeout - Timeout in ms (default: 600000)
|
|
208
|
+
* @returns The result from the response
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* const result = await proc.request('run-inference', { prompt: 'hello' });
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
async request(method, params, timeout) {
|
|
216
|
+
if (this.mode !== 'bridge') {
|
|
217
|
+
throw new KadiError('request() is only available in bridge mode', 'PROCESS_BRIDGE_ERROR', { processId: this.id, mode: this.mode });
|
|
218
|
+
}
|
|
219
|
+
if (!this.isRunning) {
|
|
220
|
+
throw new KadiError(`Process "${this.id}" is not running (state: ${this._state})`, 'PROCESS_NOT_RUNNING', { processId: this.id, state: this._state });
|
|
221
|
+
}
|
|
222
|
+
if (!this._reader || !this._writer) {
|
|
223
|
+
throw new KadiError('Bridge streams not initialized', 'PROCESS_BRIDGE_ERROR', { processId: this.id });
|
|
224
|
+
}
|
|
225
|
+
const id = this._bridgeIdCounter++;
|
|
226
|
+
const request = protocol.request(id, method, params ?? {});
|
|
227
|
+
this._writer.write(request);
|
|
228
|
+
const response = await this._reader.waitForResponse(id, timeout);
|
|
229
|
+
if (response.error) {
|
|
230
|
+
throw new KadiError(response.error.message, 'PROCESS_BRIDGE_ERROR', { processId: this.id, code: response.error.code, data: response.error.data });
|
|
231
|
+
}
|
|
232
|
+
return response.result;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Send a JSON-RPC notification (no response expected).
|
|
236
|
+
* Only available in bridge mode.
|
|
237
|
+
*/
|
|
238
|
+
notify(method, params) {
|
|
239
|
+
if (this.mode !== 'bridge') {
|
|
240
|
+
throw new KadiError('notify() is only available in bridge mode', 'PROCESS_BRIDGE_ERROR', { processId: this.id, mode: this.mode });
|
|
241
|
+
}
|
|
242
|
+
if (!this.isRunning || !this._writer) {
|
|
243
|
+
throw new KadiError(`Process "${this.id}" is not running`, 'PROCESS_NOT_RUNNING', { processId: this.id });
|
|
244
|
+
}
|
|
245
|
+
// Notifications have no id
|
|
246
|
+
const notification = {
|
|
247
|
+
jsonrpc: '2.0',
|
|
248
|
+
method,
|
|
249
|
+
params: params ?? {},
|
|
250
|
+
};
|
|
251
|
+
const json = JSON.stringify(notification);
|
|
252
|
+
const contentLength = Buffer.byteLength(json, 'utf-8');
|
|
253
|
+
const header = `Content-Length: ${contentLength}\r\n\r\n`;
|
|
254
|
+
this._proc.stdin.write(header + json);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Write raw data to the process stdin.
|
|
258
|
+
* Available in piped and bridge modes.
|
|
259
|
+
*/
|
|
260
|
+
write(data) {
|
|
261
|
+
if (this.mode === 'headless') {
|
|
262
|
+
throw new KadiError('write() is not available in headless mode', 'PROCESS_BRIDGE_ERROR', { processId: this.id });
|
|
263
|
+
}
|
|
264
|
+
if (!this.isRunning || !this._proc.stdin) {
|
|
265
|
+
throw new KadiError(`Process "${this.id}" is not running`, 'PROCESS_NOT_RUNNING', { processId: this.id });
|
|
266
|
+
}
|
|
267
|
+
this._proc.stdin.write(data);
|
|
268
|
+
}
|
|
269
|
+
// ─────────────────────────────────────────────────────────────────
|
|
270
|
+
// LIFECYCLE
|
|
271
|
+
// ─────────────────────────────────────────────────────────────────
|
|
272
|
+
/**
|
|
273
|
+
* Kill this process.
|
|
274
|
+
* Sends SIGTERM, waits for grace period, then SIGKILL.
|
|
275
|
+
*/
|
|
276
|
+
async kill(signal = 'SIGTERM') {
|
|
277
|
+
if (!this.isRunning)
|
|
278
|
+
return;
|
|
279
|
+
this._proc.kill(signal);
|
|
280
|
+
await new Promise((resolve) => {
|
|
281
|
+
const forceKillTimer = setTimeout(() => {
|
|
282
|
+
if (this.isRunning) {
|
|
283
|
+
this._proc.kill('SIGKILL');
|
|
284
|
+
}
|
|
285
|
+
resolve();
|
|
286
|
+
}, this._killGracePeriod);
|
|
287
|
+
this._proc.once('exit', () => {
|
|
288
|
+
clearTimeout(forceKillTimer);
|
|
289
|
+
resolve();
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Wait for the process to exit.
|
|
295
|
+
* Resolves immediately if already exited.
|
|
296
|
+
*/
|
|
297
|
+
waitForExit() {
|
|
298
|
+
if (!this.isRunning) {
|
|
299
|
+
return Promise.resolve({
|
|
300
|
+
exitCode: this._exitCode,
|
|
301
|
+
signal: this._signal,
|
|
302
|
+
duration: this._endedAt
|
|
303
|
+
? this._endedAt.getTime() - this._startedAt.getTime()
|
|
304
|
+
: 0,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
return new Promise((resolve) => {
|
|
308
|
+
this._exitWaiters.push({ resolve });
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Get a snapshot of this process's info.
|
|
313
|
+
*/
|
|
314
|
+
getInfo() {
|
|
315
|
+
const endedAt = this._endedAt;
|
|
316
|
+
return {
|
|
317
|
+
id: this.id,
|
|
318
|
+
pid: this.pid,
|
|
319
|
+
state: this._state,
|
|
320
|
+
mode: this.mode,
|
|
321
|
+
startedAt: this._startedAt,
|
|
322
|
+
endedAt,
|
|
323
|
+
exitCode: this._exitCode,
|
|
324
|
+
signal: this._signal,
|
|
325
|
+
duration: endedAt
|
|
326
|
+
? endedAt.getTime() - this._startedAt.getTime()
|
|
327
|
+
: null,
|
|
328
|
+
command: this._command,
|
|
329
|
+
args: this._args,
|
|
330
|
+
cwd: this._cwd,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// ─────────────────────────────────────────────────────────────────
|
|
334
|
+
// INTERNAL: EXIT HANDLING
|
|
335
|
+
// ─────────────────────────────────────────────────────────────────
|
|
336
|
+
handleExit(code, signal) {
|
|
337
|
+
if (this._timeoutTimer) {
|
|
338
|
+
clearTimeout(this._timeoutTimer);
|
|
339
|
+
this._timeoutTimer = null;
|
|
340
|
+
}
|
|
341
|
+
this._exitCode = code;
|
|
342
|
+
this._signal = signal;
|
|
343
|
+
this._endedAt = new Date();
|
|
344
|
+
if (this._state === 'running') {
|
|
345
|
+
this._state = signal ? 'killed' : 'exited';
|
|
346
|
+
}
|
|
347
|
+
// Cancel any pending bridge requests
|
|
348
|
+
if (this._reader) {
|
|
349
|
+
this._reader.cancelAll(new KadiError('Process exited', 'PROCESS_NOT_RUNNING', { processId: this.id }));
|
|
350
|
+
}
|
|
351
|
+
const exitInfo = {
|
|
352
|
+
exitCode: code,
|
|
353
|
+
signal,
|
|
354
|
+
duration: this._endedAt.getTime() - this._startedAt.getTime(),
|
|
355
|
+
};
|
|
356
|
+
this.emit('exit', exitInfo);
|
|
357
|
+
this.resolveExitWaiters();
|
|
358
|
+
}
|
|
359
|
+
resolveExitWaiters() {
|
|
360
|
+
const info = {
|
|
361
|
+
exitCode: this._exitCode,
|
|
362
|
+
signal: this._signal,
|
|
363
|
+
duration: this._endedAt
|
|
364
|
+
? this._endedAt.getTime() - this._startedAt.getTime()
|
|
365
|
+
: 0,
|
|
366
|
+
};
|
|
367
|
+
for (const waiter of this._exitWaiters) {
|
|
368
|
+
waiter.resolve(info);
|
|
369
|
+
}
|
|
370
|
+
this._exitWaiters.length = 0;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
374
|
+
// PROCESS MANAGER
|
|
375
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
376
|
+
/**
|
|
377
|
+
* Manages background child processes.
|
|
378
|
+
*
|
|
379
|
+
* Provides spawn, status, kill, and lifecycle management across
|
|
380
|
+
* headless, piped, and bridge execution modes.
|
|
381
|
+
*/
|
|
382
|
+
export class ProcessManager {
|
|
383
|
+
/** Active and recently exited processes */
|
|
384
|
+
processes = new Map();
|
|
385
|
+
/** Cleanup handler registered with process.on('exit') */
|
|
386
|
+
cleanupRegistered = false;
|
|
387
|
+
constructor() {
|
|
388
|
+
this.registerCleanup();
|
|
389
|
+
}
|
|
390
|
+
// ─────────────────────────────────────────────────────────────────
|
|
391
|
+
// SPAWN
|
|
392
|
+
// ─────────────────────────────────────────────────────────────────
|
|
393
|
+
/**
|
|
394
|
+
* Spawn a new managed background process.
|
|
395
|
+
*
|
|
396
|
+
* @param id - Unique name for this process
|
|
397
|
+
* @param options - Spawn configuration (command, args, mode, etc.)
|
|
398
|
+
* @returns A ManagedProcess handle
|
|
399
|
+
* @throws PROCESS_ALREADY_EXISTS if a running process with this id exists
|
|
400
|
+
* @throws PROCESS_SPAWN_FAILED if the command fails to start
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* ```typescript
|
|
404
|
+
* const build = await pm.spawn('build', {
|
|
405
|
+
* command: 'docker',
|
|
406
|
+
* args: ['build', '-t', 'my-app', '.'],
|
|
407
|
+
* cwd: '/path/to/project',
|
|
408
|
+
* mode: 'piped',
|
|
409
|
+
* });
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
async spawn(id, options) {
|
|
413
|
+
// Check for duplicate running process
|
|
414
|
+
const existing = this.processes.get(id);
|
|
415
|
+
if (existing && existing.isRunning) {
|
|
416
|
+
throw new KadiError(`Process "${id}" is already running`, 'PROCESS_ALREADY_EXISTS', {
|
|
417
|
+
processId: id,
|
|
418
|
+
pid: existing.pid,
|
|
419
|
+
hint: `Kill it first with pm.kill("${id}") or use a different name`,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
// Replace dead process entry
|
|
423
|
+
if (existing) {
|
|
424
|
+
this.processes.delete(id);
|
|
425
|
+
}
|
|
426
|
+
// Determine stdio configuration based on mode
|
|
427
|
+
const stdio = this.getStdioConfig(options.mode);
|
|
428
|
+
// Spawn the child process
|
|
429
|
+
let proc;
|
|
430
|
+
try {
|
|
431
|
+
proc = spawn(options.command, options.args ?? [], {
|
|
432
|
+
stdio,
|
|
433
|
+
cwd: options.cwd,
|
|
434
|
+
env: options.env
|
|
435
|
+
? { ...process.env, ...options.env }
|
|
436
|
+
: undefined,
|
|
437
|
+
detached: options.mode === 'headless', // Detach headless processes
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
throw new KadiError(`Failed to spawn process "${id}": ${options.command}`, 'PROCESS_SPAWN_FAILED', {
|
|
442
|
+
processId: id,
|
|
443
|
+
command: options.command,
|
|
444
|
+
args: options.args,
|
|
445
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
446
|
+
hint: 'Check that the command exists and is executable',
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
// Verify PID was assigned
|
|
450
|
+
if (proc.pid === undefined) {
|
|
451
|
+
throw new KadiError(`Failed to spawn process "${id}": no PID assigned`, 'PROCESS_SPAWN_FAILED', { processId: id, command: options.command });
|
|
452
|
+
}
|
|
453
|
+
// Handle spawn errors (e.g., ENOENT — command not found)
|
|
454
|
+
// This must be set up synchronously before the next tick
|
|
455
|
+
const spawnError = await new Promise((resolve) => {
|
|
456
|
+
proc.once('error', (err) => resolve(err));
|
|
457
|
+
// If no error by next tick, the spawn succeeded
|
|
458
|
+
setImmediate(() => resolve(null));
|
|
459
|
+
});
|
|
460
|
+
if (spawnError) {
|
|
461
|
+
throw new KadiError(`Failed to spawn process "${id}": ${spawnError.message}`, 'PROCESS_SPAWN_FAILED', {
|
|
462
|
+
processId: id,
|
|
463
|
+
command: options.command,
|
|
464
|
+
reason: spawnError.message,
|
|
465
|
+
hint: 'Check that the command exists and is executable',
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
// Create and track the managed process
|
|
469
|
+
const managed = new ManagedProcess(id, proc, options);
|
|
470
|
+
this.processes.set(id, managed);
|
|
471
|
+
return managed;
|
|
472
|
+
}
|
|
473
|
+
// ─────────────────────────────────────────────────────────────────
|
|
474
|
+
// STATUS & QUERYING
|
|
475
|
+
// ─────────────────────────────────────────────────────────────────
|
|
476
|
+
/**
|
|
477
|
+
* Get status information for a specific process.
|
|
478
|
+
*
|
|
479
|
+
* @param id - Process identifier
|
|
480
|
+
* @returns ProcessInfo snapshot
|
|
481
|
+
* @throws PROCESS_NOT_FOUND if no process with this id exists
|
|
482
|
+
*/
|
|
483
|
+
getStatus(id) {
|
|
484
|
+
const proc = this.processes.get(id);
|
|
485
|
+
if (!proc) {
|
|
486
|
+
throw new KadiError(`Process "${id}" not found`, 'PROCESS_NOT_FOUND', {
|
|
487
|
+
processId: id,
|
|
488
|
+
available: Array.from(this.processes.keys()),
|
|
489
|
+
hint: 'Use pm.list() to see all managed processes',
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
return proc.getInfo();
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Get the ManagedProcess handle for a specific process.
|
|
496
|
+
* Useful when you need to attach events or call bridge methods.
|
|
497
|
+
*
|
|
498
|
+
* @param id - Process identifier
|
|
499
|
+
* @returns The ManagedProcess instance
|
|
500
|
+
* @throws PROCESS_NOT_FOUND if no process with this id exists
|
|
501
|
+
*/
|
|
502
|
+
get(id) {
|
|
503
|
+
const proc = this.processes.get(id);
|
|
504
|
+
if (!proc) {
|
|
505
|
+
throw new KadiError(`Process "${id}" not found`, 'PROCESS_NOT_FOUND', {
|
|
506
|
+
processId: id,
|
|
507
|
+
available: Array.from(this.processes.keys()),
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
return proc;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Get buffered output for a process (piped/bridge modes).
|
|
514
|
+
*
|
|
515
|
+
* @param id - Process identifier
|
|
516
|
+
* @returns Buffered stdout and stderr
|
|
517
|
+
*/
|
|
518
|
+
getOutput(id) {
|
|
519
|
+
return this.get(id).getOutput();
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* List all managed processes, optionally filtered by state.
|
|
523
|
+
*
|
|
524
|
+
* @param options - Optional filter criteria
|
|
525
|
+
* @returns Array of ProcessInfo snapshots
|
|
526
|
+
*/
|
|
527
|
+
list(options) {
|
|
528
|
+
const results = [];
|
|
529
|
+
for (const proc of this.processes.values()) {
|
|
530
|
+
const info = proc.getInfo();
|
|
531
|
+
if (options?.state && info.state !== options.state)
|
|
532
|
+
continue;
|
|
533
|
+
if (options?.mode && info.mode !== options.mode)
|
|
534
|
+
continue;
|
|
535
|
+
results.push(info);
|
|
536
|
+
}
|
|
537
|
+
return results;
|
|
538
|
+
}
|
|
539
|
+
// ─────────────────────────────────────────────────────────────────
|
|
540
|
+
// LIFECYCLE MANAGEMENT
|
|
541
|
+
// ─────────────────────────────────────────────────────────────────
|
|
542
|
+
/**
|
|
543
|
+
* Wait for a process to exit.
|
|
544
|
+
*
|
|
545
|
+
* @param id - Process identifier
|
|
546
|
+
* @returns Exit information
|
|
547
|
+
*/
|
|
548
|
+
async waitFor(id) {
|
|
549
|
+
return this.get(id).waitForExit();
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Kill a specific process.
|
|
553
|
+
*
|
|
554
|
+
* @param id - Process identifier
|
|
555
|
+
* @param signal - Signal to send (default: SIGTERM)
|
|
556
|
+
*/
|
|
557
|
+
async kill(id, signal) {
|
|
558
|
+
const proc = this.processes.get(id);
|
|
559
|
+
if (!proc) {
|
|
560
|
+
throw new KadiError(`Process "${id}" not found`, 'PROCESS_NOT_FOUND', { processId: id });
|
|
561
|
+
}
|
|
562
|
+
await proc.kill(signal);
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Kill all running managed processes.
|
|
566
|
+
*/
|
|
567
|
+
async killAll() {
|
|
568
|
+
const kills = [];
|
|
569
|
+
for (const proc of this.processes.values()) {
|
|
570
|
+
if (proc.isRunning) {
|
|
571
|
+
kills.push(proc.kill());
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
await Promise.allSettled(kills);
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Remove exited/errored/killed processes from tracking.
|
|
578
|
+
*
|
|
579
|
+
* @param options - Optional criteria for which processes to remove
|
|
580
|
+
*/
|
|
581
|
+
prune(options) {
|
|
582
|
+
const now = Date.now();
|
|
583
|
+
for (const [id, proc] of this.processes) {
|
|
584
|
+
if (proc.isRunning)
|
|
585
|
+
continue;
|
|
586
|
+
const info = proc.getInfo();
|
|
587
|
+
if (options?.olderThan !== undefined) {
|
|
588
|
+
const endedAt = info.endedAt?.getTime() ?? now;
|
|
589
|
+
const ageSeconds = (now - endedAt) / 1000;
|
|
590
|
+
if (ageSeconds >= options.olderThan) {
|
|
591
|
+
this.processes.delete(id);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
// No filter — remove all non-running
|
|
596
|
+
this.processes.delete(id);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Graceful shutdown: SIGTERM all running processes, wait, then SIGKILL remainders.
|
|
602
|
+
*/
|
|
603
|
+
async shutdown() {
|
|
604
|
+
await this.killAll();
|
|
605
|
+
this.prune();
|
|
606
|
+
}
|
|
607
|
+
// ─────────────────────────────────────────────────────────────────
|
|
608
|
+
// INTERNAL
|
|
609
|
+
// ─────────────────────────────────────────────────────────────────
|
|
610
|
+
/**
|
|
611
|
+
* Determine stdio configuration based on process mode.
|
|
612
|
+
*/
|
|
613
|
+
getStdioConfig(mode) {
|
|
614
|
+
switch (mode) {
|
|
615
|
+
case 'headless':
|
|
616
|
+
return ['ignore', 'ignore', 'ignore'];
|
|
617
|
+
case 'piped':
|
|
618
|
+
return ['ignore', 'pipe', 'pipe'];
|
|
619
|
+
case 'bridge':
|
|
620
|
+
return ['pipe', 'pipe', 'pipe'];
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Register a cleanup handler to kill child processes when the parent exits.
|
|
625
|
+
*/
|
|
626
|
+
registerCleanup() {
|
|
627
|
+
if (this.cleanupRegistered)
|
|
628
|
+
return;
|
|
629
|
+
this.cleanupRegistered = true;
|
|
630
|
+
const cleanup = () => {
|
|
631
|
+
for (const proc of this.processes.values()) {
|
|
632
|
+
if (proc.isRunning) {
|
|
633
|
+
try {
|
|
634
|
+
proc.kill('SIGTERM').catch(() => { });
|
|
635
|
+
}
|
|
636
|
+
catch {
|
|
637
|
+
// Best-effort cleanup
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
process.on('exit', cleanup);
|
|
643
|
+
process.on('SIGTERM', cleanup);
|
|
644
|
+
process.on('SIGINT', cleanup);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
//# sourceMappingURL=process-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../src/process-manager.ts"],"names":[],"mappings":"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"}
|