@camstack/system 1.2.359 → 1.2.361
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/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 +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- 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/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 +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/address-book.d.ts +17 -17
- package/dist/builtins/local-network/local-network.addon.d.ts +21 -18
- package/dist/builtins/local-network/local-network.addon.js +85 -83
- package/dist/builtins/local-network/local-network.addon.mjs +85 -83
- 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.d.ts +16 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js +52 -14
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +52 -14
- 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-orchestrator.addon.js +2 -2
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +2 -2
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{child-cap-dispatch-Dm1TRDCr.mjs → child-cap-dispatch-StBHObCA.mjs} +1 -1
- package/dist/{child-cap-dispatch-t61ohDpb.js → child-cap-dispatch-T3Kdhbb3.js} +1 -1
- package/dist/{composition-sources-CHlfWHl6.mjs → composition-sources-DASc7NBj.mjs} +1 -1
- package/dist/{composition-sources-CCU_KizO.js → composition-sources-DFgayxCc.js} +1 -1
- package/dist/{dist-pTfD_M-U.mjs → dist-C9Rk6KVv.mjs} +100 -84
- package/dist/{dist-W_X44h-s.js → dist-Cv1lnnQq.js} +109 -93
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{retired-settings-keys-jglEzgWR.js → retired-settings-keys-BGNqRNhu.js} +1 -1
- package/dist/{retired-settings-keys-CK_ifret.mjs → retired-settings-keys-BjG8bQVI.mjs} +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ar as EventCategory, Ft as isAppLinkBase, Kt as localNetworkCapability, Rn as validateCustomAddress, Rt as
|
|
1
|
+
import { Ar as EventCategory, Ft as isAppLinkBase, Kt as localNetworkCapability, Rn as validateCustomAddress, Rt as isDefaultAppConnectionEndpoint, Sn as resolveNotificationTapAddress, ar as BaseAddon, cn as pickAppLinkHub, hn as resolveAppConnections, s as CAMSTACK_APP_LINK_ORIGIN, tt as checkRoleAddress, yn as resolveHubAddressRoles } from "../../dist-C9Rk6KVv.mjs";
|
|
2
2
|
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-CJHogwTl.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import * as os from "node:os";
|
|
@@ -9,24 +9,26 @@ import { isIP } from "node:net";
|
|
|
9
9
|
* The Network page's address book, as pure functions: custom addresses plus
|
|
10
10
|
* the role flags carried by detected and custom addresses alike.
|
|
11
11
|
*
|
|
12
|
-
* ## Storage
|
|
12
|
+
* ## Storage (D734)
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* `
|
|
17
|
-
* `signInBaseUrl` (agents / sign-in, D729 amendment). A single-valued role is
|
|
14
|
+
* One key per role: `appConnectionUrls` (`appConnections`, many),
|
|
15
|
+
* `notificationTapUrl` (`notificationTaps`), `publicEndpointUrl`
|
|
16
|
+
* (`publicEndpoint`) and `internalUrl` (`internal`). A single-valued role is
|
|
18
17
|
* one URL — at most one address carries it, by construction. `''` / `[]` is
|
|
19
18
|
* automatic. The custom addresses are `customAddresses` (url + label).
|
|
20
19
|
*
|
|
21
|
-
*
|
|
20
|
+
* The keys of the earlier role names (`viewerBaseUrls`, `notificationBaseUrl`,
|
|
21
|
+
* `signInBaseUrl`, `agentBaseUrl`) are not read: a value stored under one is
|
|
22
|
+
* ignored, and that role is automatic until the operator flags it again
|
|
23
|
+
* (D734 — one install, re-set by hand, no migration code).
|
|
22
24
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* as a custom row instead of vanishing
|
|
25
|
+
* ## A flag on an address that is in no row
|
|
26
|
+
*
|
|
27
|
+
* A flagged detected address can disappear (a DHCP move) while it still wins
|
|
28
|
+
* its role. {@link normalizeAddressBook} turns every single-valued flag that
|
|
29
|
+
* names neither a detected address, a custom one nor the app link into a
|
|
30
|
+
* custom address carrying that role, on every read; the next write persists
|
|
31
|
+
* the result — so the address shows as a custom row instead of vanishing.
|
|
30
32
|
*/
|
|
31
33
|
var CustomAddressListSchema = z.array(z.object({
|
|
32
34
|
baseUrl: z.string(),
|
|
@@ -34,14 +36,14 @@ var CustomAddressListSchema = z.array(z.object({
|
|
|
34
36
|
}));
|
|
35
37
|
/** The label a flag promoted to a custom address is given. */
|
|
36
38
|
var PROMOTED_LABEL = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
notificationTaps: "Notification taps address",
|
|
40
|
+
internal: "Internal address",
|
|
41
|
+
publicEndpoint: "Public endpoint"
|
|
40
42
|
};
|
|
41
43
|
var SINGLE_ROLES = [
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
44
|
+
"notificationTaps",
|
|
45
|
+
"internal",
|
|
46
|
+
"publicEndpoint"
|
|
45
47
|
];
|
|
46
48
|
function flagOf(value) {
|
|
47
49
|
return value === "" ? null : value;
|
|
@@ -58,10 +60,10 @@ function parseCustomAddresses(raw) {
|
|
|
58
60
|
*/
|
|
59
61
|
function normalizeAddressBook(stored, detectedUrls) {
|
|
60
62
|
const flags = {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
appConnections: stored.appConnectionUrls,
|
|
64
|
+
notificationTaps: flagOf(stored.notificationTapUrl),
|
|
65
|
+
internal: flagOf(stored.internalUrl),
|
|
66
|
+
publicEndpoint: flagOf(stored.publicEndpointUrl)
|
|
65
67
|
};
|
|
66
68
|
const custom = [...parseCustomAddresses(stored.customAddresses)];
|
|
67
69
|
for (const role of SINGLE_ROLES) {
|
|
@@ -82,10 +84,10 @@ function normalizeAddressBook(stored, detectedUrls) {
|
|
|
82
84
|
function addressBookPatch(state) {
|
|
83
85
|
return {
|
|
84
86
|
customAddresses: state.custom,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
appConnectionUrls: state.flags.appConnections,
|
|
88
|
+
notificationTapUrl: state.flags.notificationTaps ?? "",
|
|
89
|
+
publicEndpointUrl: state.flags.publicEndpoint ?? "",
|
|
90
|
+
internalUrl: state.flags.internal ?? ""
|
|
89
91
|
};
|
|
90
92
|
}
|
|
91
93
|
function withCustomAddress(state, entry) {
|
|
@@ -101,20 +103,20 @@ function withoutCustomAddress(state, baseUrl) {
|
|
|
101
103
|
return {
|
|
102
104
|
custom: state.custom.filter((c) => c.baseUrl !== baseUrl),
|
|
103
105
|
flags: {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
appConnections: state.flags.appConnections.filter((u) => u !== baseUrl),
|
|
107
|
+
notificationTaps: clear(state.flags.notificationTaps),
|
|
108
|
+
internal: clear(state.flags.internal),
|
|
109
|
+
publicEndpoint: clear(state.flags.publicEndpoint)
|
|
108
110
|
}
|
|
109
111
|
};
|
|
110
112
|
}
|
|
111
113
|
/** Every role back to automatic for `role`. */
|
|
112
114
|
function withRoleAutomatic(state, role) {
|
|
113
|
-
return role === "
|
|
115
|
+
return role === "appConnections" ? {
|
|
114
116
|
...state,
|
|
115
117
|
flags: {
|
|
116
118
|
...state.flags,
|
|
117
|
-
|
|
119
|
+
appConnections: []
|
|
118
120
|
}
|
|
119
121
|
} : {
|
|
120
122
|
...state,
|
|
@@ -127,24 +129,24 @@ function withRoleAutomatic(state, role) {
|
|
|
127
129
|
/**
|
|
128
130
|
* Flag or unflag `baseUrl` for `role`. A single-valued role flagged here moves
|
|
129
131
|
* off every other address (it is one URL); unflagging the address that carries
|
|
130
|
-
* it returns the role to automatic. The app link flagged for
|
|
131
|
-
* the automatic answer, stored as automatic — one representation.
|
|
132
|
-
* toggles inside the set; toggling while automatic starts
|
|
133
|
-
* set (`
|
|
132
|
+
* it returns the role to automatic. The app link flagged for notification
|
|
133
|
+
* taps IS the automatic answer, stored as automatic — one representation.
|
|
134
|
+
* `appConnections` toggles inside the set; toggling while automatic starts
|
|
135
|
+
* from the automatic set (`autoApp`) so the other addresses keep racing.
|
|
134
136
|
*/
|
|
135
|
-
function withRoleFlag(state, role, baseUrl, enabled,
|
|
136
|
-
if (role === "
|
|
137
|
-
const without = (state.flags.
|
|
138
|
-
const
|
|
137
|
+
function withRoleFlag(state, role, baseUrl, enabled, autoApp) {
|
|
138
|
+
if (role === "appConnections") {
|
|
139
|
+
const without = (state.flags.appConnections.length === 0 ? autoApp : state.flags.appConnections).filter((u) => u !== baseUrl);
|
|
140
|
+
const appConnections = enabled ? [...without, baseUrl] : without;
|
|
139
141
|
return {
|
|
140
142
|
...state,
|
|
141
143
|
flags: {
|
|
142
144
|
...state.flags,
|
|
143
|
-
|
|
145
|
+
appConnections
|
|
144
146
|
}
|
|
145
147
|
};
|
|
146
148
|
}
|
|
147
|
-
if (role === "
|
|
149
|
+
if (role === "notificationTaps" && isAppLinkBase(baseUrl)) return withRoleAutomatic(state, role);
|
|
148
150
|
if (enabled) return {
|
|
149
151
|
...state,
|
|
150
152
|
flags: {
|
|
@@ -157,12 +159,12 @@ function withRoleFlag(state, role, baseUrl, enabled, autoViewer) {
|
|
|
157
159
|
/** What a row may be flagged with. */
|
|
158
160
|
function eligibleRoles(endpoint) {
|
|
159
161
|
if (endpoint.kind === "loopback") return [];
|
|
160
|
-
if (endpoint.kind === "app-link") return ["
|
|
162
|
+
if (endpoint.kind === "app-link") return ["notificationTaps"];
|
|
161
163
|
return [
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
"
|
|
165
|
-
"
|
|
164
|
+
"appConnections",
|
|
165
|
+
"notificationTaps",
|
|
166
|
+
"internal",
|
|
167
|
+
"publicEndpoint"
|
|
166
168
|
];
|
|
167
169
|
}
|
|
168
170
|
/** A custom address as a palette entry (`kind: 'custom'`). */
|
|
@@ -179,27 +181,27 @@ function customEndpoint(entry) {
|
|
|
179
181
|
}
|
|
180
182
|
/**
|
|
181
183
|
* The palette a client races: the detected endpoints plus the custom
|
|
182
|
-
* addresses flagged `
|
|
184
|
+
* addresses flagged `appConnections` (a detected URL is never listed twice).
|
|
183
185
|
*/
|
|
184
|
-
function
|
|
186
|
+
function withAppConnectionCustomEndpoints(detected, state) {
|
|
185
187
|
const known = new Set(detected.map((e) => e.baseUrl));
|
|
186
|
-
const wanted = new Set(state.flags.
|
|
188
|
+
const wanted = new Set(state.flags.appConnections);
|
|
187
189
|
const extra = state.custom.filter((c) => wanted.has(c.baseUrl) && !known.has(c.baseUrl)).map(customEndpoint);
|
|
188
190
|
return [...detected, ...extra];
|
|
189
191
|
}
|
|
190
192
|
/** Every role's answer — the resolvers from `@camstack/types`, nothing else. */
|
|
191
|
-
function resolveRoles(state,
|
|
192
|
-
return resolveHubAddressRoles(
|
|
193
|
+
function resolveRoles(state, appPalette, httpsPalette) {
|
|
194
|
+
return resolveHubAddressRoles(withAppConnectionCustomEndpoints(appPalette, state), httpsPalette, state.flags);
|
|
193
195
|
}
|
|
194
196
|
function flagsOn(state, baseUrl) {
|
|
195
197
|
const out = [];
|
|
196
|
-
if (state.flags.
|
|
198
|
+
if (state.flags.appConnections.includes(baseUrl)) out.push("appConnections");
|
|
197
199
|
for (const role of SINGLE_ROLES) if (state.flags[role] === baseUrl) out.push(role);
|
|
198
200
|
return out;
|
|
199
201
|
}
|
|
200
202
|
function inUseOn(roles, baseUrl) {
|
|
201
203
|
const out = [];
|
|
202
|
-
if (roles.
|
|
204
|
+
if (roles.appConnections.resolved.includes(baseUrl)) out.push("appConnections");
|
|
203
205
|
for (const role of SINGLE_ROLES) if (roles[role].resolved === baseUrl) out.push(role);
|
|
204
206
|
return out;
|
|
205
207
|
}
|
|
@@ -213,7 +215,7 @@ function buildAddressBook(state, inputs) {
|
|
|
213
215
|
detected,
|
|
214
216
|
custom: customUrls.has(endpoint.baseUrl),
|
|
215
217
|
flags: endpoint.kind === "app-link" ? [] : flagsOn(state, endpoint.baseUrl),
|
|
216
|
-
inUse: endpoint.kind === "app-link" ? roles.
|
|
218
|
+
inUse: endpoint.kind === "app-link" ? roles.notificationTaps.resolved === "https://camstack.zentik.app" ? ["notificationTaps"] : [] : inUseOn(roles, endpoint.baseUrl),
|
|
217
219
|
eligible: eligibleRoles(endpoint)
|
|
218
220
|
});
|
|
219
221
|
return {
|
|
@@ -418,20 +420,20 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
418
420
|
super({
|
|
419
421
|
allowedAddresses: [],
|
|
420
422
|
bootSeeded: false,
|
|
421
|
-
|
|
422
|
-
|
|
423
|
+
notificationTapUrl: "",
|
|
424
|
+
appConnectionUrls: [],
|
|
423
425
|
customAddresses: [],
|
|
424
426
|
httpEnabled: true,
|
|
425
427
|
httpPort: DEFAULT_LAN_HTTP_PORT,
|
|
426
428
|
localHostname: "",
|
|
427
|
-
|
|
428
|
-
|
|
429
|
+
internalUrl: "",
|
|
430
|
+
publicEndpointUrl: ""
|
|
429
431
|
});
|
|
430
432
|
}
|
|
431
433
|
/**
|
|
432
|
-
* The detected palettes, with the mesh reconciled once: `https` (what
|
|
433
|
-
* and
|
|
434
|
-
*
|
|
434
|
+
* The detected palettes, with the mesh reconciled once: `https` (what
|
|
435
|
+
* `internal` and `publicEndpoint` resolve over, and the app link's `hub=`)
|
|
436
|
+
* and `http` (what the app-connections race resolves over).
|
|
435
437
|
*/
|
|
436
438
|
async palettes() {
|
|
437
439
|
const port = (await this.resolveAdvertisedListenPort(null)).port;
|
|
@@ -469,7 +471,7 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
469
471
|
const detectedUrls = new Set(detected.map((e) => e.baseUrl));
|
|
470
472
|
const changed = change(this.addressState(detected), {
|
|
471
473
|
detectedUrls,
|
|
472
|
-
|
|
474
|
+
autoApp: [...new Set(detected.filter(isDefaultAppConnectionEndpoint).map((e) => e.baseUrl))]
|
|
473
475
|
});
|
|
474
476
|
const after = normalizeAddressBook({
|
|
475
477
|
...addressBookPatch(changed),
|
|
@@ -486,18 +488,18 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
486
488
|
source: r.source === "flagged" ? "override" : r.source
|
|
487
489
|
});
|
|
488
490
|
return {
|
|
489
|
-
cluster: legacy(roles.
|
|
490
|
-
browser: legacy(roles.
|
|
491
|
+
cluster: legacy(roles.internal),
|
|
492
|
+
browser: legacy(roles.publicEndpoint)
|
|
491
493
|
};
|
|
492
494
|
}
|
|
493
495
|
/**
|
|
494
496
|
* Flag / unflag `baseUrl` for `role`, or (`baseUrl: null`) return the role
|
|
495
497
|
* to automatic. Validated: the address must be a row (detected, custom, or
|
|
496
|
-
* the app link for
|
|
497
|
-
* http only when accepted. A refusal stores nothing.
|
|
498
|
+
* the app link for `notificationTaps`), never loopback, and
|
|
499
|
+
* `publicEndpoint` on plain http only when accepted. A refusal stores nothing.
|
|
498
500
|
*/
|
|
499
501
|
async setRole(role, baseUrl, enabled, acceptInsecureHttp) {
|
|
500
|
-
if (baseUrl === null || baseUrl.trim() === "" && role !== "
|
|
502
|
+
if (baseUrl === null || baseUrl.trim() === "" && role !== "appConnections") {
|
|
501
503
|
await this.changeAddressBook((state) => withRoleAutomatic(state, role));
|
|
502
504
|
this.ctx.logger.info("local-network: address role set to automatic", { meta: { role } });
|
|
503
505
|
return;
|
|
@@ -510,8 +512,8 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
510
512
|
}
|
|
511
513
|
await this.changeAddressBook((state, ctx) => {
|
|
512
514
|
const known = isAppLinkBase(target) || ctx.detectedUrls.has(target) || state.custom.some((c) => c.baseUrl === target);
|
|
513
|
-
if (enabled && !known && role !== "
|
|
514
|
-
return withRoleFlag(state, role, target, enabled, ctx.
|
|
515
|
+
if (enabled && !known && role !== "notificationTaps") throw new Error(`${target} is not in the address list — add it as a custom address first.`);
|
|
516
|
+
return withRoleFlag(state, role, target, enabled, ctx.autoApp);
|
|
515
517
|
});
|
|
516
518
|
this.ctx.logger.info("local-network: address role set", { meta: {
|
|
517
519
|
role,
|
|
@@ -520,7 +522,7 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
520
522
|
} });
|
|
521
523
|
}
|
|
522
524
|
async notificationEndpointSnapshot() {
|
|
523
|
-
const resolution =
|
|
525
|
+
const resolution = resolveNotificationTapAddress(this.storedFlags());
|
|
524
526
|
const port = (await this.resolveAdvertisedListenPort(null)).port;
|
|
525
527
|
return {
|
|
526
528
|
baseUrl: resolution.flagged,
|
|
@@ -529,7 +531,7 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
529
531
|
};
|
|
530
532
|
}
|
|
531
533
|
async snapshotViewerEndpoints() {
|
|
532
|
-
const resolution =
|
|
534
|
+
const resolution = resolveAppConnections(await this.buildAdvertisedEndpoints(false, false, "http", (await this.resolveAdvertisedListenPort(null)).port), this.storedFlags());
|
|
533
535
|
return {
|
|
534
536
|
baseUrls: resolution.flagged,
|
|
535
537
|
resolved: resolution.resolved
|
|
@@ -537,14 +539,14 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
537
539
|
}
|
|
538
540
|
/**
|
|
539
541
|
* Palette shown to clients: LAN IPv4, stable LAN IPv6, public, mesh, and
|
|
540
|
-
* the custom addresses flagged `
|
|
541
|
-
* HTTP/WS race is a subset (`
|
|
542
|
+
* the custom addresses flagged `appConnections` (`kind: 'custom'`). The app
|
|
543
|
+
* HTTP/WS race is a subset (`selectAppConnectionEndpoints`) — IPv6 stays in this
|
|
542
544
|
* list because ICE/WebRTC gathers it regardless.
|
|
543
545
|
*/
|
|
544
546
|
async buildAdvertisedEndpoints(includeLoopback, ipv4Only, scheme, httpsPort) {
|
|
545
547
|
await this.reconcileMeshEndpoint();
|
|
546
548
|
const detected = this.endpointsNow(includeLoopback, ipv4Only, scheme, httpsPort);
|
|
547
|
-
return
|
|
549
|
+
return withAppConnectionCustomEndpoints(detected, this.addressState(detected));
|
|
548
550
|
}
|
|
549
551
|
/** The DETECTED palette from the cached interface/tunnel/mesh state. */
|
|
550
552
|
endpointsNow(includeLoopback, ipv4Only, scheme, httpsPort) {
|
|
@@ -591,22 +593,22 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
591
593
|
},
|
|
592
594
|
getNotificationEndpoint: async () => this.notificationEndpointSnapshot(),
|
|
593
595
|
setNotificationEndpoint: async ({ baseUrl }) => {
|
|
594
|
-
await this.setRole("
|
|
596
|
+
await this.setRole("notificationTaps", baseUrl, true, false);
|
|
595
597
|
return this.notificationEndpointSnapshot();
|
|
596
598
|
},
|
|
597
599
|
getViewerEndpoints: async () => this.snapshotViewerEndpoints(),
|
|
598
600
|
setViewerEndpoints: async ({ baseUrls }) => {
|
|
599
|
-
const
|
|
601
|
+
const appConnections = [...new Set(baseUrls.filter((u) => u.length > 0))];
|
|
600
602
|
await this.changeAddressBook((state) => ({
|
|
601
603
|
...state,
|
|
602
604
|
flags: {
|
|
603
605
|
...state.flags,
|
|
604
|
-
|
|
606
|
+
appConnections
|
|
605
607
|
}
|
|
606
608
|
}));
|
|
607
|
-
this.ctx.logger.info("local-network:
|
|
608
|
-
count:
|
|
609
|
-
auto:
|
|
609
|
+
this.ctx.logger.info("local-network: app connections set", { meta: {
|
|
610
|
+
count: appConnections.length,
|
|
611
|
+
auto: appConnections.length === 0
|
|
610
612
|
} });
|
|
611
613
|
return this.snapshotViewerEndpoints();
|
|
612
614
|
},
|
|
@@ -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-Cv1lnnQq.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 { Jt as logDestinationCapability, ar as BaseAddon } from "../../dist-
|
|
1
|
+
import { Jt as logDestinationCapability, ar as BaseAddon } from "../../dist-C9Rk6KVv.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
|
|
@@ -151,6 +151,22 @@ export default class NativeMetricsAddon extends BaseAddon<NativeMetricsConfig> {
|
|
|
151
151
|
* is why the retention default is six hours rather than three days.
|
|
152
152
|
*/
|
|
153
153
|
private startProcessSnapshotTimer;
|
|
154
|
+
/**
|
|
155
|
+
* Run one periodic tick so that it can NEVER become an unhandled rejection.
|
|
156
|
+
*
|
|
157
|
+
* `clearInterval` in `onShutdown` stops FUTURE ticks; it cannot recall one
|
|
158
|
+
* already in flight. A process tick awaits a `/proc` read and a
|
|
159
|
+
* `$process.list` broker call, and a graceful stop shuts this addon down
|
|
160
|
+
* (nulling `ctx`) while the agent goes on to stop its other addons and the
|
|
161
|
+
* broker — so the tick resumed with no context, its catch block reached for
|
|
162
|
+
* `this.ctx.logger`, threw again, and the rejection of the timer callback
|
|
163
|
+
* killed `docker stop CamStack-Agent` with exit code 1 (little-unraid,
|
|
164
|
+
* 2026-10-07 04:22Z). The ticks themselves now capture their context once
|
|
165
|
+
* and stand down when it is gone; this wrapper is the backstop.
|
|
166
|
+
*
|
|
167
|
+
* A tick before `initialize()` or after `shutdown()` is a no-op.
|
|
168
|
+
*/
|
|
169
|
+
private runTick;
|
|
154
170
|
protected onShutdown(): Promise<void>;
|
|
155
171
|
/**
|
|
156
172
|
* Resolve this node's short id (strips any `nodeId/addonId` suffix).
|
|
@@ -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-Cv1lnnQq.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");
|
|
@@ -3014,7 +3014,8 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3014
3014
|
}
|
|
3015
3015
|
this.memoryProbe = new ContainerMemoryProbe({ logger: this.ctx.logger.child("ContainerMemory") });
|
|
3016
3016
|
this.pidsProbe = new ContainerPidsProbe({ logger: this.ctx.logger.child("ContainerPids") });
|
|
3017
|
-
this.snapshotTimer
|
|
3017
|
+
if (this.snapshotTimer) clearInterval(this.snapshotTimer);
|
|
3018
|
+
this.snapshotTimer = setInterval(() => this.runTick("resources snapshot", () => this.emitResourcesSnapshot()), METRICS_SNAPSHOT_INTERVAL_MS);
|
|
3018
3019
|
this.startProcessSnapshotTimer();
|
|
3019
3020
|
this.ctx.addDisposer(this.ctx.eventBus.subscribe({ category: require_dist.EventCategory.MetricsNodeProcessesSnapshot }, (event) => this.retainSnapshot(event.data.nodeId, event.data.timestamp, event.data.processes, event.data.containerMemory)));
|
|
3020
3021
|
return [{
|
|
@@ -3179,7 +3180,33 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3179
3180
|
*/
|
|
3180
3181
|
startProcessSnapshotTimer() {
|
|
3181
3182
|
if (this.processSnapshotTimer) clearInterval(this.processSnapshotTimer);
|
|
3182
|
-
this.processSnapshotTimer =
|
|
3183
|
+
this.processSnapshotTimer = null;
|
|
3184
|
+
if (!this.ctxIfReady) return;
|
|
3185
|
+
this.processSnapshotTimer = setInterval(() => this.runTick("process snapshot", () => this.emitProcessesSnapshot()), this.loadConfig.cadenceSec * 1e3);
|
|
3186
|
+
}
|
|
3187
|
+
/**
|
|
3188
|
+
* Run one periodic tick so that it can NEVER become an unhandled rejection.
|
|
3189
|
+
*
|
|
3190
|
+
* `clearInterval` in `onShutdown` stops FUTURE ticks; it cannot recall one
|
|
3191
|
+
* already in flight. A process tick awaits a `/proc` read and a
|
|
3192
|
+
* `$process.list` broker call, and a graceful stop shuts this addon down
|
|
3193
|
+
* (nulling `ctx`) while the agent goes on to stop its other addons and the
|
|
3194
|
+
* broker — so the tick resumed with no context, its catch block reached for
|
|
3195
|
+
* `this.ctx.logger`, threw again, and the rejection of the timer callback
|
|
3196
|
+
* killed `docker stop CamStack-Agent` with exit code 1 (little-unraid,
|
|
3197
|
+
* 2026-10-07 04:22Z). The ticks themselves now capture their context once
|
|
3198
|
+
* and stand down when it is gone; this wrapper is the backstop.
|
|
3199
|
+
*
|
|
3200
|
+
* A tick before `initialize()` or after `shutdown()` is a no-op.
|
|
3201
|
+
*/
|
|
3202
|
+
runTick(name, tick) {
|
|
3203
|
+
if (!this.ctxIfReady) return;
|
|
3204
|
+
tick().catch((err) => {
|
|
3205
|
+
this.ctxIfReady?.logger.warn("native-metrics tick failed — this interval will be missing", { meta: {
|
|
3206
|
+
tick: name,
|
|
3207
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3208
|
+
} });
|
|
3209
|
+
});
|
|
3183
3210
|
}
|
|
3184
3211
|
async onShutdown() {
|
|
3185
3212
|
if (this.snapshotTimer) {
|
|
@@ -3197,9 +3224,9 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3197
3224
|
* Resolve this node's short id (strips any `nodeId/addonId` suffix).
|
|
3198
3225
|
* Returns null when the provider or event bus isn't available yet.
|
|
3199
3226
|
*/
|
|
3200
|
-
resolveSnapshotNodeId() {
|
|
3201
|
-
if (!this.provider || !
|
|
3202
|
-
const rawNodeId =
|
|
3227
|
+
resolveSnapshotNodeId(ctx) {
|
|
3228
|
+
if (!this.provider || !ctx.eventBus) return null;
|
|
3229
|
+
const rawNodeId = ctx.kernel.localNodeId ?? ctx.id;
|
|
3203
3230
|
return rawNodeId.includes("/") ? rawNodeId.split("/")[0] : rawNodeId;
|
|
3204
3231
|
}
|
|
3205
3232
|
/**
|
|
@@ -3208,13 +3235,16 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3208
3235
|
* runs at samplingIntervalMs). Fires on METRICS_SNAPSHOT_INTERVAL_MS.
|
|
3209
3236
|
*/
|
|
3210
3237
|
async emitResourcesSnapshot() {
|
|
3238
|
+
const ctx = this.ctxIfReady;
|
|
3239
|
+
if (!ctx) return;
|
|
3211
3240
|
const provider = this.provider;
|
|
3212
|
-
const eventBus =
|
|
3213
|
-
const nodeId = this.resolveSnapshotNodeId();
|
|
3241
|
+
const eventBus = ctx.eventBus;
|
|
3242
|
+
const nodeId = this.resolveSnapshotNodeId(ctx);
|
|
3214
3243
|
if (!provider || !eventBus || !nodeId) return;
|
|
3215
3244
|
const timestamp = Date.now();
|
|
3216
3245
|
try {
|
|
3217
3246
|
const snapshot = await provider.getCached() ?? await provider.collectSnapshot();
|
|
3247
|
+
if (this.ctxIfReady !== ctx) return;
|
|
3218
3248
|
const coarse = coarsenResourcesSnapshot(snapshot);
|
|
3219
3249
|
const prev = this.lastResourcesEmit;
|
|
3220
3250
|
const heartbeatDue = !prev || timestamp - prev.emittedAt >= METRICS_SNAPSHOT_HEARTBEAT_MS;
|
|
@@ -3247,12 +3277,18 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3247
3277
|
* A failed scan emits nothing, which is the same signal: nobody reported.
|
|
3248
3278
|
*/
|
|
3249
3279
|
async emitProcessesSnapshot() {
|
|
3250
|
-
const
|
|
3251
|
-
|
|
3280
|
+
const ctx = this.ctxIfReady;
|
|
3281
|
+
if (!ctx) return;
|
|
3282
|
+
const eventBus = ctx.eventBus;
|
|
3283
|
+
const nodeId = this.resolveSnapshotNodeId(ctx);
|
|
3252
3284
|
if (!this.provider || !eventBus || !nodeId) return;
|
|
3253
3285
|
const timestamp = Date.now();
|
|
3254
3286
|
try {
|
|
3255
3287
|
const processes = await this.listNodeProcesses();
|
|
3288
|
+
if (this.ctxIfReady !== ctx) {
|
|
3289
|
+
ctx.logger.debug("process snapshot dropped — addon shut down mid-tick", { meta: { nodeId } });
|
|
3290
|
+
return;
|
|
3291
|
+
}
|
|
3256
3292
|
const containerMemory = this.memoryProbe?.sample() ?? null;
|
|
3257
3293
|
this.pidsProbe?.sample();
|
|
3258
3294
|
eventBus.emit(require_dist.createEvent(require_dist.EventCategory.MetricsNodeProcessesSnapshot, {
|
|
@@ -3266,20 +3302,21 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3266
3302
|
containerMemory
|
|
3267
3303
|
}));
|
|
3268
3304
|
} catch (err) {
|
|
3269
|
-
|
|
3305
|
+
ctx.logger.warn("process snapshot skipped — this interval will be missing", { meta: {
|
|
3270
3306
|
nodeId,
|
|
3271
3307
|
error: err instanceof Error ? err.message : String(err)
|
|
3272
3308
|
} });
|
|
3273
3309
|
}
|
|
3274
3310
|
}
|
|
3275
3311
|
async onConfigChanged() {
|
|
3312
|
+
if (!this.ctxIfReady) return;
|
|
3276
3313
|
this.provider?.stopSampling();
|
|
3277
3314
|
this.provider?.startSampling(this.config.samplingIntervalMs);
|
|
3278
3315
|
this.applyLoadSeriesConfig();
|
|
3279
3316
|
this.startProcessSnapshotTimer();
|
|
3280
3317
|
}
|
|
3281
3318
|
async listWorkerInstances() {
|
|
3282
|
-
const broker = this.
|
|
3319
|
+
const broker = this.ctxIfReady?.kernel.cluster?.broker;
|
|
3283
3320
|
if (!broker) return [];
|
|
3284
3321
|
try {
|
|
3285
3322
|
return await broker.call("$process.list");
|
|
@@ -3326,6 +3363,7 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3326
3363
|
* walk behind them — live in `process-classification.ts`.
|
|
3327
3364
|
*/
|
|
3328
3365
|
async listNodeProcesses() {
|
|
3366
|
+
const selfNodeId = this.ctx.kernel.cluster?.broker?.nodeID ?? "hub";
|
|
3329
3367
|
const ps = await this.runPs();
|
|
3330
3368
|
if (ps.length === 0) return [];
|
|
3331
3369
|
const managed = /* @__PURE__ */ new Map();
|
|
@@ -3339,7 +3377,7 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3339
3377
|
return buildNodeProcesses({
|
|
3340
3378
|
psRows: ps,
|
|
3341
3379
|
selfPid: process.pid,
|
|
3342
|
-
selfNodeId
|
|
3380
|
+
selfNodeId,
|
|
3343
3381
|
managed,
|
|
3344
3382
|
cpuSplits
|
|
3345
3383
|
});
|
|
@@ -3355,7 +3393,7 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3355
3393
|
try {
|
|
3356
3394
|
return await readProcessTable();
|
|
3357
3395
|
} catch (err) {
|
|
3358
|
-
this.
|
|
3396
|
+
this.ctxIfReady?.logger.warn("process table read failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
3359
3397
|
return [];
|
|
3360
3398
|
}
|
|
3361
3399
|
}
|