@crouton-kit/crouter 0.3.60 → 0.3.62

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.
Files changed (120) hide show
  1. package/dist/builtin-views/canvas/core.mjs +2 -1
  2. package/dist/clients/attach/__tests__/context-message.test.d.ts +1 -0
  3. package/dist/clients/attach/__tests__/context-message.test.js +46 -0
  4. package/dist/clients/attach/__tests__/transport-relay.test.d.ts +1 -0
  5. package/dist/clients/attach/__tests__/transport-relay.test.js +176 -0
  6. package/dist/clients/attach/attach-cmd.js +747 -743
  7. package/dist/clients/attach/canvas-panels.d.ts +4 -4
  8. package/dist/clients/attach/canvas-panels.js +108 -37
  9. package/dist/clients/attach/chat-view.js +11 -0
  10. package/dist/clients/attach/context-message.d.ts +3 -1
  11. package/dist/clients/attach/context-message.js +30 -21
  12. package/dist/clients/attach/graph-overlay.d.ts +8 -1
  13. package/dist/clients/attach/graph-overlay.js +201 -22
  14. package/dist/clients/attach/transport-relay.d.ts +51 -0
  15. package/dist/clients/attach/transport-relay.js +196 -0
  16. package/dist/clients/attach/transport-socket.d.ts +29 -0
  17. package/dist/clients/attach/transport-socket.js +164 -0
  18. package/dist/clients/attach/transport.d.ts +32 -0
  19. package/dist/clients/attach/transport.js +26 -0
  20. package/dist/clients/attach/view-socket.d.ts +40 -44
  21. package/dist/clients/attach/view-socket.js +94 -197
  22. package/dist/clients/web/server.js +1 -0
  23. package/dist/commands/memory/delete.js +3 -3
  24. package/dist/commands/memory/origin.js +1 -1
  25. package/dist/commands/memory/read.js +3 -3
  26. package/dist/commands/memory/shared.d.ts +8 -6
  27. package/dist/commands/memory/shared.js +23 -9
  28. package/dist/commands/memory/write.js +3 -3
  29. package/dist/commands/node-worktree.js +9 -9
  30. package/dist/commands/node.js +71 -23
  31. package/dist/commands/sys/doctor.js +1 -0
  32. package/dist/core/__tests__/broker-fork-seam.test.js +17 -2
  33. package/dist/core/__tests__/broker-snapshot-history.test.js +12 -2
  34. package/dist/core/__tests__/canvas-inbox-watcher.test.js +79 -0
  35. package/dist/core/__tests__/context-intro.test.js +11 -0
  36. package/dist/core/__tests__/fixtures/fake-engine.d.ts +9 -4
  37. package/dist/core/__tests__/fixtures/fake-engine.js +12 -4
  38. package/dist/core/__tests__/kickoff.test.js +21 -0
  39. package/dist/core/__tests__/spawn-root.test.js +60 -0
  40. package/dist/core/__tests__/worktree.test.js +24 -1
  41. package/dist/core/canvas/browse/app.d.ts +2 -0
  42. package/dist/core/canvas/browse/app.js +83 -48
  43. package/dist/core/canvas/db.js +3 -2
  44. package/dist/core/canvas/index.d.ts +1 -0
  45. package/dist/core/canvas/index.js +1 -0
  46. package/dist/core/canvas/nav-model.d.ts +5 -3
  47. package/dist/core/canvas/nav-model.js +7 -6
  48. package/dist/core/canvas/paths.d.ts +8 -0
  49. package/dist/core/canvas/paths.js +10 -0
  50. package/dist/core/canvas/render.d.ts +6 -0
  51. package/dist/core/canvas/render.js +117 -20
  52. package/dist/core/canvas/source.d.ts +26 -0
  53. package/dist/core/canvas/source.js +29 -0
  54. package/dist/core/canvas/status-glyph.js +1 -0
  55. package/dist/core/canvas/types.d.ts +4 -3
  56. package/dist/core/config.d.ts +18 -2
  57. package/dist/core/config.js +88 -10
  58. package/dist/core/fault-classifier.d.ts +2 -2
  59. package/dist/core/memory-resolver.d.ts +9 -6
  60. package/dist/core/memory-resolver.js +28 -4
  61. package/dist/core/profiles/__tests__/root-profile.test.d.ts +1 -0
  62. package/dist/core/profiles/__tests__/root-profile.test.js +69 -0
  63. package/dist/core/profiles/manifest.d.ts +2 -0
  64. package/dist/core/profiles/manifest.js +40 -7
  65. package/dist/core/profiles/select.d.ts +4 -4
  66. package/dist/core/profiles/select.js +13 -13
  67. package/dist/core/runtime/__tests__/spawn-env.test.d.ts +1 -0
  68. package/dist/core/runtime/__tests__/spawn-env.test.js +268 -0
  69. package/dist/core/runtime/bearings.d.ts +5 -4
  70. package/dist/core/runtime/bearings.js +12 -6
  71. package/dist/core/runtime/broker.js +59 -7
  72. package/dist/core/runtime/fault-recovery.js +4 -0
  73. package/dist/core/runtime/front-door.js +1 -1
  74. package/dist/core/runtime/host.js +43 -3
  75. package/dist/core/runtime/kickoff.js +6 -0
  76. package/dist/core/runtime/launch.d.ts +6 -1
  77. package/dist/core/runtime/launch.js +7 -1
  78. package/dist/core/runtime/nodes.d.ts +7 -6
  79. package/dist/core/runtime/nodes.js +3 -3
  80. package/dist/core/runtime/situational-context.d.ts +12 -0
  81. package/dist/core/runtime/situational-context.js +48 -0
  82. package/dist/core/runtime/spawn-env.d.ts +27 -0
  83. package/dist/core/runtime/spawn-env.js +169 -0
  84. package/dist/core/runtime/spawn.d.ts +16 -7
  85. package/dist/core/runtime/spawn.js +8 -2
  86. package/dist/core/substrate/session-cache.js +7 -1
  87. package/dist/core/substrate/subject.d.ts +1 -1
  88. package/dist/core/view/remote-canvas-target.d.ts +3 -0
  89. package/dist/core/view/remote-canvas-target.js +25 -0
  90. package/dist/core/worktree.d.ts +4 -4
  91. package/dist/core/worktree.js +41 -19
  92. package/dist/daemon/crtrd.js +32 -0
  93. package/dist/daemon/predicate-eval.js +12 -1
  94. package/dist/index.d.ts +1 -0
  95. package/dist/index.js +4 -0
  96. package/dist/pi-extensions/canvas-bash-valve.d.ts +2 -0
  97. package/dist/pi-extensions/canvas-bash-valve.js +238 -0
  98. package/dist/pi-extensions/canvas-inbox-watcher.d.ts +12 -0
  99. package/dist/pi-extensions/canvas-inbox-watcher.js +41 -5
  100. package/dist/types.d.ts +32 -0
  101. package/dist/types.js +7 -0
  102. package/dist/web-client/apple-touch-icon.png +0 -0
  103. package/dist/web-client/assets/index-BnmSLNLa.css +2 -0
  104. package/dist/web-client/assets/index-BvzxXXGU.js +77 -0
  105. package/dist/web-client/assets/workbox-window.prod.es5-Bd17z0YL.js +2 -0
  106. package/dist/web-client/icon-192.png +0 -0
  107. package/dist/web-client/icon-512.png +0 -0
  108. package/dist/web-client/icon-maskable-512.png +0 -0
  109. package/dist/web-client/index.html +6 -3
  110. package/dist/web-client/manifest.webmanifest +1 -0
  111. package/dist/web-client/sw.js +1 -0
  112. package/docs/public-api.md +5 -3
  113. package/package.json +4 -3
  114. package/dist/web-client/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
  115. package/dist/web-client/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
  116. package/dist/web-client/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
  117. package/dist/web-client/assets/index-72YQdji_.js +0 -77
  118. package/dist/web-client/assets/index-bDhImjJy.css +0 -2
  119. package/dist/web-client/assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2 +0 -0
  120. package/dist/web-client/assets/instrument-sans-latin-wght-normal-BbzFLZTg.woff2 +0 -0
