@camstack/system 1.2.353 → 1.2.355

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 (82) 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/calendars/calendar-prefs-store.d.ts +7 -0
  14. package/dist/builtins/calendars/calendars.addon.js +3 -2
  15. package/dist/builtins/calendars/calendars.addon.mjs +3 -2
  16. package/dist/builtins/camera-grid/index.js +1 -1
  17. package/dist/builtins/camera-grid/index.mjs +1 -1
  18. package/dist/builtins/composer/composer.addon.js +56 -5
  19. package/dist/builtins/composer/composer.addon.mjs +56 -5
  20. package/dist/builtins/console-logging/index.js +1 -1
  21. package/dist/builtins/console-logging/index.mjs +1 -1
  22. package/dist/builtins/core-blocks/block-settings-store.d.ts +5 -0
  23. package/dist/builtins/core-blocks/core-blocks.addon.js +7 -2
  24. package/dist/builtins/core-blocks/core-blocks.addon.mjs +7 -2
  25. package/dist/builtins/device-manager/device-manager.addon.js +3 -3
  26. package/dist/builtins/device-manager/device-manager.addon.mjs +3 -3
  27. package/dist/builtins/hub-forwarder/index.js +1 -1
  28. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  29. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  30. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  31. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  32. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  33. package/dist/builtins/local-network/local-network.addon.d.ts +21 -6
  34. package/dist/builtins/local-network/local-network.addon.js +81 -54
  35. package/dist/builtins/local-network/local-network.addon.mjs +81 -55
  36. package/dist/builtins/loki-logging/index.js +1 -1
  37. package/dist/builtins/loki-logging/index.mjs +1 -1
  38. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  39. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  40. package/dist/builtins/platform-probe/index.js +1 -1
  41. package/dist/builtins/platform-probe/index.mjs +1 -1
  42. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  43. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  44. package/dist/builtins/snapshot/index.js +1 -1
  45. package/dist/builtins/snapshot/index.mjs +1 -1
  46. package/dist/builtins/sqlite-storage/analyze-stats.d.ts +0 -2
  47. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  48. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  49. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +9 -11
  50. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  51. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  52. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +2 -2
  53. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +2 -2
  54. package/dist/builtins/system-config/system-config.addon.js +1 -1
  55. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  56. package/dist/builtins/winston-logging/index.js +1 -1
  57. package/dist/builtins/winston-logging/index.mjs +1 -1
  58. package/dist/{child-cap-dispatch-CvYlCq60.js → child-cap-dispatch-CqV-7-QZ.js} +27 -86
  59. package/dist/{child-cap-dispatch-C6IpRb4P.mjs → child-cap-dispatch-DMx2axev.mjs} +28 -81
  60. package/dist/{composition-sources-CAFDvTRs.js → composition-sources-Bk-MjK1D.js} +1 -1
  61. package/dist/{composition-sources-BGlDi3CT.mjs → composition-sources-Dpl1G0Gr.mjs} +1 -1
  62. package/dist/{dist-CXVXFdJX.js → dist-BWicEEVA.js} +233 -79
  63. package/dist/{dist-Cm556q2j.mjs → dist-OCi0K-iM.mjs} +216 -80
  64. package/dist/http/reverse-proxy.d.ts +11 -1
  65. package/dist/index.d.ts +1 -1
  66. package/dist/index.js +71 -56
  67. package/dist/index.mjs +72 -56
  68. package/dist/kernel/config-schema.d.ts +1 -0
  69. package/dist/kernel/event-plane-report.d.ts +7 -36
  70. package/dist/kernel/fs-utils.d.ts +1 -5
  71. package/dist/kernel/heap-spaces.d.ts +2 -23
  72. package/dist/kernel/index.d.ts +0 -2
  73. package/dist/kernel/moleculer/event-bus-core.d.ts +6 -28
  74. package/dist/kernel/moleculer/process-service.d.ts +2 -5
  75. package/dist/kernel/moleculer/runner-native-allocator.d.ts +1 -6
  76. package/dist/kernel/transport/index.d.ts +1 -1
  77. package/dist/kernel/transport/local-child-registry.d.ts +8 -41
  78. package/dist/kernel/transport/uds-event-bridge.d.ts +2 -2
  79. package/dist/{retired-settings-keys-D24IW4qo.js → retired-settings-keys-DLJ7MvIB.js} +1 -1
  80. package/dist/{retired-settings-keys-DzkSqnH0.mjs → retired-settings-keys-TDzxE6GA.mjs} +1 -1
  81. package/package.json +1 -1
  82. package/dist/kernel/heap-allocation-sample.d.ts +0 -107
