@camstack/system 1.2.113 → 1.2.114

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 (52) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  12. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  15. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  17. package/dist/builtins/hub-forwarder/index.js +1 -1
  18. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  19. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  20. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  21. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  22. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  23. package/dist/builtins/local-network/local-network.addon.d.ts +33 -1
  24. package/dist/builtins/local-network/local-network.addon.js +87 -2
  25. package/dist/builtins/local-network/local-network.addon.mjs +87 -3
  26. package/dist/builtins/loki-logging/index.js +1 -1
  27. package/dist/builtins/loki-logging/index.mjs +1 -1
  28. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  30. package/dist/builtins/platform-probe/index.js +1 -1
  31. package/dist/builtins/platform-probe/index.mjs +1 -1
  32. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  34. package/dist/builtins/snapshot/index.js +1 -1
  35. package/dist/builtins/snapshot/index.mjs +1 -1
  36. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  38. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
  40. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  41. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  42. package/dist/builtins/system-config/system-config.addon.js +1 -1
  43. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  44. package/dist/builtins/winston-logging/index.js +1 -1
  45. package/dist/builtins/winston-logging/index.mjs +1 -1
  46. package/dist/{dist-B2CtwVW2.js → dist-C1Aea3c6.js} +79 -4
  47. package/dist/{dist-Duz_uJSQ.mjs → dist-CJ8RRSvQ.mjs} +79 -4
  48. package/dist/index.js +1 -1
  49. package/dist/index.mjs +1 -1
  50. package/dist/{retired-settings-keys-CYDGGkyM.js → retired-settings-keys-B52KsNz8.js} +1 -1
  51. package/dist/{retired-settings-keys-BtVT2f6O.mjs → retired-settings-keys-C1efPfyW.mjs} +1 -1
  52. package/package.json +1 -1
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Gt as EventCategory, v as addonPagesCapability, vt as errMsg, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { Gt as EventCategory, v as addonPagesCapability, vt as errMsg, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Gt as EventCategory, b as addonWidgetsCapability, vt as errMsg, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { Gt as EventCategory, b as addonWidgetsCapability, vt as errMsg, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  //#region src/builtins/alerts/prune-policy.ts
8
8
  /**
9
9
  * Ids older than `cutoffMs`, oldest first.
@@ -1,4 +1,4 @@
1
- import { Gt as EventCategory, Mt as createEvent, n as AlertSchema, vt as errMsg, x as alertsCapability, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { Gt as EventCategory, Mt as createEvent, n as AlertSchema, vt as errMsg, x as alertsCapability, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  //#region src/builtins/alerts/prune-policy.ts
3
3
  /**
4
4
  * Ids older than `cutoffMs`, oldest first.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let buffer = require("buffer");
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -1,4 +1,4 @@
1
- import { C as backupCapability, Gt as EventCategory, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { C as backupCapability, Gt as EventCategory, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { randomBytes, randomUUID } from "node:crypto";
3
3
  import { Buffer as Buffer$1 } from "buffer";
4
4
  import * as fsp from "node:fs/promises";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/console-logging/console-destination.ts
9
9
  var LEVEL_RANK = {
@@ -1,4 +1,4 @@
1
- import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/console-logging/console-destination.ts
4
4
  var LEVEL_RANK = {
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-B2CtwVW2.js");
3
+ const require_dist = require("../../dist-C1Aea3c6.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,4 +1,4 @@
1
- import { D as coreBlockAddonId, O as coreBlockIdFromAddonId, c as CoreBlockSchema, k as coreBlocksCapability, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, vt as errMsg, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { D as coreBlockAddonId, O as coreBlockIdFromAddonId, c as CoreBlockSchema, k as coreBlocksCapability, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, vt as errMsg, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
7
- const require_retired_settings_keys = require("../../retired-settings-keys-CYDGGkyM.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
+ const require_retired_settings_keys = require("../../retired-settings-keys-B52KsNz8.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let _camstack_types_node = require("@camstack/types/node");
10
10
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -1,5 +1,5 @@
1
- import { Ct as DeviceFeature, E as buildStreamParamsConfigSchema, F as deviceStatusCapability, Gt as EventCategory, Lt as isDeviceConfigCap, N as deviceManagerCapability, Ot as WELL_KNOWN_TAB_MAP, P as deviceStateCapability, R as enumerateItemArrayFields, Tt as DeviceType, Wt as sleep, a as CAP_NAMES_WITH_STATUS, at as runtimeStatePolicyFor, et as normalizeUnit, nt as parseStreamParamsFormPatch, p as STREAM_PROFILE_META, t as ALL_CAPABILITY_DEFINITIONS, u as DeviceStatusSchema, vt as errMsg, wt as DeviceRole, yt as BaseAddon, z as enumerateSchemaFields } from "../../dist-Duz_uJSQ.mjs";
2
- import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-BtVT2f6O.mjs";
1
+ import { Ct as DeviceFeature, E as buildStreamParamsConfigSchema, F as deviceStatusCapability, Gt as EventCategory, Lt as isDeviceConfigCap, N as deviceManagerCapability, Ot as WELL_KNOWN_TAB_MAP, P as deviceStateCapability, R as enumerateItemArrayFields, Tt as DeviceType, Wt as sleep, a as CAP_NAMES_WITH_STATUS, at as runtimeStatePolicyFor, et as normalizeUnit, nt as parseStreamParamsFormPatch, p as STREAM_PROFILE_META, t as ALL_CAPABILITY_DEFINITIONS, u as DeviceStatusSchema, vt as errMsg, wt as DeviceRole, yt as BaseAddon, z as enumerateSchemaFields } from "../../dist-CJ8RRSvQ.mjs";
2
+ import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-C1efPfyW.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
5
5
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  //#region src/builtins/doorbell/trigger-engine.ts
8
8
  /**
9
9
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -1,4 +1,4 @@
1
- import { G as isSameAddonId, Gt as EventCategory, I as doorbellCapability, Mt as createEvent, Tt as DeviceType, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { G as isSameAddonId, Gt as EventCategory, I as doorbellCapability, Mt as createEvent, Tt as DeviceType, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  //#region src/builtins/doorbell/trigger-engine.ts
3
3
  /**
4
4
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
9
9
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,4 +1,4 @@
1
- import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
4
4
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-B2CtwVW2.js");
3
+ const require_dist = require("../../dist-C1Aea3c6.js");
4
4
  //#region src/builtins/liveness-monitor/liveness-checks.ts
5
5
  var NO_DEVICES = "liveness:no-devices";
6
6
  var ALL_OFFLINE = "liveness:all-devices-offline";
@@ -1,4 +1,4 @@
1
- import { Gt as EventCategory, Mt as createEvent, vt as errMsg, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { Gt as EventCategory, Mt as createEvent, vt as errMsg, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  //#region src/builtins/liveness-monitor/liveness-checks.ts
3
3
  var NO_DEVICES = "liveness:no-devices";
4
4
  var ALL_OFFLINE = "liveness:all-devices-offline";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  node_crypto = require_chunk.__toESM(node_crypto);
9
9
  let crypto$1 = require("crypto");
@@ -1,5 +1,5 @@
1
1
  import { i as __require, o as __toESM, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
2
- import { S as authProviderCapability, _ as UserRecordSchema, ht as userManagementCapability, m as ScopedTokenSchema, r as ApiKeyRecordSchema, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
2
+ import { S as authProviderCapability, _ as UserRecordSchema, ht as userManagementCapability, m as ScopedTokenSchema, r as ApiKeyRecordSchema, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
3
3
  import * as crypto$2 from "node:crypto";
4
4
  import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
5
5
  import nodeCrypto from "crypto";
@@ -1,4 +1,4 @@
1
- import { ProviderRegistration, LocalInterface, ConnectionEndpoint, BaseAddon } from '@camstack/types';
1
+ import { ProviderRegistration, LocalInterface, ConnectionEndpoint, AdvertisedLocalPort, BaseAddon } from '@camstack/types';
2
2
  /**
3
3
  * Cached mesh-reachable endpoint, learned from the `mesh-network`
4
4
  * provider via the `MeshNetworkChanged` event-tail + pull-reconcile.
@@ -10,6 +10,15 @@ interface MeshEndpointCache {
10
10
  /** Hub HTTPS port the mesh endpoint targets. */
11
11
  readonly port: number;
12
12
  }
13
+ /** The three places a hub listen port can be learned, most authoritative first. */
14
+ export interface ListenPortSources {
15
+ /** `server.port` from the hub's live config section; null when unreadable. */
16
+ readonly configured: number | null;
17
+ /** `CAMSTACK_PORT` off the environment; null when unset / unparseable. */
18
+ readonly env: number | null;
19
+ /** The port the CALLER passed. A guess about our socket, not a fact. */
20
+ readonly callerHint: number | null;
21
+ }
13
22
  interface LocalNetworkConfig {
14
23
  /** Empty = "auto" (every non-loopback / non-link-local address
15
24
  * participates). Non-empty restricts the candidate set to only
@@ -77,9 +86,32 @@ export declare class LocalNetworkAddon extends BaseAddon<LocalNetworkConfig> {
77
86
  * not joined simply contributes nothing.
78
87
  */
79
88
  private reconcileMeshEndpoint;
89
+ /**
90
+ * The port this hub is actually listening on, plus the provenance of the
91
+ * number. The boot loader merges the `CAMSTACK_PORT` env override into the
92
+ * persisted `server` section (`server/backend/src/boot/boot-config.ts`), so
93
+ * reading that section gives the authoritative live port — no new setting,
94
+ * and the same source `tailscale-client` already stamps its mesh endpoints
95
+ * from, so the two cannot drift.
96
+ */
97
+ private resolveAdvertisedListenPort;
98
+ /** `server.port` from the live config section, or null when unreadable. */
99
+ private readConfiguredPort;
80
100
  private enumerate;
81
101
  private detectChanges;
82
102
  }
103
+ /**
104
+ * Pick the port to advertise, and say where it came from.
105
+ *
106
+ * The caller's hint ranks BELOW everything the hub knows about itself and above
107
+ * nothing but the compiled default — it is kept only so a client written
108
+ * against the old echoing contract behaves exactly as it did before, and it is
109
+ * labelled `caller-hint` so that client's successor can refuse to trust it.
110
+ * Values outside the valid port range are dropped rather than advertised: a
111
+ * malformed `server.port` must fall through to the next source, not produce a
112
+ * URL nothing can bind.
113
+ */
114
+ export declare function resolveAdvertisedPort(sources: ListenPortSources): AdvertisedLocalPort;
83
115
  /**
84
116
  * Lift `os.networkInterfaces()` into our `LocalInterface[]` shape with
85
117
  * kind classification + preferred flag. Pure — takes the raw result so
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-B2CtwVW2.js");
3
+ const require_dist = require("../../dist-C1Aea3c6.js");
4
4
  let node_os = require("node:os");
5
5
  node_os = require_chunk.__toESM(node_os);
6
6
  //#region src/builtins/local-network/local-network.addon.ts
@@ -28,6 +28,12 @@ node_os = require_chunk.__toESM(node_os);
28
28
  */
29
29
  var MESH_PROVIDER_ADDON_IDS = ["tailscale-client"];
30
30
  var POLL_INTERVAL_MS = 3e4;
31
+ /**
32
+ * Compiled-in boot default — mirrors `CONFIG_DEFAULTS.server.port` in
33
+ * `server/backend/src/boot/boot-config.ts`. Used only when neither the live
34
+ * config section, the env, nor the caller says anything.
35
+ */
36
+ var DEFAULT_HUB_PORT = 4443;
31
37
  var LocalNetworkAddon = class extends require_dist.BaseAddon {
32
38
  pollTimer = null;
33
39
  lastSnapshotKey = "";
@@ -84,8 +90,17 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
84
90
  const scheme = input.scheme ?? "http";
85
91
  const allow = this.config.allowedAddresses;
86
92
  const interfaces = applyAllowlist(this.enumerate(), allow);
93
+ const localPort = await this.resolveAdvertisedListenPort(input.port ?? null);
94
+ if (input.port !== void 0 && input.port !== localPort.port) this.ctx.logger.info("local-network: caller port hint overridden", { meta: {
95
+ hint: input.port,
96
+ advertised: localPort.port,
97
+ source: localPort.source
98
+ } });
87
99
  await this.reconcileMeshEndpoint();
88
- return { endpoints: buildEndpoints(interfaces, input.port, includeLoopback, ipv4Only, this.publicHostname, scheme, this.meshEndpoint) };
100
+ return {
101
+ endpoints: buildEndpoints(interfaces, localPort.port, includeLoopback, ipv4Only, this.publicHostname, scheme, this.meshEndpoint),
102
+ localPort
103
+ };
89
104
  },
90
105
  getNotificationEndpoint: async () => ({
91
106
  baseUrl: this.config.notificationBaseUrl === "" ? null : this.config.notificationBaseUrl,
@@ -214,6 +229,39 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
214
229
  }
215
230
  }
216
231
  }
