@camstack/system 1.2.358 → 1.2.360
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 +69 -0
- package/dist/builtins/local-network/local-network.addon.d.ts +43 -47
- package/dist/builtins/local-network/local-network.addon.js +366 -81
- package/dist/builtins/local-network/local-network.addon.mjs +367 -80
- 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-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- 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-C2dFiu87.mjs → child-cap-dispatch-StBHObCA.mjs} +1 -1
- package/dist/{child-cap-dispatch-BWk99kmA.js → child-cap-dispatch-T3Kdhbb3.js} +1 -1
- package/dist/{composition-sources-D8Hapyav.mjs → composition-sources-DASc7NBj.mjs} +1 -1
- package/dist/{composition-sources-BaHPH661.js → composition-sources-DFgayxCc.js} +1 -1
- package/dist/{dist-D3bhbMY3.mjs → dist-C9Rk6KVv.mjs} +381 -115
- package/dist/{dist-DzAHit0a.js → dist-Cv1lnnQq.js} +414 -118
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{retired-settings-keys-CkXoPjKy.js → retired-settings-keys-BGNqRNhu.js} +1 -1
- package/dist/{retired-settings-keys-B6AFmtlO.mjs → retired-settings-keys-BjG8bQVI.mjs} +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,234 @@
|
|
|
1
|
-
import {
|
|
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
|
+
import { z } from "zod";
|
|
3
4
|
import * as os from "node:os";
|
|
4
5
|
import { readFileSync } from "node:fs";
|
|
5
6
|
import { isIP } from "node:net";
|
|
7
|
+
//#region src/builtins/local-network/address-book.ts
|
|
8
|
+
/**
|
|
9
|
+
* The Network page's address book, as pure functions: custom addresses plus
|
|
10
|
+
* the role flags carried by detected and custom addresses alike.
|
|
11
|
+
*
|
|
12
|
+
* ## Storage (D734)
|
|
13
|
+
*
|
|
14
|
+
* One key per role: `appConnectionUrls` (`appConnections`, many),
|
|
15
|
+
* `notificationTapUrl` (`notificationTaps`), `publicEndpointUrl`
|
|
16
|
+
* (`publicEndpoint`) and `internalUrl` (`internal`). A single-valued role is
|
|
17
|
+
* one URL — at most one address carries it, by construction. `''` / `[]` is
|
|
18
|
+
* automatic. The custom addresses are `customAddresses` (url + label).
|
|
19
|
+
*
|
|
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).
|
|
24
|
+
*
|
|
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.
|
|
32
|
+
*/
|
|
33
|
+
var CustomAddressListSchema = z.array(z.object({
|
|
34
|
+
baseUrl: z.string(),
|
|
35
|
+
label: z.string()
|
|
36
|
+
}));
|
|
37
|
+
/** The label a flag promoted to a custom address is given. */
|
|
38
|
+
var PROMOTED_LABEL = {
|
|
39
|
+
notificationTaps: "Notification taps address",
|
|
40
|
+
internal: "Internal address",
|
|
41
|
+
publicEndpoint: "Public endpoint"
|
|
42
|
+
};
|
|
43
|
+
var SINGLE_ROLES = [
|
|
44
|
+
"notificationTaps",
|
|
45
|
+
"internal",
|
|
46
|
+
"publicEndpoint"
|
|
47
|
+
];
|
|
48
|
+
function flagOf(value) {
|
|
49
|
+
return value === "" ? null : value;
|
|
50
|
+
}
|
|
51
|
+
/** Parse the stored custom list; a malformed store reads as empty, never throws. */
|
|
52
|
+
function parseCustomAddresses(raw) {
|
|
53
|
+
const parsed = CustomAddressListSchema.safeParse(raw ?? []);
|
|
54
|
+
return parsed.success ? parsed.data : [];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The address book as it stands: stored custom addresses, plus every
|
|
58
|
+
* single-valued flag that names neither a detected address, a custom one nor
|
|
59
|
+
* the app link, promoted to a custom address (see the module doc).
|
|
60
|
+
*/
|
|
61
|
+
function normalizeAddressBook(stored, detectedUrls) {
|
|
62
|
+
const flags = {
|
|
63
|
+
appConnections: stored.appConnectionUrls,
|
|
64
|
+
notificationTaps: flagOf(stored.notificationTapUrl),
|
|
65
|
+
internal: flagOf(stored.internalUrl),
|
|
66
|
+
publicEndpoint: flagOf(stored.publicEndpointUrl)
|
|
67
|
+
};
|
|
68
|
+
const custom = [...parseCustomAddresses(stored.customAddresses)];
|
|
69
|
+
for (const role of SINGLE_ROLES) {
|
|
70
|
+
const url = flags[role];
|
|
71
|
+
if (url === null || isAppLinkBase(url) || detectedUrls.has(url)) continue;
|
|
72
|
+
if (custom.some((c) => c.baseUrl === url)) continue;
|
|
73
|
+
custom.push({
|
|
74
|
+
baseUrl: url,
|
|
75
|
+
label: PROMOTED_LABEL[role]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
custom,
|
|
80
|
+
flags
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** The stored-settings patch that persists `state`. */
|
|
84
|
+
function addressBookPatch(state) {
|
|
85
|
+
return {
|
|
86
|
+
customAddresses: state.custom,
|
|
87
|
+
appConnectionUrls: state.flags.appConnections,
|
|
88
|
+
notificationTapUrl: state.flags.notificationTaps ?? "",
|
|
89
|
+
publicEndpointUrl: state.flags.publicEndpoint ?? "",
|
|
90
|
+
internalUrl: state.flags.internal ?? ""
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function withCustomAddress(state, entry) {
|
|
94
|
+
const custom = state.custom.some((c) => c.baseUrl === entry.baseUrl) ? state.custom.map((c) => c.baseUrl === entry.baseUrl ? entry : c) : [...state.custom, entry];
|
|
95
|
+
return {
|
|
96
|
+
...state,
|
|
97
|
+
custom
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** Drop a custom address and every flag it carried (those roles turn automatic). */
|
|
101
|
+
function withoutCustomAddress(state, baseUrl) {
|
|
102
|
+
const clear = (flag) => flag === baseUrl ? null : flag;
|
|
103
|
+
return {
|
|
104
|
+
custom: state.custom.filter((c) => c.baseUrl !== baseUrl),
|
|
105
|
+
flags: {
|
|
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)
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** Every role back to automatic for `role`. */
|
|
114
|
+
function withRoleAutomatic(state, role) {
|
|
115
|
+
return role === "appConnections" ? {
|
|
116
|
+
...state,
|
|
117
|
+
flags: {
|
|
118
|
+
...state.flags,
|
|
119
|
+
appConnections: []
|
|
120
|
+
}
|
|
121
|
+
} : {
|
|
122
|
+
...state,
|
|
123
|
+
flags: {
|
|
124
|
+
...state.flags,
|
|
125
|
+
[role]: null
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Flag or unflag `baseUrl` for `role`. A single-valued role flagged here moves
|
|
131
|
+
* off every other address (it is one URL); unflagging the address that carries
|
|
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.
|
|
136
|
+
*/
|
|
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;
|
|
141
|
+
return {
|
|
142
|
+
...state,
|
|
143
|
+
flags: {
|
|
144
|
+
...state.flags,
|
|
145
|
+
appConnections
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (role === "notificationTaps" && isAppLinkBase(baseUrl)) return withRoleAutomatic(state, role);
|
|
150
|
+
if (enabled) return {
|
|
151
|
+
...state,
|
|
152
|
+
flags: {
|
|
153
|
+
...state.flags,
|
|
154
|
+
[role]: baseUrl
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
return state.flags[role] === baseUrl ? withRoleAutomatic(state, role) : state;
|
|
158
|
+
}
|
|
159
|
+
/** What a row may be flagged with. */
|
|
160
|
+
function eligibleRoles(endpoint) {
|
|
161
|
+
if (endpoint.kind === "loopback") return [];
|
|
162
|
+
if (endpoint.kind === "app-link") return ["notificationTaps"];
|
|
163
|
+
return [
|
|
164
|
+
"appConnections",
|
|
165
|
+
"notificationTaps",
|
|
166
|
+
"internal",
|
|
167
|
+
"publicEndpoint"
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
/** A custom address as a palette entry (`kind: 'custom'`). */
|
|
171
|
+
function customEndpoint(entry) {
|
|
172
|
+
return {
|
|
173
|
+
label: entry.label === "" ? "Custom address" : entry.label,
|
|
174
|
+
baseUrl: entry.baseUrl,
|
|
175
|
+
kind: "custom",
|
|
176
|
+
interfaceKind: "other",
|
|
177
|
+
plausible: true,
|
|
178
|
+
plausibleReason: "",
|
|
179
|
+
priority: 200
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* The palette a client races: the detected endpoints plus the custom
|
|
184
|
+
* addresses flagged `appConnections` (a detected URL is never listed twice).
|
|
185
|
+
*/
|
|
186
|
+
function withAppConnectionCustomEndpoints(detected, state) {
|
|
187
|
+
const known = new Set(detected.map((e) => e.baseUrl));
|
|
188
|
+
const wanted = new Set(state.flags.appConnections);
|
|
189
|
+
const extra = state.custom.filter((c) => wanted.has(c.baseUrl) && !known.has(c.baseUrl)).map(customEndpoint);
|
|
190
|
+
return [...detected, ...extra];
|
|
191
|
+
}
|
|
192
|
+
/** Every role's answer — the resolvers from `@camstack/types`, nothing else. */
|
|
193
|
+
function resolveRoles(state, appPalette, httpsPalette) {
|
|
194
|
+
return resolveHubAddressRoles(withAppConnectionCustomEndpoints(appPalette, state), httpsPalette, state.flags);
|
|
195
|
+
}
|
|
196
|
+
function flagsOn(state, baseUrl) {
|
|
197
|
+
const out = [];
|
|
198
|
+
if (state.flags.appConnections.includes(baseUrl)) out.push("appConnections");
|
|
199
|
+
for (const role of SINGLE_ROLES) if (state.flags[role] === baseUrl) out.push(role);
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
function inUseOn(roles, baseUrl) {
|
|
203
|
+
const out = [];
|
|
204
|
+
if (roles.appConnections.resolved.includes(baseUrl)) out.push("appConnections");
|
|
205
|
+
for (const role of SINGLE_ROLES) if (roles[role].resolved === baseUrl) out.push(role);
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
/** The Addresses table: app link, detected rows, then custom-only rows. */
|
|
209
|
+
function buildAddressBook(state, inputs) {
|
|
210
|
+
const roles = resolveRoles(state, inputs.rowsPalette, inputs.httpsPalette);
|
|
211
|
+
const customUrls = new Set(state.custom.map((c) => c.baseUrl));
|
|
212
|
+
const detectedUrls = new Set(inputs.rowsPalette.map((e) => e.baseUrl));
|
|
213
|
+
const row = (endpoint, detected) => ({
|
|
214
|
+
...endpoint,
|
|
215
|
+
detected,
|
|
216
|
+
custom: customUrls.has(endpoint.baseUrl),
|
|
217
|
+
flags: endpoint.kind === "app-link" ? [] : flagsOn(state, endpoint.baseUrl),
|
|
218
|
+
inUse: endpoint.kind === "app-link" ? roles.notificationTaps.resolved === "https://camstack.zentik.app" ? ["notificationTaps"] : [] : inUseOn(roles, endpoint.baseUrl),
|
|
219
|
+
eligible: eligibleRoles(endpoint)
|
|
220
|
+
});
|
|
221
|
+
return {
|
|
222
|
+
rows: [
|
|
223
|
+
row(inputs.appLink, false),
|
|
224
|
+
...inputs.rowsPalette.map((e) => row(e, true)),
|
|
225
|
+
...state.custom.filter((c) => !detectedUrls.has(c.baseUrl)).map((c) => row(customEndpoint(c), false))
|
|
226
|
+
],
|
|
227
|
+
roles,
|
|
228
|
+
appLinkHub: inputs.appLinkHub
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
6
232
|
//#region src/builtins/local-network/ipv6-lan.ts
|
|
7
233
|
/**
|
|
8
234
|
* IPv6 helpers for LAN advertisement: prefix matching (privacy addresses
|
|
@@ -194,72 +420,137 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
194
420
|
super({
|
|
195
421
|
allowedAddresses: [],
|
|
196
422
|
bootSeeded: false,
|
|
197
|
-
|
|
198
|
-
|
|
423
|
+
notificationTapUrl: "",
|
|
424
|
+
appConnectionUrls: [],
|
|
425
|
+
customAddresses: [],
|
|
199
426
|
httpEnabled: true,
|
|
200
427
|
httpPort: DEFAULT_LAN_HTTP_PORT,
|
|
201
428
|
localHostname: "",
|
|
202
|
-
|
|
203
|
-
|
|
429
|
+
internalUrl: "",
|
|
430
|
+
publicEndpointUrl: ""
|
|
204
431
|
});
|
|
205
432
|
}
|
|
206
433
|
/**
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
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).
|
|
210
437
|
*/
|
|
211
|
-
async
|
|
438
|
+
async palettes() {
|
|
212
439
|
const port = (await this.resolveAdvertisedListenPort(null)).port;
|
|
213
|
-
|
|
440
|
+
await this.reconcileMeshEndpoint();
|
|
214
441
|
return {
|
|
215
|
-
|
|
216
|
-
|
|
442
|
+
http: this.endpointsNow(false, false, "http", port),
|
|
443
|
+
https: this.endpointsNow(false, false, "https", port)
|
|
217
444
|
};
|
|
218
445
|
}
|
|
446
|
+
/** The stored address book, normalised against what is detected now. */
|
|
447
|
+
addressState(detected) {
|
|
448
|
+
return normalizeAddressBook(this.config, new Set(detected.map((e) => e.baseUrl)));
|
|
449
|
+
}
|
|
450
|
+
/** The role flags alone — they do not depend on what is detected. */
|
|
451
|
+
storedFlags() {
|
|
452
|
+
return normalizeAddressBook(this.config, /* @__PURE__ */ new Set()).flags;
|
|
453
|
+
}
|
|
454
|
+
/** `getAddresses`: the Addresses table and every role's answer. */
|
|
455
|
+
async addressBook(scheme) {
|
|
456
|
+
const { http, https } = await this.palettes();
|
|
457
|
+
return buildAddressBook(this.addressState([...http, ...https]), {
|
|
458
|
+
rowsPalette: scheme === "http" ? http : https,
|
|
459
|
+
httpsPalette: https,
|
|
460
|
+
appLink: appLinkEndpoint(),
|
|
461
|
+
appLinkHub: pickAppLinkHub(https)
|
|
462
|
+
});
|
|
463
|
+
}
|
|
219
464
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* 2026-10-06; before it AUTO was the tunnel, then the mesh). The app link is
|
|
223
|
-
* not a hub address: the NC's signed media and buttons keep their own hub
|
|
224
|
-
* ladder when it is flagged; only viewer taps take the app form.
|
|
465
|
+
* Apply one change to the address book and persist it, normalised (a flag
|
|
466
|
+
* naming an address in no row becomes a custom address in the same write).
|
|
225
467
|
*/
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
468
|
+
async changeAddressBook(change) {
|
|
469
|
+
const { http, https } = await this.palettes();
|
|
470
|
+
const detected = [...http, ...https];
|
|
471
|
+
const detectedUrls = new Set(detected.map((e) => e.baseUrl));
|
|
472
|
+
const changed = change(this.addressState(detected), {
|
|
473
|
+
detectedUrls,
|
|
474
|
+
autoApp: [...new Set(detected.filter(isDefaultAppConnectionEndpoint).map((e) => e.baseUrl))]
|
|
475
|
+
});
|
|
476
|
+
const after = normalizeAddressBook({
|
|
477
|
+
...addressBookPatch(changed),
|
|
478
|
+
customAddresses: changed.custom
|
|
479
|
+
}, detectedUrls);
|
|
480
|
+
await this.updateGlobalSettings(addressBookPatch(after));
|
|
481
|
+
}
|
|
482
|
+
/** The deprecated `getHubAddresses` shape, from the role resolvers. */
|
|
483
|
+
async snapshotHubAddresses() {
|
|
484
|
+
const { roles } = await this.addressBook("https");
|
|
485
|
+
const legacy = (r) => ({
|
|
486
|
+
override: r.flagged,
|
|
487
|
+
resolved: r.resolved,
|
|
488
|
+
source: r.source === "flagged" ? "override" : r.source
|
|
489
|
+
});
|
|
490
|
+
return {
|
|
491
|
+
cluster: legacy(roles.internal),
|
|
492
|
+
browser: legacy(roles.publicEndpoint)
|
|
493
|
+
};
|
|
229
494
|
}
|
|
230
495
|
/**
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
496
|
+
* Flag / unflag `baseUrl` for `role`, or (`baseUrl: null`) return the role
|
|
497
|
+
* to automatic. Validated: the address must be a row (detected, custom, or
|
|
498
|
+
* the app link for `notificationTaps`), never loopback, and
|
|
499
|
+
* `publicEndpoint` on plain http only when accepted. A refusal stores nothing.
|
|
234
500
|
*/
|
|
235
|
-
async
|
|
236
|
-
|
|
237
|
-
|
|
501
|
+
async setRole(role, baseUrl, enabled, acceptInsecureHttp) {
|
|
502
|
+
if (baseUrl === null || baseUrl.trim() === "" && role !== "appConnections") {
|
|
503
|
+
await this.changeAddressBook((state) => withRoleAutomatic(state, role));
|
|
504
|
+
this.ctx.logger.info("local-network: address role set to automatic", { meta: { role } });
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
let target = baseUrl.trim();
|
|
508
|
+
if (enabled) {
|
|
509
|
+
const check = checkRoleAddress(role, target, { acceptInsecureHttp });
|
|
510
|
+
if (!check.ok) throw new Error(check.reason);
|
|
511
|
+
target = check.baseUrl;
|
|
512
|
+
}
|
|
513
|
+
await this.changeAddressBook((state, ctx) => {
|
|
514
|
+
const known = isAppLinkBase(target) || ctx.detectedUrls.has(target) || state.custom.some((c) => c.baseUrl === target);
|
|
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);
|
|
517
|
+
});
|
|
518
|
+
this.ctx.logger.info("local-network: address role set", { meta: {
|
|
519
|
+
role,
|
|
520
|
+
baseUrl: target,
|
|
521
|
+
enabled
|
|
522
|
+
} });
|
|
238
523
|
}
|
|
239
524
|
async notificationEndpointSnapshot() {
|
|
240
|
-
const
|
|
525
|
+
const resolution = resolveNotificationTapAddress(this.storedFlags());
|
|
526
|
+
const port = (await this.resolveAdvertisedListenPort(null)).port;
|
|
241
527
|
return {
|
|
242
|
-
baseUrl:
|
|
243
|
-
resolved:
|
|
244
|
-
appLinkHub: await this.
|
|
528
|
+
baseUrl: resolution.flagged,
|
|
529
|
+
resolved: resolution.resolved,
|
|
530
|
+
appLinkHub: pickAppLinkHub(await this.buildAdvertisedEndpoints(false, false, "https", port))
|
|
245
531
|
};
|
|
246
532
|
}
|
|
247
533
|
async snapshotViewerEndpoints() {
|
|
248
|
-
const
|
|
534
|
+
const resolution = resolveAppConnections(await this.buildAdvertisedEndpoints(false, false, "http", (await this.resolveAdvertisedListenPort(null)).port), this.storedFlags());
|
|
249
535
|
return {
|
|
250
|
-
baseUrls:
|
|
251
|
-
resolved:
|
|
536
|
+
baseUrls: resolution.flagged,
|
|
537
|
+
resolved: resolution.resolved
|
|
252
538
|
};
|
|
253
539
|
}
|
|
254
540
|
/**
|
|
255
|
-
* Palette shown
|
|
256
|
-
*
|
|
257
|
-
*
|
|
541
|
+
* Palette shown to clients: LAN IPv4, stable LAN IPv6, public, mesh, and
|
|
542
|
+
* the custom addresses flagged `appConnections` (`kind: 'custom'`). The app
|
|
543
|
+
* HTTP/WS race is a subset (`selectAppConnectionEndpoints`) — IPv6 stays in this
|
|
544
|
+
* list because ICE/WebRTC gathers it regardless.
|
|
258
545
|
*/
|
|
259
546
|
async buildAdvertisedEndpoints(includeLoopback, ipv4Only, scheme, httpsPort) {
|
|
260
|
-
const allow = this.config.allowedAddresses;
|
|
261
|
-
const interfaces = applyAllowlist(this.enumerate(), allow);
|
|
262
547
|
await this.reconcileMeshEndpoint();
|
|
548
|
+
const detected = this.endpointsNow(includeLoopback, ipv4Only, scheme, httpsPort);
|
|
549
|
+
return withAppConnectionCustomEndpoints(detected, this.addressState(detected));
|
|
550
|
+
}
|
|
551
|
+
/** The DETECTED palette from the cached interface/tunnel/mesh state. */
|
|
552
|
+
endpointsNow(includeLoopback, ipv4Only, scheme, httpsPort) {
|
|
553
|
+
const interfaces = applyAllowlist(this.enumerate(), this.config.allowedAddresses);
|
|
263
554
|
const httpState = readLanHttpState();
|
|
264
555
|
return buildEndpoints(interfaces, httpsPort, includeLoopback, ipv4Only, this.publicHostname, scheme, this.meshEndpoint, {
|
|
265
556
|
enabled: this.config.httpEnabled !== false,
|
|
@@ -302,35 +593,50 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
302
593
|
},
|
|
303
594
|
getNotificationEndpoint: async () => this.notificationEndpointSnapshot(),
|
|
304
595
|
setNotificationEndpoint: async ({ baseUrl }) => {
|
|
305
|
-
await this.
|
|
306
|
-
this.ctx.logger.info("local-network: notification endpoint set", { meta: { baseUrl: baseUrl ?? "(auto)" } });
|
|
596
|
+
await this.setRole("notificationTaps", baseUrl, true, false);
|
|
307
597
|
return this.notificationEndpointSnapshot();
|
|
308
598
|
},
|
|
309
|
-
getHubAddresses: async () => this.snapshotHubAddresses(),
|
|
310
|
-
setHubAddress: async ({ audience, baseUrl }) => {
|
|
311
|
-
let stored = "";
|
|
312
|
-
if (baseUrl !== null && baseUrl.trim() !== "") {
|
|
313
|
-
const check = validateHubAddressOverride(baseUrl);
|
|
314
|
-
if (!check.ok) throw new Error(check.reason);
|
|
315
|
-
stored = check.baseUrl;
|
|
316
|
-
}
|
|
317
|
-
await this.updateGlobalSettings(audience === "cluster" ? { agentBaseUrl: stored } : { signInBaseUrl: stored });
|
|
318
|
-
this.ctx.logger.info("local-network: hub address override set", { meta: {
|
|
319
|
-
audience,
|
|
320
|
-
baseUrl: stored === "" ? "(auto)" : stored
|
|
321
|
-
} });
|
|
322
|
-
return this.snapshotHubAddresses();
|
|
323
|
-
},
|
|
324
599
|
getViewerEndpoints: async () => this.snapshotViewerEndpoints(),
|
|
325
600
|
setViewerEndpoints: async ({ baseUrls }) => {
|
|
326
|
-
const
|
|
327
|
-
await this.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
601
|
+
const appConnections = [...new Set(baseUrls.filter((u) => u.length > 0))];
|
|
602
|
+
await this.changeAddressBook((state) => ({
|
|
603
|
+
...state,
|
|
604
|
+
flags: {
|
|
605
|
+
...state.flags,
|
|
606
|
+
appConnections
|
|
607
|
+
}
|
|
608
|
+
}));
|
|
609
|
+
this.ctx.logger.info("local-network: app connections set", { meta: {
|
|
610
|
+
count: appConnections.length,
|
|
611
|
+
auto: appConnections.length === 0
|
|
331
612
|
} });
|
|
332
613
|
return this.snapshotViewerEndpoints();
|
|
333
614
|
},
|
|
615
|
+
getAddresses: async (input) => this.addressBook(input?.scheme ?? "https"),
|
|
616
|
+
addCustomAddress: async ({ baseUrl, label }) => {
|
|
617
|
+
const check = validateCustomAddress(baseUrl);
|
|
618
|
+
if (!check.ok) throw new Error(check.reason);
|
|
619
|
+
const entry = {
|
|
620
|
+
baseUrl: check.baseUrl,
|
|
621
|
+
label: label.trim()
|
|
622
|
+
};
|
|
623
|
+
await this.changeAddressBook((state) => withCustomAddress(state, entry));
|
|
624
|
+
this.ctx.logger.info("local-network: custom address added", { meta: {
|
|
625
|
+
baseUrl: entry.baseUrl,
|
|
626
|
+
label: entry.label
|
|
627
|
+
} });
|
|
628
|
+
return this.addressBook("https");
|
|
629
|
+
},
|
|
630
|
+
removeCustomAddress: async ({ baseUrl }) => {
|
|
631
|
+
await this.changeAddressBook((state) => withoutCustomAddress(state, baseUrl));
|
|
632
|
+
this.ctx.logger.info("local-network: custom address removed", { meta: { baseUrl } });
|
|
633
|
+
return this.addressBook("https");
|
|
634
|
+
},
|
|
635
|
+
setAddressRole: async ({ role, baseUrl, enabled, acceptInsecureHttp }) => {
|
|
636
|
+
await this.setRole(role, baseUrl, enabled, acceptInsecureHttp === true);
|
|
637
|
+
return this.addressBook("https");
|
|
638
|
+
},
|
|
639
|
+
getHubAddresses: async () => this.snapshotHubAddresses(),
|
|
334
640
|
getAllowedAddresses: async () => ({ addresses: this.config.allowedAddresses }),
|
|
335
641
|
resetAllowlistToBestMatch: async () => {
|
|
336
642
|
const seed = autoSeedAllowlist(this.enumerate());
|
|
@@ -872,25 +1178,6 @@ function buildEndpoints(interfaces, port, includeLoopback, ipv4Only, publicHostn
|
|
|
872
1178
|
});
|
|
873
1179
|
return out.toSorted((a, b) => a.priority - b.priority);
|
|
874
1180
|
}
|
|
875
|
-
/** AUTO viewer race: LAN IPv4 + the public tunnel. Never IPv6, mesh, or loopback. */
|
|
876
|
-
function isDefaultViewerEndpoint(endpoint) {
|
|
877
|
-
if (endpoint.kind === "lan-ipv4") return true;
|
|
878
|
-
return endpoint.kind === "public" && endpoint.interfaceKind === "public";
|
|
879
|
-
}
|
|
880
|
-
/**
|
|
881
|
-
* The URLs the SDK should race for API / viewer access.
|
|
882
|
-
*
|
|
883
|
-
* Empty `viewerBaseUrls` = AUTO (IPv4 + public tunnel). A non-empty list is
|
|
884
|
-
* the operator's pin; if none of those URLs are in the current palette,
|
|
885
|
-
* fall back to AUTO rather than racing an empty set.
|
|
886
|
-
*/
|
|
887
|
-
function selectViewerEndpoints(endpoints, viewerBaseUrls) {
|
|
888
|
-
const auto = endpoints.filter(isDefaultViewerEndpoint);
|
|
889
|
-
if (viewerBaseUrls.length === 0) return auto;
|
|
890
|
-
const wanted = new Set(viewerBaseUrls);
|
|
891
|
-
const explicit = endpoints.filter((e) => wanted.has(e.baseUrl));
|
|
892
|
-
return explicit.length > 0 ? explicit : auto;
|
|
893
|
-
}
|
|
894
1181
|
/**
|
|
895
1182
|
* First-boot heuristic: which addresses should the allowlist start
|
|
896
1183
|
* with? Includes LAN + Wi-Fi IPv4 addresses + plausible IPv6:
|
|
@@ -1019,4 +1306,4 @@ function readLinuxIpv6Flags() {
|
|
|
1019
1306
|
}
|
|
1020
1307
|
}
|
|
1021
1308
|
//#endregion
|
|
1022
|
-
export { ALLOWLIST_RECONCILE_INTERVAL_MS, LocalNetworkAddon, appLinkEndpoint, applyAllowlist, autoSeedAllowlist, buildEndpoints, classifyKind, enumerateOsInterfaces, explainNonPlausible, ipv6Prefix64Key,
|
|
1309
|
+
export { ALLOWLIST_RECONCILE_INTERVAL_MS, LocalNetworkAddon, appLinkEndpoint, applyAllowlist, autoSeedAllowlist, buildEndpoints, classifyKind, enumerateOsInterfaces, explainNonPlausible, ipv6Prefix64Key, isPlausibleAutoSeed, parseProcIfInet6, pickPreferred, prefixLen, reconcileAllowlist, resolveAdvertisedPort, 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-
|
|
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 {
|
|
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
|
|
@@ -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");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ar as EventCategory, Mt as foldSnapshotByFunction, ar as BaseAddon, en as metricsProviderCapability, gn as resolveBucketMs, hr as createEvent, ir as errMsg, pn as reducePoints } from "../../dist-C9Rk6KVv.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-Cv1lnnQq.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 { Ar as EventCategory, Ct as enumerateInferenceDevices, En as scoreRuntimes, _r as emitReadiness, ar as BaseAddon, ir as errMsg, un as platformProbeCapability } from "../../dist-C9Rk6KVv.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-Cv1lnnQq.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 { Ar as EventCategory, ar as BaseAddon } from "../../dist-C9Rk6KVv.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-Cv1lnnQq.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 { $t as memoizePinnedFfmpeg, Ar as EventCategory, Fn as streamQualityLabel, Sr as nodePin, X as batteryCapability, Y as bareAddonId, _t as deriveBatteryPresence, a as BatteryStatusSchema, ar as BaseAddon, ir as errMsg, kn as snapshotCapability, rr as DeviceType, tr as DeviceFeature } from "../../dist-C9Rk6KVv.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-Cv1lnnQq.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 { Nn as storageProviderCapability, ar as BaseAddon, jt as filesystemBrowseCapability } from "../../dist-C9Rk6KVv.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
|
|
@@ -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_hub_cap_forward = require("../../hub-cap-forward-DmHNjbB0.js");
|
|
8
8
|
let zod = require("zod");
|
|
9
9
|
let node_crypto = require("node:crypto");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { An as storageCapability, Ar as EventCategory, B as StorageLocationTypeSchema, Cr as parseJsonObject, Dn as settingsStoreCapability, Dt as evictionPolicyOfLocation, E as LegacyStorageLocationDefaultSchema, Hn as withLocationMode, M as REDACTED_SECRET, Pt as gbToBytes, Qt as mayWriteToLocation, R as StorageCleanupJobSchema, V as StorageMigrationJobSchema, Wt as legacyModeOf, Zt as mayReadLocation, ar as BaseAddon, bn as resolveLocationMode, jn as storageMigrationCapability, nt as collectSecretConfigKeys, tn as modeMayWrite, z as StorageLocationModeSchema, zt as isLocationEnabled } from "../../dist-C9Rk6KVv.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";
|