@tlockney/cdp-kernel 0.4.0 → 0.5.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/dist/kernel.js CHANGED
@@ -7,7 +7,7 @@
7
7
  import { Buffer } from "node:buffer";
8
8
  import process from "node:process";
9
9
  import { randomUUID } from "node:crypto";
10
- import { decodeMessage, encodeMessage, PROTOCOL_VERSION, verify } from "./wire.js";
10
+ import { decodeMessage, encodeMessage, makeHeader, PROTOCOL_VERSION, verify } from "./wire.js";
11
11
  import { PubSocket, RouterSocket, startHeartbeat } from "./zmtp.js";
12
12
  import { Interrupted, Target } from "./target.js";
13
13
  import { BunTarget } from "./bun-target.js";
@@ -28,6 +28,10 @@ export function parseConnectionInfo(json) {
28
28
  if (typeof info[port] !== "number")
29
29
  throw new Error(`connection file: missing ${port}`);
30
30
  }
31
+ for (const field of ["ip", "key"]) {
32
+ if (info[field] !== undefined && typeof info[field] !== "string")
33
+ throw new Error(`connection file: ${field} is not a string`);
34
+ }
31
35
  if (info.transport !== undefined && info.transport !== "tcp") {
32
36
  throw new Error(`unsupported transport ${info.transport}; only tcp is implemented`);
33
37
  }
@@ -46,6 +50,18 @@ export function parseConnectionInfo(json) {
46
50
  hb_port: info.hb_port,
47
51
  };
48
52
  }