@@ -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-CXVXFdJX.js");
3
+ const require_dist = require("../../dist-BWicEEVA.js");
4
4
  const require_tls = require("../../tls-ue_Xefmv.js");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  let node_path = require("node:path");
@@ -8,40 +8,6 @@ let node_os = require("node:os");
8
8
  node_os = require_chunk.__toESM(node_os);
9
9
  let node_fs = require("node:fs");
10
10
  let node_net = require("node:net");
11
- //#region src/builtins/local-network/local-hostname.ts
12
- var DNS_NAME = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])$/;
13
- /**
14
- * Parse the operator-typed local hostname.
15
- *
16
- * Empty / whitespace → `null` (advertise the raw LAN IP). A DNS name is
17
- * lowercased. A URL, port suffix, path, or bare IP is rejected so it cannot
18
- * leak into a SAN or a candidate URL.
19
- */
20
- function parseLocalHostname(raw) {
21
- const trimmed = raw.trim();
22
- if (trimmed === "") return {
23
- ok: true,
24
- value: null
25
- };
26
- if (/[\s/:?#]/.test(trimmed) || trimmed.includes("\\")) return {
27
- ok: false,
28
- error: "Enter a DNS name such as camstack.lan, without a scheme or port."
29
- };
30
- const lowered = trimmed.toLowerCase();
31
- if ((0, node_net.isIP)(lowered) !== 0) return {
32
- ok: false,
33
- error: "Use the address allowlist for IPs — this field is a DNS name."
34
- };
35
- if (!DNS_NAME.test(lowered)) return {
36
- ok: false,
37
- error: "Enter a DNS name such as camstack.lan, without a scheme or port."
38
- };
39
- return {
40
- ok: true,
41
- value: lowered
42
- };
43
- }
44
- //#endregion
45
11
  //#region src/builtins/local-network/ipv6-lan.ts
46
12
  /**
47
13
  * IPv6 helpers for LAN advertisement: prefix matching (privacy addresses
@@ -147,6 +113,40 @@ function parseIpv6Groups(address) {
147
113
  ];
148
114
  }
149
115
  //#endregion
116
+ //#region src/builtins/local-network/local-hostname.ts
117
+ var DNS_NAME = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])$/;
118
+ /**
119
+ * Parse the operator-typed local hostname.
120
+ *
121
+ * Empty / whitespace → `null` (advertise the raw LAN IP). A DNS name is
122
+ * lowercased. A URL, port suffix, path, or bare IP is rejected so it cannot
123
+ * leak into a SAN or a candidate URL.
124
+ */
125
+ function parseLocalHostname(raw) {
126
+ const trimmed = raw.trim();
127
+ if (trimmed === "") return {
128
+ ok: true,
129
+ value: null
130
+ };
131
+ if (/[\s/:?#]/.test(trimmed) || trimmed.includes("\\")) return {
132
+ ok: false,
133
+ error: "Enter a DNS name such as camstack.lan, without a scheme or port."
134
+ };
135
+ const lowered = trimmed.toLowerCase();
136
+ if ((0, node_net.isIP)(lowered) !== 0) return {
137
+ ok: false,
138
+ error: "Use the address allowlist for IPs — this field is a DNS name."
139
+ };
140
+ if (!DNS_NAME.test(lowered)) return {
141
+ ok: false,
142
+ error: "Enter a DNS name such as camstack.lan, without a scheme or port."
143
+ };
144
+ return {
145
+ ok: true,
146
+ value: lowered
147
+ };
148
+ }
149
+ //#endregion
150
150
  //#region src/builtins/local-network/local-network.addon.ts
151
151
  /**
152
152
  * local-network — hub-only system cap implementation.
@@ -207,19 +207,32 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
207
207
  });
208
208
  }
209
209
  /**
210
- * What AUTO currently resolves to for outbound artifact links: the first
211
- * PUBLIC candidate, never loopback and never a LAN address — a link is
212
- * opened on someone's phone, off this network, possibly hours later.
213
- * `null` when nothing public is reachable (the caller then ships bytes only
214
- * rather than a link that 404s).
210
+ * The effective flagged notification address: the operator's explicit
211
+ * choice, else AUTO — the CamStack app link (D709, operator decision
212
+ * 2026-10-06; before it AUTO was the tunnel, then the mesh). The app link is
213
+ * not a hub address: the NC's signed media and buttons keep their own hub
214
+ * ladder when it is flagged; only viewer taps take the app form.
215
215
  */
216
216
  resolveNotificationEndpoint() {
217
217
  const explicit = this.config.notificationBaseUrl;
218
- if (explicit !== "") return explicit;
219
- if (this.publicHostname !== "") return `https://${this.publicHostname}`;
220
- const mesh = this.meshEndpoint;
221
- if (mesh !== null && mesh.host !== "") return `https://${mesh.host}:${mesh.port}`;
222
- return null;
218
+ return explicit !== "" ? explicit : require_dist.CAMSTACK_APP_LINK_ORIGIN;
219
+ }
220
+ /**
221
+ * The `hub=` an app link carries: the best public HTTPS address in this
222
+ * cap's own palette (tunnel, then mesh) — so it can never disagree with
223
+ * what the Network page lists. See `pickAppLinkHub`.
224
+ */
225
+ async resolveAppLinkHub() {
226
+ const port = (await this.resolveAdvertisedListenPort(null)).port;
227
+ return require_dist.pickAppLinkHub(await this.buildAdvertisedEndpoints(false, false, "https", port));
228
+ }
229
+ async notificationEndpointSnapshot() {
230
+ const explicit = this.config.notificationBaseUrl;
231
+ return {
232
+ baseUrl: explicit === "" ? null : explicit,
233
+ resolved: this.resolveNotificationEndpoint(),
234
+ appLinkHub: await this.resolveAppLinkHub()
235
+ };
223
236
  }
224
237
  async snapshotViewerEndpoints() {
225
238
  const selected = selectViewerEndpoints(await this.buildAdvertisedEndpoints(false, false, "http", (await this.resolveAdvertisedListenPort(null)).port), this.config.viewerBaseUrls);
@@ -271,22 +284,17 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
271
284
  advertised: localPort.port,
272
285
  source: localPort.source
273
286
  } });
