llm-cli-gateway 2.7.0 → 2.8.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/CHANGELOG.md +37 -0
- package/README.md +5 -1
- package/dist/acp/client.d.ts +78 -0
- package/dist/acp/client.js +201 -0
- package/dist/acp/errors.d.ts +63 -0
- package/dist/acp/errors.js +139 -0
- package/dist/acp/json-rpc-stdio.d.ts +71 -0
- package/dist/acp/json-rpc-stdio.js +375 -0
- package/dist/acp/process-manager.d.ts +66 -0
- package/dist/acp/process-manager.js +364 -0
- package/dist/acp/provider-registry.d.ts +24 -0
- package/dist/acp/provider-registry.js +82 -0
- package/dist/acp/types.d.ts +557 -0
- package/dist/acp/types.js +335 -0
- package/dist/async-job-manager.d.ts +2 -0
- package/dist/async-job-manager.js +45 -16
- package/dist/cache-stats.js +17 -10
- package/dist/codex-json-parser.d.ts +3 -0
- package/dist/codex-json-parser.js +17 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.js +119 -0
- package/dist/http-transport.js +2 -1
- package/dist/index.js +18 -15
- package/dist/pricing.d.ts +1 -1
- package/dist/pricing.js +67 -2
- package/dist/provider-tool-capabilities.d.ts +38 -0
- package/dist/provider-tool-capabilities.js +142 -0
- package/dist/request-context.d.ts +1 -0
- package/dist/request-helpers.d.ts +4 -4
- package/dist/upstream-contracts.d.ts +27 -0
- package/dist/upstream-contracts.js +131 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { AcpProcessExitError, AcpProtocolError, AcpTimeoutError, redactAcpMessage, } from "./errors.js";
|
|
2
|
+
import { noopLogger } from "../logger.js";
|
|
3
|
+
function isJsonRpcId(value) {
|
|
4
|
+
return typeof value === "number" || typeof value === "string";
|
|
5
|
+
}
|
|
6
|
+
function isJsonRpcErrorObject(value) {
|
|
7
|
+
return (value !== null &&
|
|
8
|
+
typeof value === "object" &&
|
|
9
|
+
typeof value.code === "number" &&
|
|
10
|
+
typeof value.message === "string");
|
|
11
|
+
}
|
|
12
|
+
export class JsonRpcStdioTransport {
|
|
13
|
+
streams;
|
|
14
|
+
logger;
|
|
15
|
+
provider;
|
|
16
|
+
defaultTimeoutMs;
|
|
17
|
+
onNotification;
|
|
18
|
+
onRequest;
|
|
19
|
+
onActivity;
|
|
20
|
+
onClose;
|
|
21
|
+
pending = new Map();
|
|
22
|
+
nextId = 1;
|
|
23
|
+
stdoutBuffer = "";
|
|
24
|
+
stderrBuffer = "";
|
|
25
|
+
closed = false;
|
|
26
|
+
exitError = null;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.streams = options.streams;
|
|
29
|
+
this.logger = options.logger ?? noopLogger;
|
|
30
|
+
this.provider = options.provider;
|
|
31
|
+
this.defaultTimeoutMs = options.defaultTimeoutMs ?? 0;
|
|
32
|
+
this.onNotification = options.onNotification;
|
|
33
|
+
this.onRequest = options.onRequest;
|
|
34
|
+
this.onActivity = options.onActivity;
|
|
35
|
+
this.onClose = options.onClose;
|
|
36
|
+
this.attach();
|
|
37
|
+
}
|
|
38
|
+
attach() {
|
|
39
|
+
const { stdout, stderr } = this.streams;
|
|
40
|
+
stdout.setEncoding("utf8");
|
|
41
|
+
stdout.on("data", (chunk) => this.onStdoutData(chunk));
|
|
42
|
+
stdout.on("error", (err) => {
|
|
43
|
+
this.logger.error("acp.transport.stdout.error", {
|
|
44
|
+
provider: this.provider,
|
|
45
|
+
errorClass: err.name,
|
|
46
|
+
});
|
|
47
|
+
this.handleStreamClose();
|
|
48
|
+
});
|
|
49
|
+
stdout.on("close", () => this.handleStreamClose());
|
|
50
|
+
stdout.on("end", () => this.handleStreamClose());
|
|
51
|
+
if (stderr) {
|
|
52
|
+
stderr.setEncoding("utf8");
|
|
53
|
+
stderr.on("data", (chunk) => this.onStderrData(chunk));
|
|
54
|
+
stderr.on("error", (err) => {
|
|
55
|
+
this.logger.error("acp.transport.stderr.error", {
|
|
56
|
+
provider: this.provider,
|
|
57
|
+
errorClass: err.name,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
onStdoutData(chunk) {
|
|
63
|
+
this.stdoutBuffer += chunk;
|
|
64
|
+
let newlineIndex = this.stdoutBuffer.indexOf("\n");
|
|
65
|
+
while (newlineIndex !== -1) {
|
|
66
|
+
const line = this.stdoutBuffer.slice(0, newlineIndex);
|
|
67
|
+
this.stdoutBuffer = this.stdoutBuffer.slice(newlineIndex + 1);
|
|
68
|
+
this.handleLine(line);
|
|
69
|
+
newlineIndex = this.stdoutBuffer.indexOf("\n");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
onStderrData(chunk) {
|
|
73
|
+
this.stderrBuffer += chunk;
|
|
74
|
+
let newlineIndex = this.stderrBuffer.indexOf("\n");
|
|
75
|
+
while (newlineIndex !== -1) {
|
|
76
|
+
const line = this.stderrBuffer.slice(0, newlineIndex).replace(/\r$/, "");
|
|
77
|
+
this.stderrBuffer = this.stderrBuffer.slice(newlineIndex + 1);
|
|
78
|
+
if (line.length > 0) {
|
|
79
|
+
const redacted = redactAcpMessage(line) !== line;
|
|
80
|
+
this.logger.debug("acp.provider.stderr", {
|
|
81
|
+
provider: this.provider,
|
|
82
|
+
bytes: Buffer.byteLength(line, "utf8"),
|
|
83
|
+
redacted,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
newlineIndex = this.stderrBuffer.indexOf("\n");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
handleLine(line) {
|
|
90
|
+
const trimmed = line.trim();
|
|
91
|
+
if (trimmed.length === 0) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
let parsed;
|
|
95
|
+
try {
|
|
96
|
+
parsed = JSON.parse(trimmed);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
this.logger.error("acp.transport.invalid_json", {
|
|
100
|
+
provider: this.provider,
|
|
101
|
+
errorClass: "SyntaxError",
|
|
102
|
+
bytes: trimmed.length,
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
107
|
+
this.logger.error("acp.transport.invalid_message", { provider: this.provider });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const hasId = isJsonRpcId(parsed.id);
|
|
111
|
+
const hasMethod = typeof parsed.method === "string";
|
|
112
|
+
if (hasMethod || hasId) {
|
|
113
|
+
this.emitActivity();
|
|
114
|
+
}
|
|
115
|
+
if (hasMethod && hasId) {
|
|
116
|
+
this.dispatchInboundRequest(parsed);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (hasMethod && !hasId) {
|
|
120
|
+
this.dispatchNotification(parsed.method, parsed.params);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (hasId) {
|
|
124
|
+
this.resolveResponse(parsed.id, parsed);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
this.logger.error("acp.transport.unroutable_message", { provider: this.provider });
|
|
128
|
+
}
|
|
129
|
+
dispatchNotification(method, params) {
|
|
130
|
+
const notification = { jsonrpc: "2.0", method, params };
|
|
131
|
+
try {
|
|
132
|
+
this.onNotification?.(notification);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
this.logger.error("acp.transport.notification_handler_error", {
|
|
136
|
+
provider: this.provider,
|
|
137
|
+
method,
|
|
138
|
+
errorClass: err instanceof Error ? err.name : "unknown",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
dispatchInboundRequest(parsed) {
|
|
143
|
+
const request = {
|
|
144
|
+
jsonrpc: "2.0",
|
|
145
|
+
id: parsed.id,
|
|
146
|
+
method: parsed.method,
|
|
147
|
+
params: parsed.params,
|
|
148
|
+
};
|
|
149
|
+
try {
|
|
150
|
+
this.onRequest?.(request);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
this.logger.error("acp.transport.request_handler_error", {
|
|
154
|
+
provider: this.provider,
|
|
155
|
+
method: request.method,
|
|
156
|
+
errorClass: err instanceof Error ? err.name : "unknown",
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
resolveResponse(id, parsed) {
|
|
161
|
+
const pending = this.pending.get(id);
|
|
162
|
+
if (!pending) {
|
|
163
|
+
this.logger.debug("acp.transport.orphan_response", { provider: this.provider });
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.pending.delete(id);
|
|
167
|
+
if (pending.timer) {
|
|
168
|
+
clearTimeout(pending.timer);
|
|
169
|
+
}
|
|
170
|
+
if (parsed.error !== undefined && parsed.error !== null) {
|
|
171
|
+
if (isJsonRpcErrorObject(parsed.error)) {
|
|
172
|
+
pending.reject(new AcpProtocolError(`ACP request ${pending.method} failed with JSON-RPC error ${parsed.error.code}.`, {
|
|
173
|
+
provider: this.provider,
|
|
174
|
+
code: parsed.error.code,
|
|
175
|
+
debug: {
|
|
176
|
+
method: pending.method,
|
|
177
|
+
code: parsed.error.code,
|
|
178
|
+
providerMessage: parsed.error.message,
|
|
179
|
+
},
|
|
180
|
+
}));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
pending.reject(new AcpProtocolError(`ACP request ${pending.method} returned a malformed error object.`, {
|
|
184
|
+
provider: this.provider,
|
|
185
|
+
debug: { method: pending.method },
|
|
186
|
+
}));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
pending.resolve(parsed.result);
|
|
190
|
+
}
|
|
191
|
+
request(method, params, timeoutMs) {
|
|
192
|
+
if (this.exitError) {
|
|
193
|
+
return Promise.reject(this.exitError);
|
|
194
|
+
}
|
|
195
|
+
if (this.closed) {
|
|
196
|
+
return Promise.reject(new AcpProcessExitError(this.provider ?? "unknown", {
|
|
197
|
+
debug: { method, reason: "transport_closed" },
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
const id = this.nextId++;
|
|
201
|
+
const effectiveTimeout = timeoutMs ?? this.defaultTimeoutMs;
|
|
202
|
+
this.emitActivity();
|
|
203
|
+
return new Promise((resolve, reject) => {
|
|
204
|
+
const pending = { method, resolve, reject, timer: null };
|
|
205
|
+
if (effectiveTimeout > 0) {
|
|
206
|
+
pending.timer = setTimeout(() => {
|
|
207
|
+
if (this.pending.get(id) === pending) {
|
|
208
|
+
this.pending.delete(id);
|
|
209
|
+
this.logger.error("acp.transport.timeout", {
|
|
210
|
+
provider: this.provider,
|
|
211
|
+
method,
|
|
212
|
+
timeoutMs: effectiveTimeout,
|
|
213
|
+
});
|
|
214
|
+
reject(new AcpTimeoutError(method, effectiveTimeout, {
|
|
215
|
+
provider: this.provider,
|
|
216
|
+
debug: { method },
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
219
|
+
}, effectiveTimeout);
|
|
220
|
+
pending.timer.unref?.();
|
|
221
|
+
}
|
|
222
|
+
this.pending.set(id, pending);
|
|
223
|
+
const frame = JSON.stringify({
|
|
224
|
+
jsonrpc: "2.0",
|
|
225
|
+
id,
|
|
226
|
+
method,
|
|
227
|
+
...(params !== undefined ? { params } : {}),
|
|
228
|
+
}) + "\n";
|
|
229
|
+
try {
|
|
230
|
+
this.streams.stdin.write(frame);
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
this.pending.delete(id);
|
|
234
|
+
if (pending.timer) {
|
|
235
|
+
clearTimeout(pending.timer);
|
|
236
|
+
}
|
|
237
|
+
reject(new AcpProcessExitError(this.provider ?? "unknown", {
|
|
238
|
+
debug: {
|
|
239
|
+
method,
|
|
240
|
+
reason: "stdin_write_failed",
|
|
241
|
+
errorClass: err instanceof Error ? err.name : "unknown",
|
|
242
|
+
},
|
|
243
|
+
}));
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
notify(method, params) {
|
|
248
|
+
if (this.exitError || this.closed) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const frame = JSON.stringify({ jsonrpc: "2.0", method, ...(params !== undefined ? { params } : {}) }) +
|
|
252
|
+
"\n";
|
|
253
|
+
try {
|
|
254
|
+
this.streams.stdin.write(frame);
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
this.logger.error("acp.transport.notify_write_failed", {
|
|
258
|
+
provider: this.provider,
|
|
259
|
+
method,
|
|
260
|
+
errorClass: err instanceof Error ? err.name : "unknown",
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
respond(id, result) {
|
|
265
|
+
if (this.exitError || this.closed) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const frame = JSON.stringify({ jsonrpc: "2.0", id, result }) + "\n";
|
|
269
|
+
try {
|
|
270
|
+
this.streams.stdin.write(frame);
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
this.logger.error("acp.transport.respond_write_failed", {
|
|
274
|
+
provider: this.provider,
|
|
275
|
+
errorClass: err instanceof Error ? err.name : "unknown",
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
respondError(id, error) {
|
|
280
|
+
if (this.exitError || this.closed) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const frame = JSON.stringify({
|
|
284
|
+
jsonrpc: "2.0",
|
|
285
|
+
id,
|
|
286
|
+
error: {
|
|
287
|
+
code: error.code,
|
|
288
|
+
message: error.message,
|
|
289
|
+
...(error.data !== undefined ? { data: error.data } : {}),
|
|
290
|
+
},
|
|
291
|
+
}) + "\n";
|
|
292
|
+
try {
|
|
293
|
+
this.streams.stdin.write(frame);
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
this.logger.error("acp.transport.respond_error_write_failed", {
|
|
297
|
+
provider: this.provider,
|
|
298
|
+
errorClass: err instanceof Error ? err.name : "unknown",
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
handleProcessExit(exitCode, signal) {
|
|
303
|
+
if (this.exitError) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
this.exitError = new AcpProcessExitError(this.provider ?? "unknown", {
|
|
307
|
+
exitCode,
|
|
308
|
+
signal,
|
|
309
|
+
debug: { exitCode, signal },
|
|
310
|
+
});
|
|
311
|
+
this.logger.error("acp.process.exit", {
|
|
312
|
+
provider: this.provider,
|
|
313
|
+
exitCode,
|
|
314
|
+
signal,
|
|
315
|
+
pending: this.pending.size,
|
|
316
|
+
});
|
|
317
|
+
this.failPending(this.exitError);
|
|
318
|
+
this.closed = true;
|
|
319
|
+
}
|
|
320
|
+
handleStreamClose() {
|
|
321
|
+
if (this.exitError || this.closed) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
this.exitError = new AcpProcessExitError(this.provider ?? "unknown", {
|
|
325
|
+
debug: { reason: "stdout_closed" },
|
|
326
|
+
});
|
|
327
|
+
this.logger.error("acp.transport.stdout_closed", {
|
|
328
|
+
provider: this.provider,
|
|
329
|
+
pending: this.pending.size,
|
|
330
|
+
});
|
|
331
|
+
this.failPending(this.exitError);
|
|
332
|
+
this.closed = true;
|
|
333
|
+
this.emitClose();
|
|
334
|
+
}
|
|
335
|
+
emitActivity() {
|
|
336
|
+
try {
|
|
337
|
+
this.onActivity?.();
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
emitClose() {
|
|
343
|
+
try {
|
|
344
|
+
this.onClose?.();
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
failPending(error) {
|
|
350
|
+
for (const [id, pending] of this.pending) {
|
|
351
|
+
if (pending.timer) {
|
|
352
|
+
clearTimeout(pending.timer);
|
|
353
|
+
}
|
|
354
|
+
this.pending.delete(id);
|
|
355
|
+
pending.reject(error);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
dispose() {
|
|
359
|
+
if (this.closed) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
this.closed = true;
|
|
363
|
+
const error = this.exitError ??
|
|
364
|
+
new AcpProcessExitError(this.provider ?? "unknown", {
|
|
365
|
+
debug: { reason: "disposed" },
|
|
366
|
+
});
|
|
367
|
+
this.failPending(error);
|
|
368
|
+
}
|
|
369
|
+
get pendingCount() {
|
|
370
|
+
return this.pending.size;
|
|
371
|
+
}
|
|
372
|
+
get isClosed() {
|
|
373
|
+
return this.closed;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
import { AcpClient, type AcpClientCallbacks, type HostServices } from "./client.js";
|
|
3
|
+
import { AcpError } from "./errors.js";
|
|
4
|
+
import { JsonRpcStdioTransport } from "./json-rpc-stdio.js";
|
|
5
|
+
import type { AcpConfig, AcpProviderConfig } from "../config.js";
|
|
6
|
+
import type { Logger } from "../logger.js";
|
|
7
|
+
import type { CliType } from "../session-manager.js";
|
|
8
|
+
export type ProcessEnv = Record<string, string | undefined>;
|
|
9
|
+
export type TerminationSignal = "SIGTERM" | "SIGKILL" | "SIGINT" | "SIGHUP";
|
|
10
|
+
export interface AcpChildProcess {
|
|
11
|
+
readonly pid?: number | undefined;
|
|
12
|
+
readonly stdin: Writable | null;
|
|
13
|
+
readonly stdout: Readable | null;
|
|
14
|
+
readonly stderr: Readable | null;
|
|
15
|
+
on(event: "exit", listener: (code: number | null, signal: TerminationSignal | null) => void): unknown;
|
|
16
|
+
on(event: "error", listener: (err: Error) => void): unknown;
|
|
17
|
+
kill(signal?: TerminationSignal | number): boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface ResolvedAcpSpawn {
|
|
20
|
+
readonly command: string;
|
|
21
|
+
readonly args: readonly string[];
|
|
22
|
+
readonly cwd: string;
|
|
23
|
+
readonly env: ProcessEnv;
|
|
24
|
+
}
|
|
25
|
+
export type AcpSpawnFn = (resolved: ResolvedAcpSpawn) => AcpChildProcess;
|
|
26
|
+
export type AcpProcessState = "starting" | "running" | "exited" | "quarantined";
|
|
27
|
+
export interface AcpProcessManagerOptions {
|
|
28
|
+
readonly config: AcpConfig;
|
|
29
|
+
readonly logger?: Logger;
|
|
30
|
+
readonly spawn?: AcpSpawnFn;
|
|
31
|
+
readonly baseEnv?: ProcessEnv;
|
|
32
|
+
}
|
|
33
|
+
export interface StartProviderOptions {
|
|
34
|
+
readonly provider: CliType;
|
|
35
|
+
readonly cwd?: string;
|
|
36
|
+
readonly hostServices: HostServices;
|
|
37
|
+
readonly callbacks?: AcpClientCallbacks;
|
|
38
|
+
readonly idleTimeoutMs?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface ManagedAcpProcess {
|
|
41
|
+
readonly provider: CliType;
|
|
42
|
+
readonly pid: number | undefined;
|
|
43
|
+
readonly transport: JsonRpcStdioTransport;
|
|
44
|
+
readonly client: AcpClient;
|
|
45
|
+
readonly state: AcpProcessState;
|
|
46
|
+
readonly exitCode: number | null;
|
|
47
|
+
readonly signal: string | null;
|
|
48
|
+
readonly terminalError: AcpError | null;
|
|
49
|
+
readonly resolved: ResolvedAcpSpawn;
|
|
50
|
+
shutdown(signal?: TerminationSignal): void;
|
|
51
|
+
isHealthy(): boolean;
|
|
52
|
+
}
|
|
53
|
+
export declare function buildProviderEnv(provider: CliType, providerConfig: AcpProviderConfig, baseEnv: ProcessEnv): ProcessEnv;
|
|
54
|
+
export declare function resolveProviderSpawn(provider: CliType, config: AcpConfig, baseEnv: ProcessEnv, cwd?: string): ResolvedAcpSpawn;
|
|
55
|
+
export declare const defaultSpawn: AcpSpawnFn;
|
|
56
|
+
export declare class AcpProcessManager {
|
|
57
|
+
private readonly config;
|
|
58
|
+
private readonly logger;
|
|
59
|
+
private readonly spawnFn;
|
|
60
|
+
private readonly baseEnv;
|
|
61
|
+
private readonly live;
|
|
62
|
+
constructor(options: AcpProcessManagerOptions);
|
|
63
|
+
start(options: StartProviderOptions): Promise<ManagedAcpProcess>;
|
|
64
|
+
shutdownAll(signal?: TerminationSignal): void;
|
|
65
|
+
get liveCount(): number;
|
|
66
|
+
}
|