@ai-matrx/desktop-protocol 0.17.0 → 0.17.2
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 +30 -0
- package/dist/client.cjs +18 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/client.js +18 -1
- package/dist/client.js.map +1 -1
- package/dist/frame.cjs +18 -1
- package/dist/frame.cjs.map +1 -1
- package/dist/frame.js +18 -1
- package/dist/frame.js.map +1 -1
- package/dist/index.cjs +20 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -8
- package/dist/index.d.ts +3 -8
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/{models-Dj_B9wHj.d.cts → models-QWCUcQuU.d.cts} +8 -2
- package/dist/{models-Dj_B9wHj.d.ts → models-QWCUcQuU.d.ts} +8 -2
- package/dist/react.cjs +71 -2
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +27 -1
- package/dist/react.d.ts +27 -1
- package/dist/react.js +71 -2
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/dist/frame.cjs
CHANGED
|
@@ -38,7 +38,7 @@ __export(frame_exports, {
|
|
|
38
38
|
module.exports = __toCommonJS(frame_exports);
|
|
39
39
|
|
|
40
40
|
// src/error.ts
|
|
41
|
-
var DesktopProtocolError = class extends Error {
|
|
41
|
+
var DesktopProtocolError = class _DesktopProtocolError extends Error {
|
|
42
42
|
code;
|
|
43
43
|
retryable;
|
|
44
44
|
data;
|
|
@@ -49,6 +49,23 @@ var DesktopProtocolError = class extends Error {
|
|
|
49
49
|
this.retryable = options.retryable ?? false;
|
|
50
50
|
this.data = options.data;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry
|
|
54
|
+
* (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an
|
|
55
|
+
* error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.
|
|
56
|
+
*/
|
|
57
|
+
static from(caught) {
|
|
58
|
+
if (caught instanceof _DesktopProtocolError) return caught;
|
|
59
|
+
const e = caught;
|
|
60
|
+
if (e && typeof e === "object" && e.name === "DesktopProtocolError" && typeof e.code === "string") {
|
|
61
|
+
return new _DesktopProtocolError(e.code, typeof e.message === "string" ? e.message : String(e.code), {
|
|
62
|
+
retryable: e.retryable === true,
|
|
63
|
+
...e.data && typeof e.data === "object" ? { data: e.data } : {},
|
|
64
|
+
cause: caught
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return new _DesktopProtocolError("INTERNAL", caught instanceof Error ? caught.message : String(caught), { cause: caught });
|
|
68
|
+
}
|
|
52
69
|
/** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */
|
|
53
70
|
toBody() {
|
|
54
71
|
return this.data === void 0 ? { code: this.code, message: this.message, retryable: this.retryable } : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };
|
package/dist/frame.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/frame.ts","../src/error.ts"],"sourcesContent":["/**\n * `@ai-matrx/desktop-protocol/frame` — the 16-byte binary frame codec. ZOD-FREE by design: the\n * relay Worker's hot path imports only this entry and must stay tiny.\n *\n * off size field notes\n * 0 u8 version = 1\n * 1 u8 flags bit0 FIN (last frame of this stream), bit1 REPLAY (from ring buffer),\n * bits2-7 reserved = 0\n * 2 u8 channel see FrameChannel\n * 3 u8 reserved = 0\n * 4 u32 cid relay connection id. 0 on every client↔relay hop; the relay stamps it on\n * the relay→device hop and strips it on the way back (rewrites 4 bytes in\n * place — the relay never parses payloads).\n * 8 u32 sid stream id, announced by the core in stream{meta:{kind:\"open\",sid}}\n * 12 u32 seq per-stream frame sequence, starts at 1, strictly +1\n *\n * Big-endian. Payload length = message length − 16 (one frame = one WS binary message).\n * Python struct format: \">BBBBIII\". The zod twin (FrameHeader in the root entry, and so the JSON\n * Schema and Pydantic model) is checked against this file by src/frame.test.ts.\n */\nimport { DesktopProtocolError } from \"./error\";\n\nexport const FRAME_HEADER_BYTES = 16 as const;\nexport const FRAME_VERSION = 1 as const;\nexport const FRAME_STRUCT_FORMAT = \">BBBBIII\" as const;\nexport const FrameFlag = { FIN: 0b01, REPLAY: 0b10 } as const;\nexport const FrameChannel = {\n /** core→client: pty output, fs.read_stream bytes */\n DATA: 0,\n /** core→client: stderr of a non-pty process (reserved for exec.spawn in v1.1) */\n STDERR: 1,\n /** client→core: keystrokes / stdin to sid */\n STDIN: 2,\n /** core→client: serialized terminal screen (attach when since_seq is outside the ring) */\n SNAPSHOT: 3,\n /** relay↔device hop only: a JSON envelope carried inside a binary frame so the relay can route\n * by cid without parsing JSON. Never seen by clients. */\n ENVELOPE: 255,\n} as const;\n\nexport type FrameChannelValue = (typeof FrameChannel)[keyof typeof FrameChannel];\n\nexport interface FrameHeader {\n version: typeof FRAME_VERSION;\n /** 0..3 — FrameFlag bits. */\n flags: number;\n channel: FrameChannelValue;\n cid: number;\n sid: number;\n seq: number;\n}\n\nexport interface DecodedFrame {\n header: FrameHeader;\n /** A VIEW into the input buffer (no copy). Copy it if you keep it past the input's lifetime. */\n payload: Uint8Array;\n}\n\nconst U32_MAX = 4_294_967_295;\nconst CHANNELS: ReadonlySet<number> = new Set(Object.values(FrameChannel));\n\nfunction frameError(message: string): DesktopProtocolError {\n return new DesktopProtocolError(\"PROTOCOL_ERROR\", message);\n}\n\nfunction checkU32(name: string, value: number): void {\n if (!Number.isInteger(value) || value < 0 || value > U32_MAX) throw frameError(`frame ${name} is not a u32: ${value}`);\n}\n\n/** Validate a header without zod. Throws DesktopProtocolError(PROTOCOL_ERROR). */\nexport function assertFrameHeader(h: FrameHeader): void {\n if (h.version !== FRAME_VERSION) throw frameError(`unsupported frame version ${String(h.version)}`);\n if (!Number.isInteger(h.flags) || h.flags < 0 || h.flags > 3) throw frameError(`reserved frame flag bits set: ${h.flags}`);\n if (!CHANNELS.has(h.channel)) throw frameError(`unknown frame channel ${String(h.channel)}`);\n checkU32(\"cid\", h.cid);\n checkU32(\"sid\", h.sid);\n checkU32(\"seq\", h.seq);\n}\n\nexport function encodeFrame(h: FrameHeader, payload: Uint8Array): Uint8Array {\n assertFrameHeader(h);\n const out = new Uint8Array(FRAME_HEADER_BYTES + payload.byteLength);\n const v = new DataView(out.buffer);\n v.setUint8(0, h.version);\n v.setUint8(1, h.flags);\n v.setUint8(2, h.channel);\n v.setUint8(3, 0);\n v.setUint32(4, h.cid);\n v.setUint32(8, h.sid);\n v.setUint32(12, h.seq);\n out.set(payload, FRAME_HEADER_BYTES);\n return out;\n}\n\nexport function decodeFrame(buf: ArrayBuffer | Uint8Array): DecodedFrame {\n const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);\n if (bytes.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${bytes.byteLength} bytes`);\n const v = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n if (v.getUint8(3) !== 0) throw frameError(\"reserved frame byte 3 is not 0\");\n const header = {\n version: v.getUint8(0),\n flags: v.getUint8(1),\n channel: v.getUint8(2),\n cid: v.getUint32(4),\n sid: v.getUint32(8),\n seq: v.getUint32(12),\n } as FrameHeader;\n assertFrameHeader(header);\n return { header, payload: bytes.subarray(FRAME_HEADER_BYTES) };\n}\n\n/** Read only the 4-byte cid (relay routing) without decoding the rest. */\nexport function readCid(frame: Uint8Array): number {\n if (frame.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${frame.byteLength} bytes`);\n return new DataView(frame.buffer, frame.byteOffset, frame.byteLength).getUint32(4);\n}\n\n/** Relay hot path: rewrite only the cid IN PLACE, never touch the payload. Returns the same array. */\nexport function stampCid(frame: Uint8Array, cid: number): Uint8Array {\n if (frame.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${frame.byteLength} bytes`);\n checkU32(\"cid\", cid);\n new DataView(frame.buffer, frame.byteOffset, frame.byteLength).setUint32(4, cid);\n return frame;\n}\n\nexport function hasFlag(header: FrameHeader, flag: (typeof FrameFlag)[keyof typeof FrameFlag]): boolean {\n return (header.flags & flag) !== 0;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Relay ↔ device hop (never seen by clients). Every JSON envelope rides an ENVELOPE frame with\n// flags 0, sid 0, seq 0 and the client's cid; byte frames carry that cid too.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** cid 0 on an ENVELOPE frame from the device: the relay delivers it to every client. */\nexport const BROADCAST_CID = 0 as const;\n\n/**\n * Connection end: an ENVELOPE frame with FIN, sid 0, seq 0 and NO payload, for connection `cid`.\n * Relay → device: client `cid` is gone — cancel its streams, detach its sessions (idempotent; it\n * may arrive more than once). Device → relay: close client `cid`.\n */\nexport function connectionEndFrame(cid: number): Uint8Array {\n if (cid === BROADCAST_CID) throw frameError(\"a connection end needs a client cid, not the broadcast cid 0\");\n return encodeFrame({ version: FRAME_VERSION, flags: FrameFlag.FIN, channel: FrameChannel.ENVELOPE, cid, sid: 0, seq: 0 }, new Uint8Array(0));\n}\n\n/** True for a decoded connection-end frame (see connectionEndFrame). */\nexport function isConnectionEnd(frame: DecodedFrame): boolean {\n const h = frame.header;\n return h.channel === FrameChannel.ENVELOPE && hasFlag(h, FrameFlag.FIN) && frame.payload.byteLength === 0 && h.cid !== BROADCAST_CID;\n}\n","/**\n * The one error type every package entry throws. It carries the wire ErrorCode (SPEC §5), so a\n * caller branches on `code`/`retryable`/`data.reason` and never parses a message. Zod-free on\n * purpose: `./frame` (the relay hot path) throws it without pulling the schema graph.\n */\nimport type { ErrorCodeName, ErrorData } from \"./types\";\n\nexport class DesktopProtocolError extends Error {\n readonly code: ErrorCodeName;\n readonly retryable: boolean;\n readonly data: ErrorData | undefined;\n\n constructor(code: ErrorCodeName, message: string, options: { retryable?: boolean; data?: ErrorData; cause?: unknown } = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"DesktopProtocolError\";\n this.code = code;\n this.retryable = options.retryable ?? false;\n this.data = options.data;\n }\n\n /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */\n toBody(): { code: ErrorCodeName; message: string; retryable: boolean; data?: ErrorData } {\n return this.data === undefined\n ? { code: this.code, message: this.message, retryable: this.retryable }\n : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };\n }\n}\n\nexport function isDesktopProtocolError(value: unknown): value is DesktopProtocolError {\n return value instanceof DesktopProtocolError || (value instanceof Error && value.name === \"DesktopProtocolError\" && \"code\" in value);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/frame.ts","../src/error.ts"],"sourcesContent":["/**\n * `@ai-matrx/desktop-protocol/frame` — the 16-byte binary frame codec. ZOD-FREE by design: the\n * relay Worker's hot path imports only this entry and must stay tiny.\n *\n * off size field notes\n * 0 u8 version = 1\n * 1 u8 flags bit0 FIN (last frame of this stream), bit1 REPLAY (from ring buffer),\n * bits2-7 reserved = 0\n * 2 u8 channel see FrameChannel\n * 3 u8 reserved = 0\n * 4 u32 cid relay connection id. 0 on every client↔relay hop; the relay stamps it on\n * the relay→device hop and strips it on the way back (rewrites 4 bytes in\n * place — the relay never parses payloads).\n * 8 u32 sid stream id, announced by the core in stream{meta:{kind:\"open\",sid}}\n * 12 u32 seq per-stream frame sequence, starts at 1, strictly +1\n *\n * Big-endian. Payload length = message length − 16 (one frame = one WS binary message).\n * Python struct format: \">BBBBIII\". The zod twin (FrameHeader in the root entry, and so the JSON\n * Schema and Pydantic model) is checked against this file by src/frame.test.ts.\n */\nimport { DesktopProtocolError } from \"./error\";\n\nexport const FRAME_HEADER_BYTES = 16 as const;\nexport const FRAME_VERSION = 1 as const;\nexport const FRAME_STRUCT_FORMAT = \">BBBBIII\" as const;\nexport const FrameFlag = { FIN: 0b01, REPLAY: 0b10 } as const;\nexport const FrameChannel = {\n /** core→client: pty output, fs.read_stream bytes */\n DATA: 0,\n /** core→client: stderr of a non-pty process (reserved for exec.spawn in v1.1) */\n STDERR: 1,\n /** client→core: keystrokes / stdin to sid */\n STDIN: 2,\n /** core→client: serialized terminal screen (attach when since_seq is outside the ring) */\n SNAPSHOT: 3,\n /** relay↔device hop only: a JSON envelope carried inside a binary frame so the relay can route\n * by cid without parsing JSON. Never seen by clients. */\n ENVELOPE: 255,\n} as const;\n\nexport type FrameChannelValue = (typeof FrameChannel)[keyof typeof FrameChannel];\n\nexport interface FrameHeader {\n version: typeof FRAME_VERSION;\n /** 0..3 — FrameFlag bits. */\n flags: number;\n channel: FrameChannelValue;\n cid: number;\n sid: number;\n seq: number;\n}\n\nexport interface DecodedFrame {\n header: FrameHeader;\n /** A VIEW into the input buffer (no copy). Copy it if you keep it past the input's lifetime. */\n payload: Uint8Array;\n}\n\nconst U32_MAX = 4_294_967_295;\nconst CHANNELS: ReadonlySet<number> = new Set(Object.values(FrameChannel));\n\nfunction frameError(message: string): DesktopProtocolError {\n return new DesktopProtocolError(\"PROTOCOL_ERROR\", message);\n}\n\nfunction checkU32(name: string, value: number): void {\n if (!Number.isInteger(value) || value < 0 || value > U32_MAX) throw frameError(`frame ${name} is not a u32: ${value}`);\n}\n\n/** Validate a header without zod. Throws DesktopProtocolError(PROTOCOL_ERROR). */\nexport function assertFrameHeader(h: FrameHeader): void {\n if (h.version !== FRAME_VERSION) throw frameError(`unsupported frame version ${String(h.version)}`);\n if (!Number.isInteger(h.flags) || h.flags < 0 || h.flags > 3) throw frameError(`reserved frame flag bits set: ${h.flags}`);\n if (!CHANNELS.has(h.channel)) throw frameError(`unknown frame channel ${String(h.channel)}`);\n checkU32(\"cid\", h.cid);\n checkU32(\"sid\", h.sid);\n checkU32(\"seq\", h.seq);\n}\n\nexport function encodeFrame(h: FrameHeader, payload: Uint8Array): Uint8Array {\n assertFrameHeader(h);\n const out = new Uint8Array(FRAME_HEADER_BYTES + payload.byteLength);\n const v = new DataView(out.buffer);\n v.setUint8(0, h.version);\n v.setUint8(1, h.flags);\n v.setUint8(2, h.channel);\n v.setUint8(3, 0);\n v.setUint32(4, h.cid);\n v.setUint32(8, h.sid);\n v.setUint32(12, h.seq);\n out.set(payload, FRAME_HEADER_BYTES);\n return out;\n}\n\nexport function decodeFrame(buf: ArrayBuffer | Uint8Array): DecodedFrame {\n const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);\n if (bytes.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${bytes.byteLength} bytes`);\n const v = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n if (v.getUint8(3) !== 0) throw frameError(\"reserved frame byte 3 is not 0\");\n const header = {\n version: v.getUint8(0),\n flags: v.getUint8(1),\n channel: v.getUint8(2),\n cid: v.getUint32(4),\n sid: v.getUint32(8),\n seq: v.getUint32(12),\n } as FrameHeader;\n assertFrameHeader(header);\n return { header, payload: bytes.subarray(FRAME_HEADER_BYTES) };\n}\n\n/** Read only the 4-byte cid (relay routing) without decoding the rest. */\nexport function readCid(frame: Uint8Array): number {\n if (frame.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${frame.byteLength} bytes`);\n return new DataView(frame.buffer, frame.byteOffset, frame.byteLength).getUint32(4);\n}\n\n/** Relay hot path: rewrite only the cid IN PLACE, never touch the payload. Returns the same array. */\nexport function stampCid(frame: Uint8Array, cid: number): Uint8Array {\n if (frame.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${frame.byteLength} bytes`);\n checkU32(\"cid\", cid);\n new DataView(frame.buffer, frame.byteOffset, frame.byteLength).setUint32(4, cid);\n return frame;\n}\n\nexport function hasFlag(header: FrameHeader, flag: (typeof FrameFlag)[keyof typeof FrameFlag]): boolean {\n return (header.flags & flag) !== 0;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Relay ↔ device hop (never seen by clients). Every JSON envelope rides an ENVELOPE frame with\n// flags 0, sid 0, seq 0 and the client's cid; byte frames carry that cid too.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** cid 0 on an ENVELOPE frame from the device: the relay delivers it to every client. */\nexport const BROADCAST_CID = 0 as const;\n\n/**\n * Connection end: an ENVELOPE frame with FIN, sid 0, seq 0 and NO payload, for connection `cid`.\n * Relay → device: client `cid` is gone — cancel its streams, detach its sessions (idempotent; it\n * may arrive more than once). Device → relay: close client `cid`.\n */\nexport function connectionEndFrame(cid: number): Uint8Array {\n if (cid === BROADCAST_CID) throw frameError(\"a connection end needs a client cid, not the broadcast cid 0\");\n return encodeFrame({ version: FRAME_VERSION, flags: FrameFlag.FIN, channel: FrameChannel.ENVELOPE, cid, sid: 0, seq: 0 }, new Uint8Array(0));\n}\n\n/** True for a decoded connection-end frame (see connectionEndFrame). */\nexport function isConnectionEnd(frame: DecodedFrame): boolean {\n const h = frame.header;\n return h.channel === FrameChannel.ENVELOPE && hasFlag(h, FrameFlag.FIN) && frame.payload.byteLength === 0 && h.cid !== BROADCAST_CID;\n}\n","/**\n * The one error type every package entry throws. It carries the wire ErrorCode (SPEC §5), so a\n * caller branches on `code`/`retryable`/`data.reason` and never parses a message. Zod-free on\n * purpose: `./frame` (the relay hot path) throws it without pulling the schema graph.\n */\nimport type { ErrorCodeName, ErrorData } from \"./types\";\n\nexport class DesktopProtocolError extends Error {\n readonly code: ErrorCodeName;\n readonly retryable: boolean;\n readonly data: ErrorData | undefined;\n\n constructor(code: ErrorCodeName, message: string, options: { retryable?: boolean; data?: ErrorData; cause?: unknown } = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"DesktopProtocolError\";\n this.code = code;\n this.retryable = options.retryable ?? false;\n this.data = options.data;\n }\n\n /**\n * Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry\n * (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an\n * error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.\n */\n static from(caught: unknown): DesktopProtocolError {\n if (caught instanceof DesktopProtocolError) return caught;\n const e = caught as { name?: unknown; code?: unknown; message?: unknown; retryable?: unknown; data?: unknown } | null;\n if (e && typeof e === \"object\" && e.name === \"DesktopProtocolError\" && typeof e.code === \"string\") {\n return new DesktopProtocolError(e.code as ErrorCodeName, typeof e.message === \"string\" ? e.message : String(e.code), {\n retryable: e.retryable === true,\n ...(e.data && typeof e.data === \"object\" ? { data: e.data as ErrorData } : {}),\n cause: caught,\n });\n }\n return new DesktopProtocolError(\"INTERNAL\", caught instanceof Error ? caught.message : String(caught), { cause: caught });\n }\n\n /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */\n toBody(): { code: ErrorCodeName; message: string; retryable: boolean; data?: ErrorData } {\n return this.data === undefined\n ? { code: this.code, message: this.message, retryable: this.retryable }\n : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };\n }\n}\n\nexport function isDesktopProtocolError(value: unknown): value is DesktopProtocolError {\n return value instanceof DesktopProtocolError || (value instanceof Error && value.name === \"DesktopProtocolError\" && \"code\" in value);\n}\n"],"mappings":"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","names":[]}
|
package/dist/frame.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/error.ts
|
|
2
|
-
var DesktopProtocolError = class extends Error {
|
|
2
|
+
var DesktopProtocolError = class _DesktopProtocolError extends Error {
|
|
3
3
|
code;
|
|
4
4
|
retryable;
|
|
5
5
|
data;
|
|
@@ -10,6 +10,23 @@ var DesktopProtocolError = class extends Error {
|
|
|
10
10
|
this.retryable = options.retryable ?? false;
|
|
11
11
|
this.data = options.data;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry
|
|
15
|
+
* (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an
|
|
16
|
+
* error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.
|
|
17
|
+
*/
|
|
18
|
+
static from(caught) {
|
|
19
|
+
if (caught instanceof _DesktopProtocolError) return caught;
|
|
20
|
+
const e = caught;
|
|
21
|
+
if (e && typeof e === "object" && e.name === "DesktopProtocolError" && typeof e.code === "string") {
|
|
22
|
+
return new _DesktopProtocolError(e.code, typeof e.message === "string" ? e.message : String(e.code), {
|
|
23
|
+
retryable: e.retryable === true,
|
|
24
|
+
...e.data && typeof e.data === "object" ? { data: e.data } : {},
|
|
25
|
+
cause: caught
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return new _DesktopProtocolError("INTERNAL", caught instanceof Error ? caught.message : String(caught), { cause: caught });
|
|
29
|
+
}
|
|
13
30
|
/** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */
|
|
14
31
|
toBody() {
|
|
15
32
|
return this.data === void 0 ? { code: this.code, message: this.message, retryable: this.retryable } : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };
|
package/dist/frame.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/error.ts","../src/frame.ts"],"sourcesContent":["/**\n * The one error type every package entry throws. It carries the wire ErrorCode (SPEC §5), so a\n * caller branches on `code`/`retryable`/`data.reason` and never parses a message. Zod-free on\n * purpose: `./frame` (the relay hot path) throws it without pulling the schema graph.\n */\nimport type { ErrorCodeName, ErrorData } from \"./types\";\n\nexport class DesktopProtocolError extends Error {\n readonly code: ErrorCodeName;\n readonly retryable: boolean;\n readonly data: ErrorData | undefined;\n\n constructor(code: ErrorCodeName, message: string, options: { retryable?: boolean; data?: ErrorData; cause?: unknown } = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"DesktopProtocolError\";\n this.code = code;\n this.retryable = options.retryable ?? false;\n this.data = options.data;\n }\n\n /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */\n toBody(): { code: ErrorCodeName; message: string; retryable: boolean; data?: ErrorData } {\n return this.data === undefined\n ? { code: this.code, message: this.message, retryable: this.retryable }\n : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };\n }\n}\n\nexport function isDesktopProtocolError(value: unknown): value is DesktopProtocolError {\n return value instanceof DesktopProtocolError || (value instanceof Error && value.name === \"DesktopProtocolError\" && \"code\" in value);\n}\n","/**\n * `@ai-matrx/desktop-protocol/frame` — the 16-byte binary frame codec. ZOD-FREE by design: the\n * relay Worker's hot path imports only this entry and must stay tiny.\n *\n * off size field notes\n * 0 u8 version = 1\n * 1 u8 flags bit0 FIN (last frame of this stream), bit1 REPLAY (from ring buffer),\n * bits2-7 reserved = 0\n * 2 u8 channel see FrameChannel\n * 3 u8 reserved = 0\n * 4 u32 cid relay connection id. 0 on every client↔relay hop; the relay stamps it on\n * the relay→device hop and strips it on the way back (rewrites 4 bytes in\n * place — the relay never parses payloads).\n * 8 u32 sid stream id, announced by the core in stream{meta:{kind:\"open\",sid}}\n * 12 u32 seq per-stream frame sequence, starts at 1, strictly +1\n *\n * Big-endian. Payload length = message length − 16 (one frame = one WS binary message).\n * Python struct format: \">BBBBIII\". The zod twin (FrameHeader in the root entry, and so the JSON\n * Schema and Pydantic model) is checked against this file by src/frame.test.ts.\n */\nimport { DesktopProtocolError } from \"./error\";\n\nexport const FRAME_HEADER_BYTES = 16 as const;\nexport const FRAME_VERSION = 1 as const;\nexport const FRAME_STRUCT_FORMAT = \">BBBBIII\" as const;\nexport const FrameFlag = { FIN: 0b01, REPLAY: 0b10 } as const;\nexport const FrameChannel = {\n /** core→client: pty output, fs.read_stream bytes */\n DATA: 0,\n /** core→client: stderr of a non-pty process (reserved for exec.spawn in v1.1) */\n STDERR: 1,\n /** client→core: keystrokes / stdin to sid */\n STDIN: 2,\n /** core→client: serialized terminal screen (attach when since_seq is outside the ring) */\n SNAPSHOT: 3,\n /** relay↔device hop only: a JSON envelope carried inside a binary frame so the relay can route\n * by cid without parsing JSON. Never seen by clients. */\n ENVELOPE: 255,\n} as const;\n\nexport type FrameChannelValue = (typeof FrameChannel)[keyof typeof FrameChannel];\n\nexport interface FrameHeader {\n version: typeof FRAME_VERSION;\n /** 0..3 — FrameFlag bits. */\n flags: number;\n channel: FrameChannelValue;\n cid: number;\n sid: number;\n seq: number;\n}\n\nexport interface DecodedFrame {\n header: FrameHeader;\n /** A VIEW into the input buffer (no copy). Copy it if you keep it past the input's lifetime. */\n payload: Uint8Array;\n}\n\nconst U32_MAX = 4_294_967_295;\nconst CHANNELS: ReadonlySet<number> = new Set(Object.values(FrameChannel));\n\nfunction frameError(message: string): DesktopProtocolError {\n return new DesktopProtocolError(\"PROTOCOL_ERROR\", message);\n}\n\nfunction checkU32(name: string, value: number): void {\n if (!Number.isInteger(value) || value < 0 || value > U32_MAX) throw frameError(`frame ${name} is not a u32: ${value}`);\n}\n\n/** Validate a header without zod. Throws DesktopProtocolError(PROTOCOL_ERROR). */\nexport function assertFrameHeader(h: FrameHeader): void {\n if (h.version !== FRAME_VERSION) throw frameError(`unsupported frame version ${String(h.version)}`);\n if (!Number.isInteger(h.flags) || h.flags < 0 || h.flags > 3) throw frameError(`reserved frame flag bits set: ${h.flags}`);\n if (!CHANNELS.has(h.channel)) throw frameError(`unknown frame channel ${String(h.channel)}`);\n checkU32(\"cid\", h.cid);\n checkU32(\"sid\", h.sid);\n checkU32(\"seq\", h.seq);\n}\n\nexport function encodeFrame(h: FrameHeader, payload: Uint8Array): Uint8Array {\n assertFrameHeader(h);\n const out = new Uint8Array(FRAME_HEADER_BYTES + payload.byteLength);\n const v = new DataView(out.buffer);\n v.setUint8(0, h.version);\n v.setUint8(1, h.flags);\n v.setUint8(2, h.channel);\n v.setUint8(3, 0);\n v.setUint32(4, h.cid);\n v.setUint32(8, h.sid);\n v.setUint32(12, h.seq);\n out.set(payload, FRAME_HEADER_BYTES);\n return out;\n}\n\nexport function decodeFrame(buf: ArrayBuffer | Uint8Array): DecodedFrame {\n const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);\n if (bytes.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${bytes.byteLength} bytes`);\n const v = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n if (v.getUint8(3) !== 0) throw frameError(\"reserved frame byte 3 is not 0\");\n const header = {\n version: v.getUint8(0),\n flags: v.getUint8(1),\n channel: v.getUint8(2),\n cid: v.getUint32(4),\n sid: v.getUint32(8),\n seq: v.getUint32(12),\n } as FrameHeader;\n assertFrameHeader(header);\n return { header, payload: bytes.subarray(FRAME_HEADER_BYTES) };\n}\n\n/** Read only the 4-byte cid (relay routing) without decoding the rest. */\nexport function readCid(frame: Uint8Array): number {\n if (frame.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${frame.byteLength} bytes`);\n return new DataView(frame.buffer, frame.byteOffset, frame.byteLength).getUint32(4);\n}\n\n/** Relay hot path: rewrite only the cid IN PLACE, never touch the payload. Returns the same array. */\nexport function stampCid(frame: Uint8Array, cid: number): Uint8Array {\n if (frame.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${frame.byteLength} bytes`);\n checkU32(\"cid\", cid);\n new DataView(frame.buffer, frame.byteOffset, frame.byteLength).setUint32(4, cid);\n return frame;\n}\n\nexport function hasFlag(header: FrameHeader, flag: (typeof FrameFlag)[keyof typeof FrameFlag]): boolean {\n return (header.flags & flag) !== 0;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Relay ↔ device hop (never seen by clients). Every JSON envelope rides an ENVELOPE frame with\n// flags 0, sid 0, seq 0 and the client's cid; byte frames carry that cid too.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** cid 0 on an ENVELOPE frame from the device: the relay delivers it to every client. */\nexport const BROADCAST_CID = 0 as const;\n\n/**\n * Connection end: an ENVELOPE frame with FIN, sid 0, seq 0 and NO payload, for connection `cid`.\n * Relay → device: client `cid` is gone — cancel its streams, detach its sessions (idempotent; it\n * may arrive more than once). Device → relay: close client `cid`.\n */\nexport function connectionEndFrame(cid: number): Uint8Array {\n if (cid === BROADCAST_CID) throw frameError(\"a connection end needs a client cid, not the broadcast cid 0\");\n return encodeFrame({ version: FRAME_VERSION, flags: FrameFlag.FIN, channel: FrameChannel.ENVELOPE, cid, sid: 0, seq: 0 }, new Uint8Array(0));\n}\n\n/** True for a decoded connection-end frame (see connectionEndFrame). */\nexport function isConnectionEnd(frame: DecodedFrame): boolean {\n const h = frame.header;\n return h.channel === FrameChannel.ENVELOPE && hasFlag(h, FrameFlag.FIN) && frame.payload.byteLength === 0 && h.cid !== BROADCAST_CID;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/error.ts","../src/frame.ts"],"sourcesContent":["/**\n * The one error type every package entry throws. It carries the wire ErrorCode (SPEC §5), so a\n * caller branches on `code`/`retryable`/`data.reason` and never parses a message. Zod-free on\n * purpose: `./frame` (the relay hot path) throws it without pulling the schema graph.\n */\nimport type { ErrorCodeName, ErrorData } from \"./types\";\n\nexport class DesktopProtocolError extends Error {\n readonly code: ErrorCodeName;\n readonly retryable: boolean;\n readonly data: ErrorData | undefined;\n\n constructor(code: ErrorCodeName, message: string, options: { retryable?: boolean; data?: ErrorData; cause?: unknown } = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"DesktopProtocolError\";\n this.code = code;\n this.retryable = options.retryable ?? false;\n this.data = options.data;\n }\n\n /**\n * Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry\n * (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an\n * error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.\n */\n static from(caught: unknown): DesktopProtocolError {\n if (caught instanceof DesktopProtocolError) return caught;\n const e = caught as { name?: unknown; code?: unknown; message?: unknown; retryable?: unknown; data?: unknown } | null;\n if (e && typeof e === \"object\" && e.name === \"DesktopProtocolError\" && typeof e.code === \"string\") {\n return new DesktopProtocolError(e.code as ErrorCodeName, typeof e.message === \"string\" ? e.message : String(e.code), {\n retryable: e.retryable === true,\n ...(e.data && typeof e.data === \"object\" ? { data: e.data as ErrorData } : {}),\n cause: caught,\n });\n }\n return new DesktopProtocolError(\"INTERNAL\", caught instanceof Error ? caught.message : String(caught), { cause: caught });\n }\n\n /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */\n toBody(): { code: ErrorCodeName; message: string; retryable: boolean; data?: ErrorData } {\n return this.data === undefined\n ? { code: this.code, message: this.message, retryable: this.retryable }\n : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };\n }\n}\n\nexport function isDesktopProtocolError(value: unknown): value is DesktopProtocolError {\n return value instanceof DesktopProtocolError || (value instanceof Error && value.name === \"DesktopProtocolError\" && \"code\" in value);\n}\n","/**\n * `@ai-matrx/desktop-protocol/frame` — the 16-byte binary frame codec. ZOD-FREE by design: the\n * relay Worker's hot path imports only this entry and must stay tiny.\n *\n * off size field notes\n * 0 u8 version = 1\n * 1 u8 flags bit0 FIN (last frame of this stream), bit1 REPLAY (from ring buffer),\n * bits2-7 reserved = 0\n * 2 u8 channel see FrameChannel\n * 3 u8 reserved = 0\n * 4 u32 cid relay connection id. 0 on every client↔relay hop; the relay stamps it on\n * the relay→device hop and strips it on the way back (rewrites 4 bytes in\n * place — the relay never parses payloads).\n * 8 u32 sid stream id, announced by the core in stream{meta:{kind:\"open\",sid}}\n * 12 u32 seq per-stream frame sequence, starts at 1, strictly +1\n *\n * Big-endian. Payload length = message length − 16 (one frame = one WS binary message).\n * Python struct format: \">BBBBIII\". The zod twin (FrameHeader in the root entry, and so the JSON\n * Schema and Pydantic model) is checked against this file by src/frame.test.ts.\n */\nimport { DesktopProtocolError } from \"./error\";\n\nexport const FRAME_HEADER_BYTES = 16 as const;\nexport const FRAME_VERSION = 1 as const;\nexport const FRAME_STRUCT_FORMAT = \">BBBBIII\" as const;\nexport const FrameFlag = { FIN: 0b01, REPLAY: 0b10 } as const;\nexport const FrameChannel = {\n /** core→client: pty output, fs.read_stream bytes */\n DATA: 0,\n /** core→client: stderr of a non-pty process (reserved for exec.spawn in v1.1) */\n STDERR: 1,\n /** client→core: keystrokes / stdin to sid */\n STDIN: 2,\n /** core→client: serialized terminal screen (attach when since_seq is outside the ring) */\n SNAPSHOT: 3,\n /** relay↔device hop only: a JSON envelope carried inside a binary frame so the relay can route\n * by cid without parsing JSON. Never seen by clients. */\n ENVELOPE: 255,\n} as const;\n\nexport type FrameChannelValue = (typeof FrameChannel)[keyof typeof FrameChannel];\n\nexport interface FrameHeader {\n version: typeof FRAME_VERSION;\n /** 0..3 — FrameFlag bits. */\n flags: number;\n channel: FrameChannelValue;\n cid: number;\n sid: number;\n seq: number;\n}\n\nexport interface DecodedFrame {\n header: FrameHeader;\n /** A VIEW into the input buffer (no copy). Copy it if you keep it past the input's lifetime. */\n payload: Uint8Array;\n}\n\nconst U32_MAX = 4_294_967_295;\nconst CHANNELS: ReadonlySet<number> = new Set(Object.values(FrameChannel));\n\nfunction frameError(message: string): DesktopProtocolError {\n return new DesktopProtocolError(\"PROTOCOL_ERROR\", message);\n}\n\nfunction checkU32(name: string, value: number): void {\n if (!Number.isInteger(value) || value < 0 || value > U32_MAX) throw frameError(`frame ${name} is not a u32: ${value}`);\n}\n\n/** Validate a header without zod. Throws DesktopProtocolError(PROTOCOL_ERROR). */\nexport function assertFrameHeader(h: FrameHeader): void {\n if (h.version !== FRAME_VERSION) throw frameError(`unsupported frame version ${String(h.version)}`);\n if (!Number.isInteger(h.flags) || h.flags < 0 || h.flags > 3) throw frameError(`reserved frame flag bits set: ${h.flags}`);\n if (!CHANNELS.has(h.channel)) throw frameError(`unknown frame channel ${String(h.channel)}`);\n checkU32(\"cid\", h.cid);\n checkU32(\"sid\", h.sid);\n checkU32(\"seq\", h.seq);\n}\n\nexport function encodeFrame(h: FrameHeader, payload: Uint8Array): Uint8Array {\n assertFrameHeader(h);\n const out = new Uint8Array(FRAME_HEADER_BYTES + payload.byteLength);\n const v = new DataView(out.buffer);\n v.setUint8(0, h.version);\n v.setUint8(1, h.flags);\n v.setUint8(2, h.channel);\n v.setUint8(3, 0);\n v.setUint32(4, h.cid);\n v.setUint32(8, h.sid);\n v.setUint32(12, h.seq);\n out.set(payload, FRAME_HEADER_BYTES);\n return out;\n}\n\nexport function decodeFrame(buf: ArrayBuffer | Uint8Array): DecodedFrame {\n const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);\n if (bytes.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${bytes.byteLength} bytes`);\n const v = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n if (v.getUint8(3) !== 0) throw frameError(\"reserved frame byte 3 is not 0\");\n const header = {\n version: v.getUint8(0),\n flags: v.getUint8(1),\n channel: v.getUint8(2),\n cid: v.getUint32(4),\n sid: v.getUint32(8),\n seq: v.getUint32(12),\n } as FrameHeader;\n assertFrameHeader(header);\n return { header, payload: bytes.subarray(FRAME_HEADER_BYTES) };\n}\n\n/** Read only the 4-byte cid (relay routing) without decoding the rest. */\nexport function readCid(frame: Uint8Array): number {\n if (frame.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${frame.byteLength} bytes`);\n return new DataView(frame.buffer, frame.byteOffset, frame.byteLength).getUint32(4);\n}\n\n/** Relay hot path: rewrite only the cid IN PLACE, never touch the payload. Returns the same array. */\nexport function stampCid(frame: Uint8Array, cid: number): Uint8Array {\n if (frame.byteLength < FRAME_HEADER_BYTES) throw frameError(`short frame: ${frame.byteLength} bytes`);\n checkU32(\"cid\", cid);\n new DataView(frame.buffer, frame.byteOffset, frame.byteLength).setUint32(4, cid);\n return frame;\n}\n\nexport function hasFlag(header: FrameHeader, flag: (typeof FrameFlag)[keyof typeof FrameFlag]): boolean {\n return (header.flags & flag) !== 0;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Relay ↔ device hop (never seen by clients). Every JSON envelope rides an ENVELOPE frame with\n// flags 0, sid 0, seq 0 and the client's cid; byte frames carry that cid too.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** cid 0 on an ENVELOPE frame from the device: the relay delivers it to every client. */\nexport const BROADCAST_CID = 0 as const;\n\n/**\n * Connection end: an ENVELOPE frame with FIN, sid 0, seq 0 and NO payload, for connection `cid`.\n * Relay → device: client `cid` is gone — cancel its streams, detach its sessions (idempotent; it\n * may arrive more than once). Device → relay: close client `cid`.\n */\nexport function connectionEndFrame(cid: number): Uint8Array {\n if (cid === BROADCAST_CID) throw frameError(\"a connection end needs a client cid, not the broadcast cid 0\");\n return encodeFrame({ version: FRAME_VERSION, flags: FrameFlag.FIN, channel: FrameChannel.ENVELOPE, cid, sid: 0, seq: 0 }, new Uint8Array(0));\n}\n\n/** True for a decoded connection-end frame (see connectionEndFrame). */\nexport function isConnectionEnd(frame: DecodedFrame): boolean {\n const h = frame.header;\n return h.channel === FrameChannel.ENVELOPE && hasFlag(h, FrameFlag.FIN) && frame.payload.byteLength === 0 && h.cid !== BROADCAST_CID;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -437,7 +437,7 @@ var ProtocolErrorBody = import_zod2.z.strictObject({
|
|
|
437
437
|
var import_zod3 = require("zod");
|
|
438
438
|
|
|
439
439
|
// src/error.ts
|
|
440
|
-
var DesktopProtocolError = class extends Error {
|
|
440
|
+
var DesktopProtocolError = class _DesktopProtocolError extends Error {
|
|
441
441
|
code;
|
|
442
442
|
retryable;
|
|
443
443
|
data;
|
|
@@ -448,6 +448,23 @@ var DesktopProtocolError = class extends Error {
|
|
|
448
448
|
this.retryable = options.retryable ?? false;
|
|
449
449
|
this.data = options.data;
|
|
450
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry
|
|
453
|
+
* (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an
|
|
454
|
+
* error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.
|
|
455
|
+
*/
|
|
456
|
+
static from(caught) {
|
|
457
|
+
if (caught instanceof _DesktopProtocolError) return caught;
|
|
458
|
+
const e = caught;
|
|
459
|
+
if (e && typeof e === "object" && e.name === "DesktopProtocolError" && typeof e.code === "string") {
|
|
460
|
+
return new _DesktopProtocolError(e.code, typeof e.message === "string" ? e.message : String(e.code), {
|
|
461
|
+
retryable: e.retryable === true,
|
|
462
|
+
...e.data && typeof e.data === "object" ? { data: e.data } : {},
|
|
463
|
+
cause: caught
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
return new _DesktopProtocolError("INTERNAL", caught instanceof Error ? caught.message : String(caught), { cause: caught });
|
|
467
|
+
}
|
|
451
468
|
/** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */
|
|
452
469
|
toBody() {
|
|
453
470
|
return this.data === void 0 ? { code: this.code, message: this.message, retryable: this.retryable } : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };
|
|
@@ -2440,6 +2457,7 @@ function builtOps() {
|
|
|
2440
2457
|
// src/tools/status.ts
|
|
2441
2458
|
var TOOL_LEVELS = ["programmatic", "protocol", "chat"];
|
|
2442
2459
|
var TOOL_PLATFORMS = ["darwin", "win32", "linux"];
|
|
2460
|
+
var UUID_IN_NAME = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;
|
|
2443
2461
|
var REQUIRED = {
|
|
2444
2462
|
programmatic: [{ label: "in process", ok: (e) => e.transport === "in_process" }],
|
|
2445
2463
|
protocol: [
|
|
@@ -2453,7 +2471,7 @@ function cellFor(tool2, platform, level, evidence) {
|
|
|
2453
2471
|
if (!tool2.platforms.includes(platform)) return { state: "unsupported", missing: [], last_run_at_ms: null, evidence: [] };
|
|
2454
2472
|
const latest = /* @__PURE__ */ new Map();
|
|
2455
2473
|
for (const e of evidence) {
|
|
2456
|
-
const key = `${e.suite}\0${e.test}\0${e.transport}\0${e.remote_network}`;
|
|
2474
|
+
const key = `${e.suite}\0${e.test.replace(UUID_IN_NAME, "<id>")}\0${e.transport}\0${e.remote_network}`;
|
|
2457
2475
|
const prev = latest.get(key);
|
|
2458
2476
|
if (!prev || prev.run_at_ms < e.run_at_ms) latest.set(key, e);
|
|
2459
2477
|
}
|