287
+ const endpoints = await this.buildAdvertisedEndpoints(includeLoopback, input.ipv4Only ?? false, scheme, localPort.port);
274
288
  return {
275
- endpoints: await this.buildAdvertisedEndpoints(includeLoopback, input.ipv4Only ?? false, scheme, localPort.port),
289
+ endpoints: input.includeAppLink === true ? [appLinkEndpoint(), ...endpoints] : endpoints,
276
290
  localPort
277
291
  };
278
292
  },
279
- getNotificationEndpoint: async () => ({
280
- baseUrl: this.config.notificationBaseUrl === "" ? null : this.config.notificationBaseUrl,
281
- resolved: this.resolveNotificationEndpoint()
282
- }),
293
+ getNotificationEndpoint: async () => this.notificationEndpointSnapshot(),
283
294
  setNotificationEndpoint: async ({ baseUrl }) => {
284
295
  await this.updateGlobalSettings({ notificationBaseUrl: baseUrl ?? "" });
285
296
  this.ctx.logger.info("local-network: notification endpoint set", { meta: { baseUrl: baseUrl ?? "(auto)" } });
286
- return {
287
- baseUrl: baseUrl === "" ? null : baseUrl,
288
- resolved: this.resolveNotificationEndpoint()
289
- };
297
+ return this.notificationEndpointSnapshot();
290
298
  },
291
299
  getViewerEndpoints: async () => this.snapshotViewerEndpoints(),
292
300
  setViewerEndpoints: async ({ baseUrls }) => {
@@ -729,6 +737,24 @@ function pickPreferred(interfaces) {
729
737
  return prefixLen(b.netmask) - prefixLen(a.netmask);
730
738
  })[0] ?? null;
731
739
  }