53
+ /** How long interrupt_request may wait on the target before it's answered anyway (#121). */
54
+ const INTERRUPT_REPLY_WAIT_MS = 5_000;
55
+ const NO_INPUT = { value: null, interrupted: false };
56
+ /** Shell requests other than execute_request, and their reply types. */
57
+ const SHELL_REPLIES = {
58
+ kernel_info_request: "kernel_info_reply",
59
+ complete_request: "complete_reply",
60
+ is_complete_request: "is_complete_reply",
61
+ inspect_request: "inspect_reply",
62
+ history_request: "history_reply",
63
+ comm_info_request: "comm_info_reply",
64
+ };
49
65
  export class Kernel {
50
66
  #conn;
51
67
  #runtime;
@@ -69,6 +85,16 @@ export class Kernel {
69
85
  #parent = {};
70
86
  #allowStdin = false;
71
87
  #executing = false;
88
+ /**
89
+ * stop_on_error (#123): how many of the shell messages queued when a cell
90
+ * failed are still to come, and whether the one being handled is one.
91
+ */
92
+ #abortQueued = 0;
93
+ #aborting = false;
94
+ /** The input_request waiting for its input_reply, if any. */
95
+ #input = null;
96
+ /** An interrupt is waiting on the target. */
97
+ #interrupting = false;
72
98
  // Open comms, for comm_info_request: comm_id -> target_name (#9).
73
99
  #comms = new Map();
74
100
  #shuttingDown = false;
@@ -90,8 +116,17 @@ export class Kernel {
90
116
  // Start the runtime eagerly so the first cell doesn't pay for it.
91
117
  this.#ensureTarget().catch((err) => this.#log(`target failed to start: ${err}`));
92
118
  process.on("SIGINT", () => void this.#interrupt());
119
+ // Exiting runs the exit hook that kills the runtime (src/children.ts);
120
+ // these signals' default action would skip it (#218).
121
+ for (const [signal, code] of [["SIGTERM", 143], ["SIGHUP", 129]]) {
122
+ process.on(signal, () => {
123
+ this.#shuttingDown = true;
124
+ process.exit(code);
125
+ });
126
+ }
93
127
  await Promise.all([this.#loop(this.#shell, (m, p) => this.#handleShell(m, p)),
94
- this.#loop(this.#control, (m, p) => this.#handleControl(m, p))]);
128
+ this.#loop(this.#control, (m, p) => this.#handleControl(m, p)),
129
+ this.#loop(this.#stdin, (m) => this.#handleStdin(m))]);
95
130
  }
96
131
  #log(text) {
97
132
  process.stderr.write(`[cdp-kernel] ${text}\n`);
@@ -205,9 +240,11 @@ export class Kernel {
205
240
  #targetEvents() {
206
241
  return {
207
242
  stream: (name, text) => this.#publish("stream", { name, text }),
208
- message: (msg) => void this.#onTargetMessage(msg),
243
+ message: (msg) => void this.#onTargetMessage(msg).catch((err) => this.#log(`error handling target message: ${err}`)),
209
244
  exit: (code, note) => {
210
245
  this.#target = null;
246
+ // Its prompt, if any, died with it; a late reply is dropped.
247
+ this.#input = null;
211
248
  void this.#debugger?.attachTarget(null);
212
249
  if (!this.#shuttingDown) {
213
250
  const text = note ?? `[cdp-kernel] ${this.#runtime} exited (${code}); restarting on next cell`;
@@ -225,18 +262,37 @@ export class Kernel {
225
262
  });
226
263
  return;
227
264
  }
228
- let value = null;
265
+ // The answer goes to the target that asked, not one started since.
266
+ const target = this.#target;
267
+ let answer = NO_INPUT;
229
268
  try {
230
- value = await this.#requestInput(msg.prompt, msg.password);
269
+ answer = await this.#requestInput(msg.prompt, msg.password);
231
270
  }
232
271
  finally {
233
272
  // Always answer exactly once so the target's blocking read returns.
234
- this.#target?.replyInput(value);
273
+ if (target !== null && target === this.#target)
274
+ target.replyInput(answer.value, answer.interrupted);
235
275
  }
236
276
  }
237
277
  async #interrupt() {
238
- if (this.#target && this.#executing)
278
+ // A target blocked reading its prompt's answer can't run
279
+ // terminateExecution, and would never answer it. Make the prompt throw
280
+ // instead, which ends the cell (#121).
281
+ const input = this.#input;
282
+ if (input !== null) {
283
+ this.#input = null;
284
+ input.settle({ value: null, interrupted: true });
285
+ return;
286
+ }
287
+ if (!this.#target || !this.#executing)
288
+ return;
289
+ this.#interrupting = true;
290
+ try {
239
291
  await this.#target.interrupt();
292
+ }
293
+ finally {
294
+ this.#interrupting = false;
295
+ }
240
296
  }
241
297
  // --- messaging --------------------------------------------------------------
242
298
  #encode(msgType, content, parentHeader, extra = {}) {
@@ -254,6 +310,11 @@ export class Kernel {
254
310
  async #loop(socket, handle) {
255
311
  while (!this.#shuttingDown) {
256
312
  const { peer, frames } = await socket.recv();
313
+ if (socket === this.#shell) {
314
+ this.#aborting = this.#abortQueued > 0;
315
+ if (this.#aborting)
316
+ this.#abortQueued--;
317
+ }
257
318
  try {
258
319
  const msg = decodeMessage(frames);
259
320
  // Unsigned or forged messages are dropped, as in Deno.
@@ -292,40 +353,48 @@ export class Kernel {
292
353
  }
293
354
  // --- control channel ------------------------------------------------------
294
355
  async #handleControl(msg, peer) {
356
+ // Every request is wrapped in busy/idle, as on shell (#124). JupyterLab
357
+ // treats a debug request as done only once the kernel reports idle for
358
+ // it. (Jupyter Server leaves debug_request out of the kernel's status.)
359
+ this.#status("busy", msg.header);
360
+ try {
361
+ await this.#controlRequest(this.#control, msg, peer);
362
+ }
363
+ finally {
364
+ this.#status("idle", msg.header);
365
+ }
366
+ }
367
+ /** Control requests; shutdown and interrupt may also arrive on shell (#124). */
368
+ async #controlRequest(socket, msg, peer) {
295
369
  switch (msg.header.msg_type) {
296
370
  case "kernel_info_request":
297
- this.#reply(this.#control, peer, msg, "kernel_info_reply", this.#kernelInfo());
371
+ this.#reply(socket, peer, msg, "kernel_info_reply", this.#kernelInfo());
298
372
  break;
299
373
  case "interrupt_request":
300
- await this.#interrupt();
301
- this.#reply(this.#control, peer, msg, "interrupt_reply", { status: "ok" });
374
+ // Answer even if the target doesn't (#121); the interrupt goes on.
375
+ await Promise.race([
376
+ this.#interrupt().catch((err) => this.#log(`interrupt failed: ${err}`)),
377
+ new Promise((r) => setTimeout(r, INTERRUPT_REPLY_WAIT_MS)),
378
+ ]);
379
+ this.#reply(socket, peer, msg, "interrupt_reply", { status: "ok" });
302
380
  break;
303
381
  case "debug_request": {
304
- // JupyterLab treats a debug request as done only once the kernel
305
- // reports idle for it, as ipykernel does for control messages.
306
- // (Jupyter Server leaves debug_request out of the kernel's status.)
307
- this.#status("busy", msg.header);
308
382
  let reply;
309
- try {
310
- if (this.#debugger === null) {
311
- reply = { type: "response", request_seq: msg.content.seq, command: msg.content.command, success: false, message: `${this.#runtime} targets have no debugger`, body: {} };
312
- }
313
- else {
314
- // Everything but debugInfo talks to the target, so make sure there is one.
315
- if (msg.content.command !== "debugInfo")
316
- await this.#ensureTarget().catch(() => null);
317
- reply = await this.#debugger.handle(msg.content);
318
- }
319
- this.#reply(this.#control, peer, msg, "debug_reply", reply);
383
+ if (this.#debugger === null) {
384
+ reply = { type: "response", request_seq: msg.content.seq, command: msg.content.command, success: false, message: `${this.#runtime} targets have no debugger`, body: {} };
320
385
  }
321
- finally {
322
- this.#status("idle", msg.header);
386
+ else {
387
+ // Everything but debugInfo talks to the target, so make sure there is one.
388
+ if (msg.content.command !== "debugInfo")
389
+ await this.#ensureTarget().catch(() => null);
390
+ reply = await this.#debugger.handle(msg.content);
323
391
  }
392
+ this.#reply(socket, peer, msg, "debug_reply", reply);
324
393
  break;
325
394
  }
326
395
  case "shutdown_request": {
327
396
  const restart = msg.content.restart === true;
328
- this.#reply(this.#control, peer, msg, "shutdown_reply", { status: "ok", restart });
397
+ this.#reply(socket, peer, msg, "shutdown_reply", { status: "ok", restart });
329
398
  this.#shuttingDown = true;
330
399
  this.#target?.kill();
331
400
  // Frontends spawn a fresh kernel even on restart; exit after the
@@ -367,8 +436,27 @@ export class Kernel {
367
436
  case "comm_close":
368
437
  await this.#handleComm(msg);
369
438
  break;
439
+ case "shutdown_request":
440
+ case "interrupt_request":
441
+ // Deprecated on shell, but still allowed, and ipykernel takes them.
442
+ await this.#controlRequest(this.#shell, msg, peer);
443
+ break;
444
+ default:
445
+ this.#log(`ignoring unknown shell message ${type}`);
370
446
  }
371
447
  }
448
+ catch (err) {
449
+ // E.g. the runtime failed to launch. Answer anyway, so the frontend
450
+ // doesn't wait on the request forever (#125).
451
+ const replyType = SHELL_REPLIES[type];
452
+ if (replyType !== undefined) {
453
+ const name = err instanceof Error ? err.name : "Error";
454
+ const message = err instanceof Error ? err.message : String(err);
455
+ const error = { ename: name, evalue: message, traceback: [`${name}: ${message}`] };
456
+ this.#reply(this.#shell, peer, msg, replyType, { status: "error", ...error });
457
+ }
458
+ throw err;
459
+ }
372
460
  finally {
373
461
  this.#status("idle", msg.header);
374
462
  }
@@ -417,23 +505,41 @@ export class Kernel {
417
505
  };
418
506
  }
419
507
  async #requestInput(prompt, password) {
508
+ // The target asked while an interrupt was already waiting on it.
509
+ if (this.#interrupting)
510
+ return { value: null, interrupted: true };
420
511
  const parent = this.#parent;
421
512
  if (!this.#allowStdin || typeof parent.msg_id !== "string")
422
- return null;
513
+ return NO_INPUT;
423
514
  if (this.#stdin.peers.size === 0)
424
515
  await new Promise((r) => setTimeout(r, 100));
425
516
  if (this.#stdin.peers.size === 0)
426
- return null;
427
- this.#stdin.sendAll(this.#encode("input_request", { prompt, password }, parent));
428
- for (;;) {
429
- const { frames } = await this.#stdin.recv();
430
- const reply = decodeMessage(frames);
431
- if (!verify(this.#conn.key, reply.signedParts, reply.sig))
432
- continue;
433
- if (reply.header.msg_type === "input_reply") {
434
- return typeof reply.content.value === "string" ? reply.content.value : null;
435
- }
436
- }
517
+ return NO_INPUT;
518
+ const header = makeHeader(this.#session, "input_request");
519
+ const answer = new Promise((settle) => (this.#input = { msgId: header.msg_id, settle }));
520
+ this.#stdin.sendAll(encodeMessage(this.#session, this.#conn.key, "input_request", { prompt, password }, {
521
+ header,
522
+ parentHeader: parent,
523
+ }));
524
+ return answer;
525
+ }
526
+ /** stdin carries only input_replys. */
527
+ async #handleStdin(reply) {
528
+ if (reply.header.msg_type !== "input_reply")
529
+ return;
530
+ const input = this.#input;
531
+ // A reply with nothing waiting is late (its prompt was interrupted), and
532
+ // one naming another request is for an earlier prompt: drop both rather
533
+ // than answer the next prompt with them (#121). Some clients
534
+ // (jupyter_client's input()) send no parent header, so only a present
535
+ // one is checked.
536
+ if (input === null)
537
+ return;
538
+ const parentId = reply.parentHeader.msg_id;
539
+ if (typeof parentId === "string" && parentId !== "" && parentId !== input.msgId)
540
+ return;
541
+ this.#input = null;
542
+ input.settle({ value: typeof reply.content.value === "string" ? reply.content.value : null, interrupted: false });
437
543
  }
438
544
  async #evaluateCell(target, code) {
439
545
  // Object-literal wrapping (Deno's evaluate_line_with_object_wrapping):
@@ -458,6 +564,13 @@ export class Kernel {
458
564
  }
459
565
  async #execute(msg, peer) {
460
566
  const parent = msg.header;
567
+ if (this.#aborting) {
568
+ // Queued behind a failed cell: not run, not counted (#123).
569
+ this.#status("busy", parent);
570
+ this.#reply(this.#shell, peer, msg, "execute_reply", { status: "aborted" });
571
+ this.#status("idle", parent);
572
+ return;
573
+ }
461
574
  const code = String(msg.content.code ?? "");
462
575
  const silent = msg.content.silent === true;
463
576
  if (!silent && msg.content.store_history !== false) {
@@ -468,7 +581,9 @@ export class Kernel {
468
581
  this.#parent = parent;
469
582
  this.#allowStdin = msg.content.allow_stdin === true;
470
583
  this.#status("busy", parent);
471
- this.#publish("execute_input", { code, execution_count: count });
584
+ // Silent requests broadcast nothing on iopub but status (#124).
585
+ if (!silent)
586
+ this.#publish("execute_input", { code, execution_count: count });
472
587
  if (this.#projectWarning !== null) {
473
588
  // Shown once, in the first cell, where the user will see it.
474
589
  this.#publish("stream", { name: "stderr", text: `[cdp-kernel] ${this.#projectWarning}\n` });
@@ -518,6 +633,13 @@ export class Kernel {
518
633
  this.#executing = false;
519
634
  }
520
635
  if (error) {
636
+ if (!silent && msg.content.stop_on_error !== false) {
637
+ // Abort what was queued behind this cell (JupyterLab's Run All sends
638
+ // every cell at once), but nothing sent after the client sees the
639
+ // error. A macrotask first lets bytes already received be parsed.
640
+ await new Promise((r) => setTimeout(r, 0));
641
+ this.#abortQueued = this.#shell.incoming.size;
642
+ }
521
643
  this.#publish("error", error);
522
644
  this.#reply(this.#shell, peer, msg, "execute_reply", { status: "error", execution_count: count, ...error });
523
645
  }
package/dist/mod.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  /**
2
- * cdp-kernel: Deno's Jupyter kernel, extracted to run notebook cells in any
3
- * runtime whose inspector speaks the Chrome DevTools Protocol (Node and
4
- * Deno today).
2
+ * cdp-kernel: a Jupyter kernel for TypeScript and JavaScript, extracted from
3
+ * Deno's, that runs notebooks on several JavaScript runtimes. The kernel host
4
+ * runs on Node or Deno; cells run in a separate target runtime: Node or Deno
5
+ * over the Chrome DevTools Protocol, or Bun, workerd and celld, which
6
+ * evaluate cells in-process.
5
7
  *
6
8
  * Most people want the CLI (`cdp-kernel install`). This module exposes
7
9
  * the kernel for embedding: give it a parsed connection file and a target
package/dist/mod.js CHANGED
@@ -1,7 +1,9 @@
1
1
  /**
2
- * cdp-kernel: Deno's Jupyter kernel, extracted to run notebook cells in any
3
- * runtime whose inspector speaks the Chrome DevTools Protocol (Node and
4
- * Deno today).
2
+ * cdp-kernel: a Jupyter kernel for TypeScript and JavaScript, extracted from
3
+ * Deno's, that runs notebooks on several JavaScript runtimes. The kernel host
4
+ * runs on Node or Deno; cells run in a separate target runtime: Node or Deno
5
+ * over the Chrome DevTools Protocol, or Bun, workerd and celld, which
6
+ * evaluate cells in-process.
5
7
  *
6
8
  * Most people want the CLI (`cdp-kernel install`). This module exposes
7
9
  * the kernel for embedding: give it a parsed connection file and a target
package/dist/package.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /** Kept equal to package.json and deno.jsonc by test/package.test.ts. */
2
- export declare const VERSION = "0.4.0";
2
+ export declare const VERSION = "0.5.0";
3
3
  /**
4
4
  * The UTC day this version was released, set with VERSION. The publish
5
5
  * workflow refuses a tag unless it's today or yesterday, so it can't go
6
6
  * stale: see dependencyCutoff.
7
7
  */
8
- export declare const RELEASE_DATE = "2026-10-15";
8
+ export declare const RELEASE_DATE = "2026-10-16";
9
9
  /**
10
10
  * Deno blocks packages less than 24 hours old (minimum dependency age), so
11
11
  * a kernelspec relaunching a release from JSR would fail on release day. It
package/dist/package.js CHANGED
@@ -14,13 +14,13 @@ import { join, sep } from "node:path";
14
14
  import process from "node:process";
15
15
  import { fileURLToPath } from "node:url";
16
16
  /** Kept equal to package.json and deno.jsonc by test/package.test.ts. */
17
- export const VERSION = "0.4.0";
17
+ export const VERSION = "0.5.0";
18
18
  /**
19
19
  * The UTC day this version was released, set with VERSION. The publish
20
20
  * workflow refuses a tag unless it's today or yesterday, so it can't go
21
21
  * stale: see dependencyCutoff.
22
22
  */
23
- export const RELEASE_DATE = "2026-10-15";
23
+ export const RELEASE_DATE = "2026-10-16";
24
24
  /**
25
25
  * Deno blocks packages less than 24 hours old (minimum dependency age), so
26
26
  * a kernelspec relaunching a release from JSR would fail on release day. It
@@ -82,7 +82,7 @@ export function kernelArgv(host, execPath, origin, args) {
82
82
  return [execPath, "run", "--allow-all", ...flags, entry, ...args];
83
83
  }
84
84
  export function cacheRoot() {
85
- const base = process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
85
+ const base = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
86
86
  return join(base, "cdp-kernel");
87
87
  }
88
88
  async function fetchText(url) {
package/dist/target.d.ts CHANGED
@@ -3,7 +3,7 @@ import { type CellTransformOptions, transformCell } from "./transform.ts";
3
3
  import type { DebugChannel } from "./debugger.ts";
4
4
  /** Targets driven over CDP. */
5
5
  export type CdpRuntime = "node" | "deno";
6
- /** Bun and workerd have their own backends: src/bun-target.ts, src/workerd-target.ts. */
6
+ /** Bun, workerd and celld have their own backends: src/bun-target.ts, src/workerd-target.ts, src/celld-target.ts. */
7
7
  export type RuntimeName = CdpRuntime | "bun" | "workerd" | "celld";
8
8
  /** What the kernel needs from a target runtime. */
9
9
  export interface KernelTarget {
@@ -42,7 +42,8 @@ export interface KernelTarget {
42
42
  inspect(path: string, detail: number): Promise<string | null>;
43
43
  /** The Debugger domain, on targets driven over CDP (#18). */
44
44
  readonly debug?: DebugChannel;
45
- replyInput(value: string | null): void;
45
+ /** Answers the target's pending prompt; `interrupted` makes it throw instead (#121). */
46
+ replyInput(value: string | null, interrupted?: boolean): void;
46
47
  kill(): void;
47
48
  }
48
49
  export interface LaunchOptions {
@@ -56,6 +57,13 @@ export declare function launchCommand(runtime: CdpRuntime, bootstrap: string, {
56
57
  cmd: string;
57
58
  args: string[];
58
59
  };
60
+ /**
61
+ * The esbuild binary this host's esbuild runs, for a Node target to compile
62
+ * imported TypeScript Node can't strip (#73); null when it can't be found.
63
+ * Resolved from esbuild's own location, so it's found in a node_modules
64
+ * install and in Deno's npm cache alike.
65
+ */
66
+ export declare function esbuildBinary(resolve?: (specifier: string) => string, platform?: string, arch?: string): string | null;
59
67
  /** Messages the bootstrap sends through the CDP binding. */
60
68
  export type TargetMessage = {
61
69
  kind: "iopub";
@@ -70,6 +78,8 @@ export type TargetMessage = {
70
78
  password: boolean;
71
79
  barrier: number;
72
80
  };
81
+ /** The parsed value, or undefined for text that isn't JSON. */
82
+ export declare function parseJson(text: string): unknown;
73
83
  export declare function isTargetMessage(value: unknown): value is TargetMessage;
74
84
  /**
75
85
  * Splits one output pipe into text and barrier markers, and releases them
@@ -88,6 +98,19 @@ export declare class PipeDemux {
88
98
  /** Release everything (the process died). */
89
99
  close(): void;
90
100
  }
101
+ /**
102
+ * Removes the runtime's own stderr lines (see INSPECTOR_CHATTER) and passes
103
+ * everything else on. A chatter line can arrive split across chunks, so a
104
+ * trailing partial line that could still turn out to be one is held back
105
+ * until its newline arrives; any other partial line passes straight through.
106
+ */
107
+ export declare class StderrFilter {
108
+ #private;
109
+ constructor(runtime: CdpRuntime, emit: (text: string) => void);
110
+ push(chunk: string): void;
111
+ /** Releases a held partial line (the process died). */
112
+ flush(): void;
113
+ }
91
114
  /** Promises keyed by barrier number, resolvable before or after first use. */
92
115
  export declare class Gates {
93
116
  #private;
@@ -118,6 +141,7 @@ export declare class Target implements KernelTarget {
118
141
  executable?: string;
119
142
  denoCompat?: boolean;
120
143
  denoConfig?: string;
144
+ startTimeoutMs?: number;
121
145
  }): Promise<Target>;
122
146
  /** The Debugger domain on this target's session, for src/debugger.ts. */
123
147
  get debug(): DebugChannel;
@@ -133,6 +157,10 @@ export declare class Target implements KernelTarget {
133
157
  * evaluate is abandoned here and the session stays usable.
134
158
  */
135
159
  interrupt(): Promise<void>;
160
+ /**
161
+ * For synchronous functions. With awaitPromise the inspector would wrap
162
+ * even a plain return value in a promise it holds only weakly (#55).
163
+ */
136
164
  callFunctionOn(functionDeclaration: string, args: CdpParams[]): Promise<CdpParams>;
137
165
  /** The broadcastResult step of Deno's execute flow (jupyter/mod.rs). */
138
166
  broadcastResult(executionCount: number, result: RemoteObject): Promise<void>;
@@ -158,6 +186,6 @@ export declare class Target implements KernelTarget {
158
186
  propertyNames(expression: string): Promise<string[]>;
159
187
  inspect(path: string, detail: number): Promise<string | null>;
160
188
  lexicalNames(): Promise<string[]>;
161
- replyInput(value: string | null): void;
189
+ replyInput(value: string | null, interrupted?: boolean): void;
162
190
  kill(): void;
163
191
  }