kimi-plugin-cc 2.4.4 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +21 -0
- package/README.md +50 -34
- package/dist/commands/swarm.js +1 -1
- package/dist/dsh/companion.js +174 -0
- package/dist/dsh/index.js +156 -0
- package/dist/hooks/config-safety.js +3 -2
- package/dist/hooks/install-paths.js +2 -2
- package/dist/kimi-engine.js +1 -0
- package/dist/swarm-activation.js +1 -1
- package/dist/version.js +1 -1
- package/docs/migration.md +4 -4
- package/docs/models.md +5 -1
- package/docs/registry-releases.md +20 -17
- package/docs/safety.md +28 -15
- package/docs/swarm-recovery.md +1 -1
- package/docs/upstream-2.4.1-certification.md +54 -0
- package/package.json +41 -6
- package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/kimi-codex/dist/commands/swarm.js +1 -1
- package/plugins/kimi-codex/dist/dsh/companion.js +174 -0
- package/plugins/kimi-codex/dist/dsh/index.js +156 -0
- package/plugins/kimi-codex/dist/hooks/config-safety.js +3 -2
- package/plugins/kimi-codex/dist/hooks/install-paths.js +2 -2
- package/plugins/kimi-codex/dist/kimi-engine.js +1 -0
- package/plugins/kimi-codex/dist/swarm-activation.js +1 -1
- package/plugins/kimi-codex/dist/version.js +1 -1
- package/plugins/kimi-dsh/LICENSE +201 -0
- package/plugins/kimi-dsh/README.md +80 -0
- package/plugins/kimi-dsh/cordis.patch.yml +45 -0
- package/plugins/kimi-dsh/dist/background-spawn.js +216 -0
- package/plugins/kimi-dsh/dist/cli-cancellation.js +43 -0
- package/plugins/kimi-dsh/dist/cli-client.js +876 -0
- package/plugins/kimi-dsh/dist/commands/ask.js +336 -0
- package/plugins/kimi-dsh/dist/commands/cancel.js +216 -0
- package/plugins/kimi-dsh/dist/commands/cli-helpers.js +201 -0
- package/plugins/kimi-dsh/dist/commands/models.js +140 -0
- package/plugins/kimi-dsh/dist/commands/not-implemented.js +4 -0
- package/plugins/kimi-dsh/dist/commands/pursue.js +307 -0
- package/plugins/kimi-dsh/dist/commands/recover-swarm.js +135 -0
- package/plugins/kimi-dsh/dist/commands/registry.js +74 -0
- package/plugins/kimi-dsh/dist/commands/repair-sessions.js +160 -0
- package/plugins/kimi-dsh/dist/commands/replay.js +124 -0
- package/plugins/kimi-dsh/dist/commands/rescue.js +347 -0
- package/plugins/kimi-dsh/dist/commands/result.js +66 -0
- package/plugins/kimi-dsh/dist/commands/review-gate.js +390 -0
- package/plugins/kimi-dsh/dist/commands/review.js +220 -0
- package/plugins/kimi-dsh/dist/commands/setup.js +1047 -0
- package/plugins/kimi-dsh/dist/commands/status.js +27 -0
- package/plugins/kimi-dsh/dist/commands/swarm.js +754 -0
- package/plugins/kimi-dsh/dist/commands/worker.js +25 -0
- package/plugins/kimi-dsh/dist/companion.js +97 -0
- package/plugins/kimi-dsh/dist/config.js +22 -0
- package/plugins/kimi-dsh/dist/dsh/companion.js +174 -0
- package/plugins/kimi-dsh/dist/dsh/index.js +156 -0
- package/plugins/kimi-dsh/dist/errors.js +40 -0
- package/plugins/kimi-dsh/dist/git.js +224 -0
- package/plugins/kimi-dsh/dist/help.js +43 -0
- package/plugins/kimi-dsh/dist/hook-escape.js +71 -0
- package/plugins/kimi-dsh/dist/hooks/approval-hook.js +101 -0
- package/plugins/kimi-dsh/dist/hooks/approval-policy.js +211 -0
- package/plugins/kimi-dsh/dist/hooks/config-safety.js +653 -0
- package/plugins/kimi-dsh/dist/hooks/install-paths.js +428 -0
- package/plugins/kimi-dsh/dist/hooks/install.js +210 -0
- package/plugins/kimi-dsh/dist/hooks/managed-block.js +619 -0
- package/plugins/kimi-dsh/dist/hooks/review-gate-stop.js +19 -0
- package/plugins/kimi-dsh/dist/job-id.js +12 -0
- package/plugins/kimi-dsh/dist/job-store.js +659 -0
- package/plugins/kimi-dsh/dist/jobs.js +217 -0
- package/plugins/kimi-dsh/dist/kimi-command.js +70 -0
- package/plugins/kimi-dsh/dist/kimi-engine.js +632 -0
- package/plugins/kimi-dsh/dist/kimi-errors.js +260 -0
- package/plugins/kimi-dsh/dist/kimi-home.js +9 -0
- package/plugins/kimi-dsh/dist/kimi-timeouts.js +52 -0
- package/plugins/kimi-dsh/dist/kimi-version-probe.js +216 -0
- package/plugins/kimi-dsh/dist/logging.js +22 -0
- package/plugins/kimi-dsh/dist/mcp/binding.js +58 -0
- package/plugins/kimi-dsh/dist/mcp/job-control.js +345 -0
- package/plugins/kimi-dsh/dist/mcp/launcher.js +257 -0
- package/plugins/kimi-dsh/dist/mcp/operations.js +301 -0
- package/plugins/kimi-dsh/dist/mcp/protocol.js +275 -0
- package/plugins/kimi-dsh/dist/mcp/server.js +8 -0
- package/plugins/kimi-dsh/dist/mcp/stdio.js +78 -0
- package/plugins/kimi-dsh/dist/mcp/supervised-child.js +15 -0
- package/plugins/kimi-dsh/dist/mcp/tool-result.js +7 -0
- package/plugins/kimi-dsh/dist/mcp/workspace.js +34 -0
- package/plugins/kimi-dsh/dist/model-command.js +32 -0
- package/plugins/kimi-dsh/dist/native-v2-preflight.js +417 -0
- package/plugins/kimi-dsh/dist/package.json +1 -0
- package/plugins/kimi-dsh/dist/parsing.js +620 -0
- package/plugins/kimi-dsh/dist/paths.js +27 -0
- package/plugins/kimi-dsh/dist/plugin-data.js +98 -0
- package/plugins/kimi-dsh/dist/process-tree.js +479 -0
- package/plugins/kimi-dsh/dist/prompt-size.js +18 -0
- package/plugins/kimi-dsh/dist/render.js +177 -0
- package/plugins/kimi-dsh/dist/rescue-approval.js +865 -0
- package/plugins/kimi-dsh/dist/schemas/review-gate-output.js +43 -0
- package/plugins/kimi-dsh/dist/session-recovery.js +200 -0
- package/plugins/kimi-dsh/dist/session-title.js +293 -0
- package/plugins/kimi-dsh/dist/stream-json.js +346 -0
- package/plugins/kimi-dsh/dist/swarm-activation.js +834 -0
- package/plugins/kimi-dsh/dist/swarm-recovery.js +134 -0
- package/plugins/kimi-dsh/dist/types.js +1 -0
- package/plugins/kimi-dsh/dist/vendor/shell-quote/LICENSE +24 -0
- package/plugins/kimi-dsh/dist/vendor/shell-quote/index.js +1 -0
- package/plugins/kimi-dsh/dist/vendor/shell-quote/parse.js +229 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/LICENSE +24 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/date.js +156 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/error.js +67 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/extract.js +293 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/parse.js +164 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/primitive.js +151 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/struct.js +156 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/util.js +57 -0
- package/plugins/kimi-dsh/dist/version.js +7 -0
- package/plugins/kimi-dsh/package.json +48 -0
- package/plugins/kimi-dsh/scripts/companion.sh +37 -0
- package/plugins/kimi-dsh/scripts/review-gate-hook.sh +32 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { access, appendFile, constants as fsConstants } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { RuntimeError, formatError } from "./errors.js";
|
|
6
|
+
import { preferStableNodePath } from "./hooks/install-paths.js";
|
|
7
|
+
import { markJobFailed, waitForTerminalJob } from "./jobs.js";
|
|
8
|
+
import { JobStore } from "./job-store.js";
|
|
9
|
+
import { readArtifact } from "./render.js";
|
|
10
|
+
// Resolves to runtime/companion.ts in dev (via tsx) and dist/companion.js in production (compiled
|
|
11
|
+
// with tsc to the same relative layout). The extension is derived from the running module so the
|
|
12
|
+
// child-process spawn in startBackgroundJob targets the right entrypoint in both modes.
|
|
13
|
+
const isCompiledRuntime = import.meta.url.endsWith(".js");
|
|
14
|
+
const companionEntrypoint = fileURLToPath(new URL(isCompiledRuntime ? "./companion.js" : "./companion.ts", import.meta.url));
|
|
15
|
+
const companionProjectRoot = path.resolve(path.dirname(companionEntrypoint), "..");
|
|
16
|
+
const FAILURE_PERSIST_RETRY_DELAYS_MS = [100, 200, 400];
|
|
17
|
+
export async function startBackgroundJob(job, prompt, context, paths, options) {
|
|
18
|
+
// Background spawn and hook verification must resolve the same stable Node
|
|
19
|
+
// path: the worker re-verifies the byte-exact hook command from its own
|
|
20
|
+
// runtime path, so a different spelling than setup pinned fails closed. For
|
|
21
|
+
// example, spawn(process.execPath) can resolve …/Cellar/node/<ver>/bin/node
|
|
22
|
+
// while setup pinned /opt/homebrew/bin/node.
|
|
23
|
+
//
|
|
24
|
+
// An explicit spawn override need not be absolute because spawn() resolves
|
|
25
|
+
// bare names through PATH. The hook command separately requires an absolute
|
|
26
|
+
// override because /bin/sh -c may have a sanitized PATH. Only the no-override
|
|
27
|
+
// default uses preferStableNodePath to match the verifier.
|
|
28
|
+
const override = context.env.KIMI_PLUGIN_CC_NODE_BIN;
|
|
29
|
+
const nodeBinary = override !== undefined && override.length > 0
|
|
30
|
+
? override
|
|
31
|
+
: preferStableNodePath(process.execPath);
|
|
32
|
+
// Only fast-path the `fs.access` check for absolute/relative paths. A bare
|
|
33
|
+
// name like "node" is resolved by spawn() via PATH; the spawn `error` listener
|
|
34
|
+
// will still surface ENOENT on nextTick for that path, classified as
|
|
35
|
+
// spawnFailedErrorCode via the listener below.
|
|
36
|
+
if (nodeBinary.includes("/") || nodeBinary.includes("\\")) {
|
|
37
|
+
try {
|
|
38
|
+
await access(nodeBinary, fsConstants.X_OK);
|
|
39
|
+
}
|
|
40
|
+
catch (accessError) {
|
|
41
|
+
const code = accessError.code;
|
|
42
|
+
if (code === "ENOENT" || code === "EACCES" || code === "EPERM") {
|
|
43
|
+
const classified = new RuntimeError(options.nodeBinInvalidErrorCode, `Configured Node binary is not executable: ${nodeBinary}. Set KIMI_PLUGIN_CC_NODE_BIN to a valid Node >=22.5 executable and retry.`, options.spawnStage, accessError instanceof Error ? { cause: accessError } : undefined);
|
|
44
|
+
await persistBackgroundFailure(paths, job.job_id, classified, options.failedSummary, "node-bin-invalid");
|
|
45
|
+
throw classified;
|
|
46
|
+
}
|
|
47
|
+
// Any other errno (EIO, etc.) — fall through to the spawn attempt and let the
|
|
48
|
+
// existing spawn error path surface it.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const b64 = Buffer.from(prompt, "utf8").toString("base64");
|
|
52
|
+
const spawnArgs = isCompiledRuntime
|
|
53
|
+
? [companionEntrypoint, "worker", options.workerKind, job.job_id]
|
|
54
|
+
: ["--import", "tsx", companionEntrypoint, "worker", options.workerKind, job.job_id];
|
|
55
|
+
const child = spawn(nodeBinary, spawnArgs, {
|
|
56
|
+
cwd: companionProjectRoot,
|
|
57
|
+
detached: true,
|
|
58
|
+
stdio: "ignore",
|
|
59
|
+
env: {
|
|
60
|
+
...context.env,
|
|
61
|
+
KIMI_PLUGIN_CC_WORKSPACE_CWD: context.cwd,
|
|
62
|
+
[options.promptEnvVar]: b64,
|
|
63
|
+
[options.reusedSessionEnvVar]: options.reusedSession ? "1" : "0",
|
|
64
|
+
...(options.extraEnv ?? {}),
|
|
65
|
+
// A worker must reopen the exact store its parent selected, regardless of cwd.
|
|
66
|
+
KIMI_PLUGIN_CC_DATA: path.resolve(paths.claudePluginData),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
let spawnReportedFailure = false;
|
|
70
|
+
const spawnErrorPromise = new Promise((resolve) => {
|
|
71
|
+
let settled = false;
|
|
72
|
+
const settle = (value) => {
|
|
73
|
+
if (settled) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
settled = true;
|
|
77
|
+
resolve(value);
|
|
78
|
+
};
|
|
79
|
+
child.once("error", (spawnError) => {
|
|
80
|
+
spawnReportedFailure = true;
|
|
81
|
+
settle(new RuntimeError(options.spawnFailedErrorCode, `Background ${options.workerKind} worker failed to spawn: ${spawnError.message}`, options.spawnStage, { cause: spawnError }));
|
|
82
|
+
});
|
|
83
|
+
// Give the event loop one tick to deliver a synchronous spawn error
|
|
84
|
+
// (ENOENT fires on process.nextTick). If nothing fires by then, the
|
|
85
|
+
// child is live and we can proceed.
|
|
86
|
+
setImmediate(() => settle(null));
|
|
87
|
+
});
|
|
88
|
+
child.on("close", (exitCode, signal) => {
|
|
89
|
+
if (spawnReportedFailure) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (exitCode === null || exitCode === 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// The worker exited non-zero before any state update. If the job is
|
|
96
|
+
// still in its spawn phase, the worker never reached executeJob.
|
|
97
|
+
const classified = new RuntimeError(options.earlyExitErrorCode, `Background ${options.workerKind} worker exited with code ${exitCode}${signal ? ` (signal ${signal})` : ""} before reporting a result.`, options.spawnStage);
|
|
98
|
+
void markEarlyExit(paths, job.job_id, classified, options.failedSummary);
|
|
99
|
+
});
|
|
100
|
+
child.unref();
|
|
101
|
+
const spawnError = await spawnErrorPromise;
|
|
102
|
+
if (spawnError) {
|
|
103
|
+
await markSpawnFailure(paths, job.job_id, spawnError, options.failedSummary);
|
|
104
|
+
throw spawnError;
|
|
105
|
+
}
|
|
106
|
+
const store = new JobStore(paths);
|
|
107
|
+
try {
|
|
108
|
+
store.updateRunningJob(job.job_id, {
|
|
109
|
+
pid: child.pid ?? null,
|
|
110
|
+
phase: "worker-spawned",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
store.close();
|
|
115
|
+
}
|
|
116
|
+
if (options.wait) {
|
|
117
|
+
const completed = await waitForTerminalJob(() => new JobStore(paths), job.job_id);
|
|
118
|
+
if (!completed.final_output_path) {
|
|
119
|
+
throw new RuntimeError(options.missingResultErrorCode, describeMissingResult(completed, options.workerKind), options.waitStage);
|
|
120
|
+
}
|
|
121
|
+
return readArtifact(completed.final_output_path);
|
|
122
|
+
}
|
|
123
|
+
return `${JSON.stringify({
|
|
124
|
+
job_id: job.job_id,
|
|
125
|
+
command_type: job.command_type,
|
|
126
|
+
}, null, 2)}\n`;
|
|
127
|
+
}
|
|
128
|
+
async function markSpawnFailure(paths, jobId, classified, failedSummary) {
|
|
129
|
+
await persistBackgroundFailure(paths, jobId, classified, failedSummary, "spawn-failed");
|
|
130
|
+
}
|
|
131
|
+
async function markEarlyExit(paths, jobId, classified, failedSummary) {
|
|
132
|
+
await persistBackgroundFailure(paths, jobId, classified, failedSummary, "early-exit", (current) => {
|
|
133
|
+
// Only mark failure if the worker exited before advancing past the spawn phase.
|
|
134
|
+
// Once the worker reaches worker-running or turn-running, the child owns the
|
|
135
|
+
// job state and the parent's close listener must not race with it.
|
|
136
|
+
return current.phase === "worker-spawned" || current.phase === "queued";
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async function persistBackgroundFailure(paths, jobId, classified, failedSummary, event, shouldMark = () => true) {
|
|
140
|
+
let lastWriteError;
|
|
141
|
+
for (let attempt = 0; attempt <= FAILURE_PERSIST_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
142
|
+
let store;
|
|
143
|
+
try {
|
|
144
|
+
store = new JobStore(paths);
|
|
145
|
+
const current = store.getJob(jobId);
|
|
146
|
+
if (!current || current.status !== "running" || !shouldMark(current)) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
await markJobFailed(store, paths, current, classified, failedSummary, { phase: "failed" });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
catch (writeError) {
|
|
153
|
+
lastWriteError = writeError;
|
|
154
|
+
if (attempt < FAILURE_PERSIST_RETRY_DELAYS_MS.length) {
|
|
155
|
+
await sleep(FAILURE_PERSIST_RETRY_DELAYS_MS[attempt]);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
try {
|
|
160
|
+
store?.close();
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// The persistence attempt outcome above is the signal that matters.
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
await appendStuckJobTelemetry(paths, {
|
|
168
|
+
event,
|
|
169
|
+
job_id: jobId,
|
|
170
|
+
failed_summary: failedSummary,
|
|
171
|
+
error: {
|
|
172
|
+
code: classified.code,
|
|
173
|
+
stage: classified.stage,
|
|
174
|
+
message: classified.message,
|
|
175
|
+
details: classified.details,
|
|
176
|
+
},
|
|
177
|
+
persistence_error: formatError(lastWriteError),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
async function appendStuckJobTelemetry(paths, entry) {
|
|
181
|
+
const stuckPath = path.join(paths.pluginRoot, "stuck-jobs.jsonl");
|
|
182
|
+
try {
|
|
183
|
+
await appendFile(stuckPath, `${JSON.stringify({ ts: new Date().toISOString(), ...entry })}\n`, "utf8");
|
|
184
|
+
}
|
|
185
|
+
catch (appendError) {
|
|
186
|
+
process.stderr.write(`[kimi-plugin-cc] failed to append stuck job telemetry at ${stuckPath}: ${formatError(appendError)}\n`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function sleep(ms) {
|
|
190
|
+
return new Promise((resolve) => {
|
|
191
|
+
setTimeout(resolve, ms);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Builds a descriptive message for the rare edge case where `waitForTerminalJob`
|
|
196
|
+
* returns a terminal job whose `final_output_path` is null (e.g. `markJobFailed`'s
|
|
197
|
+
* own artifact write failed). The normal background --wait failure path returns
|
|
198
|
+
* the rendered failure artifact via `readArtifact` — that artifact already
|
|
199
|
+
* carries the code, stage, and message via `renderTerminalJobArtifact`. This
|
|
200
|
+
* helper is belt-and-suspenders for the near-unreachable case where even the
|
|
201
|
+
* failure artifact could not be written. Intended for terminal job records only;
|
|
202
|
+
* a running record would render the generic "finished without a rendered result."
|
|
203
|
+
* sentence, which would be misleading.
|
|
204
|
+
*/
|
|
205
|
+
export function describeMissingResult(job, workerKind = "rescue") {
|
|
206
|
+
const base = `Background ${workerKind} job ${job.job_id} finished without a rendered result`;
|
|
207
|
+
if (job.status === "failed" || job.status === "cancelled") {
|
|
208
|
+
const code = job.error?.code || "unknown";
|
|
209
|
+
const stage = job.error?.stage || "unknown";
|
|
210
|
+
// `||` (not `??`) intentionally: empty-string error.message or summary
|
|
211
|
+
// should fall through to the next fallback, not render as blank.
|
|
212
|
+
const detail = job.error?.message || job.summary || "no further detail";
|
|
213
|
+
return `${base} (${job.status}, ${code}, stage ${stage}): ${detail}`;
|
|
214
|
+
}
|
|
215
|
+
return `${base}.`;
|
|
216
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
export function createCliCancellationHandlers() {
|
|
3
|
+
const controller = new AbortController();
|
|
4
|
+
let cancelling = false;
|
|
5
|
+
let disposed = false;
|
|
6
|
+
const onSignal = () => {
|
|
7
|
+
if (cancelling)
|
|
8
|
+
return;
|
|
9
|
+
cancelling = true;
|
|
10
|
+
// Abort BEFORE running through dispose — the cli-client may still be
|
|
11
|
+
// in its pre-spawn signal check or its post-spawn wait, both of which
|
|
12
|
+
// honor signal.aborted.
|
|
13
|
+
try {
|
|
14
|
+
controller.abort();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// AbortController.abort() can't throw in standard runtimes, but
|
|
18
|
+
// belt-and-suspenders for runtime quirks (e.g. test harnesses
|
|
19
|
+
// overriding global AbortController).
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
// Use `process.on` (not `once`) so dispose() can remove the listener
|
|
23
|
+
// via the function reference. `process.once` wraps the callback
|
|
24
|
+
// internally and depending on Node version removeListener may miss it.
|
|
25
|
+
// Idempotency comes from the `if (cancelling) return` guard.
|
|
26
|
+
process.on("SIGTERM", onSignal);
|
|
27
|
+
process.on("SIGINT", onSignal);
|
|
28
|
+
return {
|
|
29
|
+
get cancelling() {
|
|
30
|
+
return cancelling;
|
|
31
|
+
},
|
|
32
|
+
get signal() {
|
|
33
|
+
return controller.signal;
|
|
34
|
+
},
|
|
35
|
+
dispose() {
|
|
36
|
+
if (disposed)
|
|
37
|
+
return;
|
|
38
|
+
disposed = true;
|
|
39
|
+
process.off("SIGTERM", onSignal);
|
|
40
|
+
process.off("SIGINT", onSignal);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|