740
+ /**
741
+ * The CamStack app link as a palette entry (D709): the one address on the
742
+ * Network page that is not this hub's. Flagging it for notifications (the
743
+ * default) makes viewer taps open the native app, with the hub carried as
744
+ * `hub=`. `kind: 'app-link'` keeps it out of every race and ranking; it is
745
+ * only emitted on `includeAppLink: true`.
746
+ */
747
+ function appLinkEndpoint() {
748
+ return {
749
+ label: "CamStack app link",
750
+ baseUrl: require_dist.CAMSTACK_APP_LINK_ORIGIN,
751
+ kind: "app-link",
752
+ interfaceKind: "public",
753
+ plausible: true,
754
+ plausibleReason: "",
755
+ priority: -1
756
+ };
757
+ }
732
758
  function buildEndpoints(interfaces, port, includeLoopback, ipv4Only, publicHostname, scheme = "http", meshEndpoint = null, lanHttp = null) {
733
759
  const out = [];
734
760
  let priority = 0;
@@ -949,6 +975,7 @@ function readLinuxIpv6Flags() {
949
975
  //#endregion
950
976
  exports.ALLOWLIST_RECONCILE_INTERVAL_MS = ALLOWLIST_RECONCILE_INTERVAL_MS;
951
977
  exports.LocalNetworkAddon = LocalNetworkAddon;
978
+ exports.appLinkEndpoint = appLinkEndpoint;
952
979
  exports.applyAllowlist = applyAllowlist;
953
980
  exports.autoSeedAllowlist = autoSeedAllowlist;
954
981
  exports.buildEndpoints = buildEndpoints;
@@ -1,44 +1,10 @@
1
- import { Vt as localNetworkCapability, Yn as BaseAddon, br as EventCategory } from "../../dist-Cm556q2j.mjs";
1
+ import { Cr as EventCategory, Qn as BaseAddon, Ut as localNetworkCapability, an as pickAppLinkHub, s as CAMSTACK_APP_LINK_ORIGIN } from "../../dist-OCi0K-iM.mjs";
2
2
  import { S as readLanHttpState, _ as DEFAULT_LAN_HTTP_PORT, a as writeTlsMode, i as writeExtraSans, l as reissueTlsLeaf, n as readTlsAccessStatus, o as validateUploadedTls, r as readTlsMode, s as ensureTlsCert, t as readExtraSans, y as applyLanHttp } from "../../tls-DTlVWwAI.mjs";
3
3
  import { mkdir, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
5
  import * as os from "node:os";
6
6
  import { readFileSync } from "node:fs";
7
7
  import { isIP } from "node:net";
8
- //#region src/builtins/local-network/local-hostname.ts
9
- var DNS_NAME = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])$/;
10
- /**
11
- * Parse the operator-typed local hostname.
12
- *
13
- * Empty / whitespace → `null` (advertise the raw LAN IP). A DNS name is
14
- * lowercased. A URL, port suffix, path, or bare IP is rejected so it cannot
15
- * leak into a SAN or a candidate URL.
16
- */
17
- function parseLocalHostname(raw) {
18
- const trimmed = raw.trim();
19
- if (trimmed === "") return {
20
- ok: true,
21
- value: null
22
- };
23
- if (/[\s/:?#]/.test(trimmed) || trimmed.includes("\\")) return {
24
- ok: false,
25
- error: "Enter a DNS name such as camstack.lan, without a scheme or port."
26
- };
27
- const lowered = trimmed.toLowerCase();
28
- if (isIP(lowered) !== 0) return {
29
- ok: false,
30
- error: "Use the address allowlist for IPs — this field is a DNS name."
31
- };
32
- if (!DNS_NAME.test(lowered)) return {
33
- ok: false,
34
- error: "Enter a DNS name such as camstack.lan, without a scheme or port."
35
- };
36
- return {
37
- ok: true,
38
- value: lowered
39
- };
40
- }
41
- //#endregion
42
8
  //#region src/builtins/local-network/ipv6-lan.ts
43
9
  /**
44
10
  * IPv6 helpers for LAN advertisement: prefix matching (privacy addresses
@@ -144,6 +110,40 @@ function parseIpv6Groups(address) {
144
110
  ];
145
111
  }
146
112
  //#endregion
113
+ //#region src/builtins/local-network/local-hostname.ts
114
+ var DNS_NAME = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])$/;
115
+ /**
116
+ * Parse the operator-typed local hostname.
117
+ *
118
+ * Empty / whitespace → `null` (advertise the raw LAN IP). A DNS name is
119
+ * lowercased. A URL, port suffix, path, or bare IP is rejected so it cannot
120
+ * leak into a SAN or a candidate URL.
121
+ */
122
+ function parseLocalHostname(raw) {
123
+ const trimmed = raw.trim();
124
+ if (trimmed === "") return {
125
+ ok: true,
126
+ value: null
127
+ };
128
+ if (/[\s/:?#]/.test(trimmed) || trimmed.includes("\\")) return {
129
+ ok: false,
130
+ error: "Enter a DNS name such as camstack.lan, without a scheme or port."
131
+ };
132
+ const lowered = trimmed.toLowerCase();
133
+ if (isIP(lowered) !== 0) return {
134
+ ok: false,
135
+ error: "Use the address allowlist for IPs — this field is a DNS name."
136
+ };
137
+ if (!DNS_NAME.test(lowered)) return {
138
+ ok: false,
139
+ error: "Enter a DNS name such as camstack.lan, without a scheme or port."
140
+ };
141
+ return {
142
+ ok: true,
143
+ value: lowered
144
+ };
145
+ }
146
+ //#endregion
147
147
  //#region src/builtins/local-network/local-network.addon.ts
148
148
  /**
149
149
  * local-network — hub-only system cap implementation.
@@ -204,19 +204,32 @@ var LocalNetworkAddon = class extends BaseAddon {
204
204
  });
205
205
  }
206
206
  /**
207
- * What AUTO currently resolves to for outbound artifact links: the first
208
- * PUBLIC candidate, never loopback and never a LAN address — a link is
209
- * opened on someone's phone, off this network, possibly hours later.
210
- * `null` when nothing public is reachable (the caller then ships bytes only
211
- * rather than a link that 404s).
207
+ * The effective flagged notification address: the operator's explicit
208
+ * choice, else AUTO — the CamStack app link (D709, operator decision
209
+ * 2026-10-06; before it AUTO was the tunnel, then the mesh). The app link is
210
+ * not a hub address: the NC's signed media and buttons keep their own hub
211
+ * ladder when it is flagged; only viewer taps take the app form.
212
212
  */
213
213
  resolveNotificationEndpoint() {
214
214
  const explicit = this.config.notificationBaseUrl;
215
- if (explicit !== "") return explicit;
216
- if (this.publicHostname !== "") return `https://${this.publicHostname}`;
217
- const mesh = this.meshEndpoint;
218
- if (mesh !== null && mesh.host !== "") return `https://${mesh.host}:${mesh.port}`;
219
- return null;
215
+ return explicit !== "" ? explicit : CAMSTACK_APP_LINK_ORIGIN;
216
+ }
217
+ /**
218
+ * The `hub=` an app link carries: the best public HTTPS address in this
219
+ * cap's own palette (tunnel, then mesh) — so it can never disagree with
220
+ * what the Network page lists. See `pickAppLinkHub`.
221
+ */
222
+ async resolveAppLinkHub() {
223
+ const port = (await this.resolveAdvertisedListenPort(null)).port;
224
+ return pickAppLinkHub(await this.buildAdvertisedEndpoints(false, false, "https", port));
225
+ }
226
+ async notificationEndpointSnapshot() {
227
+ const explicit = this.config.notificationBaseUrl;
228
+ return {
229
+ baseUrl: explicit === "" ? null : explicit,
230
+ resolved: this.resolveNotificationEndpoint(),
231
+ appLinkHub: await this.resolveAppLinkHub()
232
+ };
220
233
  }
221
234
  async snapshotViewerEndpoints() {
222
235
  const selected = selectViewerEndpoints(await this.buildAdvertisedEndpoints(false, false, "http", (await this.resolveAdvertisedListenPort(null)).port), this.config.viewerBaseUrls);
@@ -268,22 +281,17 @@ var LocalNetworkAddon = class extends BaseAddon {
268
281
  advertised: localPort.port,
269
282
  source: localPort.source
270
283
  } });
284
+ const endpoints = await this.buildAdvertisedEndpoints(includeLoopback, input.ipv4Only ?? false, scheme, localPort.port);
271
285
  return {
272
- endpoints: await this.buildAdvertisedEndpoints(includeLoopback, input.ipv4Only ?? false, scheme, localPort.port),
286
+ endpoints: input.includeAppLink === true ? [appLinkEndpoint(), ...endpoints] : endpoints,
273
287
  localPort
274
288
  };
275
289
  },
276
- getNotificationEndpoint: async () => ({
277
- baseUrl: this.config.notificationBaseUrl === "" ? null : this.config.notificationBaseUrl,
278
- resolved: this.resolveNotificationEndpoint()
279
- }),
290
+ getNotificationEndpoint: async () => this.notificationEndpointSnapshot(),
280
291
  setNotificationEndpoint: async ({ baseUrl }) => {
281
292
  await this.updateGlobalSettings({ notificationBaseUrl: baseUrl ?? "" });
282
293
  this.ctx.logger.info("local-network: notification endpoint set", { meta: { baseUrl: baseUrl ?? "(auto)" } });
283
- return {
284
- baseUrl: baseUrl === "" ? null : baseUrl,
285
- resolved: this.resolveNotificationEndpoint()
286
- };
294
+ return this.notificationEndpointSnapshot();
287
295
  },
288
296
  getViewerEndpoints: async () => this.snapshotViewerEndpoints(),
289
297
  setViewerEndpoints: async ({ baseUrls }) => {
@@ -726,6 +734,24 @@ function pickPreferred(interfaces) {
726
734
  return prefixLen(b.netmask) - prefixLen(a.netmask);
727
735
  })[0] ?? null;
728
736
  }
