@camstack/system 1.2.414 → 1.2.416
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.
- package/dist/addon-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/addon-utils.js +1 -1
- package/dist/addon-utils.mjs +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +3 -3
- package/dist/builtins/keychain/index.mjs +3 -3
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +9 -8
- package/dist/builtins/local-auth/local-auth.addon.mjs +4 -3
- package/dist/builtins/local-network/hub-mdns-ciao.d.ts +7 -0
- package/dist/builtins/local-network/hub-mdns.d.ts +58 -0
- package/dist/builtins/local-network/local-network.addon.d.ts +11 -0
- package/dist/builtins/local-network/local-network.addon.js +194 -3
- package/dist/builtins/local-network/local-network.addon.mjs +195 -3
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-location-band-monitor.d.ts +43 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +6 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +112 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +112 -1
- package/dist/builtins/system-config/system-config.addon.js +80 -785
- package/dist/builtins/system-config/system-config.addon.mjs +52 -757
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{child-cap-dispatch-fnYkJDjG.js → child-cap-dispatch-B87B0FrC.js} +1 -1
- package/dist/{child-cap-dispatch-CpYnlLNt.mjs → child-cap-dispatch-CmfT-OR2.mjs} +1 -1
- package/dist/{composition-sources-BX6Caql_.js → composition-sources-DBcOVpGn.js} +1 -1
- package/dist/{composition-sources-QndhKNb5.mjs → composition-sources-DEZqZLPT.mjs} +1 -1
- package/dist/{constants-vMEWqCqG.mjs → constants-BSBtmzm3.mjs} +1 -1
- package/dist/{constants-CMXQ1QIC.js → constants-C4jZO_qO.js} +1 -1
- package/dist/{dist-ItJ3qAAA.mjs → dist-DwE9yHdk.mjs} +67 -3
- package/dist/{dist-BcnmH70Z.js → dist-DxiDI8hZ.js} +78 -2
- package/dist/{event-loop-stall-monitor-DoFaUgFf.js → event-loop-stall-monitor-E2hFvhDg.js} +1 -1
- package/dist/{event-loop-stall-monitor-CogzcENb.mjs → event-loop-stall-monitor-LBZskC1S.mjs} +1 -1
- package/dist/index.js +7 -7
- package/dist/index.mjs +7 -7
- package/dist/{jwt-signing-key-CST1LdM6.js → jwt-signing-key-CsC-FxSm.js} +1 -1
- package/dist/{jwt-signing-key-CpMSrZuI.mjs → jwt-signing-key-DLbjTIuA.mjs} +1 -1
- package/dist/lib-B_R6sPC3.mjs +7106 -0
- package/dist/lib-DOJ5yyWA.js +7110 -0
- package/dist/{ms-DlIWNkkk.mjs → ms-CgH2OFlD.mjs} +2 -42
- package/dist/{ms-t68_FaqE.js → ms-LB3giGoU.js} +1 -48
- package/dist/{restore-archive-pSlTKRgU.js → restore-archive-BtKjaVua.js} +1 -1
- package/dist/{restore-archive-8F_M_wW0.mjs → restore-archive-CImzbuRo.mjs} +1 -1
- package/dist/{retired-settings-keys-D3E47aR_.js → retired-settings-keys-Cl1tnkmq.js} +1 -1
- package/dist/{retired-settings-keys-CbkMnFXg.mjs → retired-settings-keys-D1WsCdUY.mjs} +1 -1
- package/dist/safe-buffer-B1OjL1I9.mjs +42 -0
- package/dist/safe-buffer-D3aoa-7X.js +46 -0
- package/dist/src-CnAqG6EF.js +714 -0
- package/dist/src-hG4grMU4.mjs +709 -0
- package/dist/{tls-BWpwU-d5.js → tls-C0TRPfqc.js} +1 -1
- package/dist/{tls-C2NT-Orr.mjs → tls-D-2dPY-5.mjs} +1 -1
- package/dist/{x509.es-Xf3K_brW.mjs → x509.es-CAjSsrkk.mjs} +643 -2
- package/dist/{x509.es-BhTzcFhZ.js → x509.es-Dzl0BbmG.js} +653 -0
- package/package.json +2 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("../../dist-
|
|
4
|
-
const require_tls = require("../../tls-
|
|
3
|
+
const require_dist = require("../../dist-DxiDI8hZ.js");
|
|
4
|
+
const require_tls = require("../../tls-C0TRPfqc.js");
|
|
5
5
|
let zod = require("zod");
|
|
6
6
|
let node_net = require("node:net");
|
|
7
7
|
let node_os = require("node:os");
|
|
@@ -337,6 +337,163 @@ function parseIpv6Groups(address) {
|
|
|
337
337
|
];
|
|
338
338
|
}
|
|
339
339
|
//#endregion
|
|
340
|
+
//#region src/builtins/local-network/hub-mdns.ts
|
|
341
|
+
/**
|
|
342
|
+
* Hub mDNS advertisement (D838) — how `camstack login` / `camstack discover`
|
|
343
|
+
* find a hub on the LAN.
|
|
344
|
+
*
|
|
345
|
+
* The hub publishes ONE `_camstack._tcp` service for its HTTPS endpoint. There
|
|
346
|
+
* is no mesh channel in it (D826 retired mesh UDP discovery on purpose) and
|
|
347
|
+
* nothing secret: the TXT record carries the node id, the version, the role
|
|
348
|
+
* and the address URL — finding a hub grants nothing, every client still signs
|
|
349
|
+
* in.
|
|
350
|
+
*
|
|
351
|
+
* The advertised address is NOT ranked here. It is the `internal` role of the
|
|
352
|
+
* address book (the address flagged for LAN clients, else the automatic LAN
|
|
353
|
+
* pick) — this module only reads the answer `local-network` already gives.
|
|
354
|
+
*
|
|
355
|
+
* The responder is injected so the logic is tested with a fake; the real one
|
|
356
|
+
* wraps `@homebridge/ciao`.
|
|
357
|
+
*/
|
|
358
|
+
/** The announcement for the current address, or null when nothing qualifies. */
|
|
359
|
+
function buildHubAnnouncement(inputs) {
|
|
360
|
+
if (inputs.internalUrl === null) return null;
|
|
361
|
+
let url;
|
|
362
|
+
try {
|
|
363
|
+
url = new URL(inputs.internalUrl);
|
|
364
|
+
} catch {
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
if (url.protocol !== "https:") return null;
|
|
368
|
+
const port = url.port === "" ? 443 : Number.parseInt(url.port, 10);
|
|
369
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) return null;
|
|
370
|
+
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
371
|
+
const txt = {
|
|
372
|
+
[require_dist.HUB_MDNS_TXT_KEYS.nodeId]: inputs.nodeId,
|
|
373
|
+
[require_dist.HUB_MDNS_TXT_KEYS.role]: "hub",
|
|
374
|
+
[require_dist.HUB_MDNS_TXT_KEYS.url]: url.origin
|
|
375
|
+
};
|
|
376
|
+
if (inputs.version !== null) txt[require_dist.HUB_MDNS_TXT_KEYS.version] = inputs.version;
|
|
377
|
+
return {
|
|
378
|
+
name: `camstack-${inputs.nodeId}`,
|
|
379
|
+
type: require_dist.HUB_MDNS_SERVICE_TYPE,
|
|
380
|
+
port,
|
|
381
|
+
address: (0, node_net.isIP)(host) !== 0 ? host : null,
|
|
382
|
+
txt
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
function announcementKey(a) {
|
|
386
|
+
return JSON.stringify([
|
|
387
|
+
a.name,
|
|
388
|
+
a.type,
|
|
389
|
+
a.port,
|
|
390
|
+
a.address,
|
|
391
|
+
a.txt
|
|
392
|
+
]);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Keeps one mDNS service in step with the hub's `internal` address. `sync()`
|
|
396
|
+
* is safe to call on any change; it never throws — a failure to advertise is
|
|
397
|
+
* logged and the boot goes on.
|
|
398
|
+
*/
|
|
399
|
+
var HubMdnsAdvertiser = class {
|
|
400
|
+
options;
|
|
401
|
+
current = null;
|
|
402
|
+
running = false;
|
|
403
|
+
chain = Promise.resolve();
|
|
404
|
+
constructor(options) {
|
|
405
|
+
this.options = options;
|
|
406
|
+
}
|
|
407
|
+
async start() {
|
|
408
|
+
this.running = true;
|
|
409
|
+
await this.sync();
|
|
410
|
+
}
|
|
411
|
+
/** Re-read the address and re-announce when it changed. Serialised. */
|
|
412
|
+
sync() {
|
|
413
|
+
this.chain = this.chain.then(() => this.syncOnce());
|
|
414
|
+
return this.chain;
|
|
415
|
+
}
|
|
416
|
+
async stop() {
|
|
417
|
+
this.running = false;
|
|
418
|
+
await (this.chain = this.chain.then(() => this.withdraw()));
|
|
419
|
+
}
|
|
420
|
+
async syncOnce() {
|
|
421
|
+
if (!this.running) return;
|
|
422
|
+
const { logger } = this.options;
|
|
423
|
+
try {
|
|
424
|
+
const internalUrl = await this.options.readInternalUrl();
|
|
425
|
+
const next = buildHubAnnouncement({
|
|
426
|
+
nodeId: this.options.nodeId,
|
|
427
|
+
version: this.options.version,
|
|
428
|
+
internalUrl
|
|
429
|
+
});
|
|
430
|
+
if (next === null) {
|
|
431
|
+
if (this.current !== null) {
|
|
432
|
+
await this.withdraw();
|
|
433
|
+
logger.info("hub mDNS: no internal address, advertisement withdrawn");
|
|
434
|
+
}
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const key = announcementKey(next);
|
|
438
|
+
if (this.current?.key === key) return;
|
|
439
|
+
await this.withdraw();
|
|
440
|
+
const handle = await this.options.publisher.publish(next);
|
|
441
|
+
this.current = {
|
|
442
|
+
key,
|
|
443
|
+
handle
|
|
444
|
+
};
|
|
445
|
+
logger.info("hub mDNS: advertising _camstack._tcp", { meta: {
|
|
446
|
+
url: next.txt[require_dist.HUB_MDNS_TXT_KEYS.url],
|
|
447
|
+
port: next.port
|
|
448
|
+
} });
|
|
449
|
+
} catch (err) {
|
|
450
|
+
logger.warn("hub mDNS: could not advertise the hub (logins still work with --server)", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
async withdraw() {
|
|
454
|
+
const held = this.current;
|
|
455
|
+
this.current = null;
|
|
456
|
+
if (held === null) return;
|
|
457
|
+
try {
|
|
458
|
+
await held.handle.stop();
|
|
459
|
+
} catch (err) {
|
|
460
|
+
this.options.logger.warn("hub mDNS: withdraw failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
/**
|
|
465
|
+
* The address a hub advertises: the `internal` role's answer, exactly as the
|
|
466
|
+
* address book resolved it. Never re-ranked here (D729, D732).
|
|
467
|
+
*/
|
|
468
|
+
function internalUrlOf(book) {
|
|
469
|
+
return book.roles.internal.resolved;
|
|
470
|
+
}
|
|
471
|
+
//#endregion
|
|
472
|
+
//#region src/builtins/local-network/hub-mdns-ciao.ts
|
|
473
|
+
function createCiaoPublisher() {
|
|
474
|
+
return { async publish(announcement) {
|
|
475
|
+
const { getResponder } = await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("../../lib-DOJ5yyWA.js").default));
|
|
476
|
+
const responder = getResponder();
|
|
477
|
+
const service = responder.createService({
|
|
478
|
+
name: announcement.name,
|
|
479
|
+
type: announcement.type,
|
|
480
|
+
port: announcement.port,
|
|
481
|
+
txt: { ...announcement.txt },
|
|
482
|
+
...announcement.address !== null ? { restrictedAddresses: [announcement.address] } : {}
|
|
483
|
+
});
|
|
484
|
+
try {
|
|
485
|
+
await service.advertise();
|
|
486
|
+
} catch (err) {
|
|
487
|
+
await responder.shutdown().catch(() => void 0);
|
|
488
|
+
throw err;
|
|
489
|
+
}
|
|
490
|
+
return { stop: async () => {
|
|
491
|
+
await service.destroy();
|
|
492
|
+
await responder.shutdown();
|
|
493
|
+
} };
|
|
494
|
+
} };
|
|
495
|
+
}
|
|
496
|
+
//#endregion
|
|
340
497
|
//#region src/builtins/local-network/local-hostname.ts
|
|
341
498
|
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])$/;
|
|
342
499
|
/**
|
|
@@ -419,6 +576,7 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
|
|
|
419
576
|
* `null` when no mesh provider is joined.
|
|
420
577
|
*/
|
|
421
578
|
meshEndpoint = null;
|
|
579
|
+
mdns = null;
|
|
422
580
|
constructor() {
|
|
423
581
|
super({
|
|
424
582
|
allowedAddresses: [],
|
|
@@ -430,7 +588,8 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
|
|
|
430
588
|
httpPort: require_tls.DEFAULT_LAN_HTTP_PORT,
|
|
431
589
|
localHostname: "",
|
|
432
590
|
internalUrl: "",
|
|
433
|
-
publicEndpointUrl: ""
|
|
591
|
+
publicEndpointUrl: "",
|
|
592
|
+
mdnsAdvertise: true
|
|
434
593
|
});
|
|
435
594
|
}
|
|
436
595
|
/**
|
|
@@ -481,6 +640,7 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
|
|
|
481
640
|
customAddresses: changed.custom
|
|
482
641
|
}, detectedUrls);
|
|
483
642
|
await this.updateGlobalSettings(addressBookPatch(after));
|
|
643
|
+
await this.mdns?.sync();
|
|
484
644
|
}
|
|
485
645
|
/** The deprecated `getHubAddresses` shape, from the role resolvers. */
|
|
486
646
|
async snapshotHubAddresses() {
|
|
@@ -716,6 +876,7 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
|
|
|
716
876
|
} catch (err) {
|
|
717
877
|
this.ctx.logger.warn("local-network: LAN HTTP not applied at boot", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
718
878
|
}
|
|
879
|
+
await this.applyMdnsAdvertisement();
|
|
719
880
|
return [{
|
|
720
881
|
capability: require_dist.localNetworkCapability,
|
|
721
882
|
provider
|
|
@@ -749,6 +910,14 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
|
|
|
749
910
|
equals: true
|
|
750
911
|
}
|
|
751
912
|
}),
|
|
913
|
+
this.field({
|
|
914
|
+
type: "boolean",
|
|
915
|
+
key: "mdnsAdvertise",
|
|
916
|
+
label: "Announce this hub on the LAN (mDNS)",
|
|
917
|
+
description: "Lets `camstack login` find the hub without an address. Advertises only the HTTPS address, never a secret. Needs UDP 5353 multicast to reach the LAN (host networking or a dedicated container IP).",
|
|
918
|
+
style: "switch",
|
|
919
|
+
default: true
|
|
920
|
+
}),
|
|
752
921
|
this.field({
|
|
753
922
|
type: "text",
|
|
754
923
|
key: "localHostname",
|
|
@@ -762,8 +931,29 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
|
|
|
762
931
|
}
|
|
763
932
|
async onConfigChanged() {
|
|
764
933
|
await this.applyLanAccessFromSettings();
|
|
934
|
+
await this.applyMdnsAdvertisement();
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Start, re-announce or stop the hub's mDNS service (D838). Never throws:
|
|
938
|
+
* the advertiser logs its own failures and boot goes on.
|
|
939
|
+
*/
|
|
940
|
+
async applyMdnsAdvertisement() {
|
|
941
|
+
if (this.config.mdnsAdvertise === false) {
|
|
942
|
+
await this.mdns?.stop();
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
if (this.mdns === null) this.mdns = new HubMdnsAdvertiser({
|
|
946
|
+
publisher: createCiaoPublisher(),
|
|
947
|
+
logger: this.ctx.logger,
|
|
948
|
+
nodeId: this.ctx.kernel.localNodeId ?? "hub",
|
|
949
|
+
version: null,
|
|
950
|
+
readInternalUrl: async () => internalUrlOf(await this.addressBook("https"))
|
|
951
|
+
});
|
|
952
|
+
await this.mdns.start();
|
|
765
953
|
}
|
|
766
954
|
async onShutdown() {
|
|
955
|
+
await this.mdns?.stop();
|
|
956
|
+
this.mdns = null;
|
|
767
957
|
if (this.pollTimer) {
|
|
768
958
|
clearInterval(this.pollTimer);
|
|
769
959
|
this.pollTimer = null;
|
|
@@ -959,6 +1149,7 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
|
|
|
959
1149
|
} });
|
|
960
1150
|
this.lastSnapshotKey = key;
|
|
961
1151
|
this.persistReconciledAllowlist(interfaces);
|
|
1152
|
+
this.mdns?.sync();
|
|
962
1153
|
this.ctx.eventBus?.emit({
|
|
963
1154
|
id: `local-network-changed-${Date.now()}`,
|
|
964
1155
|
timestamp: /* @__PURE__ */ new Date(),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
|
|
2
|
+
import { D as HUB_MDNS_TXT_KEYS, E as HUB_MDNS_SERVICE_TYPE, En as resolveHubAddressRoles, Ht as isDefaultAppConnectionEndpoint, Kn as validateCustomAddress, Qt as localNetworkCapability, Sn as resolveAppConnections, ai as EventCategory, at as checkRoleAddress, hn as pickAppLinkHub, kn as resolveNotificationTapAddress, pr as BaseAddon, s as CAMSTACK_APP_LINK_ORIGIN, zt as isAppLinkBase } from "../../dist-DwE9yHdk.mjs";
|
|
3
|
+
import { P as readLanHttpState, _ as writeExtraSans, h as readTlsMode, i as storeUploadedCertificate, j as applyLanHttp, k as DEFAULT_LAN_HTTP_PORT, m as readTlsAccessStatus, n as regenerateCertificate, p as readExtraSans, r as revertToGeneratedCertificate, t as applyCertificateToListener, u as servedHttpsLeafFingerprint, y as ensureTlsCert } from "../../tls-D-2dPY-5.mjs";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { isIP } from "node:net";
|
|
5
6
|
import * as os from "node:os";
|
|
@@ -334,6 +335,163 @@ function parseIpv6Groups(address) {
|
|
|
334
335
|
];
|
|
335
336
|
}
|
|
336
337
|
//#endregion
|
|
338
|
+
//#region src/builtins/local-network/hub-mdns.ts
|
|
339
|
+
/**
|
|
340
|
+
* Hub mDNS advertisement (D838) — how `camstack login` / `camstack discover`
|
|
341
|
+
* find a hub on the LAN.
|
|
342
|
+
*
|
|
343
|
+
* The hub publishes ONE `_camstack._tcp` service for its HTTPS endpoint. There
|
|
344
|
+
* is no mesh channel in it (D826 retired mesh UDP discovery on purpose) and
|
|
345
|
+
* nothing secret: the TXT record carries the node id, the version, the role
|
|
346
|
+
* and the address URL — finding a hub grants nothing, every client still signs
|
|
347
|
+
* in.
|
|
348
|
+
*
|
|
349
|
+
* The advertised address is NOT ranked here. It is the `internal` role of the
|
|
350
|
+
* address book (the address flagged for LAN clients, else the automatic LAN
|
|
351
|
+
* pick) — this module only reads the answer `local-network` already gives.
|
|
352
|
+
*
|
|
353
|
+
* The responder is injected so the logic is tested with a fake; the real one
|
|
354
|
+
* wraps `@homebridge/ciao`.
|
|
355
|
+
*/
|
|
356
|
+
/** The announcement for the current address, or null when nothing qualifies. */
|
|
357
|
+
function buildHubAnnouncement(inputs) {
|
|
358
|
+
if (inputs.internalUrl === null) return null;
|
|
359
|
+
let url;
|
|
360
|
+
try {
|
|
361
|
+
url = new URL(inputs.internalUrl);
|
|
362
|
+
} catch {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
if (url.protocol !== "https:") return null;
|
|
366
|
+
const port = url.port === "" ? 443 : Number.parseInt(url.port, 10);
|
|
367
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) return null;
|
|
368
|
+
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
369
|
+
const txt = {
|
|
370
|
+
[HUB_MDNS_TXT_KEYS.nodeId]: inputs.nodeId,
|
|
371
|
+
[HUB_MDNS_TXT_KEYS.role]: "hub",
|
|
372
|
+
[HUB_MDNS_TXT_KEYS.url]: url.origin
|
|
373
|
+
};
|
|
374
|
+
if (inputs.version !== null) txt[HUB_MDNS_TXT_KEYS.version] = inputs.version;
|
|
375
|
+
return {
|
|
376
|
+
name: `camstack-${inputs.nodeId}`,
|
|
377
|
+
type: HUB_MDNS_SERVICE_TYPE,
|
|
378
|
+
port,
|
|
379
|
+
address: isIP(host) !== 0 ? host : null,
|
|
380
|
+
txt
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function announcementKey(a) {
|
|
384
|
+
return JSON.stringify([
|
|
385
|
+
a.name,
|
|
386
|
+
a.type,
|
|
387
|
+
a.port,
|
|
388
|
+
a.address,
|
|
389
|
+
a.txt
|
|
390
|
+
]);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Keeps one mDNS service in step with the hub's `internal` address. `sync()`
|
|
394
|
+
* is safe to call on any change; it never throws — a failure to advertise is
|
|
395
|
+
* logged and the boot goes on.
|
|
396
|
+
*/
|
|
397
|
+
var HubMdnsAdvertiser = class {
|
|
398
|
+
options;
|
|
399
|
+
current = null;
|
|
400
|
+
running = false;
|
|
401
|
+
chain = Promise.resolve();
|
|
402
|
+
constructor(options) {
|
|
403
|
+
this.options = options;
|
|
404
|
+
}
|
|
405
|
+
async start() {
|
|
406
|
+
this.running = true;
|
|
407
|
+
await this.sync();
|
|
408
|
+
}
|
|
409
|
+
/** Re-read the address and re-announce when it changed. Serialised. */
|
|
410
|
+
sync() {
|
|
411
|
+
this.chain = this.chain.then(() => this.syncOnce());
|
|
412
|
+
return this.chain;
|
|
413
|
+
}
|
|
414
|
+
async stop() {
|
|
415
|
+
this.running = false;
|
|
416
|
+
await (this.chain = this.chain.then(() => this.withdraw()));
|
|
417
|
+
}
|
|
418
|
+
async syncOnce() {
|
|
419
|
+
if (!this.running) return;
|
|
420
|
+
const { logger } = this.options;
|
|
421
|
+
try {
|
|
422
|
+
const internalUrl = await this.options.readInternalUrl();
|
|
423
|
+
const next = buildHubAnnouncement({
|
|
424
|
+
nodeId: this.options.nodeId,
|
|
425
|
+
version: this.options.version,
|
|
426
|
+
internalUrl
|
|
427
|
+
});
|
|
428
|
+
if (next === null) {
|
|
429
|
+
if (this.current !== null) {
|
|
430
|
+
await this.withdraw();
|
|
431
|
+
logger.info("hub mDNS: no internal address, advertisement withdrawn");
|
|
432
|
+
}
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
const key = announcementKey(next);
|
|
436
|
+
if (this.current?.key === key) return;
|
|
437
|
+
await this.withdraw();
|
|
438
|
+
const handle = await this.options.publisher.publish(next);
|
|
439
|
+
this.current = {
|
|
440
|
+
key,
|
|
441
|
+
handle
|
|
442
|
+
};
|
|
443
|
+
logger.info("hub mDNS: advertising _camstack._tcp", { meta: {
|
|
444
|
+
url: next.txt[HUB_MDNS_TXT_KEYS.url],
|
|
445
|
+
port: next.port
|
|
446
|
+
} });
|
|
447
|
+
} catch (err) {
|
|
448
|
+
logger.warn("hub mDNS: could not advertise the hub (logins still work with --server)", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
async withdraw() {
|
|
452
|
+
const held = this.current;
|
|
453
|
+
this.current = null;
|
|
454
|
+
if (held === null) return;
|
|
455
|
+
try {
|
|
456
|
+
await held.handle.stop();
|
|
457
|
+
} catch (err) {
|
|
458
|
+
this.options.logger.warn("hub mDNS: withdraw failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
/**
|
|
463
|
+
* The address a hub advertises: the `internal` role's answer, exactly as the
|
|
464
|
+
* address book resolved it. Never re-ranked here (D729, D732).
|
|
465
|
+
*/
|
|
466
|
+
function internalUrlOf(book) {
|
|
467
|
+
return book.roles.internal.resolved;
|
|
468
|
+
}
|
|
469
|
+
//#endregion
|
|
470
|
+
//#region src/builtins/local-network/hub-mdns-ciao.ts
|
|
471
|
+
function createCiaoPublisher() {
|
|
472
|
+
return { async publish(announcement) {
|
|
473
|
+
const { getResponder } = await import("../../lib-B_R6sPC3.mjs").then((m) => /* @__PURE__ */ __toESM(m.default));
|
|
474
|
+
const responder = getResponder();
|
|
475
|
+
const service = responder.createService({
|
|
476
|
+
name: announcement.name,
|
|
477
|
+
type: announcement.type,
|
|
478
|
+
port: announcement.port,
|
|
479
|
+
txt: { ...announcement.txt },
|
|
480
|
+
...announcement.address !== null ? { restrictedAddresses: [announcement.address] } : {}
|
|
481
|
+
});
|
|
482
|
+
try {
|
|
483
|
+
await service.advertise();
|
|
484
|
+
} catch (err) {
|
|
485
|
+
await responder.shutdown().catch(() => void 0);
|
|
486
|
+
throw err;
|
|
487
|
+
}
|
|
488
|
+
return { stop: async () => {
|
|
489
|
+
await service.destroy();
|
|
490
|
+
await responder.shutdown();
|
|
491
|
+
} };
|
|
492
|
+
} };
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
337
495
|
//#region src/builtins/local-network/local-hostname.ts
|
|
338
496
|
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])$/;
|
|
339
497
|
/**
|
|
@@ -416,6 +574,7 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
416
574
|
* `null` when no mesh provider is joined.
|
|
417
575
|
*/
|
|
418
576
|
meshEndpoint = null;
|
|
577
|
+
mdns = null;
|
|
419
578
|
constructor() {
|
|
420
579
|
super({
|
|
421
580
|
allowedAddresses: [],
|
|
@@ -427,7 +586,8 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
427
586
|
httpPort: DEFAULT_LAN_HTTP_PORT,
|
|
428
587
|
localHostname: "",
|
|
429
588
|
internalUrl: "",
|
|
430
|
-
publicEndpointUrl: ""
|
|
589
|
+
publicEndpointUrl: "",
|
|
590
|
+
mdnsAdvertise: true
|
|
431
591
|
});
|
|
432
592
|
}
|
|
433
593
|
/**
|
|
@@ -478,6 +638,7 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
478
638
|
customAddresses: changed.custom
|
|
479
639
|
}, detectedUrls);
|
|
480
640
|
await this.updateGlobalSettings(addressBookPatch(after));
|
|
641
|
+
await this.mdns?.sync();
|
|
481
642
|
}
|
|
482
643
|
/** The deprecated `getHubAddresses` shape, from the role resolvers. */
|
|
483
644
|
async snapshotHubAddresses() {
|
|
@@ -713,6 +874,7 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
713
874
|
} catch (err) {
|
|
714
875
|
this.ctx.logger.warn("local-network: LAN HTTP not applied at boot", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
715
876
|
}
|
|
877
|
+
await this.applyMdnsAdvertisement();
|
|
716
878
|
return [{
|
|
717
879
|
capability: localNetworkCapability,
|
|
718
880
|
provider
|
|
@@ -746,6 +908,14 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
746
908
|
equals: true
|
|
747
909
|
}
|
|
748
910
|
}),
|
|
911
|
+
this.field({
|
|
912
|
+
type: "boolean",
|
|
913
|
+
key: "mdnsAdvertise",
|
|
914
|
+
label: "Announce this hub on the LAN (mDNS)",
|
|
915
|
+
description: "Lets `camstack login` find the hub without an address. Advertises only the HTTPS address, never a secret. Needs UDP 5353 multicast to reach the LAN (host networking or a dedicated container IP).",
|
|
916
|
+
style: "switch",
|
|
917
|
+
default: true
|
|
918
|
+
}),
|
|
749
919
|
this.field({
|
|
750
920
|
type: "text",
|
|
751
921
|
key: "localHostname",
|
|
@@ -759,8 +929,29 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
759
929
|
}
|
|
760
930
|
async onConfigChanged() {
|
|
761
931
|
await this.applyLanAccessFromSettings();
|
|
932
|
+
await this.applyMdnsAdvertisement();
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Start, re-announce or stop the hub's mDNS service (D838). Never throws:
|
|
936
|
+
* the advertiser logs its own failures and boot goes on.
|
|
937
|
+
*/
|
|
938
|
+
async applyMdnsAdvertisement() {
|
|
939
|
+
if (this.config.mdnsAdvertise === false) {
|
|
940
|
+
await this.mdns?.stop();
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
if (this.mdns === null) this.mdns = new HubMdnsAdvertiser({
|
|
944
|
+
publisher: createCiaoPublisher(),
|
|
945
|
+
logger: this.ctx.logger,
|
|
946
|
+
nodeId: this.ctx.kernel.localNodeId ?? "hub",
|
|
947
|
+
version: null,
|
|
948
|
+
readInternalUrl: async () => internalUrlOf(await this.addressBook("https"))
|
|
949
|
+
});
|
|
950
|
+
await this.mdns.start();
|
|
762
951
|
}
|
|
763
952
|
async onShutdown() {
|
|
953
|
+
await this.mdns?.stop();
|
|
954
|
+
this.mdns = null;
|
|
764
955
|
if (this.pollTimer) {
|
|
765
956
|
clearInterval(this.pollTimer);
|
|
766
957
|
this.pollTimer = null;
|
|
@@ -956,6 +1147,7 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
956
1147
|
} });
|
|
957
1148
|
this.lastSnapshotKey = key;
|
|
958
1149
|
this.persistReconciledAllowlist(interfaces);
|
|
1150
|
+
this.mdns?.sync();
|
|
959
1151
|
this.ctx.eventBus?.emit({
|
|
960
1152
|
id: `local-network-changed-${Date.now()}`,
|
|
961
1153
|
timestamp: /* @__PURE__ */ new Date(),
|
|
@@ -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-
|
|
6
|
+
const require_dist = require("../../dist-DxiDI8hZ.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 {
|
|
1
|
+
import { en as logDestinationCapability, pr as BaseAddon } from "../../dist-DwE9yHdk.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-
|
|
6
|
+
const require_dist = require("../../dist-DxiDI8hZ.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 {
|
|
1
|
+
import { Cn as resolveBucketMs, Ft as foldSnapshotByFunction, Or as createEvent, ai as EventCategory, bn as reducePoints, fr as errMsg, pr as BaseAddon, sn as metricsProviderCapability } from "../../dist-DwE9yHdk.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-
|
|
6
|
+
const require_dist = require("../../dist-DxiDI8hZ.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 {
|
|
2
|
+
import { Et as enumerateInferenceDevices, Mr as emitReadiness, Nn as scoreRuntimes, _n as platformProbeCapability, ai as EventCategory, fr as errMsg, pr as BaseAddon } from "../../dist-DwE9yHdk.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-
|
|
6
|
+
const require_dist = require("../../dist-DxiDI8hZ.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 {
|
|
1
|
+
import { ai as EventCategory, pr as BaseAddon } from "../../dist-DwE9yHdk.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-
|
|
6
|
+
const require_dist = require("../../dist-DxiDI8hZ.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 {
|
|
1
|
+
import { $ as bareAddonId, Hn as streamQualityLabel, In as snapshotCapability, Ur as nodePin, a as BatteryStatusSchema, ai as EventCategory, bt as deriveBatteryPresence, dr as DeviceType, et as batteryCapability, fr as errMsg, lr as DeviceFeature, on as memoizePinnedFfmpeg, pr as BaseAddon } from "../../dist-DwE9yHdk.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";
|
|
@@ -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-
|
|
6
|
+
const require_dist = require("../../dist-DxiDI8hZ.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 {
|
|
1
|
+
import { Bn as storageProviderCapability, Pt as filesystemBrowseCapability, pr as BaseAddon } from "../../dist-DwE9yHdk.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";
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StorageLocationBandMonitor — free-space bands for Notification Center.
|
|
3
|
+
*
|
|
4
|
+
* Capacity that failed to measure is `null` and becomes CRITICAL (D393) —
|
|
5
|
+
* never folded into 0% or 100%. Disabled / non-managed locations are absent
|
|
6
|
+
* from the sweep input and clear any open episode without an `ok` event.
|
|
7
|
+
*
|
|
8
|
+
* Pure, injected clock, one transition per band change.
|
|
9
|
+
*/
|
|
10
|
+
export type StorageLocationBand = "ok" | "low" | "critical";
|
|
11
|
+
export interface StorageLocationBandConfig {
|
|
12
|
+
/** Free percent at/below which the location is low (default 15). */
|
|
13
|
+
readonly lowPercent: number;
|
|
14
|
+
/** Free percent at/below which the location is critical (default 5). */
|
|
15
|
+
readonly criticalPercent: number;
|
|
16
|
+
}
|
|
17
|
+
export declare const DEFAULT_STORAGE_LOCATION_BAND_CONFIG: StorageLocationBandConfig;
|
|
18
|
+
export interface StorageLocationBandSample {
|
|
19
|
+
readonly locationId: string;
|
|
20
|
+
readonly nodeId?: string;
|
|
21
|
+
readonly locationName?: string;
|
|
22
|
+
/** Free percent, or `null` when the probe failed. */
|
|
23
|
+
readonly freePercent: number | null;
|
|
24
|
+
}
|
|
25
|
+
export interface StorageLocationBandEvent {
|
|
26
|
+
readonly locationId: string;
|
|
27
|
+
readonly nodeId?: string;
|
|
28
|
+
readonly locationName?: string;
|
|
29
|
+
readonly band: StorageLocationBand;
|
|
30
|
+
readonly freePercent: number | null;
|
|
31
|
+
readonly reason: string;
|
|
32
|
+
readonly previous: StorageLocationBand | null;
|
|
33
|
+
}
|
|
34
|
+
export declare class StorageLocationBandMonitor {
|
|
35
|
+
private readonly config;
|
|
36
|
+
private readonly band;
|
|
37
|
+
constructor(config?: StorageLocationBandConfig);
|
|
38
|
+
/**
|
|
39
|
+
* Observe one sweep of managed locations. Locations missing from `samples`
|
|
40
|
+
* drop their episode without emitting `ok` (disabled / removed).
|
|
41
|
+
*/
|
|
42
|
+
observe(samples: readonly StorageLocationBandSample[]): readonly StorageLocationBandEvent[];
|
|
43
|
+
}
|