232
+ /**
233
+ * The port this hub is actually listening on, plus the provenance of the
234
+ * number. The boot loader merges the `CAMSTACK_PORT` env override into the
235
+ * persisted `server` section (`server/backend/src/boot/boot-config.ts`), so
236
+ * reading that section gives the authoritative live port — no new setting,
237
+ * and the same source `tailscale-client` already stamps its mesh endpoints
238
+ * from, so the two cannot drift.
239
+ */
240
+ async resolveAdvertisedListenPort(callerHint) {
241
+ const envRaw = Number.parseInt(process.env["CAMSTACK_PORT"] ?? "", 10);
242
+ return resolveAdvertisedPort({
243
+ configured: await this.readConfiguredPort(),
244
+ env: Number.isNaN(envRaw) ? null : envRaw,
245
+ callerHint
246
+ });
247
+ }
248
+ /** `server.port` from the live config section, or null when unreadable. */
249
+ async readConfiguredPort() {
250
+ const settings = this.ctx.settings;
251
+ if (!settings) return null;
252
+ try {
253
+ const raw = (await settings.getSection("server"))["port"];
254
+ if (typeof raw === "number") return raw;
255
+ if (typeof raw === "string") {
256
+ const parsed = Number.parseInt(raw, 10);
257
+ return Number.isNaN(parsed) ? null : parsed;
258
+ }
259
+ return null;
260
+ } catch (err) {
261
+ this.ctx.logger.warn("local-network: could not read server.port", { meta: { error: err instanceof Error ? err.message : String(err) } });
262
+ return null;
263
+ }
264
+ }
217
265
  enumerate() {
218
266
  return enumerateOsInterfaces(node_os.networkInterfaces());
219
267
  }