737
+ /**
738
+ * The CamStack app link as a palette entry (D709): the one address on the
739
+ * Network page that is not this hub's. Flagging it for notifications (the
740
+ * default) makes viewer taps open the native app, with the hub carried as
741
+ * `hub=`. `kind: 'app-link'` keeps it out of every race and ranking; it is
742
+ * only emitted on `includeAppLink: true`.
743
+ */
744
+ function appLinkEndpoint() {
745
+ return {
746
+ label: "CamStack app link",
747
+ baseUrl: CAMSTACK_APP_LINK_ORIGIN,
748
+ kind: "app-link",
749
+ interfaceKind: "public",
750
+ plausible: true,
751
+ plausibleReason: "",
752
+ priority: -1
753
+ };
754
+ }
729
755
  function buildEndpoints(interfaces, port, includeLoopback, ipv4Only, publicHostname, scheme = "http", meshEndpoint = null, lanHttp = null) {
730
756
  const out = [];
731
757
  let priority = 0;
@@ -944,4 +970,4 @@ function readLinuxIpv6Flags() {
944
970
  }
945
971
  }
946
972
  //#endregion
947
- export { ALLOWLIST_RECONCILE_INTERVAL_MS, LocalNetworkAddon, applyAllowlist, autoSeedAllowlist, buildEndpoints, classifyKind, enumerateOsInterfaces, explainNonPlausible, ipv6Prefix64Key, isDefaultViewerEndpoint, isPlausibleAutoSeed, parseProcIfInet6, pickPreferred, prefixLen, reconcileAllowlist, resolveAdvertisedPort, selectViewerEndpoints, shouldAdvertiseIpv6 };
973
+ export { ALLOWLIST_RECONCILE_INTERVAL_MS, LocalNetworkAddon, appLinkEndpoint, applyAllowlist, autoSeedAllowlist, buildEndpoints, classifyKind, enumerateOsInterfaces, explainNonPlausible, ipv6Prefix64Key, isDefaultViewerEndpoint, isPlausibleAutoSeed, parseProcIfInet6, pickPreferred, prefixLen, reconcileAllowlist, resolveAdvertisedPort, selectViewerEndpoints, shouldAdvertiseIpv6 };
@@ -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-CXVXFdJX.js");
6
+ const require_dist = require("../../dist-BWicEEVA.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 { Ut as logDestinationCapability, Yn as BaseAddon } from "../../dist-Cm556q2j.mjs";
1
+ import { Gt as logDestinationCapability, Qn as BaseAddon } from "../../dist-OCi0K-iM.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-CXVXFdJX.js");
6
+ const require_dist = require("../../dist-BWicEEVA.js");
7
7
  const require_proc_stat = require("../../proc-stat-DL-WUOhl.js");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let node_os = require("node:os");