@@ -0,0 +1,164 @@
1
+ // transport-socket.ts — the LOCAL unix-socket Transport. This is the ORIGINAL
2
+ // `ViewSocketClient` wire logic (node:net + the bounded FrameDecoder),
3
+ // unchanged in behavior, extracted behind the `Transport` interface so
4
+ // ViewSocketClient can also ride a `RelayTransport` (transport-relay.ts).
5
+ //
6
+ // Connects to a node's ALREADY-running headless broker over its unix socket
7
+ // (`nodeDir(id)/view.sock`). NEVER spawns pi, NEVER opens the session
8
+ // `.jsonl` — if the broker is not running, connect fails with a
9
+ // BrokerUnavailableError.
10
+ import { EventEmitter } from 'node:events';
11
+ import { createConnection } from 'node:net';
12
+ import { viewSocketPath } from '../../core/canvas/paths.js';
13
+ import { classify } from '../../core/fault-classifier.js';
14
+ import { clearFault, recordFault } from '../../core/runtime/fault.js';
15
+ import { CLIENT_READ_CAPS, encodeFrame, FrameDecoder, FrameOverflowError, } from '../../core/runtime/broker-protocol.js';
16
+ import { BrokerUnavailableError } from './transport.js';
17
+ export class SocketTransport extends EventEmitter {
18
+ nodeId;
19
+ socket;
20
+ decoder = new FrameDecoder(CLIENT_READ_CAPS);
21
+ closeEmitted = false;
22
+ /** One redial fault line per reconnect EPISODE, not per attempt — see
23
+ * {@link recordSocketFault}. */
24
+ redialFaultRecorded = false;
25
+ constructor(nodeId) {
26
+ super();
27
+ this.nodeId = nodeId;
28
+ }
29
+ /** The broker binds `viewSocketPath(id)` — resolve it the same way. */
30
+ get socketPath() {
31
+ return viewSocketPath(this.nodeId);
32
+ }
33
+ recordSocketFault(op, err) {
34
+ if (op === 'redial') {
35
+ if (this.redialFaultRecorded)
36
+ return;
37
+ this.redialFaultRecorded = true;
38
+ }
39
+ const classified = classify('viewer↔broker', err);
40
+ recordFault(this.nodeId, {
41
+ link: 'viewer↔broker',
42
+ op,
43
+ kind: classified.kind,
44
+ retry: op === 'redial'
45
+ ? { disposition: 'auto', by: 'client' }
46
+ : { disposition: 'manual' },
47
+ message: err instanceof Error ? err.message : String(err),
48
+ });
49
+ }
50
+ connect() {
51
+ const socket = createConnection(this.socketPath);
52
+ this.socket = socket;
53
+ socket.on('connect', () => {
54
+ this.redialFaultRecorded = false;
55
+ clearFault(this.nodeId, { link: 'viewer↔broker' });
56
+ this.emit('connect');
57
+ });
58
+ socket.on('data', (chunk) => this.onData(chunk));
59
+ socket.on('error', (err) => this.onError(err, 'connect'));
60
+ socket.on('close', () => this.onClose());
61
+ }
62
+ /** Re-establish the socket after a broker exit, on the SAME stable
63
+ * `view.sock` path. Installs a FRESH FrameDecoder (a half-frame from the
64
+ * dead stream must not corrupt the new one). */
65
+ redial() {
66
+ this.destroy();
67
+ this.decoder = new FrameDecoder(CLIENT_READ_CAPS);
68
+ this.closeEmitted = false;
69
+ return new Promise((resolve, reject) => {
70
+ const socket = createConnection(this.socketPath);
71
+ this.socket = socket;
72
+ let settled = false;
73
+ socket.on('connect', () => {
74
+ if (settled)
75
+ return;
76
+ settled = true;
77
+ this.redialFaultRecorded = false;
78
+ clearFault(this.nodeId, { link: 'viewer↔broker' });
79
+ this.emit('connect');
80
+ resolve();
81
+ });
82
+ socket.on('data', (chunk) => this.onData(chunk));
83
+ socket.on('error', (err) => {
84
+ if (settled) {
85
+ this.onError(err, 'redial');
86
+ return;
87
+ }
88
+ settled = true;
89
+ this.recordSocketFault('redial', err);
90
+ try {
91
+ socket.destroy();
92
+ }
93
+ catch {
94
+ /* ignore */
95
+ }
96
+ reject(err);
97
+ });
98
+ socket.on('close', () => this.onClose());
99
+ });
100
+ }
101
+ send(frame) {
102
+ const sock = this.socket;
103
+ if (sock === undefined || sock.destroyed)
104
+ return;
105
+ try {
106
+ sock.write(encodeFrame(frame));
107
+ }
108
+ catch {
109
+ /* dead socket — 'close' will fire and drive teardown */
110
+ }
111
+ }
112
+ destroy() {
113
+ const sock = this.socket;
114
+ if (sock !== undefined && !sock.destroyed) {
115
+ try {
116
+ sock.destroy();
117
+ }
118
+ catch {
119
+ /* ignore */
120
+ }
121
+ }
122
+ }
123
+ onData(chunk) {
124
+ let frames;
125
+ try {
126
+ frames = this.decoder.push(chunk);
127
+ }
128
+ catch (err) {
129
+ // A broker that somehow emits an oversized frame (or a corrupt stream):
130
+ // clean error + drop, never a crash. The 'close' that follows destroy()
131
+ // is what rejects any in-flight ViewSocketClient requests.
132
+ const msg = err instanceof FrameOverflowError
133
+ ? `broker sent an oversized frame (${err.message}) — disconnecting`
134
+ : `failed to decode a broker frame: ${String(err)}`;
135
+ this.emitError(new Error(msg));
136
+ this.destroy();
137
+ return;
138
+ }
139
+ for (const raw of frames)
140
+ this.emit('frame', raw);
141
+ }
142
+ onError(err, op) {
143
+ this.recordSocketFault(op, err);
144
+ if (err.code === 'ECONNREFUSED' || err.code === 'ENOENT') {
145
+ this.emitError(new BrokerUnavailableError(this.nodeId));
146
+ }
147
+ else {
148
+ this.emitError(new Error(`view socket error: ${err.message}`));
149
+ }
150
+ // 'close' typically follows 'error'; teardown converges there.
151
+ }
152
+ onClose() {
153
+ if (this.closeEmitted)
154
+ return;
155
+ this.closeEmitted = true;
156
+ this.emit('close');
157
+ }
158
+ /** Emit `error` only when a listener exists — a bare EventEmitter `error`
159
+ * with no listener throws, and this transport must never throw uncaught. */
160
+ emitError(err) {
161
+ if (this.listenerCount('error') > 0)
162
+ this.emit('error', err);
163
+ }
164
+ }
@@ -0,0 +1,32 @@
1
+ import type { EventEmitter } from 'node:events';
2
+ import type { BrokerToClient, ClientToBroker } from '../../core/runtime/broker-protocol.js';
3
+ export interface Transport extends EventEmitter {
4
+ /** Open the connection. Emits 'connect' on success, 'error' on a fatal
5
+ * connect-time failure. Call once. */
6
+ connect(): void;
7
+ /** Re-establish the connection to the SAME target after a drop. Resolves on
8
+ * a fresh 'connect'; rejects on a retryable dial failure (the caller's
9
+ * reconnect supervisor decides whether to try again). */
10
+ redial(): Promise<void>;
11
+ /** Encode + send one client→broker frame. No-op on a dead/absent connection;
12
+ * never throws. */
13
+ send(frame: ClientToBroker): void;
14
+ /** Tear down the connection. 'close' fires if it has not already. */
15
+ destroy(): void;
16
+ on(event: 'connect', listener: () => void): this;
17
+ on(event: 'frame', listener: (frame: BrokerToClient) => void): this;
18
+ on(event: 'close', listener: () => void): this;
19
+ on(event: 'error', listener: (err: Error) => void): this;
20
+ once(event: 'connect', listener: () => void): this;
21
+ once(event: 'error', listener: (err: Error) => void): this;
22
+ off(event: 'connect', listener: () => void): this;
23
+ off(event: 'error', listener: (err: Error) => void): this;
24
+ }
25
+ /** Surfaced when a Transport's target has no reachable broker — locally, no
26
+ * socket file / nothing listening; remotely, a relay close signaling "no
27
+ * broker for this node". The command catches this to exit non-zero with a
28
+ * focus/revive hint — same meaning regardless of which Transport raised it. */
29
+ export declare class BrokerUnavailableError extends Error {
30
+ readonly nodeId: string;
31
+ constructor(nodeId: string);
32
+ }
@@ -0,0 +1,26 @@
1
+ // transport.ts — the Transport seam: ViewSocketClient's broker I/O, abstracted
2
+ // so the SAME correlation/pending/close logic can ride either the LOCAL unix
3
+ // socket (SocketTransport, transport-socket.ts) or a relayed WebSocket to a
4
+ // remote broker (RelayTransport, transport-relay.ts). Both speak the same
5
+ // broker-protocol JSON frames; only how bytes cross the wire differs.
6
+ //
7
+ // A Transport is a pure duplex frame pump — connect (dial), redial (re-dial
8
+ // the SAME target after a drop), send (one client→broker frame), destroy
9
+ // (tear down). It emits 'connect' once the peer is reachable, one 'frame' per
10
+ // decoded broker→client frame, 'error' for a surfaced problem (including a
11
+ // {@link BrokerUnavailableError} when the target has no reachable broker),
12
+ // and 'close' exactly once when the connection is gone. Everything ABOVE this
13
+ // seam — request correlation, the pending map, redial-vs-give-up policy — is
14
+ // ViewSocketClient's job and never moves into a Transport implementation.
15
+ /** Surfaced when a Transport's target has no reachable broker — locally, no
16
+ * socket file / nothing listening; remotely, a relay close signaling "no
17
+ * broker for this node". The command catches this to exit non-zero with a
18
+ * focus/revive hint — same meaning regardless of which Transport raised it. */
19
+ export class BrokerUnavailableError extends Error {
20
+ nodeId;
21
+ constructor(nodeId) {
22
+ super(`node ${nodeId} has no running broker — focus or revive it first`);
23
+ this.nodeId = nodeId;
24
+ this.name = 'BrokerUnavailableError';
25
+ }
26
+ }
@@ -1,28 +1,26 @@
1
1
  import { EventEmitter } from 'node:events';
