@camstack/system 1.2.289 → 1.2.291

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 (62) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/autotrack/index.js +1 -1
  10. package/dist/builtins/autotrack/index.mjs +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  13. package/dist/builtins/camera-grid/index.js +1 -1
  14. package/dist/builtins/camera-grid/index.mjs +1 -1
  15. package/dist/builtins/console-logging/index.js +1 -1
  16. package/dist/builtins/console-logging/index.mjs +1 -1
  17. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  18. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  19. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  20. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  21. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  22. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  23. package/dist/builtins/hub-forwarder/index.js +1 -1
  24. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  25. package/dist/builtins/liveness-monitor/liveness-monitor.addon.d.ts +9 -0
  26. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +18 -2
  27. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +18 -2
  28. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  29. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  30. package/dist/builtins/local-network/local-network.addon.js +1 -1
  31. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  32. package/dist/builtins/loki-logging/index.js +1 -1
  33. package/dist/builtins/loki-logging/index.mjs +1 -1
  34. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  35. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  36. package/dist/builtins/platform-probe/index.js +1 -1
  37. package/dist/builtins/platform-probe/index.mjs +1 -1
  38. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  39. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  40. package/dist/builtins/snapshot/index.js +1 -1
  41. package/dist/builtins/snapshot/index.mjs +1 -1
  42. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  43. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  44. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  45. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  46. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +11 -11
  47. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +11 -11
  48. package/dist/builtins/system-config/system-config.addon.js +1 -1
  49. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  50. package/dist/builtins/winston-logging/index.js +1 -1
  51. package/dist/builtins/winston-logging/index.mjs +1 -1
  52. package/dist/{dist-DKFb2936.mjs → dist-DiMNxNDh.mjs} +1474 -1017
  53. package/dist/{dist-BYnHJU96.js → dist-XnunBp0n.js} +1479 -1016
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.js +5 -3
  56. package/dist/index.mjs +4 -4
  57. package/dist/kernel/moleculer/addon-peer-bytes-facility.d.ts +25 -0
  58. package/dist/{manifest-system-deps-B8ZUclN4.js → manifest-system-deps-DTgkfBJy.js} +591 -0
  59. package/dist/{manifest-system-deps-C9T7784z.mjs → manifest-system-deps-ghRqcy_-.mjs} +585 -6
  60. package/dist/{retired-settings-keys-C6rFqHR7.js → retired-settings-keys-DQn9s_AI.js} +1 -1
  61. package/dist/{retired-settings-keys-tuNxNNpR.mjs → retired-settings-keys-DnCcgrCG.mjs} +1 -1
  62. package/package.json +1 -1
@@ -2,21 +2,22 @@ import "./hub-cap-forward-BdT320dq.mjs";
2
2
  import * as crypto$1 from "node:crypto";
3
3
  import { randomBytes, randomUUID } from "node:crypto";
4
4
  import { ensureBinary, ensureFfmpeg, ensurePython, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
5
- import { unlink } from "node:fs/promises";
5
+ import { mkdir, rm, stat, unlink } from "node:fs/promises";
6
6
  import * as path$1 from "node:path";
7
- import { isAbsolute, join } from "node:path";
7
+ import { dirname, isAbsolute, join } from "node:path";
8
8
  import * as fs from "node:fs";
9
- import { existsSync, readFileSync, readdirSync } from "node:fs";
9
+ import { createReadStream, createWriteStream, existsSync, readFileSync, readdirSync } from "node:fs";
10
10
  import { execFile } from "node:child_process";
11
11
  import * as os from "node:os";
12
12
  import { tmpdir } from "node:os";
13
13
  import { promisify } from "node:util";
14
- import { createServer } from "node:http";
14
+ import { createServer, request } from "node:http";
15
15
  import * as v8 from "node:v8";
16
16
  import { Session } from "node:inspector/promises";
17
17
  import { monitorEventLoopDelay } from "node:perf_hooks";
18
18
  import * as vm from "node:vm";
19
- import { BOOT_RECOVERY_BACKOFF_MS, DATAPLANE_SECRET_HEADER, DEVICE_CHILDREN_BATCH_MAX, DeviceType, DisposerChain, EventCategory, ReadinessRegistry, asJsonObject, asString, createDeviceProxy, createEventBusSliceSource, deviceOpsCapability, emitReadiness, errMsg, expandCapMethods, readNodePin, scopeKey, sleep, withCapInputDefaults } from "@camstack/types/addon";
19
+ import { BOOT_RECOVERY_BACKOFF_MS, DATAPLANE_SECRET_HEADER, DEVICE_CHILDREN_BATCH_MAX, DeviceType, DisposerChain, EventCategory, PEER_BYTES_CHUNK_BYTES, PEER_BYTES_MAX_OUTSTANDING, PEER_BYTES_STALL_MS, PEER_BYTES_TICKET_TTL_MS, ReadinessRegistry, asJsonObject, asString, createDeviceProxy, createEventBusSliceSource, deviceOpsCapability, emitReadiness, errMsg, expandCapMethods, readNodePin, scopeKey, sleep, withCapInputDefaults } from "@camstack/types/addon";
20
+ import { once } from "node:events";
20
21
  import { connect, createConnection, createServer as createServer$1 } from "node:net";
21
22
  import { TRPCClientError, createTRPCClient } from "@trpc/client";
22
23
  //#region src/kernel/event-plane-report.ts
@@ -2610,6 +2611,577 @@ function createAddonDataPlaneFacility(args) {
2610
2611
  };
2611
2612
  }