@@ -1,4 +1,4 @@
1
- import { Jn as errMsg, Yn as BaseAddon, Yt as metricsProviderCapability, ar as createEvent, br as EventCategory, kt as foldSnapshotByFunction, ln as resolveBucketMs, sn as reducePoints } from "../../dist-Cm556q2j.mjs";
1
+ import { Cr as EventCategory, Qn as BaseAddon, Zn as errMsg, Zt as metricsProviderCapability, cr as createEvent, fn as resolveBucketMs, jt as foldSnapshotByFunction, un as reducePoints } from "../../dist-OCi0K-iM.mjs";
2
2
  import { a as parseProcStat, i as nodeProcReader, n as elapsedSecSince, o as parseProcStatusRss, r as lifetimeCpuPercent, t as commandFromCmdline } from "../../proc-stat-CbC55Kqi.mjs";
3
3
  import { access, readFile, readdir } from "node:fs/promises";
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-CXVXFdJX.js");
6
+ const require_dist = require("../../dist-BWicEEVA.js");
7
7
  let node_os = require("node:os");
8
8
  node_os = require_chunk.__toESM(node_os);
9
9
  let node_fs = require("node:fs");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { Jn as errMsg, Yn as BaseAddon, _n as scoreRuntimes, br as EventCategory, in as platformProbeCapability, sr as emitReadiness, xt as enumerateInferenceDevices } from "../../dist-Cm556q2j.mjs";
