@pellux/goodvibes-daemon-sdk 1.9.0 → 1.10.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.
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-rest-routes.d.ts","sourceRoot":"","sources":["../src/gateway-rest-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,UAAU,gBAAgB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAYD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,gBAAgB,EAoE1D,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,OAAO,CAAC,8BAA8B,CAAC,GAChD,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAe1B"}
1
+ {"version":3,"file":"gateway-rest-routes.d.ts","sourceRoot":"","sources":["../src/gateway-rest-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,UAAU,gBAAgB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAYD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,gBAAgB,EA0E1D,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,OAAO,CAAC,8BAA8B,CAAC,GAChD,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAe1B"}
@@ -80,6 +80,12 @@ export const GATEWAY_REST_ROUTES = [
80
80
  // power.* — sleep ownership: the chip state + the owner keep-awake toggle.
81
81
  route('GET', '/api/power/status', 'power.status.get'),
82
82
  route('POST', '/api/power/keep-awake', 'power.keepAwake.set'),
83
+ // ops.memory.get — the MemoryGovernor snapshot (tier, budget, RSS/heap,
84
+ // per-cache footprints, paused jobs, tripwire state).
85
+ route('GET', '/api/ops/memory', 'ops.memory.get'),
86
+ // voice.local.* — managed local-voice runtime status + one-act install.
87
+ route('GET', '/api/voice/local/status', 'voice.local.status'),
88
+ route('POST', '/api/voice/local/install', 'voice.local.install'),
83
89
  ];
84
90
  /**
85
91
  * Dispatch a request against the gateway REST route table.
package/dist/index.d.ts CHANGED
@@ -43,5 +43,5 @@ export type { RelayServerSocket, RelayServerLogger, RelayServerLimits, } from '.
43
43
  export { createBunRelayServer } from './relay-server-entry.js';
44
44
  export type { BunRelayServerOptions } from './relay-server-entry.js';
45
45
  export { createRelayDaemonRegistration, RELAY_VIA_HEADER, isRelayTunneledRequest } from './relay-registration.js';
46
- export type { RelayDaemonRegistration, RelayDaemonRegistrationOptions, RelayRegistrationStatus, RelayRegistrationLogger, RelayClientWebSocket, } from './relay-registration.js';
46
+ export type { RelayDaemonRegistration, RelayDaemonRegistrationOptions, RelayRegistrationStats, RelayRegistrationStatus, RelayRegistrationLogger, RelayClientWebSocket, } from './relay-registration.js';
47
47
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -43,9 +43,46 @@ export interface RelayDaemonRegistrationOptions {
43
43
  readonly maxStreamsPerPipe?: number;
44
44
  /** Bounded per-stream send buffer, in chunks; overflow drops-with-notice (default 256). */
45
45
  readonly streamBufferChunks?: number;
46
+ /**
47
+ * Max simultaneously-open secure-channel pipes retained in memory (default
48
+ * 512). Each pipe holds a live secure channel derived from the connecting
49
+ * surface's session; a relay that drops pipes WITHOUT sending a pipe-close
50
+ * control frame (crash, network partition) would otherwise leave the channel
51
+ * context — and the operator-token-authenticated requests riding it — pinned
52
+ * forever. When the cap is hit the least-recently-used pipe is evicted (its
53
+ * streams closed) so retained-context count stays bounded.
54
+ */
55
+ readonly maxPipes?: number;
56
+ /**
57
+ * Max concurrently in-flight tunneled REQUESTS across all pipes (default
58
+ * 1024). Each in-flight request retains its reconstructed Request — headers
59
+ * (including the operator-token Authorization header) and any buffered body —
60
+ * for the FULL lifetime: dispatch AND response buffering (the max-memory
61
+ * phase). When the cap is hit a new request is refused with
62
+ * an honest 503 `relay-overloaded` response instead of being retained, so a
63
+ * dispatch stall (or a hot job-transition loop fanning requests) can never
64
+ * accumulate request contexts without limit.
65
+ */
66
+ readonly maxInFlightRequests?: number;
46
67
  readonly logger?: RelayRegistrationLogger;
47
68
  readonly onStatusChange?: (status: RelayRegistrationStatus) => void;
48
69
  }