2612
2613
  //#endregion
2614
+ //#region src/kernel/moleculer/addon-peer-bytes-facility.ts
2615
+ /**
2616
+ * Kernel side of the addon→addon **byte transport** (`ctx.peerBytes`, D613).
2617
+ *
2618
+ * See {@link AddonPeerBytes} for what this exists for and what it deliberately
2619
+ * does not do. This file is the mechanism:
2620
+ *
2621
+ * - **One lazy loopback listener per addon process.** Bound on the first
2622
+ * `offer()`, never before, so an addon that never offers pays no port.
2623
+ * - **One token per offer.** 24 random bytes, base64url, ONE `GET`. A replay
2624
+ * is `offer-used` (410) for as long as the original ticket would have lived,
2625
+ * so a second take is told what happened rather than "no such thing".
2626
+ * `HEAD` answers the headers and consumes nothing.
2627
+ * - **Outstanding offers are bounded and swept on every mint**, which is also
2628
+ * what makes a per-frame misuse of this facility refuse by name instead of
2629
+ * working slowly.
2630
+ * - **Every write honours `write()`'s return and awaits `drain`** (D447), and
2631
+ * a peer that has taken nothing for {@link PEER_BYTES_STALL_MS} is cut. Every
2632
+ * exit — offered, taken, sent, cut, stalled, refused, disposed — is one log
2633
+ * line, carrying `tags: { deviceId }` when the offer named a device.
2634
+ * - **The consumer's response is handed over PAUSED**, which is the whole
2635
+ * reason `open()` lives in the framework rather than in each consumer: see
2636
+ * {@link PeerBytesBody}.
2637
+ */
2638
+ /** The HOST a node id belongs to. `hub` → `hub`; `hub/runner-3` → `hub`. */
2639
+ function hostNodeIdOf(nodeId) {
2640
+ const slash = nodeId.indexOf("/");
2641
+ return slash === -1 ? nodeId : nodeId.slice(0, slash);
2642
+ }
2643
+ /** A body chunk, without a cast. A readable in binary mode yields Buffers; in
2644
+ * string mode, strings. Anything else is a broken stream and says so. */
2645
+ function asBuffer(chunk) {
2646
+ if (Buffer.isBuffer(chunk)) return chunk;
2647
+ if (typeof chunk === "string") return Buffer.from(chunk);
2648
+ throw new Error("peer-bytes: a body chunk was neither a Buffer nor a string");
2649
+ }
2650
+ function tokenOf(req) {
2651
+ const raw = req.url ?? "/";
2652
+ const query = raw.indexOf("?");
2653
+ return (query === -1 ? raw : raw.slice(0, query)).replace(/^\/+/, "");
2654
+ }
2655
+ /**
2656
+ * Write `bytes` to `res` in bounded chunks, honouring backpressure.
2657
+ *
2658
+ * D447, the consuming half stated from the producing side: a raw HTTP stream
2659
+ * writes no chunk the socket has not taken. `write()` returning false means the
2660
+ * kernel buffer is full and everything further is heap in THIS process — so we
2661
+ * wait for `drain`, and a peer that never drains within `stallMs` is cut rather
2662
+ * than held. Returns how many bytes reached the socket and why it stopped.
2663
+ */
2664
+ async function writeBufferWithBackpressure(res, bytes, stallMs) {
2665
+ const waitForDrain = () => new Promise((resolve) => {
2666
+ const settle = (outcome) => {
2667
+ clearTimeout(timer);
2668
+ res.off("drain", onDrain);
2669
+ res.off("close", onClose);
2670
+ resolve(outcome);
2671
+ };
2672
+ const onDrain = () => {
2673
+ settle("drained");
2674
+ };
2675
+ const onClose = () => {
2676
+ settle("closed");
2677
+ };
2678
+ const timer = setTimeout(() => {
2679
+ settle("stalled");
2680
+ }, stallMs);
2681
+ timer.unref();
2682
+ res.on("drain", onDrain);
2683
+ res.on("close", onClose);
2684
+ });
2685
+ let sent = 0;
2686
+ while (sent < bytes.byteLength) {
2687
+ if (res.destroyed || res.writableEnded) return {
2688
+ sent,
2689
+ outcome: "peer-gone"
2690
+ };
2691
+ const end = Math.min(sent + PEER_BYTES_CHUNK_BYTES, bytes.byteLength);
2692
+ const took = res.write(bytes.subarray(sent, end));
2693
+ sent = end;
2694
+ if (took) continue;
2695
+ const waited = await waitForDrain();
2696
+ if (waited === "drained") continue;
2697
+ return {
2698
+ sent,
2699
+ outcome: waited === "closed" ? "peer-gone" : "stalled"
2700
+ };
2701
+ }
2702
+ return {
2703
+ sent,
2704
+ outcome: "sent"
2705
+ };
2706
+ }
2707
+ function createAddonPeerBytesFacility(args) {
2708
+ const now = args.now ?? (() => Date.now());
2709
+ const ttlMs = args.ttlMs ?? PEER_BYTES_TICKET_TTL_MS;
2710
+ const maxOutstanding = args.maxOutstanding ?? PEER_BYTES_MAX_OUTSTANDING;
2711
+ const stallMs = args.stallMs ?? PEER_BYTES_STALL_MS;
2712
+ const host = args.host ?? "127.0.0.1";
2713
+ const hostNodeId = hostNodeIdOf(args.nodeId);
2714
+ const offers = /* @__PURE__ */ new Map();
2715
+ /** Tokens already taken, kept for their TTL so a replay is `offer-used`. */
2716
+ const taken = /* @__PURE__ */ new Map();
2717
+ let server = null;
2718
+ let baseUrl = null;
2719
+ let listening = null;
2720
+ const sweep = () => {
2721
+ const at = now();
2722
+ for (const [token, offer] of offers) if (offer.expiresAtMs <= at) {
2723
+ offers.delete(token);
2724
+ args.logger.info("peer-bytes: an offer expired untaken", {
2725
+ ...offer.deviceId !== void 0 ? { tags: { deviceId: offer.deviceId } } : {},
2726
+ meta: {
2727
+ label: offer.label,
2728
+ outstanding: offers.size
2729
+ }
2730
+ });
2731
+ }
2732
+ for (const [token, expiresAtMs] of taken) if (expiresAtMs <= at) taken.delete(token);
2733
+ };
2734
+ const route = (req, res) => {
2735
+ const token = tokenOf(req);
2736
+ const offer = offers.get(token);
2737
+ if (offer === void 0) {
2738
+ const status = taken.has(token) ? 410 : 404;
2739
+ res.writeHead(status, { "x-camstack-peer-bytes": status === 410 ? "offer-used" : "no-offer" });
2740
+ res.end();
2741
+ return;
2742
+ }
2743
+ if (offer.expiresAtMs <= now()) {
2744
+ offers.delete(token);
2745
+ res.writeHead(410, { "x-camstack-peer-bytes": "offer-expired" }).end();
2746
+ return;
2747
+ }
2748
+ if (req.method === "GET") {
2749
+ offers.delete(token);
2750
+ taken.set(token, offer.expiresAtMs);
2751
+ args.logger.info("peer-bytes: an offer was taken", {
2752
+ ...offer.deviceId !== void 0 ? { tags: { deviceId: offer.deviceId } } : {},
2753
+ meta: {
2754
+ label: offer.label,
2755
+ outstanding: offers.size
2756
+ }
2757
+ });
2758
+ }
2759
+ Promise.resolve(offer.handler(req, res)).catch((err) => {
2760
+ args.logger.warn("peer-bytes: an offer handler threw", {
2761
+ ...offer.deviceId !== void 0 ? { tags: { deviceId: offer.deviceId } } : {},
2762
+ meta: {
2763
+ label: offer.label,
2764
+ error: err instanceof Error ? err.message : String(err)
2765
+ }
2766
+ });
2767
+ if (!res.headersSent) res.writeHead(500);
2768
+ res.end();
2769
+ });
2770
+ };
2771
+ const listen = () => {
2772
+ if (listening !== null) return listening;
2773
+ listening = new Promise((resolve, reject) => {
2774
+ const created = createServer((req, res) => {
2775
+ route(req, res);
2776
+ });
2777
+ created.on("error", (err) => {
2778
+ listening = null;
2779
+ reject(err);
2780
+ });
2781
+ created.listen(0, host, () => {
2782
+ const address = created.address();
2783
+ if (address === null || typeof address === "string") {
2784
+ created.close();
2785
+ listening = null;
2786
+ reject(/* @__PURE__ */ new Error("the peer-bytes listener bound no TCP port"));
2787
+ return;
2788
+ }
2789
+ server = created;
2790
+ baseUrl = `http://${host}:${address.port}`;
2791
+ args.logger.info("peer-bytes: listener bound", { meta: {
2792
+ addonId: args.addonId,
2793
+ host,
2794
+ port: address.port,
2795
+ hostNodeId,
2796
+ ttlMs
2797
+ } });
2798
+ resolve(baseUrl);
2799
+ });
2800
+ });
2801
+ return listening;
2802
+ };
2803
+ const mint = async (options) => {
2804
+ sweep();
2805
+ const deviceTags = options.deviceId !== void 0 ? { tags: { deviceId: options.deviceId } } : {};
2806
+ if (offers.size >= maxOutstanding) {
2807
+ args.logger.warn("peer-bytes: offer refused — too many outstanding", {
2808
+ ...deviceTags,
2809
+ meta: {
2810
+ label: options.label,
2811
+ outstanding: offers.size,
2812
+ maxOutstanding
2813
+ }
2814
+ });
2815
+ return {
2816
+ kind: "refused",
2817
+ code: "offer-exhausted",
2818
+ detail: `${String(offers.size)} offers outstanding against a bound of ${String(maxOutstanding)}`
2819
+ };
2820
+ }
2821
+ let base;
2822
+ try {
2823
+ base = baseUrl ?? await listen();
2824
+ } catch (err) {
2825
+ const detail = err instanceof Error ? err.message : String(err);
2826
+ args.logger.warn("peer-bytes: offer refused — no listener", {
2827
+ ...deviceTags,
2828
+ meta: {
2829
+ label: options.label,
2830
+ error: detail
2831
+ }
2832
+ });
2833
+ return {
2834
+ kind: "refused",
2835
+ code: "listener-failed",
2836
+ detail
2837
+ };
2838
+ }
2839
+ const token = randomBytes(24).toString("base64url");
2840
+ const expiresAtMs = now() + (options.ttlMs ?? ttlMs);
2841
+ offers.set(token, {
2842
+ handler: options.handler,
2843
+ label: options.label,
2844
+ deviceId: options.deviceId,
2845
+ expiresAtMs
2846
+ });
2847
+ args.logger.info("peer-bytes: offer minted", {
2848
+ ...deviceTags,
2849
+ meta: {
2850
+ label: options.label,
2851
+ declaredBytes: options.declaredBytes ?? null,
2852
+ expiresInMs: expiresAtMs - now(),
2853
+ outstanding: offers.size
2854
+ }
2855
+ });
2856
+ return {
2857
+ kind: "ticket",
2858
+ ticket: {
2859
+ url: `${base}/${token}`,
2860
+ hostNodeId,
2861
+ expiresAtMs,
2862
+ declaredBytes: options.declaredBytes ?? null
2863
+ }
2864
+ };
2865
+ };
2866
+ const peerBytes = {
2867
+ hostNodeId,
2868
+ get outstanding() {
2869
+ sweep();
2870
+ return offers.size;
2871
+ },
2872
+ offer: mint,
2873
+ offerBuffer: async (options) => {
2874
+ const deviceTags = options.deviceId !== void 0 ? { tags: { deviceId: options.deviceId } } : {};
2875
+ const handler = async (req, res) => {
2876
+ res.writeHead(200, {
2877
+ "content-type": options.contentType,
2878
+ "content-length": String(options.bytes.byteLength),
2879
+ "accept-ranges": "none"
2880
+ });
2881
+ if (req.method === "HEAD") {
2882
+ res.end();
2883
+ return;
2884
+ }
2885
+ const startedAt = now();
2886
+ const result = await writeBufferWithBackpressure(res, options.bytes, stallMs);
2887
+ if (result.outcome === "sent") {
2888
+ res.end();
2889
+ args.logger.info("peer-bytes: body sent", {
2890
+ ...deviceTags,
2891
+ meta: {
2892
+ label: options.label,
2893
+ bytes: result.sent,
2894
+ tookMs: now() - startedAt,
2895
+ source: "buffer"
2896
+ }
2897
+ });
2898
+ return;
2899
+ }
2900
+ args.logger.warn("peer-bytes: body cut before it was complete", {
2901
+ ...deviceTags,
2902
+ meta: {
2903
+ label: options.label,
2904
+ branch: result.outcome,
2905
+ sent: result.sent,
2906
+ of: options.bytes.byteLength,
2907
+ stallMs,
2908
+ source: "buffer"
2909
+ }
2910
+ });
2911
+ res.destroy();
2912
+ };
2913
+ return await mint({
2914
+ handler,
2915
+ label: options.label,
2916
+ declaredBytes: options.bytes.byteLength,
2917
+ ...options.deviceId !== void 0 ? { deviceId: options.deviceId } : {},
2918
+ ...options.ttlMs !== void 0 ? { ttlMs: options.ttlMs } : {}
2919
+ });
2920
+ },
2921
+ offerFile: async (options) => {
2922
+ const deviceTags = options.deviceId !== void 0 ? { tags: { deviceId: options.deviceId } } : {};
2923
+ let declaredBytes = null;
2924
+ try {
2925
+ declaredBytes = (await stat(options.path)).size;
2926
+ } catch {
2927
+ declaredBytes = null;
2928
+ }
2929
+ const handler = async (req, res) => {
2930
+ const headers = {
2931
+ "content-type": options.contentType,
2932
+ "accept-ranges": "none"
2933
+ };
2934
+ if (declaredBytes !== null) headers["content-length"] = String(declaredBytes);
2935
+ if (req.method === "HEAD") {
2936
+ res.writeHead(200, headers).end();
2937
+ return;
2938
+ }
2939
+ const startedAt = now();
2940
+ let stream;
2941
+ try {
2942
+ stream = createReadStream(options.path);
2943
+ await once(stream, "open");
2944
+ } catch (err) {
2945
+ const detail = err instanceof Error ? err.message : String(err);
2946
+ args.logger.warn("peer-bytes: the offered file could not be opened", {
2947
+ ...deviceTags,
2948
+ meta: {
2949
+ label: options.label,
2950
+ error: detail
2951
+ }
2952
+ });
2953
+ if (!res.headersSent) res.writeHead(503, { "x-camstack-peer-bytes": "file-unreadable" });
2954
+ res.end();
2955
+ return;
2956
+ }
2957
+ res.writeHead(200, headers);
2958
+ let sent = 0;
2959
+ let lastProgressAt = now();
2960
+ stream.pipe(res);
2961
+ stream.on("data", (chunk) => {
2962
+ sent += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.byteLength;
2963
+ lastProgressAt = now();
2964
+ });
2965
+ const stallTimer = setInterval(() => {
2966
+ if (now() - lastProgressAt < stallMs) return;
2967
+ clearInterval(stallTimer);
2968
+ args.logger.warn("peer-bytes: body cut before it was complete", {
2969
+ ...deviceTags,
2970
+ meta: {
2971
+ label: options.label,
2972
+ branch: "stalled",
2973
+ sent,
2974
+ stallMs,
2975
+ source: "file"
2976
+ }
2977
+ });
2978
+ stream.destroy();
2979
+ res.destroy();
2980
+ }, Math.max(1e3, Math.floor(stallMs / 4)));
2981
+ stallTimer.unref();
2982
+ const ended = await Promise.race([once(stream, "end").then(() => true), once(res, "close").then(() => false)]).catch(() => false);
2983
+ clearInterval(stallTimer);
2984
+ if (ended) {
2985
+ args.logger.info("peer-bytes: body sent", {
2986
+ ...deviceTags,
2987
+ meta: {
2988
+ label: options.label,
2989
+ bytes: sent,
2990
+ tookMs: now() - startedAt,
2991
+ source: "file"
2992
+ }
2993
+ });
2994
+ return;
2995
+ }
2996
+ stream.destroy();
2997
+ args.logger.warn("peer-bytes: body cut before it was complete", {
2998
+ ...deviceTags,
2999
+ meta: {
3000
+ label: options.label,
3001
+ branch: "peer-gone",
3002
+ sent,
3003
+ of: declaredBytes,
3004
+ source: "file"
3005
+ }
3006
+ });
3007
+ };
3008
+ return await mint({
3009
+ handler,
3010
+ label: options.label,
3011
+ ...declaredBytes !== null ? { declaredBytes } : {},
3012
+ ...options.deviceId !== void 0 ? { deviceId: options.deviceId } : {},
3013
+ ...options.ttlMs !== void 0 ? { ttlMs: options.ttlMs } : {}
3014
+ });
3015
+ },
3016
+ open: async (ticket) => {
3017
+ if (ticket.hostNodeId !== hostNodeId) {
3018
+ args.logger.warn("peer-bytes: refusing a ticket minted on another node", { meta: {
3019
+ mintedOn: ticket.hostNodeId,
3020
+ here: hostNodeId
3021
+ } });
3022
+ return {
3023
+ kind: "refused",
3024
+ code: "cross-node",
3025
+ detail: `this ticket was minted on node "${ticket.hostNodeId}" and this process runs on "${hostNodeId}" — peer-bytes is loopback only, so there is nothing to dial`
3026
+ };
3027
+ }
3028
+ if (ticket.expiresAtMs <= now()) return {
3029
+ kind: "refused",
3030
+ code: "ticket-expired",
3031
+ detail: `this ticket expired ${String(now() - ticket.expiresAtMs)} ms ago`
3032
+ };
3033
+ let url;
3034
+ try {
3035
+ url = new URL(ticket.url);
3036
+ } catch {
3037
+ return {
3038
+ kind: "refused",
3039
+ code: "ticket-malformed",
3040
+ detail: `"${ticket.url}" is not a URL`
3041
+ };
3042
+ }
3043
+ if (url.protocol !== "http:") return {
3044
+ kind: "refused",
3045
+ code: "ticket-malformed",
3046
+ detail: `peer-bytes dials http, not "${url.protocol}"`
3047
+ };
3048
+ const req = request({
3049
+ hostname: url.hostname,
3050
+ port: url.port,
3051
+ path: url.pathname,
3052
+ method: "GET"
3053
+ });
3054
+ let response;
3055
+ try {
3056
+ response = await new Promise((resolve, reject) => {
3057
+ req.once("response", resolve);
3058
+ req.once("error", reject);
3059
+ req.end();
3060
+ });
3061
+ } catch (err) {
3062
+ const detail = err instanceof Error ? err.message : String(err);
3063
+ req.destroy();
3064
+ args.logger.warn("peer-bytes: the dial failed", { meta: { error: detail } });
3065
+ return {
3066
+ kind: "refused",
3067
+ code: "unreachable",
3068
+ detail
3069
+ };
3070
+ }
3071
+ response.pause();
3072
+ const status = response.statusCode ?? 0;
3073
+ if (status < 200 || status >= 300) {
3074
+ const reason = response.headers["x-camstack-peer-bytes"];
3075
+ response.resume();
3076
+ req.destroy();
3077
+ return {
3078
+ kind: "refused",
3079
+ code: "producer-refused",
3080
+ detail: typeof reason === "string" ? `the producer answered ${String(status)} (${reason})` : `the producer answered ${String(status)}`,
3081
+ status
3082
+ };
3083
+ }
3084
+ const lengthHeader = response.headers["content-length"];
3085
+ const declaredBytes = typeof lengthHeader === "string" && /^\d+$/.test(lengthHeader) ? Number(lengthHeader) : null;
3086
+ const contentTypeHeader = response.headers["content-type"];
3087
+ const stream = response;
3088
+ return {
3089
+ kind: "body",
3090
+ body: {
3091
+ status,
3092
+ contentType: typeof contentTypeHeader === "string" ? contentTypeHeader : null,
3093
+ declaredBytes,
3094
+ stream,
3095
+ cancel: (reason) => {
3096
+ args.logger.info("peer-bytes: the consumer gave up on a body", { meta: { reason } });
3097
+ response.destroy();
3098
+ req.destroy();
3099
+ },
3100
+ writeToFile: async (path, options) => {
3101
+ await mkdir(dirname(path), { recursive: true });
3102
+ const sink = createWriteStream(path);
3103
+ const discard = async () => {
3104
+ sink.destroy();
3105
+ if (!sink.closed) await once(sink, "close").catch(() => void 0);
3106
+ await rm(path, { force: true });
3107
+ };
3108
+ let written = 0;
3109
+ let overrun = false;
3110
+ try {
3111
+ for await (const chunk of stream) {
3112
+ const buf = asBuffer(chunk);
3113
+ written += buf.byteLength;
3114
+ if (written > options.maxBytes) {
3115
+ overrun = true;
3116
+ break;
3117
+ }
3118
+ if (!sink.write(buf)) await once(sink, "drain");
3119
+ }
3120
+ } catch (err) {
3121
+ const detail = err instanceof Error ? err.message : String(err);
3122
+ response.destroy();
3123
+ req.destroy();
3124
+ await discard();
3125
+ return {
3126
+ kind: "refused",
3127
+ code: "transfer-failed",
3128
+ detail,
3129
+ bytes: written
3130
+ };
3131
+ }
3132
+ if (overrun) {
3133
+ response.destroy();
3134
+ req.destroy();
3135
+ await discard();
3136
+ return {
3137
+ kind: "refused",
3138
+ code: "too-large",
3139
+ detail: `the body passed ${String(options.maxBytes)} bytes and was aborted`,
3140
+ bytes: written
3141
+ };
3142
+ }
3143
+ try {
3144
+ sink.end();
3145
+ await once(sink, "close");
3146
+ } catch (err) {
3147
+ const detail = err instanceof Error ? err.message : String(err);
3148
+ await discard();
3149
+ return {
3150
+ kind: "refused",
3151
+ code: "write-failed",
3152
+ detail,
3153
+ bytes: written
3154
+ };
3155
+ }
3156
+ return {
3157
+ kind: "written",
3158
+ bytes: written
3159
+ };
3160
+ }
3161
+ }
3162
+ };
3163
+ }
3164
+ };
3165
+ const dispose = async () => {
3166
+ offers.clear();
3167
+ taken.clear();
3168
+ const open = server;
3169
+ server = null;
3170
+ baseUrl = null;
3171
+ listening = null;
3172
+ if (open === null) return;
3173
+ await new Promise((resolve) => {
3174
+ open.close(() => resolve());
3175
+ open.closeAllConnections();
3176
+ });
3177
+ args.logger.info("peer-bytes: listener closed", { meta: { addonId: args.addonId } });
3178
+ };
3179
+ return {
3180
+ peerBytes,
3181
+ dispose
3182
+ };
3183
+ }
3184
+ //#endregion
2613
3185
  //#region src/kernel/deps/npm-command.ts