@@ -238,6 +286,42 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
238
286
  });
239
287
  }
240
288
  };
289
+ /**
290
+ * Pick the port to advertise, and say where it came from.
291
+ *
292
+ * The caller's hint ranks BELOW everything the hub knows about itself and above
293
+ * nothing but the compiled default — it is kept only so a client written
294
+ * against the old echoing contract behaves exactly as it did before, and it is
295
+ * labelled `caller-hint` so that client's successor can refuse to trust it.
296
+ * Values outside the valid port range are dropped rather than advertised: a
297
+ * malformed `server.port` must fall through to the next source, not produce a
298
+ * URL nothing can bind.
299
+ */
300
+ function resolveAdvertisedPort(sources) {
301
+ const configured = validPort(sources.configured);
302
+ if (configured !== null) return {
303
+ port: configured,
304
+ source: "server-config"
305
+ };
306
+ const env = validPort(sources.env);
307
+ if (env !== null) return {
308
+ port: env,
309
+ source: "server-env"
310
+ };
311
+ const hint = validPort(sources.callerHint);
312
+ if (hint !== null) return {
313
+ port: hint,
314
+ source: "caller-hint"
315
+ };
316
+ return {
317
+ port: DEFAULT_HUB_PORT,
318
+ source: "default"
319
+ };
320
+ }
321
+ function validPort(value) {
322
+ if (value === null) return null;
323
+ return Number.isInteger(value) && value >= 1 && value <= 65535 ? value : null;
324
+ }
241
325
  function enumerateOsInterfaces(ifaces) {
242
326
  const raw = [];
243
327
  for (const [name, addrs] of Object.entries(ifaces)) {
@@ -515,3 +599,4 @@ exports.explainNonPlausible = explainNonPlausible;
515
599
  exports.isPlausibleAutoSeed = isPlausibleAutoSeed;
516
600
  exports.pickPreferred = pickPreferred;
517
601
  exports.prefixLen = prefixLen;
602
+ exports.resolveAdvertisedPort = resolveAdvertisedPort;
@@ -1,4 +1,4 @@
1
- import { Gt as EventCategory, Y as localNetworkCapability, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { Gt as EventCategory, Y as localNetworkCapability, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import * as os from "node:os";
3
3
  //#region src/builtins/local-network/local-network.addon.ts
4
4
  /**
@@ -25,6 +25,12 @@ import * as os from "node:os";
25
25
  */
26
26
  var MESH_PROVIDER_ADDON_IDS = ["tailscale-client"];
27
27
  var POLL_INTERVAL_MS = 3e4;
28
+ /**
29
+ * Compiled-in boot default — mirrors `CONFIG_DEFAULTS.server.port` in
30
+ * `server/backend/src/boot/boot-config.ts`. Used only when neither the live
31
+ * config section, the env, nor the caller says anything.
32
+ */
33
+ var DEFAULT_HUB_PORT = 4443;
28
34
  var LocalNetworkAddon = class extends BaseAddon {
29
35
  pollTimer = null;
30
36
  lastSnapshotKey = "";
@@ -81,8 +87,17 @@ var LocalNetworkAddon = class extends BaseAddon {
81
87
  const scheme = input.scheme ?? "http";
82
88
  const allow = this.config.allowedAddresses;
83
89
  const interfaces = applyAllowlist(this.enumerate(), allow);
90
+ const localPort = await this.resolveAdvertisedListenPort(input.port ?? null);
91
+ if (input.port !== void 0 && input.port !== localPort.port) this.ctx.logger.info("local-network: caller port hint overridden", { meta: {
92
+ hint: input.port,
93
+ advertised: localPort.port,
94
+ source: localPort.source
95
+ } });
84
96
  await this.reconcileMeshEndpoint();
85
- return { endpoints: buildEndpoints(interfaces, input.port, includeLoopback, ipv4Only, this.publicHostname, scheme, this.meshEndpoint) };
97
+ return {
98
+ endpoints: buildEndpoints(interfaces, localPort.port, includeLoopback, ipv4Only, this.publicHostname, scheme, this.meshEndpoint),
99
+ localPort
100
+ };
86
101
  },
87
102
  getNotificationEndpoint: async () => ({
88
103
  baseUrl: this.config.notificationBaseUrl === "" ? null : this.config.notificationBaseUrl,
@@ -211,6 +226,39 @@ var LocalNetworkAddon = class extends BaseAddon {
211
226
  }
212
227
  }
213
228
  }
229
+ /**
230
+ * The port this hub is actually listening on, plus the provenance of the
231
+ * number. The boot loader merges the `CAMSTACK_PORT` env override into the
232
+ * persisted `server` section (`server/backend/src/boot/boot-config.ts`), so
233
+ * reading that section gives the authoritative live port — no new setting,
234
+ * and the same source `tailscale-client` already stamps its mesh endpoints
235
+ * from, so the two cannot drift.
236
+ */
237
+ async resolveAdvertisedListenPort(callerHint) {
238
+ const envRaw = Number.parseInt(process.env["CAMSTACK_PORT"] ?? "", 10);
239
+ return resolveAdvertisedPort({
240
+ configured: await this.readConfiguredPort(),
241
+ env: Number.isNaN(envRaw) ? null : envRaw,
242
+ callerHint
243
+ });
244
+ }
245
+ /** `server.port` from the live config section, or null when unreadable. */
246
+ async readConfiguredPort() {
247
+ const settings = this.ctx.settings;
248
+ if (!settings) return null;
249
+ try {
250
+ const raw = (await settings.getSection("server"))["port"];
251
+ if (typeof raw === "number") return raw;
252
+ if (typeof raw === "string") {
253
+ const parsed = Number.parseInt(raw, 10);
254
+ return Number.isNaN(parsed) ? null : parsed;
255
+ }
256
+ return null;
257
+ } catch (err) {
258
+ this.ctx.logger.warn("local-network: could not read server.port", { meta: { error: err instanceof Error ? err.message : String(err) } });
259
+ return null;
260
+ }
261
+ }
214
262
  enumerate() {
215
263
  return enumerateOsInterfaces(os.networkInterfaces());
216
264
  }
@@ -235,6 +283,42 @@ var LocalNetworkAddon = class extends BaseAddon {
235
283
  });
236
284
  }
237
285
  };
286
+ /**
287
+ * Pick the port to advertise, and say where it came from.
288
+ *
289
+ * The caller's hint ranks BELOW everything the hub knows about itself and above
290
+ * nothing but the compiled default — it is kept only so a client written
291
+ * against the old echoing contract behaves exactly as it did before, and it is
292
+ * labelled `caller-hint` so that client's successor can refuse to trust it.
293
+ * Values outside the valid port range are dropped rather than advertised: a
294
+ * malformed `server.port` must fall through to the next source, not produce a
295
+ * URL nothing can bind.
296
+ */
297
+ function resolveAdvertisedPort(sources) {
298
+ const configured = validPort(sources.configured);
299
+ if (configured !== null) return {
300
+ port: configured,
301
+ source: "server-config"
302
+ };
303
+ const env = validPort(sources.env);
304
+ if (env !== null) return {
305
+ port: env,
306
+ source: "server-env"
307
+ };
308
+ const hint = validPort(sources.callerHint);
309
+ if (hint !== null) return {
310
+ port: hint,
311
+ source: "caller-hint"
312
+ };
313
+ return {
314
+ port: DEFAULT_HUB_PORT,
315
+ source: "default"
316
+ };
317
+ }
318
+ function validPort(value) {
319
+ if (value === null) return null;
320
+ return Number.isInteger(value) && value >= 1 && value <= 65535 ? value : null;
321
+ }
238
322
  function enumerateOsInterfaces(ifaces) {
239
323
  const raw = [];
240
324
  for (const [name, addrs] of Object.entries(ifaces)) {
@@ -502,4 +586,4 @@ function formatKind(kind) {
502
586
  }
503
587
  }
504
588
  //#endregion
505
- export { LocalNetworkAddon, applyAllowlist, autoSeedAllowlist, buildEndpoints, classifyKind, enumerateOsInterfaces, explainNonPlausible, isPlausibleAutoSeed, pickPreferred, prefixLen };
589
+ export { LocalNetworkAddon, applyAllowlist, autoSeedAllowlist, buildEndpoints, classifyKind, enumerateOsInterfaces, explainNonPlausible, isPlausibleAutoSeed, pickPreferred, prefixLen, resolveAdvertisedPort };
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  //#region src/builtins/loki-logging/loki-payload.ts
8
8
  /**
9
9
  * Loki rejects a label name that is not a valid Prometheus label
@@ -1,4 +1,4 @@
1
- import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  //#region src/builtins/loki-logging/loki-payload.ts
3
3
  /**
4
4
  * Loki rejects a label name that is not a valid Prometheus label
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  let node_child_process = require("node:child_process");
8
8
  let node_util = require("node:util");
9
9
  let node_os = require("node:os");
@@ -1,4 +1,4 @@
1
- import { $ as metricsProviderCapability, Gt as EventCategory, Mt as createEvent, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { $ as metricsProviderCapability, Gt as EventCategory, Mt as createEvent, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { execFile, execFileSync } from "node:child_process";
3
3
  import { promisify } from "node:util";
4
4
  import * as os from "node:os";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  let node_child_process = require("node:child_process");
8
8
  let node_util = require("node:util");
9
9
  let node_os = require("node:os");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { Gt as EventCategory, L as enumerateInferenceDevices, Pt as emitReadiness, ct as scoreRuntimes, rt as platformProbeCapability, vt as errMsg, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
2
+ import { Gt as EventCategory, L as enumerateInferenceDevices, Pt as emitReadiness, ct as scoreRuntimes, rt as platformProbeCapability, vt as errMsg, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
3
3
  import { execFile } from "node:child_process";
4
4
  import { promisify } from "node:util";
5
5
  import * as os from "node:os";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
8
8
  /**
9
9
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -1,4 +1,4 @@
1
- import { Gt as EventCategory, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { Gt as EventCategory, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
3
3
  /**
4
4
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  let zod = require("zod");
8
8
  let node_crypto = require("node:crypto");
9
9
  let _camstack_types_node = require("@camstack/types/node");
@@ -1,4 +1,4 @@
1
- import { Ct as DeviceFeature, Gt as EventCategory, M as deriveBatteryPresence, Rt as nodePin, T as batteryCapability, Tt as DeviceType, i as BatteryStatusSchema, mt as streamQualityLabel, ut as snapshotCapability, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { Ct as DeviceFeature, Gt as EventCategory, M as deriveBatteryPresence, Rt as nodePin, T as batteryCapability, Tt as DeviceType, i as BatteryStatusSchema, mt as streamQualityLabel, ut as snapshotCapability, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { z } from "zod";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { signExpiringUrl, verifyExpiringUrl } from "@camstack/types/node";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { V as filesystemBrowseCapability, pt as storageProviderCapability, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { V as filesystemBrowseCapability, pt as storageProviderCapability, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { mkdir, readdir, realpath, statfs } from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
7
- const require_retired_settings_keys = require("../../retired-settings-keys-CYDGGkyM.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
+ const require_retired_settings_keys = require("../../retired-settings-keys-B52KsNz8.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_fs = require("node:fs");
10
10
  let node_module = require("node:module");
@@ -1,5 +1,5 @@
1
- import { A as dataStoreProviderCapability, Bt as parseJsonUnknown, _t as vectorStoreCapability, f as RUNTIME_DEFAULTS, gt as vectorDimFromBase64, j as decodeVectorBase64, kt as asJsonObject, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
2
- import { r as retiredKeyStoreOf, t as purgeRetiredSettingsKeys } from "../../retired-settings-keys-BtVT2f6O.mjs";
1
+ import { A as dataStoreProviderCapability, Bt as parseJsonUnknown, _t as vectorStoreCapability, f as RUNTIME_DEFAULTS, gt as vectorDimFromBase64, j as decodeVectorBase64, kt as asJsonObject, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
+ import { r as retiredKeyStoreOf, t as purgeRetiredSettingsKeys } from "../../retired-settings-keys-C1efPfyW.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { statSync } from "node:fs";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,4 +1,4 @@
1
- import { dt as storageCapability, ft as storageMigrationCapability, g as StorageMigrationJobSchema, h as StorageLocationTypeSchema, lt as settingsStoreCapability, yt as BaseAddon, zt as parseJsonObject } from "../../dist-Duz_uJSQ.mjs";
1
+ import { dt as storageCapability, ft as storageMigrationCapability, g as StorageMigrationJobSchema, h as StorageLocationTypeSchema, lt as settingsStoreCapability, yt as BaseAddon, zt as parseJsonObject } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  //#region src/builtins/system-config/system-config.addon.ts
8
8
  /**
9
9
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -1,4 +1,4 @@
1
- import { It as hydrateSchema, vt as errMsg, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { It as hydrateSchema, vt as errMsg, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  //#region src/builtins/system-config/system-config.addon.ts
3
3
  /**
4
4
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B2CtwVW2.js");
6
+ const require_dist = require("../../dist-C1Aea3c6.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-Duz_uJSQ.mjs";
1
+ import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-CJ8RRSvQ.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";
@@ -17709,6 +17709,12 @@ var CameraStatusSchema = zod.z.object({
17709
17709
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
17710
17710
  fetchedAt: zod.z.number()
17711
17711
  });
17712
+ var InferenceDeviceExclusionReasonSchema = zod.z.enum([
17713
+ "disabled",
17714
+ "unavailable",
17715
+ "cannot-host-camera-root",
17716
+ "accelerator-preferred"
17717
+ ]);
17712
17718
  var NodeInferenceDeviceSchema = zod.z.object({
17713
17719
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17714
17720
  key: zod.z.string(),
@@ -17739,7 +17745,17 @@ var NodeInferenceDeviceSchema = zod.z.object({
17739
17745
  * available per format; this is the stored selection that becomes the
17740
17746
  * default for EVERY camera landing on this accelerator.
17741
17747
  */
17742
- steps: zod.z.record(zod.z.string(), DeviceStepConfigSchema).optional()
17748
+ steps: zod.z.record(zod.z.string(), DeviceStepConfigSchema).optional(),
17749
+ /**
17750
+ * `null` when the device IS a camera-root candidate on this node; otherwise
17751
+ * the reason the dispatcher drops it. Computed by the SAME
17752
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
17753
+ * never disagree with the election — deriving it in the UI from
17754
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
17755
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
17756
+ * "an accelerator is serving" predicate).
17757
+ */
17758
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
17743
17759
  });
17744
17760
  var NodeInferenceDevicesSchema = zod.z.object({
17745
17761
  nodeId: zod.z.string(),
@@ -23840,7 +23856,41 @@ var ConnectionEndpointSchema = zod.z.object({
23840
23856
  */
23841
23857
  priority: zod.z.number()
23842
23858
  });
23843
- var GetConnectionEndpointsResultSchema = zod.z.object({ endpoints: zod.z.array(ConnectionEndpointSchema).readonly() });
23859
+ /**
23860
+ * Where the advertised local port came from. Ordered most → least
23861
+ * authoritative, and the whole point of returning it: a client must be able to
23862
+ * tell a FACT about the hub's socket from an echo of its own guess.
23863
+ */
23864
+ var LocalPortSourceEnum = zod.z.enum([
23865
+ "server-config",
23866
+ "server-env",
23867
+ "caller-hint",
23868
+ "default"
23869
+ ]);
23870
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
23871
+ var AdvertisedLocalPortSchema = zod.z.object({
23872
+ port: zod.z.number().int().min(1).max(65535),
23873
+ source: LocalPortSourceEnum
23874
+ });
23875
+ var GetConnectionEndpointsResultSchema = zod.z.object({
23876
+ endpoints: zod.z.array(ConnectionEndpointSchema).readonly(),
23877
+ /**
23878
+ * The port the hub built the LAN/loopback URLs with, and where that number
23879
+ * came from.
23880
+ *
23881
+ * Returned rather than merely applied, because "the URL is right" and "the
23882
+ * client can KNOW the URL is right" are different properties. A client that
23883
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
23884
+ * a hub that echoed the port the client sent, so it cannot decide whether to
23885
+ * race the candidate or discard it. With `source` it can: anything but
23886
+ * `caller-hint` is the hub's own socket.
23887
+ *
23888
+ * Absent on hubs predating this field — a client that finds it missing is
23889
+ * talking to an echoing hub and must degrade exactly as it does for
23890
+ * `caller-hint`.
23891
+ */
23892
+ localPort: AdvertisedLocalPortSchema
23893
+ });
23844
23894
  /**
23845
23895
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
23846
23896
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -23886,10 +23936,35 @@ var localNetworkCapability = {
23886
23936
  * Honours `getAllowedAddresses()` when set — addresses outside
23887
23937
  * the allowlist are dropped (the public tunnel + loopback are
23888
23938
  * always included as escape hatches).
23939
+ *
23940
+ * **The port is the hub's, not the caller's** (D62 — a function's fact
23941
+ * belongs to whoever already owns it). This method used to take a `port`
23942
+ * and echo it onto every LAN and loopback URL it advertised, which made
23943
+ * every client a second authority on a socket only the hub binds. A viewer
23944
+ * configured with a port-less public URL (`https://camstack.example.top`)
23945
+ * infers 443 from the scheme, asks with 443, and was handed
23946
+ * `https://192.168.1.9:443` for a hub listening on 4443. That candidate
23947
+ * cannot ever answer, and losing its race reads in the log exactly like
23948
+ * "the LAN was tried and it was slower" — so an operator sitting on his own
23949
+ * WiFi stayed on the public tunnel for weeks, with the LAN candidate born
23950
+ * dead on every single connect.
23951
+ *
23952
+ * The hub now resolves its own listen port and reports it as `localPort`,
23953
+ * with the SOURCE of the number, so a client can tell a fact from an echo.
23954
+ *
23955
+ * Out of scope on purpose: a LAN URL that is not the hub's own socket (a
23956
+ * reverse proxy in front of it on another port) is not this method's to
23957
+ * invent either — pin it with `setNotificationEndpoint` / a configured
23958
+ * origin, which is an operator statement rather than a guess.
23889
23959
  */
23890
23960
  getConnectionEndpoints: method(zod.z.object({
23891
- /** Local hub HTTP port to use in base URLs. */
23892
- port: zod.z.number().int().min(1).max(65535),
23961
+ /**
23962
+ * LEGACY HINT — do not send from new code. Kept optional so clients
23963
+ * written against the echoing contract keep working; the hub uses it
23964
+ * only when it cannot read its own port, and says so via
23965
+ * `localPort.source === 'caller-hint'`.
23966
+ */
23967
+ port: zod.z.number().int().min(1).max(65535).optional(),
23893
23968
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
23894
23969
  * candidate. Default `true`. */
23895
23970
  includeLoopback: zod.z.boolean().optional(),
@@ -17709,6 +17709,12 @@ var CameraStatusSchema = z.object({
17709
17709
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
17710
17710
  fetchedAt: z.number()
17711
17711
  });
17712
+ var InferenceDeviceExclusionReasonSchema = z.enum([
17713
+ "disabled",
17714
+ "unavailable",
17715
+ "cannot-host-camera-root",
17716
+ "accelerator-preferred"
17717
+ ]);
17712
17718
  var NodeInferenceDeviceSchema = z.object({
17713
17719
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17714
17720
  key: z.string(),
@@ -17739,7 +17745,17 @@ var NodeInferenceDeviceSchema = z.object({
17739
17745
  * available per format; this is the stored selection that becomes the
17740
17746
  * default for EVERY camera landing on this accelerator.
17741
17747
  */
17742
- steps: z.record(z.string(), DeviceStepConfigSchema).optional()
17748
+ steps: z.record(z.string(), DeviceStepConfigSchema).optional(),
17749
+ /**
17750
+ * `null` when the device IS a camera-root candidate on this node; otherwise
17751
+ * the reason the dispatcher drops it. Computed by the SAME
17752
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
17753
+ * never disagree with the election — deriving it in the UI from
17754
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
17755
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
17756
+ * "an accelerator is serving" predicate).
17757
+ */
17758
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
17743
17759
  });
17744
17760
  var NodeInferenceDevicesSchema = z.object({
17745
17761
  nodeId: z.string(),
@@ -23840,7 +23856,41 @@ var ConnectionEndpointSchema = z.object({
23840
23856
  */
23841
23857
  priority: z.number()
23842
23858
  });
23843
- var GetConnectionEndpointsResultSchema = z.object({ endpoints: z.array(ConnectionEndpointSchema).readonly() });
23859
+ /**
23860
+ * Where the advertised local port came from. Ordered most → least
23861
+ * authoritative, and the whole point of returning it: a client must be able to
23862
+ * tell a FACT about the hub's socket from an echo of its own guess.
23863
+ */
23864
+ var LocalPortSourceEnum = z.enum([
23865
+ "server-config",
23866
+ "server-env",
23867
+ "caller-hint",
23868
+ "default"
23869
+ ]);
23870
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
23871
+ var AdvertisedLocalPortSchema = z.object({
23872
+ port: z.number().int().min(1).max(65535),
23873
+ source: LocalPortSourceEnum
23874
+ });
23875
+ var GetConnectionEndpointsResultSchema = z.object({
23876
+ endpoints: z.array(ConnectionEndpointSchema).readonly(),
23877
+ /**
23878
+ * The port the hub built the LAN/loopback URLs with, and where that number
23879
+ * came from.
23880
+ *
23881
+ * Returned rather than merely applied, because "the URL is right" and "the
23882
+ * client can KNOW the URL is right" are different properties. A client that
23883
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
23884
+ * a hub that echoed the port the client sent, so it cannot decide whether to
23885
+ * race the candidate or discard it. With `source` it can: anything but
23886
+ * `caller-hint` is the hub's own socket.
23887
+ *
23888
+ * Absent on hubs predating this field — a client that finds it missing is
23889
+ * talking to an echoing hub and must degrade exactly as it does for
23890
+ * `caller-hint`.
23891
+ */
23892
+ localPort: AdvertisedLocalPortSchema
23893
+ });
23844
23894
  /**
23845
23895
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
23846
23896
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -23886,10 +23936,35 @@ var localNetworkCapability = {
23886
23936
  * Honours `getAllowedAddresses()` when set — addresses outside
23887
23937
  * the allowlist are dropped (the public tunnel + loopback are
23888
23938
  * always included as escape hatches).
23939
+ *
23940
+ * **The port is the hub's, not the caller's** (D62 — a function's fact
23941
+ * belongs to whoever already owns it). This method used to take a `port`
23942
+ * and echo it onto every LAN and loopback URL it advertised, which made
23943
+ * every client a second authority on a socket only the hub binds. A viewer
23944
+ * configured with a port-less public URL (`https://camstack.example.top`)
23945
+ * infers 443 from the scheme, asks with 443, and was handed
23946
+ * `https://192.168.1.9:443` for a hub listening on 4443. That candidate
23947
+ * cannot ever answer, and losing its race reads in the log exactly like
23948
+ * "the LAN was tried and it was slower" — so an operator sitting on his own
23949
+ * WiFi stayed on the public tunnel for weeks, with the LAN candidate born
23950
+ * dead on every single connect.
23951
+ *
23952
+ * The hub now resolves its own listen port and reports it as `localPort`,
23953
+ * with the SOURCE of the number, so a client can tell a fact from an echo.
23954
+ *
23955
+ * Out of scope on purpose: a LAN URL that is not the hub's own socket (a
23956
+ * reverse proxy in front of it on another port) is not this method's to
23957
+ * invent either — pin it with `setNotificationEndpoint` / a configured
23958
+ * origin, which is an operator statement rather than a guess.
23889
23959
  */
23890
23960
  getConnectionEndpoints: method(z.object({
23891
- /** Local hub HTTP port to use in base URLs. */
23892
- port: z.number().int().min(1).max(65535),
23961
+ /**
23962
+ * LEGACY HINT — do not send from new code. Kept optional so clients
23963
+ * written against the echoing contract keep working; the hub uses it
23964
+ * only when it cannot read its own port, and says so via
23965
+ * `localPort.source === 'caller-hint'`.
23966
+ */
23967
+ port: z.number().int().min(1).max(65535).optional(),
23893
23968
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
23894
23969
  * candidate. Default `true`. */
23895
23970
  includeLoopback: z.boolean().optional(),
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Cek0wNdY.js");
3
- const require_dist = require("./dist-B2CtwVW2.js");
3
+ const require_dist = require("./dist-C1Aea3c6.js");
4
4
  const require_builtins_alerts_alerts_addon = require("./builtins/alerts/alerts.addon.js");
5
5
  require("./builtins/alerts/index.js");
6
6
  const require_formatter = require("./formatter-DqAKDlvN.js");
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
2
- import { At as asNumber, B as extractNestedAddonId, Bt as parseJsonUnknown$1, Dt as ReadinessTimeoutError, Et as ReadinessRegistry, Ft as expandCapMethods, Gt as EventCategory$1, H as isArrayOutputSchema, Ht as resolveCapMount, J as lifecycleJobSchema, K as isVoidInput, Mt as createEvent, Nt as emitDownForOwnedCaps, Q as looseSchema, St as DEVICE_STATUS_METHOD, U as isCollectionArrayMethod, Ut as scopeKey, Vt as readinessKey, W as isObjectInput, Z as logLevelAtMost, bt as DATAPLANE_SECRET_HEADER$1, d as METHOD_ACCESS_MAP, f as RUNTIME_DEFAULTS, it as procedureAuthKey, jt as asString$1, kt as asJsonObject$1, ot as scopesAllowAddon, q as kebabToCamel, st as scopesAllowDeviceCap, t as ALL_CAPABILITY_DEFINITIONS, tt as objectInputDeclaresAddonId, vt as errMsg$1, xt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, y as addonSettingsCapability, zt as parseJsonObject } from "./dist-Duz_uJSQ.mjs";
2
+ import { At as asNumber, B as extractNestedAddonId, Bt as parseJsonUnknown$1, Dt as ReadinessTimeoutError, Et as ReadinessRegistry, Ft as expandCapMethods, Gt as EventCategory$1, H as isArrayOutputSchema, Ht as resolveCapMount, J as lifecycleJobSchema, K as isVoidInput, Mt as createEvent, Nt as emitDownForOwnedCaps, Q as looseSchema, St as DEVICE_STATUS_METHOD, U as isCollectionArrayMethod, Ut as scopeKey, Vt as readinessKey, W as isObjectInput, Z as logLevelAtMost, bt as DATAPLANE_SECRET_HEADER$1, d as METHOD_ACCESS_MAP, f as RUNTIME_DEFAULTS, it as procedureAuthKey, jt as asString$1, kt as asJsonObject$1, ot as scopesAllowAddon, q as kebabToCamel, st as scopesAllowDeviceCap, t as ALL_CAPABILITY_DEFINITIONS, tt as objectInputDeclaresAddonId, vt as errMsg$1, xt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, y as addonSettingsCapability, zt as parseJsonObject } from "./dist-CJ8RRSvQ.mjs";
3
3
  import { AlertCenterAddon } from "./builtins/alerts/alerts.addon.mjs";
4
4
  import "./builtins/alerts/index.mjs";
5
5
  import { t as formatLogLine } from "./formatter-B7qW8bPJ.mjs";
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-B2CtwVW2.js");
1
+ const require_dist = require("./dist-C1Aea3c6.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 { kt as asJsonObject } from "./dist-Duz_uJSQ.mjs";
1
+ import { kt as asJsonObject } from "./dist-CJ8RRSvQ.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.113",
3
+ "version": "1.2.114",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",