70
+ /**
71
+ * Point-in-time footprint of the retained relay contexts. Exposed so ops
72
+ * surfaces and regression tests can assert the caps hold under load.
73
+ */
74
+ export interface RelayRegistrationStats {
75
+ /** Open secure-channel pipes currently retained. */
76
+ readonly pipes: number;
77
+ /** Tunneled requests currently mid-dispatch (contexts still retained). */
78
+ readonly inFlightRequests: number;
79
+ /** Live event-subscription streams currently retained across all pipes. */
80
+ readonly streams: number;
81
+ /** Pipes evicted because the maxPipes cap was exceeded (cumulative). */
82
+ readonly droppedPipes: number;
83
+ /** Requests refused because the maxInFlightRequests cap was exceeded (cumulative). */
84
+ readonly droppedRequests: number;
85
+ }
49
86
  /** A running daemon-side relay registration. */
50
87
  export interface RelayDaemonRegistration {
51
88
  start(): void;
@@ -53,6 +90,8 @@ export interface RelayDaemonRegistration {
53
90
  readonly status: RelayRegistrationStatus;
54
91
  /** Mint a pairing payload a surface can scan to reach this daemon. */
55
92
  mintPairing(label?: string): Promise<RelayPairingPayload>;
93
+ /** Retained-context footprint, for ops visibility and cap regression tests. */
94
+ stats(): RelayRegistrationStats;
56
95
  }
57
96
  export declare function createRelayDaemonRegistration(options: RelayDaemonRegistrationOptions): RelayDaemonRegistration;
58
97
  //# sourceMappingURL=relay-registration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"relay-registration.d.ts","sourceRoot":"","sources":["../src/relay-registration.ts"],"names":[],"mappings":"AAaA,OAAO,EAeL,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACzB,MAAM,wCAAwC,CAAC;AAEhD,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,IAAI,CAAC;IACpD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;CAC1G;AAED,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AAExD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,2DAA2D;AAC3D,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,YAAY,GAAG,YAAY,GAAG,cAAc,GAAG,SAAS,CAAC;AAExG,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAChE;AAED,yDAAyD;AACzD,MAAM,WAAW,8BAA8B;IAC7C,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAC9D,yEAAyE;IACzE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,oBAAoB,CAAC;IAC/D,wDAAwD;IACxD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,mDAAmD;IACnD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,sEAAsE;IACtE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,2FAA2F;IAC3F,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC;IAC1C,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;CACrE;AAED,gDAAgD;AAChD,MAAM,WAAW,uBAAuB;IACtC,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,IAAI,CAAC;IACb,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;IACzC,sEAAsE;IACtE,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC3D;AA8FD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,8BAA8B,GAAG,uBAAuB,CA4Q9G"}
1
+ {"version":3,"file":"relay-registration.d.ts","sourceRoot":"","sources":["../src/relay-registration.ts"],"names":[],"mappings":"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"}
@@ -37,6 +37,39 @@ function toBytes(data) {
37
37
  }
38
38
  const DEFAULT_MAX_STREAMS_PER_PIPE = 8;
39
39
  const DEFAULT_STREAM_BUFFER_CHUNKS = 256;
40
+ const DEFAULT_MAX_PIPES = 512;
41
+ const DEFAULT_MAX_IN_FLIGHT_REQUESTS = 1024;
42
+ /** Ceiling on one tunneled response body; larger (or endless) bodies are refused with 502. */
43
+ const MAX_TUNNEL_RESPONSE_BYTES = 32 * 1024 * 1024;
44
+ /** Read a response body with a hard byte ceiling; over-ceiling reads are cancelled and refused. */
45
+ async function readTunnelBodyBounded(response, maxBytes) {
46
+ const body = response.body;
47
+ if (!body)
48
+ return { ok: true, bytes: new Uint8Array(0) };
49
+ const reader = body.getReader();
50
+ const chunks = [];
51
+ let total = 0;
52
+ for (;;) {
53
+ const { done, value } = await reader.read();
54
+ if (done)
55
+ break;
56
+ if (value) {
57
+ total += value.byteLength;
58
+ if (total > maxBytes) {
59
+ await reader.cancel().catch(() => { });
60
+ return { ok: false };
61
+ }
62
+ chunks.push(value);
63
+ }
64
+ }
65
+ const merged = new Uint8Array(total);
66
+ let offset = 0;
67
+ for (const chunk of chunks) {
68
+ merged.set(chunk, offset);
69
+ offset += chunk.byteLength;
70
+ }
71
+ return { ok: true, bytes: merged };
72
+ }
40
73
  /**
41
74
  * The daemon-side pump for one event subscription: it seals event-source chunks
42
75
  * into `stream-data` frames and writes them to the relay socket, applying a
@@ -122,14 +155,57 @@ export function createRelayDaemonRegistration(options) {
122
155
  const maxDelay = options.reconnectMaxDelayMs ?? 30_000;
123
156
  const maxStreamsPerPipe = options.maxStreamsPerPipe ?? DEFAULT_MAX_STREAMS_PER_PIPE;
124
157
  const streamBufferChunks = options.streamBufferChunks ?? DEFAULT_STREAM_BUFFER_CHUNKS;
158
+ const maxPipes = Math.max(1, Math.trunc(options.maxPipes ?? DEFAULT_MAX_PIPES));
159
+ const maxInFlightRequests = Math.max(1, Math.trunc(options.maxInFlightRequests ?? DEFAULT_MAX_IN_FLIGHT_REQUESTS));
125
160
  let status = 'idle';
126
161
  let socket = null;
127
162
  let stopped = false;
128
163
  let attempt = 0;
129
164
  let reconnectTimer = null;
165
+ let inFlightRequests = 0;
166
+ let droppedPipes = 0;
167
+ let droppedRequests = 0;
168
+ // Secure channels keyed by pipe. Map insertion order is the LRU order used
169
+ // to evict the coldest pipe when the maxPipes cap is exceeded; a served frame
170
+ // re-inserts its pipe so active pipes stay warm (see touchPipe).
130
171
  const channels = new Map();
131
172
  // Per-pipe live event subscriptions: pipeKey -> (streamId -> record).
132
173
  const pipeStreams = new Map();
174
+ function countStreams() {
175
+ let total = 0;
176
+ for (const streams of pipeStreams.values())
177
+ total += streams.size;
178
+ return total;
179
+ }
180
+ /** Move a pipe to the warm (most-recent) end of the LRU order. */
181
+ function touchPipe(pipeKey) {
182
+ const channel = channels.get(pipeKey);
183
+ if (!channel)
184
+ return;
185
+ channels.delete(pipeKey);
186
+ channels.set(pipeKey, channel);
187
+ }
188
+ /**
189
+ * Enforce the maxPipes cap by evicting the least-recently-used pipe(s). Each
190
+ * eviction closes the pipe's streams and drops its channel, releasing the
191
+ * retained request/session context with a structured, counted log line —
192
+ * never a silent leak.
193
+ */
194
+ function enforcePipeCap() {
195
+ while (channels.size > maxPipes) {
196
+ const oldest = channels.keys().next().value;
197
+ if (oldest === undefined)
198
+ break;
199
+ channels.delete(oldest);
200
+ closePipeStreams(oldest);
201
+ droppedPipes += 1;
202
+ logger.warn('relay pipe cap exceeded — evicted coldest pipe', {
203
+ maxPipes,
204
+ droppedPipes,
205
+ pipe: oldest,
206
+ });
207
+ }
208
+ }
133
209
  function closePipeStreams(pipeKey) {
134
210
  const streams = pipeStreams.get(pipeKey);
135
211
  if (!streams)
@@ -159,9 +235,11 @@ export function createRelayDaemonRegistration(options) {
159
235
  // First frame on a new pipe is the client's handshake initiation.
160
236
  const { keys, message2 } = await respondToHandshake(options.identity, ridBytes, payload);
161
237
  channels.set(pipeKey, new RelaySecureChannel(keys, 'daemon'));
238
+ enforcePipeCap();
162
239
  ws.send(framePipePayload(pipeIdBytes, message2));
163
240
  return;
164
241
  }
242
+ touchPipe(pipeKey);
165
243
  await serveTunneledFrame(pipeKey, existing, pipeIdBytes, payload, ws);
166
244
  }
167
245
  async function serveTunneledFrame(pipeKey, channel, pipeIdBytes, sealed, ws) {
@@ -185,24 +263,71 @@ export function createRelayDaemonRegistration(options) {
185
263
  // only; a surface should never send them. Ignore rather than trust.
186
264
  }
187
265
  async function serveTunneledRequest(header, body, channel, pipeIdBytes, ws) {
266
+ // In-flight request cap: refuse (don't retain) when saturated. Each in-flight
267
+ // request pins its reconstructed Request — headers carry the operator-token
268
+ // Authorization header — until dispatch resolves, so an unbounded backlog is
269
+ // exactly the retained-context leak this guard prevents. The refusal is an
270
+ // honest structured 503 the surface can surface and retry.
271
+ if (inFlightRequests >= maxInFlightRequests) {
272
+ droppedRequests += 1;
273
+ logger.warn('relay in-flight request cap exceeded — refused', {
274
+ maxInFlightRequests,
275
+ droppedRequests,
276
+ });
277
+ const busy = encodeTunnelFrame({ id: header.id, kind: 'response', status: 503, headers: [['content-type', 'application/json'], ['retry-after', '1']] }, encodeUtf8(JSON.stringify({ error: 'relay-overloaded', message: 'Daemon relay is at its in-flight request cap; retry shortly.' })));
278
+ ws.send(framePipePayload(pipeIdBytes, await channel.seal(busy)));
279
+ return;
280
+ }
281
+ // Abortable request: a streaming endpoint reached as a plain tunneled
282
+ // request tears down on request.signal abort; without a signal its
283
+ // producer would run (and buffer) forever.
284
+ const abort = new AbortController();
188
285
  const request = new Request(new URL(header.path, options.localBaseUrl), {
189
286
  method: header.method,
190
287
  headers: [...header.headers.map(([k, v]) => [k, v]), [RELAY_VIA_HEADER, '1']],
191
288
  ...(body.length > 0 ? { body } : {}),
289
+ signal: abort.signal,
192
290
  });
193
- let response;
291
+ // The in-flight count covers the FULL request lifetime — dispatch AND
292
+ // response buffering. Buffering is the max-memory phase; releasing the slot
293
+ // at dispatch-resolve would let N buffering requests bypass the cap.
294
+ inFlightRequests += 1;
194
295
  try {
195
- response = (await options.dispatch(request)) ?? new Response('Not found', { status: 404 });
296
+ let response;
297
+ try {
298
+ response = (await options.dispatch(request)) ?? new Response('Not found', { status: 404 });
299
+ }
300
+ catch (err) {
301
+ logger.error('relay dispatch failed', { error: String(err) });
302
+ response = new Response('Internal error', { status: 500 });
303
+ }
304
+ // A plain tunneled request must never buffer an event stream: refuse
305
+ // honestly and tear the producer down (the stream-open frame kind is the
306
+ // supported path for subscriptions).
307
+ const contentType = response.headers.get('content-type') ?? '';
308
+ if (contentType.includes('text/event-stream')) {
309
+ abort.abort();
310
+ await response.body?.cancel().catch(() => { });
311
+ const refused = encodeTunnelFrame({ id: header.id, kind: 'response', status: 501, headers: [['content-type', 'application/json']] }, encodeUtf8(JSON.stringify({ error: 'streaming-not-supported', message: 'This path serves an event stream; open it with a stream-open frame instead of a plain request.' })));
312
+ ws.send(framePipePayload(pipeIdBytes, await channel.seal(refused)));
313
+ return;
314
+ }
315
+ const bounded = await readTunnelBodyBounded(response, MAX_TUNNEL_RESPONSE_BYTES);
316
+ if (!bounded.ok) {
317
+ abort.abort();
318
+ const refused = encodeTunnelFrame({ id: header.id, kind: 'response', status: 502, headers: [['content-type', 'application/json']] }, encodeUtf8(JSON.stringify({ error: 'response-too-large', message: `Response exceeded the ${MAX_TUNNEL_RESPONSE_BYTES}-byte tunneled-request ceiling.` })));
319
+ ws.send(framePipePayload(pipeIdBytes, await channel.seal(refused)));
320
+ return;
321
+ }
322
+ const bodyBytes = bounded.bytes;
323
+ const respHeaders = [];
324
+ response.headers.forEach((value, key) => respHeaders.push([key, value]));
325
+ const out = encodeTunnelFrame({ id: header.id, kind: 'response', status: response.status, headers: respHeaders }, bodyBytes);
326
+ ws.send(framePipePayload(pipeIdBytes, await channel.seal(out)));
196
327
  }
197
- catch (err) {
198
- logger.error('relay dispatch failed', { error: String(err) });
199
- response = new Response('Internal error', { status: 500 });
328
+ finally {
329
+ inFlightRequests -= 1;
200
330
  }
201
- const bodyBytes = new Uint8Array(await response.arrayBuffer());
202
- const respHeaders = [];
203
- response.headers.forEach((value, key) => respHeaders.push([key, value]));
204
- const out = encodeTunnelFrame({ id: header.id, kind: 'response', status: response.status, headers: respHeaders }, new Uint8Array(bodyBytes.buffer.slice(bodyBytes.byteOffset, bodyBytes.byteOffset + bodyBytes.byteLength)));
205
- ws.send(framePipePayload(pipeIdBytes, await channel.seal(out)));
206
331
  }
207
332
  async function openTunneledStream(pipeKey, header, channel, pipeIdBytes, ws) {
208
333
  const streams = pipeStreams.get(pipeKey) ?? new Map();
@@ -372,5 +497,14 @@ export function createRelayDaemonRegistration(options) {
372
497
  ...(label !== undefined ? { label } : {}),
373
498
  });
374
499
  },
500
+ stats() {
501
+ return {
502
+ pipes: channels.size,
503
+ inFlightRequests,
504
+ streams: countStreams(),
505
+ droppedPipes,
506
+ droppedRequests,
507
+ };
508
+ },
375
509
  };
376
510
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-daemon-sdk",
3
- "version": "1.9.0",
3
+ "version": "1.10.1",
4
4
  "engines": {
5
5
  "bun": "1.3.10",
6
6
  "node": ">=22.0.0"
@@ -172,9 +172,9 @@
172
172
  "control-plane"
173
173
  ],
174
174
  "dependencies": {
175
- "@pellux/goodvibes-contracts": "1.9.0",
176
- "@pellux/goodvibes-errors": "1.9.0",
177
- "@pellux/goodvibes-transport-core": "1.9.0"
175
+ "@pellux/goodvibes-contracts": "1.10.1",
176
+ "@pellux/goodvibes-errors": "1.10.1",
177
+ "@pellux/goodvibes-transport-core": "1.10.1"
178
178
  },
179
179
  "publishConfig": {
180
180
  "access": "public"