2
+ import { Cr as EventCategory, Qn as BaseAddon, St as enumerateInferenceDevices, Zn as errMsg, bn as scoreRuntimes, sn as platformProbeCapability, ur as emitReadiness } from "../../dist-OCi0K-iM.mjs";
3
3
  import * as os from "node:os";
4
4
  import * as fs from "node:fs";
5
5
  import { execFile } from "node:child_process";
@@ -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-CXVXFdJX.js");
6
+ const require_dist = require("../../dist-BWicEEVA.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 { Yn as BaseAddon, br as EventCategory } from "../../dist-Cm556q2j.mjs";
1
+ import { Cr as EventCategory, Qn as BaseAddon } from "../../dist-OCi0K-iM.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-CXVXFdJX.js");
6
+ const require_dist = require("../../dist-BWicEEVA.js");
7
7
  const require_snapshot_courtesy = require("../../snapshot-courtesy-D_C5-nC0.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { En as streamQualityLabel, Gn as DeviceFeature, J as bareAddonId, Jn as errMsg, Jt as memoizePinnedFfmpeg, Y as batteryCapability, Yn as BaseAddon, a as BatteryStatusSchema, bn as snapshotCapability, br as EventCategory, fr as nodePin, ht as deriveBatteryPresence, qn as DeviceType } from "../../dist-Cm556q2j.mjs";
1
+ import { Cn as snapshotCapability, Cr as EventCategory, Jn as DeviceFeature, Qn as BaseAddon, X as batteryCapability, Xn as DeviceType, Xt as memoizePinnedFfmpeg, Y as bareAddonId, Zn as errMsg, a as BatteryStatusSchema, gt as deriveBatteryPresence, hr as nodePin, kn as streamQualityLabel } from "../../dist-OCi0K-iM.mjs";
2
2
  import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg } from "../../snapshot-courtesy-CyFs1M3I.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -8,8 +8,6 @@ import { Database } from 'better-sqlite3';
8
8
  * the cluster is serving before the engine goes quiet.
9
9
  */
10
10
  export declare const ANALYZE_DELAY_MS = 120000;
11
- /** Env var that disables the one-shot analysis. `off` is the only value read. */
12
- export declare const ANALYZE_ENV_VAR = "CAMSTACK_SQLITE_ANALYZE";
13
11
  /** What {@link runAnalyzeIfMissing} did, for the caller to log. */
