@camstack/system 1.2.116 → 1.2.118
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-utils.d.ts +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/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- 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 +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
- 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/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- 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/ipv6-lan.d.ts +26 -0
- package/dist/builtins/local-network/local-network.addon.d.ts +50 -14
- package/dist/builtins/local-network/local-network.addon.js +254 -38
- package/dist/builtins/local-network/local-network.addon.mjs +248 -39
- 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 +2 -2
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
- 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/{dist-Cri60Jd0.js → dist-C2_1HCpW.js} +149 -23
- package/dist/{dist-BpzbLPel.mjs → dist-DCTpBXm4.mjs} +149 -23
- package/dist/download/model-downloader.d.ts +18 -1
- package/dist/{file-data-plane-CuE_hBli.mjs → file-data-plane-BhKdxJgf.mjs} +49 -13
- package/dist/{file-data-plane-DUHPHa-Y.js → file-data-plane-DO8KbxCe.js} +49 -13
- package/dist/http/lan-http-bind.d.ts +12 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -3
- package/dist/index.mjs +4 -4
- package/dist/{lan-http-bind-CZ4EBeD_.js → lan-http-bind-DmgpFP6_.js} +53 -11
- package/dist/{lan-http-bind-BDJm4d-K.mjs → lan-http-bind-jKrj6OjQ.mjs} +48 -12
- package/dist/{retired-settings-keys-SrxMH3OT.mjs → retired-settings-keys-Czd76yLZ.mjs} +1 -1
- package/dist/{retired-settings-keys-BVAxT5XL.js → retired-settings-keys-DHRXLMPn.js} +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Gt as EventCategory, Y as localNetworkCapability, yt as BaseAddon } from "../../dist-
|
|
2
|
-
import {
|
|
1
|
+
import { Gt as EventCategory, Y as localNetworkCapability, yt as BaseAddon } from "../../dist-DCTpBXm4.mjs";
|
|
2
|
+
import { c as readExtraSans, d as writeExtraSans, f as writeTlsMode, g as reissueTlsLeaf, l as readTlsAccessStatus, m as ensureTlsCert, o as readLanHttpState, p as validateUploadedTls, r as applyLanHttp, t as DEFAULT_LAN_HTTP_PORT, u as readTlsMode } from "../../lan-http-bind-jKrj6OjQ.mjs";
|
|
3
3
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import * as os from "node:os";
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
6
7
|
import { isIP } from "node:net";
|
|
7
8
|
//#region src/builtins/local-network/local-hostname.ts
|
|
8
9
|
var DNS_NAME = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])$/;
|
|
@@ -38,6 +39,111 @@ function parseLocalHostname(raw) {
|
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
//#endregion
|
|
42
|
+
//#region src/builtins/local-network/ipv6-lan.ts
|
|
43
|
+
/**
|
|
44
|
+
* IPv6 helpers for LAN advertisement: prefix matching (privacy addresses
|
|
45
|
+
* rotate inside a /64), Linux `/proc/net/if_inet6` flags, and the gate
|
|
46
|
+
* that keeps temporary GUA off the connection candidate list.
|
|
47
|
+
*/
|
|
48
|
+
var IFA_F_TEMPORARY = 1;
|
|
49
|
+
var IFA_F_DEPRECATED = 32;
|
|
50
|
+
var IFA_F_TENTATIVE = 64;
|
|
51
|
+
/** First 64 bits, canonical hex groups, or null if the address is not IPv6. */
|
|
52
|
+
function ipv6Prefix64Key(address) {
|
|
53
|
+
const groups = parseIpv6Groups(address);
|
|
54
|
+
if (groups === null) return null;
|
|
55
|
+
return groups.slice(0, 4).map((g) => g.toString(16).padStart(4, "0")).join(":");
|
|
56
|
+
}
|
|
57
|
+
/** 32-char lowercase hex (same encoding as `/proc/net/if_inet6`). */
|
|
58
|
+
function ipv6ToHex32(address) {
|
|
59
|
+
const groups = parseIpv6Groups(address);
|
|
60
|
+
if (groups === null) return null;
|
|
61
|
+
return groups.map((g) => g.toString(16).padStart(4, "0")).join("");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parse Linux `if_inet6` into hex32 → flags. Unknown / malformed lines
|
|
65
|
+
* are skipped. Keys match {@link ipv6ToHex32}.
|
|
66
|
+
*/
|
|
67
|
+
function parseProcIfInet6(text) {
|
|
68
|
+
const out = /* @__PURE__ */ new Map();
|
|
69
|
+
for (const line of text.split("\n")) {
|
|
70
|
+
const trimmed = line.trim();
|
|
71
|
+
if (trimmed === "") continue;
|
|
72
|
+
const parts = trimmed.split(/\s+/);
|
|
73
|
+
const hex = parts[0]?.toLowerCase();
|
|
74
|
+
const flagsRaw = parts[4];
|
|
75
|
+
if (!hex || hex.length !== 32 || flagsRaw === void 0) continue;
|
|
76
|
+
const flags = Number.parseInt(flagsRaw, 16);
|
|
77
|
+
if (!Number.isFinite(flags)) continue;
|
|
78
|
+
out.set(hex, flags);
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Whether this IPv6 should be auto-seeded / advertised.
|
|
84
|
+
* `flags === undefined` means the platform did not tell us (macOS, tests
|
|
85
|
+
* without a proc snapshot) — keep every non-link-local plausible address.
|
|
86
|
+
*
|
|
87
|
+
* On Linux, drop addresses marked TEMPORARY / DEPRECATED / TENTATIVE, and
|
|
88
|
+
* `flags === 0` which is how this kernel reports RFC 4941 privacy temps
|
|
89
|
+
* (the stable GUA is PERMANENT|NODAD). Do **not** require PERMANENT —
|
|
90
|
+
* stateful DHCPv6 GUA often has only NODAD (0x02).
|
|
91
|
+
*/
|
|
92
|
+
function shouldAdvertiseIpv6(address, flags) {
|
|
93
|
+
const a = address.toLowerCase().split("%")[0] ?? address;
|
|
94
|
+
if (a === "::" || a === "::1") return false;
|
|
95
|
+
if (a.startsWith("fe80:")) return false;
|
|
96
|
+
if (a.startsWith("ff")) return false;
|
|
97
|
+
if (flags === void 0) return true;
|
|
98
|
+
if ((flags & IFA_F_TEMPORARY) !== 0) return false;
|
|
99
|
+
if ((flags & IFA_F_DEPRECATED) !== 0) return false;
|
|
100
|
+
if ((flags & IFA_F_TENTATIVE) !== 0) return false;
|
|
101
|
+
if (flags === 0) return false;
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
function ipv6AllowlistMatches(address, allowed) {
|
|
105
|
+
if (allowed.includes(address)) return true;
|
|
106
|
+
const key = ipv6Prefix64Key(address);
|
|
107
|
+
if (key === null) return false;
|
|
108
|
+
for (const entry of allowed) {
|
|
109
|
+
if (!entry.includes(":")) continue;
|
|
110
|
+
if (ipv6Prefix64Key(entry) === key) return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
function parseIpv6Groups(address) {
|
|
115
|
+
const raw = address.toLowerCase().split("%")[0];
|
|
116
|
+
if (!raw || raw.includes(".")) return null;
|
|
117
|
+
const sides = raw.split("::");
|
|
118
|
+
if (sides.length > 2) return null;
|
|
119
|
+
const parsePart = (part) => {
|
|
120
|
+
if (part === void 0 || part === "") return [];
|
|
121
|
+
const groups = part.split(":");
|
|
122
|
+
const out = [];
|
|
123
|
+
for (const g of groups) {
|
|
124
|
+
if (g === "") return null;
|
|
125
|
+
if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
|
|
126
|
+
out.push(Number.parseInt(g, 16));
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
};
|
|
130
|
+
if (sides.length === 1) {
|
|
131
|
+
const groups = parsePart(sides[0]);
|
|
132
|
+
if (groups === null || groups.length !== 8) return null;
|
|
133
|
+
return groups;
|
|
134
|
+
}
|
|
135
|
+
const left = parsePart(sides[0]);
|
|
136
|
+
const right = parsePart(sides[1]);
|
|
137
|
+
if (left === null || right === null) return null;
|
|
138
|
+
const fill = 8 - left.length - right.length;
|
|
139
|
+
if (fill < 0) return null;
|
|
140
|
+
return [
|
|
141
|
+
...left,
|
|
142
|
+
...Array(fill).fill(0),
|
|
143
|
+
...right
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
41
147
|
//#region src/builtins/local-network/local-network.addon.ts
|
|
42
148
|
/**
|
|
43
149
|
* local-network — hub-only system cap implementation.
|
|
@@ -63,6 +169,8 @@ function parseLocalHostname(raw) {
|
|
|
63
169
|
*/
|
|
64
170
|
var MESH_PROVIDER_ADDON_IDS = ["tailscale-client"];
|
|
65
171
|
var POLL_INTERVAL_MS = 3e4;
|
|
172
|
+
/** Drop rotated IPv6 / dead extras from the persisted allowlist. */
|
|
173
|
+
var ALLOWLIST_RECONCILE_INTERVAL_MS = 3600 * 1e3;
|
|
66
174
|
/**
|
|
67
175
|
* Compiled-in boot default — mirrors `CONFIG_DEFAULTS.server.port` in
|
|
68
176
|
* `server/backend/src/boot/boot-config.ts`. Used only when neither the live
|
|
@@ -71,6 +179,8 @@ var POLL_INTERVAL_MS = 3e4;
|
|
|
71
179
|
var DEFAULT_HUB_PORT = 4443;
|
|
72
180
|
var LocalNetworkAddon = class extends BaseAddon {
|
|
73
181
|
pollTimer = null;
|
|
182
|
+
allowlistTimer = null;
|
|
183
|
+
allowlistReconcilePending = false;
|
|
74
184
|
lastSnapshotKey = "";
|
|
75
185
|
/** Optional public hostname tracked from `NetworkTunnelStarted`/
|
|
76
186
|
* `Stopped` events on the bus. */
|
|
@@ -87,6 +197,7 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
87
197
|
allowedAddresses: [],
|
|
88
198
|
bootSeeded: false,
|
|
89
199
|
notificationBaseUrl: "",
|
|
200
|
+
viewerBaseUrls: [],
|
|
90
201
|
httpEnabled: true,
|
|
91
202
|
httpPort: DEFAULT_LAN_HTTP_PORT,
|
|
92
203
|
localHostname: ""
|
|
@@ -107,6 +218,31 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
107
218
|
if (mesh !== null && mesh.host !== "") return `https://${mesh.host}:${mesh.port}`;
|
|
108
219
|
return null;
|
|
109
220
|
}
|
|
221
|
+
async snapshotViewerEndpoints() {
|
|
222
|
+
const selected = selectViewerEndpoints(await this.buildAdvertisedEndpoints(false, false, "http", (await this.resolveAdvertisedListenPort(null)).port), this.config.viewerBaseUrls);
|
|
223
|
+
return {
|
|
224
|
+
baseUrls: this.config.viewerBaseUrls,
|
|
225
|
+
resolved: selected.map((e) => e.baseUrl)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Palette shown in the Network tab: LAN IPv4, stable LAN IPv6, public,
|
|
230
|
+
* mesh. The viewer HTTP/WS race is a subset (`selectViewerEndpoints`) —
|
|
231
|
+
* IPv6 stays in this list because ICE/WebRTC gathers it regardless.
|
|
232
|
+
*/
|
|
233
|
+
async buildAdvertisedEndpoints(includeLoopback, ipv4Only, scheme, httpsPort) {
|
|
234
|
+
const allow = this.config.allowedAddresses;
|
|
235
|
+
const interfaces = applyAllowlist(this.enumerate(), allow);
|
|
236
|
+
await this.reconcileMeshEndpoint();
|
|
237
|
+
const httpState = readLanHttpState();
|
|
238
|
+
return buildEndpoints(interfaces, httpsPort, includeLoopback, ipv4Only, this.publicHostname, scheme, this.meshEndpoint, {
|
|
239
|
+
enabled: this.config.httpEnabled !== false,
|
|
240
|
+
listening: httpState.listening,
|
|
241
|
+
httpPort: this.resolvedHttpPort(),
|
|
242
|
+
httpsPort,
|
|
243
|
+
localHostname: this.config.localHostname || null
|
|
244
|
+
});
|
|
245
|
+
}
|
|
110
246
|
async onInitialize() {
|
|
111
247
|
if (!this.config.bootSeeded) {
|
|
112
248
|
const seed = autoSeedAllowlist(this.enumerate());
|
|
@@ -116,6 +252,7 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
116
252
|
});
|
|
117
253
|
this.ctx.logger.info("local-network: first-boot auto-seed", { meta: { addresses: seed } });
|
|
118
254
|
}
|
|
255
|
+
await this.persistReconciledAllowlist();
|
|
119
256
|
const provider = {
|
|
120
257
|
list: async () => ({
|
|
121
258
|
interfaces: this.enumerate(),
|
|
@@ -123,27 +260,16 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
123
260
|
}),
|
|
124
261
|
getPreferred: async () => pickPreferred(applyAllowlist(this.enumerate(), this.config.allowedAddresses)),
|
|
125
262
|
getConnectionEndpoints: async (input) => {
|
|
126
|
-
const includeLoopback = input.includeLoopback ??
|
|
127
|
-
const ipv4Only = input.ipv4Only ?? false;
|
|
263
|
+
const includeLoopback = input.includeLoopback ?? false;
|
|
128
264
|
const scheme = input.scheme ?? "http";
|
|
129
|
-
const allow = this.config.allowedAddresses;
|
|
130
|
-
const interfaces = applyAllowlist(this.enumerate(), allow);
|
|
131
265
|
const localPort = await this.resolveAdvertisedListenPort(input.port ?? null);
|
|
132
266
|
if (input.port !== void 0 && input.port !== localPort.port) this.ctx.logger.info("local-network: caller port hint overridden", { meta: {
|
|
133
267
|
hint: input.port,
|
|
134
268
|
advertised: localPort.port,
|
|
135
269
|
source: localPort.source
|
|
136
270
|
} });
|
|
137
|
-
await this.reconcileMeshEndpoint();
|
|
138
|
-
const httpState = readLanHttpState();
|
|
139
271
|
return {
|
|
140
|
-
endpoints:
|
|
141
|
-
enabled: this.config.httpEnabled !== false,
|
|
142
|
-
listening: httpState.listening,
|
|
143
|
-
httpPort: this.resolvedHttpPort(),
|
|
144
|
-
httpsPort: localPort.port,
|
|
145
|
-
localHostname: this.config.localHostname || null
|
|
146
|
-
}),
|
|
272
|
+
endpoints: await this.buildAdvertisedEndpoints(includeLoopback, input.ipv4Only ?? false, scheme, localPort.port),
|
|
147
273
|
localPort
|
|
148
274
|
};
|
|
149
275
|
},
|
|
@@ -159,6 +285,16 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
159
285
|
resolved: this.resolveNotificationEndpoint()
|
|
160
286
|
};
|
|
161
287
|
},
|
|
288
|
+
getViewerEndpoints: async () => this.snapshotViewerEndpoints(),
|
|
289
|
+
setViewerEndpoints: async ({ baseUrls }) => {
|
|
290
|
+
const cleaned = [...new Set(baseUrls.filter((u) => u.length > 0))];
|
|
291
|
+
await this.updateGlobalSettings({ viewerBaseUrls: cleaned });
|
|
292
|
+
this.ctx.logger.info("local-network: viewer endpoints set", { meta: {
|
|
293
|
+
count: cleaned.length,
|
|
294
|
+
auto: cleaned.length === 0
|
|
295
|
+
} });
|
|
296
|
+
return this.snapshotViewerEndpoints();
|
|
297
|
+
},
|
|
162
298
|
getAllowedAddresses: async () => ({ addresses: this.config.allowedAddresses }),
|
|
163
299
|
resetAllowlistToBestMatch: async () => {
|
|
164
300
|
const seed = autoSeedAllowlist(this.enumerate());
|
|
@@ -210,6 +346,10 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
210
346
|
this.lastSnapshotKey = snapshotKey(this.enumerate());
|
|
211
347
|
this.pollTimer = setInterval(() => this.detectChanges(), POLL_INTERVAL_MS);
|
|
212
348
|
this.pollTimer.unref?.();
|
|
349
|
+
this.allowlistTimer = setInterval(() => {
|
|
350
|
+
this.persistReconciledAllowlist();
|
|
351
|
+
}, ALLOWLIST_RECONCILE_INTERVAL_MS);
|
|
352
|
+
this.allowlistTimer.unref?.();
|
|
213
353
|
this.ctx.eventBus?.subscribe({ category: EventCategory.NetworkTunnelStarted }, (event) => {
|
|
214
354
|
const data = event.data ?? {};
|
|
215
355
|
if (typeof data.url === "string") try {
|
|
@@ -285,6 +425,10 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
285
425
|
clearInterval(this.pollTimer);
|
|
286
426
|
this.pollTimer = null;
|
|
287
427
|
}
|
|
428
|
+
if (this.allowlistTimer) {
|
|
429
|
+
clearInterval(this.allowlistTimer);
|
|
430
|
+
this.allowlistTimer = null;
|
|
431
|
+
}
|
|
288
432
|
}
|
|
289
433
|
/**
|
|
290
434
|
* Other hub addons (e.g. cloudflare-tunnel) signal the active public
|
|
@@ -418,7 +562,26 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
418
562
|
}
|
|
419
563
|
}
|
|
420
564
|
enumerate() {
|
|
421
|
-
return enumerateOsInterfaces(os.networkInterfaces());
|
|
565
|
+
return enumerateOsInterfaces(os.networkInterfaces(), readLinuxIpv6Flags());
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Replace rotated IPv6 and drop addresses that no longer exist, without
|
|
569
|
+
* clearing operator extras that are still live (docker/vpn opt-in).
|
|
570
|
+
*/
|
|
571
|
+
async persistReconciledAllowlist(interfaces = this.enumerate()) {
|
|
572
|
+
if (this.allowlistReconcilePending) return;
|
|
573
|
+
this.allowlistReconcilePending = true;
|
|
574
|
+
try {
|
|
575
|
+
const next = reconcileAllowlist(this.config.allowedAddresses, interfaces);
|
|
576
|
+
if (sameAddressSet(this.config.allowedAddresses, next)) return;
|
|
577
|
+
await this.updateGlobalSettings({
|
|
578
|
+
allowedAddresses: next,
|
|
579
|
+
bootSeeded: true
|
|
580
|
+
});
|
|
581
|
+
this.ctx.logger.info("local-network: allowlist reconciled", { meta: { addresses: next } });
|
|
582
|
+
} finally {
|
|
583
|
+
this.allowlistReconcilePending = false;
|
|
584
|
+
}
|
|
422
585
|
}
|
|
423
586
|
detectChanges() {
|
|
424
587
|
const interfaces = this.enumerate();
|
|
@@ -429,6 +592,7 @@ var LocalNetworkAddon = class extends BaseAddon {
|
|
|
429
592
|
key
|
|
430
593
|
} });
|
|
431
594
|
this.lastSnapshotKey = key;
|
|
595
|
+
this.persistReconciledAllowlist(interfaces);
|
|
432
596
|
this.ctx.eventBus?.emit({
|
|
433
597
|
id: `local-network-changed-${Date.now()}`,
|
|
434
598
|
timestamp: /* @__PURE__ */ new Date(),
|
|
@@ -477,7 +641,7 @@ function validPort(value) {
|
|
|
477
641
|
if (value === null) return null;
|
|
478
642
|
return Number.isInteger(value) && value >= 1 && value <= 65535 ? value : null;
|
|
479
643
|
}
|
|
480
|
-
function enumerateOsInterfaces(ifaces) {
|
|
644
|
+
function enumerateOsInterfaces(ifaces, ipv6Flags) {
|
|
481
645
|
const raw = [];
|
|
482
646
|
for (const [name, addrs] of Object.entries(ifaces)) {
|
|
483
647
|
if (!addrs) continue;
|
|
@@ -497,8 +661,12 @@ function enumerateOsInterfaces(ifaces) {
|
|
|
497
661
|
const classified = raw.map((r) => {
|
|
498
662
|
const kind = classifyKind(r.name, r.address, r.internal);
|
|
499
663
|
const reachableKind = kind === "lan" || kind === "wifi";
|
|
500
|
-
const
|
|
501
|
-
const
|
|
664
|
+
const hex = r.family === "IPv6" ? ipv6ToHex32(r.address) : null;
|
|
665
|
+
const flags = hex !== null ? ipv6Flags?.get(hex) : void 0;
|
|
666
|
+
const rangeOk = isPlausibleAutoSeed(r.address, r.family);
|
|
667
|
+
const stableV6 = r.family !== "IPv6" || shouldAdvertiseIpv6(r.address, flags);
|
|
668
|
+
const plausible = !r.internal && reachableKind && rangeOk && stableV6;
|
|
669
|
+
const plausibleReason = plausible ? "" : r.family === "IPv6" && rangeOk && !stableV6 ? "IPv6 temporary / privacy address — rotates and is not advertised." : explainNonPlausible({
|
|
502
670
|
kind,
|
|
503
671
|
family: r.family,
|
|
504
672
|
address: r.address,
|
|
@@ -527,13 +695,14 @@ function enumerateOsInterfaces(ifaces) {
|
|
|
527
695
|
/**
|
|
528
696
|
* Filter the interface list by the operator's allowlist. Empty
|
|
529
697
|
* allowlist = no-op (every interface passes); otherwise only entries
|
|
530
|
-
* whose `address` matches an allowlist entry remain.
|
|
531
|
-
*
|
|
698
|
+
* whose `address` matches an allowlist entry remain. IPv6 matches the
|
|
699
|
+
* allowlisted /64 so a rotated privacy address does not hide the live
|
|
700
|
+
* GUA. Loopback is not an advertised client route and is not forced
|
|
701
|
+
* through.
|
|
532
702
|
*/
|
|
533
703
|
function applyAllowlist(interfaces, allowed) {
|
|
534
704
|
if (allowed.length === 0) return interfaces;
|
|
535
|
-
|
|
536
|
-
return interfaces.filter((i) => i.kind === "loopback" || set.has(i.address));
|
|
705
|
+
return interfaces.filter((i) => ipv6AllowlistMatches(i.address, allowed) || allowed.includes(i.address));
|
|
537
706
|
}
|
|
538
707
|
/**
|
|
539
708
|
* Rank interfaces and pick the auto-selected outbound one. See the
|
|
@@ -592,6 +761,13 @@ function buildEndpoints(interfaces, port, includeLoopback, ipv4Only, publicHostn
|
|
|
592
761
|
if (iface.address.startsWith("169.254.")) continue;
|
|
593
762
|
emit(iface, "lan-ipv4", `${formatKind(iface.kind)} — ${iface.name}`, `${lanScheme}://${iface.address}:${lanPort}`, 10 + priority++);
|
|
594
763
|
}
|
|
764
|
+
if (!ipv4Only) for (const iface of interfaces) {
|
|
765
|
+
if (iface.internal || iface.family !== "IPv6") continue;
|
|
766
|
+
if (iface.kind === "loopback") continue;
|
|
767
|
+
if (iface.address.toLowerCase().startsWith("fe80:")) continue;
|
|
768
|
+
if (!iface.plausible) continue;
|
|
769
|
+
emit(iface, "lan-ipv6", `${formatKind(iface.kind)} — ${iface.name} (IPv6)`, `${lanScheme}://[${iface.address}]:${lanPort}`, 10 + priority++);
|
|
770
|
+
}
|
|
595
771
|
if (publicHostname) out.push({
|
|
596
772
|
label: "Public tunnel",
|
|
597
773
|
baseUrl: `https://${publicHostname}`,
|
|
@@ -610,15 +786,6 @@ function buildEndpoints(interfaces, port, includeLoopback, ipv4Only, publicHostn
|
|
|
610
786
|
plausibleReason: "",
|
|
611
787
|
priority: 150
|
|
612
788
|
});
|
|
613
|
-
if (!ipv4Only) {
|
|
614
|
-
let v6prio = 200;
|
|
615
|
-
for (const iface of interfaces) {
|
|
616
|
-
if (iface.internal || iface.family !== "IPv6") continue;
|
|
617
|
-
if (iface.kind === "loopback") continue;
|
|
618
|
-
if (iface.address.startsWith("fe80:")) continue;
|
|
619
|
-
emit(iface, "lan-ipv6", `${formatKind(iface.kind)} — ${iface.name} (IPv6)`, `${lanScheme}://[${iface.address}]:${lanPort}`, v6prio++);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
789
|
if (includeLoopback) out.push({
|
|
623
790
|
label: "Loopback",
|
|
624
791
|
baseUrl: `${scheme}://127.0.0.1:${port}`,
|
|
@@ -630,22 +797,39 @@ function buildEndpoints(interfaces, port, includeLoopback, ipv4Only, publicHostn
|
|
|
630
797
|
});
|
|
631
798
|
return out.toSorted((a, b) => a.priority - b.priority);
|
|
632
799
|
}
|
|
800
|
+
/** AUTO viewer race: LAN IPv4 + the public tunnel. Never IPv6, mesh, or loopback. */
|
|
801
|
+
function isDefaultViewerEndpoint(endpoint) {
|
|
802
|
+
if (endpoint.kind === "lan-ipv4") return true;
|
|
803
|
+
return endpoint.kind === "public" && endpoint.interfaceKind === "public";
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* The URLs the SDK should race for API / viewer access.
|
|
807
|
+
*
|
|
808
|
+
* Empty `viewerBaseUrls` = AUTO (IPv4 + public tunnel). A non-empty list is
|
|
809
|
+
* the operator's pin; if none of those URLs are in the current palette,
|
|
810
|
+
* fall back to AUTO rather than racing an empty set.
|
|
811
|
+
*/
|
|
812
|
+
function selectViewerEndpoints(endpoints, viewerBaseUrls) {
|
|
813
|
+
const auto = endpoints.filter(isDefaultViewerEndpoint);
|
|
814
|
+
if (viewerBaseUrls.length === 0) return auto;
|
|
815
|
+
const wanted = new Set(viewerBaseUrls);
|
|
816
|
+
const explicit = endpoints.filter((e) => wanted.has(e.baseUrl));
|
|
817
|
+
return explicit.length > 0 ? explicit : auto;
|
|
818
|
+
}
|
|
633
819
|
/**
|
|
634
820
|
* First-boot heuristic: which addresses should the allowlist start
|
|
635
821
|
* with? Includes LAN + Wi-Fi IPv4 addresses + plausible IPv6:
|
|
636
822
|
*
|
|
637
823
|
* - **IPv4**: skip link-local (`169.254.*`), keep the rest.
|
|
638
824
|
* - **IPv6**: skip link-local (`fe80::*`), unspecified, and
|
|
639
|
-
* multicast. Keep ULAs (`fc00::/7`
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
* prune them from the Network Addresses tab if the rotating IPs
|
|
643
|
-
* become a nuisance.
|
|
825
|
+
* multicast. Keep ULAs (`fc00::/7`) and Global Unicast (`2000::/3`).
|
|
826
|
+
* Privacy-extension temporary addresses are skipped when Linux flags
|
|
827
|
+
* mark them non-permanent (`plausible === false`).
|
|
644
828
|
*
|
|
645
829
|
* Skips docker/vpn/loopback/other entirely — those stay opt-in.
|
|
646
830
|
*/
|
|
647
831
|
function autoSeedAllowlist(interfaces) {
|
|
648
|
-
return [...new Set(interfaces.filter((i) => !i.internal && (i.kind === "lan" || i.kind === "wifi") && isPlausibleAutoSeed(i.address, i.family)).map((i) => i.address))];
|
|
832
|
+
return [...new Set(interfaces.filter((i) => !i.internal && (i.kind === "lan" || i.kind === "wifi") && isPlausibleAutoSeed(i.address, i.family) && (i.family !== "IPv6" || i.plausible)).map((i) => i.address))];
|
|
649
833
|
}
|
|
650
834
|
/**
|
|
651
835
|
* Per-interface tooltip text surfaced on the "Unlikely usable" badge.
|
|
@@ -734,5 +918,30 @@ function formatKind(kind) {
|
|
|
734
918
|
case "other": return "Other";
|
|
735
919
|
}
|
|
736
920
|
}
|
|
921
|
+
/**
|
|
922
|
+
* Rewrite a persisted allowlist against the live interface set:
|
|
923
|
+
* current auto-seed (LAN/Wi-Fi, stable IPv6) plus still-live operator
|
|
924
|
+
* extras (docker/vpn). Empty stays empty — that is "unrestricted".
|
|
925
|
+
*/
|
|
926
|
+
function reconcileAllowlist(current, interfaces) {
|
|
927
|
+
if (current.length === 0) return [];
|
|
928
|
+
const live = new Set(interfaces.map((i) => i.address));
|
|
929
|
+
const seed = autoSeedAllowlist(interfaces);
|
|
930
|
+
const seedSet = new Set(seed);
|
|
931
|
+
const extras = current.filter((a) => live.has(a) && !seedSet.has(a) && !a.includes(":"));
|
|
932
|
+
return [...new Set([...seed, ...extras])];
|
|
933
|
+
}
|
|
934
|
+
function sameAddressSet(a, b) {
|
|
935
|
+
if (a.length !== b.length) return false;
|
|
936
|
+
const set = new Set(a);
|
|
937
|
+
return b.every((addr) => set.has(addr));
|
|
938
|
+
}
|
|
939
|
+
function readLinuxIpv6Flags() {
|
|
940
|
+
try {
|
|
941
|
+
return parseProcIfInet6(readFileSync("/proc/net/if_inet6", "utf8"));
|
|
942
|
+
} catch {
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
737
946
|
//#endregion
|
|
738
|
-
export { LocalNetworkAddon, applyAllowlist, autoSeedAllowlist, buildEndpoints, classifyKind, enumerateOsInterfaces, explainNonPlausible, isPlausibleAutoSeed, pickPreferred, prefixLen, resolveAdvertisedPort };
|
|
947
|
+
export { ALLOWLIST_RECONCILE_INTERVAL_MS, LocalNetworkAddon, applyAllowlist, autoSeedAllowlist, buildEndpoints, classifyKind, enumerateOsInterfaces, explainNonPlausible, ipv6Prefix64Key, isDefaultViewerEndpoint, isPlausibleAutoSeed, parseProcIfInet6, pickPreferred, prefixLen, reconcileAllowlist, resolveAdvertisedPort, selectViewerEndpoints, shouldAdvertiseIpv6 };
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-C2_1HCpW.js");
|
|
7
7
|
//#region src/builtins/loki-logging/loki-payload.ts
|
|
8
8
|
/**
|
|
9
9
|
* Loki rejects a label name that is not a valid Prometheus label
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-DCTpBXm4.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-C2_1HCpW.js");
|
|
7
7
|
let node_child_process = require("node:child_process");
|
|
8
8
|
let node_util = require("node:util");
|
|
9
9
|
let node_os = require("node:os");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as metricsProviderCapability, Gt as EventCategory, Mt as createEvent, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { $ as metricsProviderCapability, Gt as EventCategory, Mt as createEvent, yt as BaseAddon } from "../../dist-DCTpBXm4.mjs";
|
|
2
2
|
import { execFile, execFileSync } from "node:child_process";
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
4
|
import * as os from "node:os";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-C2_1HCpW.js");
|
|
7
7
|
let node_child_process = require("node:child_process");
|
|
8
8
|
let node_util = require("node:util");
|
|
9
9
|
let node_os = require("node:os");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import { Gt as EventCategory, L as enumerateInferenceDevices, Pt as emitReadiness, ct as scoreRuntimes, rt as platformProbeCapability, vt as errMsg, yt as BaseAddon } from "../../dist-
|
|
2
|
+
import { Gt as EventCategory, L as enumerateInferenceDevices, Pt as emitReadiness, ct as scoreRuntimes, rt as platformProbeCapability, vt as errMsg, yt as BaseAddon } from "../../dist-DCTpBXm4.mjs";
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import * as os from "node:os";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-C2_1HCpW.js");
|
|
7
7
|
//#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
|
|
8
8
|
/**
|
|
9
9
|
* Reconcile the durable `enabledProviders` set against authoritative
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Gt as EventCategory, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { Gt as EventCategory, yt as BaseAddon } from "../../dist-DCTpBXm4.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-C2_1HCpW.js");
|
|
7
7
|
let zod = require("zod");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let _camstack_types_node = require("@camstack/types/node");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ct as DeviceFeature, Gt as EventCategory, M as deriveBatteryPresence, Rt as nodePin, T as batteryCapability, Tt as DeviceType, i as BatteryStatusSchema, mt as streamQualityLabel, ut as snapshotCapability, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { Ct as DeviceFeature, Gt as EventCategory, M as deriveBatteryPresence, Rt as nodePin, T as batteryCapability, Tt as DeviceType, i as BatteryStatusSchema, mt as streamQualityLabel, ut as snapshotCapability, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-DCTpBXm4.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { signExpiringUrl, verifyExpiringUrl } from "@camstack/types/node";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-C2_1HCpW.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
let node_fs_promises = require("node:fs/promises");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { V as filesystemBrowseCapability, pt as storageProviderCapability, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { V as filesystemBrowseCapability, pt as storageProviderCapability, yt as BaseAddon } from "../../dist-DCTpBXm4.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { mkdir, readdir, realpath, statfs } from "node:fs/promises";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
7
|
-
const require_retired_settings_keys = require("../../retired-settings-keys-
|
|
6
|
+
const require_dist = require("../../dist-C2_1HCpW.js");
|
|
7
|
+
const require_retired_settings_keys = require("../../retired-settings-keys-DHRXLMPn.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let node_fs = require("node:fs");
|
|
10
10
|
let node_module = require("node:module");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as dataStoreProviderCapability, Bt as parseJsonUnknown, _t as vectorStoreCapability, f as RUNTIME_DEFAULTS, gt as vectorDimFromBase64, j as decodeVectorBase64, kt as asJsonObject, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-
|
|
2
|
-
import { r as retiredKeyStoreOf, t as purgeRetiredSettingsKeys } from "../../retired-settings-keys-
|
|
1
|
+
import { A as dataStoreProviderCapability, Bt as parseJsonUnknown, _t as vectorStoreCapability, f as RUNTIME_DEFAULTS, gt as vectorDimFromBase64, j as decodeVectorBase64, kt as asJsonObject, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-DCTpBXm4.mjs";
|
|
2
|
+
import { r as retiredKeyStoreOf, t as purgeRetiredSettingsKeys } from "../../retired-settings-keys-Czd76yLZ.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { statSync } from "node:fs";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-C2_1HCpW.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
let node_fs_promises = require("node:fs/promises");
|
|
9
9
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dt as storageCapability, ft as storageMigrationCapability, g as StorageMigrationJobSchema, h as StorageLocationTypeSchema, lt as settingsStoreCapability, yt as BaseAddon, zt as parseJsonObject } from "../../dist-
|
|
1
|
+
import { dt as storageCapability, ft as storageMigrationCapability, g as StorageMigrationJobSchema, h as StorageLocationTypeSchema, lt as settingsStoreCapability, yt as BaseAddon, zt as parseJsonObject } from "../../dist-DCTpBXm4.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-C2_1HCpW.js");
|
|
7
7
|
//#region src/builtins/system-config/system-config.addon.ts
|
|
8
8
|
/**
|
|
9
9
|
* Built-in `system-config` addon — Phase 4 of the settings redesign.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { It as hydrateSchema, vt as errMsg, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { It as hydrateSchema, vt as errMsg, yt as BaseAddon } from "../../dist-DCTpBXm4.mjs";
|
|
2
2
|
//#region src/builtins/system-config/system-config.addon.ts
|
|
3
3
|
/**
|
|
4
4
|
* Built-in `system-config` addon — Phase 4 of the settings redesign.
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-C2_1HCpW.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
let node_path = require("node:path");
|
|
9
9
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-DCTpBXm4.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
import * as path$1 from "node:path";
|
|
4
4
|
import path from "node:path";
|