2
2
  import type { NodeRow } from '../../core/canvas/types.js';
3
- import { type BrokerDataFrame, type BrokerToClient, type ClientToBroker, type DequeueFrame, type GetSettingsFrame, type GetTreeFrame, type ListModelsFrame, type ListScopedModelsFrame, type ListSessionsFrame } from '../../core/runtime/broker-protocol.js';
3
+ import type { BrokerDataFrame, BrokerToClient, ClientToBroker, DequeueFrame, GetSettingsFrame, GetTreeFrame, ListModelsFrame, ListScopedModelsFrame, ListSessionsFrame } from '../../core/runtime/broker-protocol.js';
4
+ import { type Transport } from './transport.js';
5
+ export { BrokerUnavailableError, type Transport } from './transport.js';
6
+ export { SocketTransport } from './transport-socket.js';
7
+ export { RelayTransport, type RelayTarget } from './transport-relay.js';
4
8
  /** A correlated read-op (or `dequeue`) request MINUS the client-chosen `id` —
5
9
  * {@link ViewSocketClient.request} mints the `id`, sends the frame, and resolves
6
10
  * with the matching `data` reply. The picker/operator code builds these; the
7
11
  * socket owns the correlation token so callers never hand-roll one. */
8
12
  export type ReadOpRequest = Omit<ListModelsFrame, 'id'> | Omit<ListSessionsFrame, 'id'> | Omit<GetTreeFrame, 'id'> | Omit<GetSettingsFrame, 'id'> | Omit<ListScopedModelsFrame, 'id'> | Omit<DequeueFrame, 'id'>;