2614
3186
  /**
2615
3187
  * npm invocation resolver — makes "is npm available?" a solved problem for
@@ -11187,6 +11759,12 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
11187
11759
  ...options?.dataPlaneSink !== void 0 ? { sink: options.dataPlaneSink } : {}
11188
11760
  });
11189
11761
  workerDisposerChain.add(() => dataPlaneFacility.dispose());
11762
+ const peerBytesFacility = createAddonPeerBytesFacility({
11763
+ addonId,
11764
+ logger: scopedLogger,
11765
+ nodeId
11766
+ });
11767
+ workerDisposerChain.add(() => peerBytesFacility.dispose());
11190
11768
  return {
11191
11769
  id: addonId,
11192
11770
  logger: scopedLogger,
@@ -11195,6 +11773,7 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
11195
11773
  dataDir: addonDataDir,
11196
11774
  nodeDataDir,
11197
11775
  dataPlane: dataPlaneFacility.dataPlane,
11776
+ peerBytes: peerBytesFacility.peerBytes,
11198
11777
  api,
11199
11778
  deps: new AddonDepsManager(addonDataDir, scopedLogger),
11200
11779
  settings: settingsView,
@@ -11691,4 +12270,4 @@ async function defaultRun(command, args) {
11691
12270
  await execFileAsync(command, [...args], { timeout: INSTALL_TIMEOUT_MS });
11692
12271
  }
11693
12272
  //#endregion
11694
- export { buildNativeCapProxy as $, ARRAY_BUFFER_CENSUS_SUSTAIN_MS as $t, createUdsLogger as A, createSocketDirectionCounters as An, HEAP_WATCH_WARN_RATIO as At, UDS_NO_ROUTE_PREFIX as B, createCapActionCensus as Bn, createDeferredHeapWatchSink as Bt, deviceCapResolverLink as C, SOCKET_QUEUE_TOP_N as Cn, resolveNpmInvocation as Ct, createUdsEventBridge as D, formatSocketPlane as Dn, HEAP_RECLAIM_STEADY_STATE_INTERVAL_MS as Dt, createParentUnownedCallHandler as E, diffSocketPlane as En, HEAP_RECLAIM_MIN_INTERVAL_MS as Et, callWithServiceDiscovery as F, CAP_ACTION_OVERFLOW_LABEL as Fn, RSS_BUDGET_RELEASE_RATIO as Ft, getCapUsageRegistry as G, diffEventPlane as Gn, nextRssBudgetState as Gt, CapUsageRegistry as H, EVENT_PLANE_TOP_N as Hn, createV8Reclaimer as Ht, CapRouteError as I, CAP_ACTION_UNLABELLED as In, RUNNER_HEAP_WATCH_INTERVAL_MS as It, UdsLocalTransportServer as J, shouldReclaim as Jt, createLocalTransport as K, formatEventPlane as Kn, parseRssBudgetMb as Kt, classifyCapRoute as L, EMPTY_CAP_ACTION_READING as Ln, RUNNER_RSS_BUDGET_ENV as Lt, AGENT_CAP_FWD_ACTION as M, sampleSocketDirection as Mn, HUB_MAIN_RSS_BUDGET_MB as Mt, AGENT_CAP_FWD_SERVICE as N, socketDirectionBytes as Nn, HUB_RSS_BUDGET_ENV as Nt, createUdsEventBus as O, readSocketQueues as On, HEAP_RECLAIM_TRIGGER_MB as Ot, CapRouteResolver as P, socketDirectionMessages as Pn, RSS_BUDGET_REANNOUNCE_MIN_MS as Pt, encodeFrame as Q, ARRAY_BUFFER_CENSUS_COOLDOWN_MS as Qt, LocalChildClient as R, MAX_CAP_ACTIONS_PER_PEER as Rn, ZERO_LOOP_DELAY as Rt, buildLinkChain as S, SOCKET_PLANE_TOP_N as Sn, buildNpmChildEnv as St, localProviderLink as T, createSocketPlaneReader as Tn, createAddonDataPlaneFacility as Tt, PARENT_ROUTED_PROVIDER_ADDON_ID as U, createEventPlaneMeter as Un, describeRss as Ut, readMoleculerFanoutMode as V, diffCapActionCensus as Vn, createLoopDelayMeter as Vt, __resetCapUsageRegistryForTests as W, createEventPlaneReader as Wn, hubMainRssBudget as Wt, localEndpointPath as X, startRunnerHeapWatch as Xt, SocketChannel as Y, startHeapWatch as Yt, FrameDecoder as Z, strandedMb as Zt, AddonDepsManager as _, formatOutboundQueueVerdict as _n, CapabilityHandle as _t, createUdsAddonContext as a, isArrayBufferCensusReport as an, setWorkerNativeCapsChangeListener as at, brokerCallForCap as b, formatMeshQueue as bn, installManifestNativeDeps as bt, getOrInitReadinessRegistryForClient as c, emitHeapDiagnosticReport as cn, NATIVE_PROVIDER_SERVICE_INFIX as ct, clusterEventTopic as d, readHeapSpaces as dn, capBareAction as dt, ARRAY_BUFFER_CENSUS_THRESHOLD_MB as en, buildUdsNativeCapProxy as et, getBrokerEventBus as f, selectReportedSpaces as fn, capServiceName as ft, subscribePassthrough as g, createOutboundQueueGate as gn, DeviceRegistry as gt, setNodeEventInterest as h, OUTBOUND_QUEUE_THRESHOLD_BYTES as hn, serializeTypedArrays as ht, createAddonContext as i, formatArrayBufferCensus as in, mountNativeCapService as it, createUdsLoggerWithControl as j, recordSocketFrame as jn, HUB_MAIN_HEAP_WATCH_LABEL as jt, udsChildLogToWorkerEntry as k, EMPTY_SOCKET_DIRECTION as kn, HEAP_WATCH_INTERVAL_MS as kt, setHubConnected as l, formatHeapSpaces as ln, capActionName as lt, registerEventBusService as m, OUTBOUND_QUEUE_SUSTAIN_MS as mn, deserializeTypedArrays as mt, installManifestPythonDeps as n, createArrayBufferCensusGate as nn, getWorkerNativeCapProvider as nt, getWorkerDeviceRegistry as o, DECISIVE_HEAP_SPACES as on, createAddonService as ot, getMoleculerEventStats as p, OUTBOUND_QUEUE_COOLDOWN_MS as pn, parseCapAction as pt, UdsLocalTransportClient as q, reclaimIntervalMs as qt, adaptBrokerToCluster as r, createInspectorArrayBufferCensus as rn, getWorkerNativeCapSnapshot as rt, getOrInitReadinessRegistry as s, HEAP_SPACE_REPORT_MIN_MB as sn, validateProviderRegistrations as st, installManifestSystemDeps as t, ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO as tn, createBrokerDeviceManagerApi as tt, EVENT_TOPIC_PREFIX as u, heapSpaceField as un, capActionSuffix as ut, createKernelHwAccel as v, MESH_QUEUE_TOP_N as vn, CapabilityUnavailableError as vt, ipcParentLink as w, createSocketPlaneMeter as wn, runNpm as wt, brokerTransportLink as x, EMPTY_SOCKET_PLANE_BASELINE as xn, resolveAddonClass as xt, resolveHwAccel as y, createMeshQueueReader as yn, copyBundledNativeModules as yt, LocalChildRegistry as z, capActionLabel as zn, buildHeapSample as zt };
12273
+ export { buildNativeCapProxy as $, strandedMb as $t, createUdsLogger as A, readSocketQueues as An, HEAP_RECLAIM_TRIGGER_MB as At, UDS_NO_ROUTE_PREFIX as B, MAX_CAP_ACTIONS_PER_PEER as Bn, ZERO_LOOP_DELAY as Bt, deviceCapResolverLink as C, EMPTY_SOCKET_PLANE_BASELINE as Cn, resolveNpmInvocation as Ct, createUdsEventBridge as D, createSocketPlaneReader as Dn, createAddonDataPlaneFacility as Dt, createParentUnownedCallHandler as E, createSocketPlaneMeter as En, hostNodeIdOf as Et, callWithServiceDiscovery as F, socketDirectionBytes as Fn, HUB_RSS_BUDGET_ENV as Ft, getCapUsageRegistry as G, createEventPlaneMeter as Gn, describeRss as Gt, CapUsageRegistry as H, createCapActionCensus as Hn, createDeferredHeapWatchSink as Ht, CapRouteError as I, socketDirectionMessages as In, RSS_BUDGET_REANNOUNCE_MIN_MS as It, UdsLocalTransportServer as J, formatEventPlane as Jn, parseRssBudgetMb as Jt, createLocalTransport as K, createEventPlaneReader as Kn, hubMainRssBudget as Kt, classifyCapRoute as L, CAP_ACTION_OVERFLOW_LABEL as Ln, RSS_BUDGET_RELEASE_RATIO as Lt, AGENT_CAP_FWD_ACTION as M, createSocketDirectionCounters as Mn, HEAP_WATCH_WARN_RATIO as Mt, AGENT_CAP_FWD_SERVICE as N, recordSocketFrame as Nn, HUB_MAIN_HEAP_WATCH_LABEL as Nt, createUdsEventBus as O, diffSocketPlane as On, HEAP_RECLAIM_MIN_INTERVAL_MS as Ot, CapRouteResolver as P, sampleSocketDirection as Pn, HUB_MAIN_RSS_BUDGET_MB as Pt, encodeFrame as Q, startRunnerHeapWatch as Qt, LocalChildClient as R, CAP_ACTION_UNLABELLED as Rn, RUNNER_HEAP_WATCH_INTERVAL_MS as Rt, buildLinkChain as S, formatMeshQueue as Sn, buildNpmChildEnv as St, localProviderLink as T, SOCKET_QUEUE_TOP_N as Tn, createAddonPeerBytesFacility as Tt, PARENT_ROUTED_PROVIDER_ADDON_ID as U, diffCapActionCensus as Un, createLoopDelayMeter as Ut, readMoleculerFanoutMode as V, capActionLabel as Vn, buildHeapSample as Vt, __resetCapUsageRegistryForTests as W, EVENT_PLANE_TOP_N as Wn, createV8Reclaimer as Wt, localEndpointPath as X, shouldReclaim as Xt, SocketChannel as Y, reclaimIntervalMs as Yt, FrameDecoder as Z, startHeapWatch as Zt, AddonDepsManager as _, OUTBOUND_QUEUE_THRESHOLD_BYTES as _n, CapabilityHandle as _t, createUdsAddonContext as a, createInspectorArrayBufferCensus as an, setWorkerNativeCapsChangeListener as at, brokerCallForCap as b, MESH_QUEUE_TOP_N as bn, installManifestNativeDeps as bt, getOrInitReadinessRegistryForClient as c, DECISIVE_HEAP_SPACES as cn, NATIVE_PROVIDER_SERVICE_INFIX as ct, clusterEventTopic as d, formatHeapSpaces as dn, capBareAction as dt, ARRAY_BUFFER_CENSUS_COOLDOWN_MS as en, buildUdsNativeCapProxy as et, getBrokerEventBus as f, heapSpaceField as fn, capServiceName as ft, subscribePassthrough as g, OUTBOUND_QUEUE_SUSTAIN_MS as gn, DeviceRegistry as gt, setNodeEventInterest as h, OUTBOUND_QUEUE_COOLDOWN_MS as hn, serializeTypedArrays as ht, createAddonContext as i, createArrayBufferCensusGate as in, mountNativeCapService as it, createUdsLoggerWithControl as j, EMPTY_SOCKET_DIRECTION as jn, HEAP_WATCH_INTERVAL_MS as jt, udsChildLogToWorkerEntry as k, formatSocketPlane as kn, HEAP_RECLAIM_STEADY_STATE_INTERVAL_MS as kt, setHubConnected as l, HEAP_SPACE_REPORT_MIN_MB as ln, capActionName as lt, registerEventBusService as m, selectReportedSpaces as mn, deserializeTypedArrays as mt, installManifestPythonDeps as n, ARRAY_BUFFER_CENSUS_THRESHOLD_MB as nn, getWorkerNativeCapProvider as nt, getWorkerDeviceRegistry as o, formatArrayBufferCensus as on, createAddonService as ot, getMoleculerEventStats as p, readHeapSpaces as pn, parseCapAction as pt, UdsLocalTransportClient as q, diffEventPlane as qn, nextRssBudgetState as qt, adaptBrokerToCluster as r, ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO as rn, getWorkerNativeCapSnapshot as rt, getOrInitReadinessRegistry as s, isArrayBufferCensusReport as sn, validateProviderRegistrations as st, installManifestSystemDeps as t, ARRAY_BUFFER_CENSUS_SUSTAIN_MS as tn, createBrokerDeviceManagerApi as tt, EVENT_TOPIC_PREFIX as u, emitHeapDiagnosticReport as un, capActionSuffix as ut, createKernelHwAccel as v, createOutboundQueueGate as vn, CapabilityUnavailableError as vt, ipcParentLink as w, SOCKET_PLANE_TOP_N as wn, runNpm as wt, brokerTransportLink as x, createMeshQueueReader as xn, resolveAddonClass as xt, resolveHwAccel as y, formatOutboundQueueVerdict as yn, copyBundledNativeModules as yt, LocalChildRegistry as z, EMPTY_CAP_ACTION_READING as zn, RUNNER_RSS_BUDGET_ENV as zt };
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-BYnHJU96.js");
1
+ const require_dist = require("./dist-XnunBp0n.js");
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
@@ -1,4 +1,4 @@
1
- import { xn as asJsonObject } from "./dist-DKFb2936.mjs";
1
+ import { Sn as asJsonObject } from "./dist-DiMNxNDh.mjs";
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.289",
3
+ "version": "1.2.291",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",