14
12
  export type AnalyzeOutcome = {
15
13
  readonly kind: 'already-present';
@@ -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-CXVXFdJX.js");
6
+ const require_dist = require("../../dist-BWicEEVA.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 { Ot as filesystemBrowseCapability, Yn as BaseAddon, wn as storageProviderCapability } from "../../dist-Cm556q2j.mjs";
1
+ import { At as filesystemBrowseCapability, Dn as storageProviderCapability, Qn as BaseAddon } from "../../dist-OCi0K-iM.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";
@@ -369,15 +369,6 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
369
369
  * with a number yields 0, which `sqliteOccupancy` reads as "unknown" and
370
370
  * reports as an empty file rather than inventing a ratio.
371
371
  */
372
- /**
373
- * Name the biggest tables, once, a minute after the connection opens.
374
- *
375
- * `dbSizeMb` says how big the file is and `freePct` says how much of it is
376
- * live; neither says WHOSE. Per-addon scoped collections are created on first
377
- * write and declared nowhere, so this is the only place the real set can be
378
- * enumerated. `unref` so it can never hold the process open, and
379
- * `CAMSTACK_SQLITE_STORAGE_REPORT=off` turns it off without a rebuild.
380
- */
381
372
  /**
382
373
  * Follow the file as it grows.
383
374
  *
@@ -388,8 +379,7 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
388
379
  * node escaped it only because 130 MB → 858 MB spanned seventeen days of
389
380
  * restarts.
390
381
  *
391
- * `unref` so it never holds the process open, and the same kill switch as the
392
- * storage report turns it off without a rebuild.
382
+ * `unref` so it never holds the process open.
393
383
  */
394
384
  private scheduleMapGrowthCheck;
395
385
  /**
@@ -402,6 +392,14 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
402
392
  * one thread the cluster reads configuration through.
403
393
  */
404
394
  private scheduleIndexAnalysis;
395
+ /**
396
+ * Name the biggest tables, once, a minute after the connection opens.
397
+ *
398
+ * `dbSizeMb` says how big the file is and `freePct` says how much of it is
399
+ * live; neither says WHOSE. Per-addon scoped collections are created on first
400
+ * write and declared nowhere, so this is the only place the real set can be
401
+ * enumerated. `unref` so it can never hold the process open.
402
+ */
405
403
  private scheduleStorageReport;
406
404
  private logStorageReport;
407
405
  private readPragmaNumber;
@@ -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-CXVXFdJX.js");
6
+ const require_dist = require("../../dist-BWicEEVA.js");
7
7
  const require_hub_cap_forward = require("../../hub-cap-forward-DmHNjbB0.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1745,7 +1745,7 @@ function resolveMinFreePercent(config, providerDefaultPercent, capacity, notice)
1745
1745
  *
1746
1746
  * Measured on the live hub, 2026-09-13. `storage.listLocations` returned two
1747
1747
  * `backups` locations with byte-identical capacity — `/data/backups` on
1748
- * `filesystem-storage` and `//192.168.x.x/backups` on `smb-storage`, both
1748
+ * `filesystem-storage` and `//192.168.10.89/backups` on `smb-storage`, both
1749
1749
  * `total 506107977728 / available 193348673536`. `owned` differed correctly
1750
1750
  * (9.9 GB vs 1.6 GB), so nothing about the rendering was at fault: the probe
1751
1751
  * simply never looked at the provider. It `path.resolve`d the SMB row's
@@ -1,4 +1,4 @@
1
- import { B as StorageMigrationJobSchema, Kt as mayReadLocation, L as StorageCleanupJobSchema, Mn as withLocationMode, Pt as isLocationEnabled, R as StorageLocationModeSchema, Sn as storageMigrationCapability, T as LegacyStorageLocationDefaultSchema, Tt as evictionPolicyOfLocation, Xt as modeMayWrite, Yn as BaseAddon, br as EventCategory, et as collectSecretConfigKeys, fn as resolveLocationMode, j as REDACTED_SECRET, jt as gbToBytes, pr as parseJsonObject, qt as mayWriteToLocation, vn as settingsStoreCapability, xn as storageCapability, z as StorageLocationTypeSchema, zt as legacyModeOf } from "../../dist-Cm556q2j.mjs";
1
+ import { B as StorageLocationTypeSchema, Cr as EventCategory, E as LegacyStorageLocationDefaultSchema, Et as evictionPolicyOfLocation, Fn as withLocationMode, It as isLocationEnabled, Jt as mayReadLocation, M as REDACTED_SECRET, Nt as gbToBytes, Qn as BaseAddon, Qt as modeMayWrite, R as StorageCleanupJobSchema, Tn as storageMigrationCapability, V as StorageMigrationJobSchema, Vt as legacyModeOf, Yt as mayWriteToLocation, gr as parseJsonObject, hn as resolveLocationMode, tt as collectSecretConfigKeys, wn as storageCapability, xn as settingsStoreCapability, z as StorageLocationModeSchema } from "../../dist-OCi0K-iM.mjs";
2
2
  import { t as HUB_CAP_FWD_ACTION } from "../../hub-cap-forward-BdT320dq.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -1740,7 +1740,7 @@ function resolveMinFreePercent(config, providerDefaultPercent, capacity, notice)
1740
1740
  *
1741
1741
  * Measured on the live hub, 2026-09-13. `storage.listLocations` returned two
1742
1742
  * `backups` locations with byte-identical capacity — `/data/backups` on
1743
- * `filesystem-storage` and `//192.168.x.x/backups` on `smb-storage`, both
1743
+ * `filesystem-storage` and `//192.168.10.89/backups` on `smb-storage`, both
1744
1744
  * `total 506107977728 / available 193348673536`. `owned` differed correctly
1745
1745
  * (9.9 GB vs 1.6 GB), so nothing about the rendering was at fault: the probe
1746
1746
  * simply never looked at the provider. It `path.resolve`d the SMB row's
@@ -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-CXVXFdJX.js");
6
+ const require_dist = require("../../dist-BWicEEVA.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 { Jn as errMsg, Yn as BaseAddon, lr as hydrateSchema } from "../../dist-Cm556q2j.mjs";
1
+ import { Qn as BaseAddon, Zn as errMsg, fr as hydrateSchema } from "../../dist-OCi0K-iM.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-CXVXFdJX.js");
6
+ const require_dist = require("../../dist-BWicEEVA.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 { Ut as logDestinationCapability, Yn as BaseAddon } from "../../dist-Cm556q2j.mjs";
1
+ import { Gt as logDestinationCapability, Qn as BaseAddon } from "../../dist-OCi0K-iM.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";