9
- /** Surfaced when the node has no reachable broker at connect time (no socket
10
- * file, or a stale socket with nothing listening). The command catches this to
11
- * exit non-zero with a focus/revive hint. */
12
13
  /** The reconnect supervisor's give-up predicate (extracted pure so it is
13
14
  * testable without a socket or TUI). After a broker close the viewer KEEPS
14
- * re-dialing the same `view.sock` while the node is still alive — a yield
15
+ * re-dialing the same target while the node is still alive — a yield
15
16
  * leaves `status='active'` (intent='refresh') and the daemon revives a fresh
16
17
  * broker on the same path. It gives up only when the node is genuinely gone:
17
18
  * a terminal status (done/dead/canceled) or a reaped row (null). `idle` is NOT
18
19
  * terminal — an idle-release node revives on its next inbox wake, so keep
19
20
  * trying (the supervisor waits at a relaxed cadence for as long as the row
20
- * stays live). */
21
+ * stays live). Local canvas rows only — a remote target has no local row, so
22
+ * callers driving a remote attach must not consult this (see attach-cmd.ts). */
21
23
  export declare function reconnectShouldGiveUp(row: NodeRow | null): boolean;
22
- export declare class BrokerUnavailableError extends Error {
23
- readonly nodeId: string;
24
- constructor(nodeId: string);
25
- }
26
24
  /** The plan-fixed interface (Wave 3): `connect()`, `on('frame', …)`,
27
25
  * `send(frame)`, `on('close', …)`, plus `connect`/`error` events. */
28
26
  export interface ViewSocketClient {
@@ -39,52 +37,50 @@ export interface ViewSocketClient {
39
37
  }
40
38
  export declare class ViewSocketClient extends EventEmitter {
41
39
  private readonly nodeId;
42
- private socket;
43
- private decoder;
40
+ private readonly transport;
44
41
  private closeEmitted;
42
+ /** Tracks live connect↔close state so {@link request} can fail fast on a
43
+ * dead/absent connection rather than parking a promise the reply will
44
+ * never reach (transport-agnostic mirror of the old socket.destroyed
45
+ * check). A post-close request lands here too — {@link rejectAllPending}
46
+ * already ran by the time this flips false. */
47
+ private connected;
48
+ /** The last transport-level error message, so a close can reject any
49
+ * in-flight requests with the precise reason (e.g. an oversized-frame
50
+ * overflow) instead of a generic one. Cleared on every fresh connect. */
51
+ private lastTransportError;
45
52
  /** In-flight correlated read-ops, keyed by the `id` minted in {@link request}.
46
53
  * Resolved by the matching `data` frame / rejected by the matching `error`
47
- * frame in {@link onData}, the request's timeout, or socket teardown. */
54
+ * frame in {@link onFrame}, the request's timeout, or transport teardown. */
48
55
  private pending;
49
- /** One redial fault line per reconnect EPISODE, not per attempt: the
50
- * supervisor redials for as long as the node can come back (a dormant node
51
- * may be gone for hours), so a per-attempt record would grow fault.log
52
- * without bound. Set on the first failed redial, reset on any successful
53
- * connect. */
54
- private redialFaultRecorded;
55
- constructor(nodeId: string);
56
- private recordSocketFault;
56
+ /** `transport` defaults to a {@link SocketTransport} against this node's
57
+ * local `view.sock` today's behavior, unchanged. Pass a
58
+ * {@link RelayTransport} to attach a remote node instead; every method
59
+ * below behaves identically either way. */
60
+ constructor(nodeId: string, transport?: Transport);
57
61
  /** Issue a correlated read-op and resolve with the broker's `data` reply (or
58
- * reject on the correlated `error`, a timeout, or socket teardown). Mints the
59
- * `id`, sends `{...frame, id}`, and parks a resolver consumed by {@link onData}.
60
- * The reply is narrowed by the caller on its `kind`. */
62
+ * reject on the correlated `error`, a timeout, or transport teardown). Mints
63
+ * the `id`, sends `{...frame, id}`, and parks a resolver consumed by
64
+ * {@link onFrame}. The reply is narrowed by the caller on its `kind`. */
61
65
  request(frame: ReadOpRequest): Promise<BrokerDataFrame>;
62
- /** Reject + clear every in-flight request (socket gone / decode error) so a
63
- * picker fetch never hangs past the connection it rode on. */
66
+ /** Reject + clear every in-flight request (connection gone / decode error)
67
+ * so a picker fetch never hangs past the connection it rode on. */
64
68
  private rejectAllPending;
65
- /** The broker binds `viewSocketPath(id)` resolve it the same way. */
66
- get socketPath(): string;
67
- /** Open the connection. Wire up listeners; emits `connect` on success or
68
- * `error` (a {@link BrokerUnavailableError} for ECONNREFUSED/ENOENT) on
69
- * failure. Idempotent guard is the caller's job — call once. */
69
+ /** Open the connection. Emits `connect` on success or `error` (a
70
+ * {@link BrokerUnavailableError} when the target has no reachable broker)
71
+ * on failure. Idempotent guard is the caller's job call once. */
70
72
  connect(): void;
71
- /** Re-establish the socket after a broker exit (a yield→revive cycle), on the
72
- * SAME stable `view.sock` path. Installs a FRESH FrameDecoder (a half-frame
73
- * from the dead stream must not corrupt the new one) and resets the close
74
- * guard, then re-dials. Resolves on `connect` (caller re-sends `hello`);
75
- * rejects on the dial's `error` (ECONNREFUSED while the new broker is
76
- * mid-boot, or ENOENT before it re-binds the socket) — both retryable. A
77
- * post-connect error flows through the normal `onError`→`close` path. */
73
+ /** Re-establish the connection after a broker exit (a yield→revive cycle),
74
+ * to the SAME target. Resolves on `connect` (caller re-sends `hello`);
75
+ * rejects on a retryable dial failure. */
78
76
  redial(): Promise<void>;
79
- /** Encode + write one client→broker frame. No-op on a dead/absent socket
80
- * (a `close` event drives teardown); never throws. */
77
+ /** Encode + send one client→broker frame. No-op on a dead/absent
78
+ * connection; never throws. */
81
79
  send(frame: ClientToBroker): void;
82
- /** Detach: destroy the socket. `close` fires → the caller tears down. */
80
+ /** Detach: tear down the transport. `close` fires → the caller tears down. */
83
81
  close(): void;
84
- private onData;
85
- private onError;
82
+ private onFrame;
86
83
  private onClose;
87
- private destroy;
88
84
  /** Emit `error` only when a listener exists — a bare EventEmitter `error`
89
85
  * with no listener throws, and this client must never throw uncaught. */
90
86
  private emitError;