@camstack/system 1.2.352 → 1.2.354
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 +13 -3
- package/dist/builtins/composer/composer.addon.mjs +13 -3
- 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 +3 -3
- package/dist/builtins/device-manager/device-manager.addon.mjs +3 -3
- 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/local-network.addon.d.ts +21 -6
- package/dist/builtins/local-network/local-network.addon.js +81 -54
- package/dist/builtins/local-network/local-network.addon.mjs +81 -55
- 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 +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-BAfa6tjj.mjs → child-cap-dispatch-C1x1kNJK.mjs} +1 -1
- package/dist/{child-cap-dispatch-B_0-zpI6.js → child-cap-dispatch-CRqYKHAb.js} +1 -1
- package/dist/{composition-sources-CUfM-BYp.js → composition-sources-0nuWOdNd.js} +1 -1
- package/dist/{composition-sources-CoFGIGCc.mjs → composition-sources-DzkEFd8q.mjs} +1 -1
- package/dist/{dist-DADwBtU5.mjs → dist-DVyXxn41.mjs} +176 -82
- package/dist/{dist-DeGPW01G.js → dist-DePB7uwU.js} +187 -81
- package/dist/http/reverse-proxy.d.ts +11 -1
- package/dist/index.js +55 -12
- package/dist/index.mjs +55 -12
- package/dist/kernel/config-schema.d.ts +1 -0
- package/dist/{retired-settings-keys-D8G5o4Fc.js → retired-settings-keys-BTKZdPn6.js} +1 -1
- package/dist/{retired-settings-keys-bUUnyTMg.mjs → retired-settings-keys-DFur4hJF.mjs} +1 -1
- package/package.json +1 -1
|
@@ -7936,11 +7936,11 @@ var ModelVariantGroupSchema = zod.z.object({
|
|
|
7936
7936
|
/** Quantization axis. Omit ⇒ the fp32 base build. */
|
|
7937
7937
|
precision: zod.z.enum(["fp32", "int8"]).optional(),
|
|
7938
7938
|
/**
|
|
7939
|
-
*
|
|
7940
|
-
*
|
|
7941
|
-
*
|
|
7939
|
+
* Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
|
|
7940
|
+
* an alternate training of the same family (Scrypted `relu_test`); chip
|
|
7941
|
+
* label is "Test".
|
|
7942
7942
|
*/
|
|
7943
|
-
optimization: zod.z.enum(["standard", "
|
|
7943
|
+
optimization: zod.z.enum(["standard", "test"]).optional(),
|
|
7944
7944
|
/**
|
|
7945
7945
|
* Input-resolution axis (square input side, px). Omit ⇒ the family's native
|
|
7946
7946
|
* resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
|
|
@@ -7994,9 +7994,10 @@ var ModelLicenseRecordSchema = zod.z.object({
|
|
|
7994
7994
|
* are per upstream. A persisted custom-model row written when this field was a
|
|
7995
7995
|
* string still parses — the string is lifted into `{ weights }`.
|
|
7996
7996
|
*
|
|
7997
|
-
* Every licence surface (the
|
|
7998
|
-
*
|
|
7999
|
-
*
|
|
7997
|
+
* Every machine licence surface (`THIRD_PARTY_MODELS.md`, the Hugging Face
|
|
7998
|
+
* model table) is GENERATED from these records by
|
|
7999
|
+
* `scripts/gen-model-licenses.ts` (D661). Operator-facing docs live in
|
|
8000
|
+
* `camstack/docs` → `/docs/detection/models`.
|
|
8000
8001
|
*/
|
|
8001
8002
|
var ModelLicenseSchema = zod.z.preprocess((value) => typeof value === "string" ? { weights: value } : value, ModelLicenseRecordSchema);
|
|
8002
8003
|
var ModelCatalogEntrySchema = zod.z.object({
|
|
@@ -14168,7 +14169,7 @@ var detectionPipelineCapability = {
|
|
|
14168
14169
|
* This matters here more than anywhere else in the repo, because every provider
|
|
14169
14170
|
* that knows a device's host knows its PASSWORD too: `{ host, port, username,
|
|
14170
14171
|
* password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
|
|
14171
|
-
* and `http://admin:hunter2@192.168.
|
|
14172
|
+
* and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
|
|
14172
14173
|
* camera's own page will ask for its own login. That is correct, and pre-
|
|
14173
14174
|
* filling it is the operator's business, not ours.
|
|
14174
14175
|
*
|
|
@@ -18568,7 +18569,7 @@ var modelDistributorCapability = {
|
|
|
18568
18569
|
mode: "singleton",
|
|
18569
18570
|
internal: true,
|
|
18570
18571
|
methods: { distributeModel: method(zod.z.object({
|
|
18571
|
-
|
|
18572
|
+
targetNodeId: zod.z.string(),
|
|
18572
18573
|
modelId: zod.z.string(),
|
|
18573
18574
|
format: zod.z.enum(MODEL_FORMATS),
|
|
18574
18575
|
entry: ModelCatalogEntrySchema
|
|
@@ -21163,8 +21164,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
|
|
|
21163
21164
|
disabledTargetIds: zod.z.array(zod.z.string()).default([])
|
|
21164
21165
|
});
|
|
21165
21166
|
/**
|
|
21166
|
-
* The state-summary SECTIONS (D647 / D698): the D637 kind groups that
|
|
21167
|
-
* still-true state. `
|
|
21167
|
+
* The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
|
|
21168
|
+
* report a still-true state. `addons` is the crash-looped addons only and
|
|
21169
|
+
* `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
|
|
21168
21170
|
* job is not an open state, and the alarm stays instant.
|
|
21169
21171
|
*/
|
|
21170
21172
|
var NcStateSummarySectionIdSchema = zod.z.enum([
|
|
@@ -21176,6 +21178,7 @@ var NcStateSummarySectionIdSchema = zod.z.enum([
|
|
|
21176
21178
|
"detection-blind",
|
|
21177
21179
|
"nodes",
|
|
21178
21180
|
"addons",
|
|
21181
|
+
"addon-updates",
|
|
21179
21182
|
"updates"
|
|
21180
21183
|
]);
|
|
21181
21184
|
/** One section of a previewed state summary. `unavailable` = its state could not be read. */
|
|
@@ -21670,7 +21673,19 @@ var NcAlarmModeDelaysSchema = zod.z.object({
|
|
|
21670
21673
|
* When this mode may be armed. Absent / null = always allowed (same as a
|
|
21671
21674
|
* rule with no `schedule`). Evaluated with {@link isScheduleActive}.
|
|
21672
21675
|
*/
|
|
21673
|
-
schedule: NcScheduleSchema.optional()
|
|
21676
|
+
schedule: NcScheduleSchema.optional(),
|
|
21677
|
+
/**
|
|
21678
|
+
* D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
|
|
21679
|
+
* intrusion naming the locations)? Absent = false — the entry-delay notification is the
|
|
21680
|
+
* only one, and the matches ride the triggered notification's mosaic (D705).
|
|
21681
|
+
*/
|
|
21682
|
+
notifyDuringEntryDelay: zod.z.boolean().optional(),
|
|
21683
|
+
/**
|
|
21684
|
+
* D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
|
|
21685
|
+
* Absent = false (logged, not sent). Even when true it is not sent when that same sensor
|
|
21686
|
+
* is the one that starts the entry delay — the entry-delay notification already names it.
|
|
21687
|
+
*/
|
|
21688
|
+
notifySensorRewatched: zod.z.boolean().optional()
|
|
21674
21689
|
});
|
|
21675
21690
|
var NcAlarmModeDelaysListSchema = zod.z.array(NcAlarmModeDelaysSchema).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
|
|
21676
21691
|
/**
|
|
@@ -21685,7 +21700,11 @@ var NcAlarmModeDelaysEditSchema = zod.z.object({
|
|
|
21685
21700
|
triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
|
|
21686
21701
|
postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
|
|
21687
21702
|
/** Set a schedule, or `null` to clear (mode always allowed again). */
|
|
21688
|
-
schedule: NcScheduleSchema.nullable().optional()
|
|
21703
|
+
schedule: NcScheduleSchema.nullable().optional(),
|
|
21704
|
+
/** D706: set the switch, or `null` to clear it (back to the default, false). */
|
|
21705
|
+
notifyDuringEntryDelay: zod.z.boolean().nullable().optional(),
|
|
21706
|
+
/** D706: set the switch, or `null` to clear it (back to the default, false). */
|
|
21707
|
+
notifySensorRewatched: zod.z.boolean().nullable().optional()
|
|
21689
21708
|
});
|
|
21690
21709
|
var NcAlarmModeDelaysEditListSchema = zod.z.array(NcAlarmModeDelaysEditSchema).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
|
|
21691
21710
|
/** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
|
|
@@ -22864,39 +22883,19 @@ Object.freeze(Object.fromEntries([{
|
|
|
22864
22883
|
label: "Face recognition model",
|
|
22865
22884
|
defaultModelId: "auraface-r100",
|
|
22866
22885
|
indexName: "the enrolled face gallery",
|
|
22867
|
-
options: [
|
|
22868
|
-
|
|
22869
|
-
|
|
22870
|
-
|
|
22871
|
-
},
|
|
22872
|
-
{
|
|
22873
|
-
id: "arcface-r100",
|
|
22874
|
-
label: "ArcFace (ResNet34)"
|
|
22875
|
-
},
|
|
22876
|
-
{
|
|
22877
|
-
id: "inception-resnet-v1",
|
|
22878
|
-
label: "Inception ResNet V1"
|
|
22879
|
-
}
|
|
22880
|
-
]
|
|
22886
|
+
options: [{
|
|
22887
|
+
id: "auraface-r100",
|
|
22888
|
+
label: "AuraFace R100"
|
|
22889
|
+
}]
|
|
22881
22890
|
}, {
|
|
22882
22891
|
stepId: "clip-embedding",
|
|
22883
22892
|
label: "Semantic search model",
|
|
22884
|
-
defaultModelId: "
|
|
22893
|
+
defaultModelId: "siglip2-b16-224",
|
|
22885
22894
|
indexName: "the object semantic-search index",
|
|
22886
|
-
options: [
|
|
22887
|
-
|
|
22888
|
-
|
|
22889
|
-
|
|
22890
|
-
},
|
|
22891
|
-
{
|
|
22892
|
-
id: "mobileclip-s2",
|
|
22893
|
-
label: "MobileCLIP S2"
|
|
22894
|
-
},
|
|
22895
|
-
{
|
|
22896
|
-
id: "siglip2-b16-224",
|
|
22897
|
-
label: "SigLIP2 B/16 (Apache-2.0)"
|
|
22898
|
-
}
|
|
22899
|
-
]
|
|
22895
|
+
options: [{
|
|
22896
|
+
id: "siglip2-b16-224",
|
|
22897
|
+
label: "SigLIP2 B/16 (Apache-2.0)"
|
|
22898
|
+
}]
|
|
22900
22899
|
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
22901
22900
|
var CLUSTER_STEP_SETTING_FIELDS = [{
|
|
22902
22901
|
stepId: "face-embedding",
|
|
@@ -26649,6 +26648,22 @@ var MotionSourceEnum = zod.z.enum([
|
|
|
26649
26648
|
"device-activity"
|
|
26650
26649
|
]);
|
|
26651
26650
|
/**
|
|
26651
|
+
* What opened an on-motion detection SESSION: the motion source that passed the
|
|
26652
|
+
* camera's `motionSources` gate, and what raised it (the camera itself, or a
|
|
26653
|
+
* sensor the operator attached — D672). `sensorName` is the attached sensor's
|
|
26654
|
+
* device name as the orchestrator's camera-sensors resolution last read it;
|
|
26655
|
+
* absent when unknown or when the camera itself triggered.
|
|
26656
|
+
*
|
|
26657
|
+
* Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
|
|
26658
|
+
* can say what started the session (D672 rule 11, amended 2026-10-06). The
|
|
26659
|
+
* runner never decides anything on it.
|
|
26660
|
+
*/
|
|
26661
|
+
var SessionTriggerSchema = zod.z.object({
|
|
26662
|
+
source: MotionSourceEnum,
|
|
26663
|
+
triggeredBy: MotionTriggeredBySchema,
|
|
26664
|
+
sensorName: zod.z.string().optional()
|
|
26665
|
+
});
|
|
26666
|
+
/**
|
|
26652
26667
|
* List of motion sources active on a camera. Empty array is valid:
|
|
26653
26668
|
* "no source" — happens for battery cams without firmware motion when
|
|
26654
26669
|
* the operator hasn't opted into the SW analyzer (would hold the
|
|
@@ -26900,6 +26915,14 @@ var RunnerCameraConfigSchema = zod.z.object({
|
|
|
26900
26915
|
*/
|
|
26901
26916
|
motionSources: MotionSourcesSchema.default(["analyzer"]),
|
|
26902
26917
|
/**
|
|
26918
|
+
* What opened this attach, on a SESSION attach only (see
|
|
26919
|
+
* {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
|
|
26920
|
+
* standing attach, an occupancy burst, a re-dispatch with no motion event
|
|
26921
|
+
* behind it, and every attach from an orchestrator older than the field — the
|
|
26922
|
+
* runner then names no source on the phase event.
|
|
26923
|
+
*/
|
|
26924
|
+
sessionTrigger: SessionTriggerSchema.optional(),
|
|
26925
|
+
/**
|
|
26903
26926
|
* WHICH root detector runs for this camera. The detection counterpart of
|
|
26904
26927
|
* {@link motionSources}, same shape and same discipline: a per-camera list
|
|
26905
26928
|
* of sources the system understands, vendor-agnostic, owned here.
|
|
@@ -31464,6 +31487,50 @@ var videoclipsCapability = {
|
|
|
31464
31487
|
})
|
|
31465
31488
|
}
|
|
31466
31489
|
};
|
|
31490
|
+
/** The central link domain the viewer app claims (Universal Links / App Links). */
|
|
31491
|
+
var CAMSTACK_APP_LINK_ORIGIN = "https://camstack.zentik.app";
|
|
31492
|
+
/**
|
|
31493
|
+
* Reduce a base URL to the bare origin the central page accepts as `hub=`,
|
|
31494
|
+
* or `null` when it cannot be one: not http(s), carries credentials, or does
|
|
31495
|
+
* not parse. A path/query/fragment is dropped — the central page mounts the
|
|
31496
|
+
* viewer under the origin itself.
|
|
31497
|
+
*/
|
|
31498
|
+
function toAppLinkHubOrigin(baseUrl) {
|
|
31499
|
+
let parsed;
|
|
31500
|
+
try {
|
|
31501
|
+
parsed = new URL(baseUrl);
|
|
31502
|
+
} catch {
|
|
31503
|
+
return null;
|
|
31504
|
+
}
|
|
31505
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
31506
|
+
if (parsed.username !== "" || parsed.password !== "") return null;
|
|
31507
|
+
if (parsed.hostname === "") return null;
|
|
31508
|
+
return parsed.origin;
|
|
31509
|
+
}
|
|
31510
|
+
/**
|
|
31511
|
+
* The hub address an app link carries as `hub=`, chosen from the hub's OWN
|
|
31512
|
+
* advertised palette so it always agrees with what the Network page lists:
|
|
31513
|
+
* the public tunnel first, then the mesh (Tailscale MagicDNS) — HTTPS only,
|
|
31514
|
+
* because the app and the hosted viewer reach it from off the LAN. `null`
|
|
31515
|
+
* when the hub advertises neither.
|
|
31516
|
+
*/
|
|
31517
|
+
function pickAppLinkHub(endpoints) {
|
|
31518
|
+
const rank = (e) => {
|
|
31519
|
+
if (e.kind !== "public" || !e.baseUrl.startsWith("https://")) return null;
|
|
31520
|
+
if (e.interfaceKind === "public") return 0;
|
|
31521
|
+
if (e.interfaceKind === "vpn") return 1;
|
|
31522
|
+
return null;
|
|
31523
|
+
};
|
|
31524
|
+
return endpoints.flatMap((e) => {
|
|
31525
|
+
const r = rank(e);
|
|
31526
|
+
const origin = toAppLinkHubOrigin(e.baseUrl);
|
|
31527
|
+
return r === null || origin === null ? [] : [{
|
|
31528
|
+
r,
|
|
31529
|
+
p: e.priority,
|
|
31530
|
+
origin
|
|
31531
|
+
}];
|
|
31532
|
+
}).toSorted((a, b) => a.r - b.r || a.p - b.p)[0]?.origin ?? null;
|
|
31533
|
+
}
|
|
31467
31534
|
/**
|
|
31468
31535
|
* Optional client-side hints sent at session creation to help the provider
|
|
31469
31536
|
* pick the best native source. All fields optional — a viewer that knows
|
|
@@ -36167,7 +36234,7 @@ var LocalInterfaceSchema = zod.z.object({
|
|
|
36167
36234
|
name: zod.z.string(),
|
|
36168
36235
|
family: zod.z.enum(["IPv4", "IPv6"]),
|
|
36169
36236
|
address: zod.z.string(),
|
|
36170
|
-
/** CIDR shorthand (e.g. `192.168.
|
|
36237
|
+
/** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
|
|
36171
36238
|
cidr: zod.z.string(),
|
|
36172
36239
|
/** OS netmask. Stable across calls so the SDK can hash on it. */
|
|
36173
36240
|
netmask: zod.z.string(),
|
|
@@ -36221,7 +36288,8 @@ var ConnectionEndpointSchema = zod.z.object({
|
|
|
36221
36288
|
"lan-ipv4",
|
|
36222
36289
|
"lan-ipv6",
|
|
36223
36290
|
"public",
|
|
36224
|
-
"loopback"
|
|
36291
|
+
"loopback",
|
|
36292
|
+
"app-link"
|
|
36225
36293
|
]),
|
|
36226
36294
|
/**
|
|
36227
36295
|
* Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
|
|
@@ -36290,15 +36358,27 @@ var GetConnectionEndpointsResultSchema = zod.z.object({
|
|
|
36290
36358
|
localPort: AdvertisedLocalPortSchema
|
|
36291
36359
|
});
|
|
36292
36360
|
/**
|
|
36293
|
-
* The
|
|
36294
|
-
*
|
|
36295
|
-
*
|
|
36361
|
+
* The address flagged for notification links. `baseUrl: null` = AUTO, which
|
|
36362
|
+
* since D709 is the CamStack app link; `resolved` reports the effective value,
|
|
36363
|
+
* so the UI can show it either way.
|
|
36296
36364
|
*/
|
|
36297
36365
|
var NotificationEndpointSchema = zod.z.object({
|
|
36298
36366
|
/** The operator's explicit choice, or null for AUTO. */
|
|
36299
36367
|
baseUrl: zod.z.string().nullable(),
|
|
36300
|
-
/**
|
|
36301
|
-
|
|
36368
|
+
/**
|
|
36369
|
+
* The effective flagged address: the explicit choice, else (AUTO) the
|
|
36370
|
+
* CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
|
|
36371
|
+
* answered the public tunnel / mesh here, or null.
|
|
36372
|
+
*/
|
|
36373
|
+
resolved: zod.z.string().nullable(),
|
|
36374
|
+
/**
|
|
36375
|
+
* The hub origin an app link carries as `hub=` (D709): the hub's best
|
|
36376
|
+
* public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
|
|
36377
|
+
* picked from this cap's own palette by `pickAppLinkHub`. `null` when the
|
|
36378
|
+
* hub advertises neither; the app link is then minted without `hub=`.
|
|
36379
|
+
* Absent on a pre-D709 hub.
|
|
36380
|
+
*/
|
|
36381
|
+
appLinkHub: zod.z.string().nullable()
|
|
36302
36382
|
});
|
|
36303
36383
|
/**
|
|
36304
36384
|
* The URLs the SDK / viewer should race for API access. `baseUrls` empty =
|
|
@@ -36366,7 +36446,7 @@ var localNetworkCapability = {
|
|
|
36366
36446
|
* every client a second authority on a socket only the hub binds. A viewer
|
|
36367
36447
|
* configured with a port-less public URL (`https://camstack.example.top`)
|
|
36368
36448
|
* infers 443 from the scheme, asks with 443, and was handed
|
|
36369
|
-
* `https://192.168.
|
|
36449
|
+
* `https://192.168.10.9:443` for a hub listening on 4443. That candidate
|
|
36370
36450
|
* cannot ever answer, and losing its race reads in the log exactly like
|
|
36371
36451
|
* "the LAN was tried and it was slower" — so an operator sitting on his own
|
|
36372
36452
|
* WiFi stayed on the public tunnel for weeks, with the LAN candidate born
|
|
@@ -36399,13 +36479,21 @@ var localNetworkCapability = {
|
|
|
36399
36479
|
* Pass `'https'` when the caller is itself loaded over HTTPS
|
|
36400
36480
|
* to avoid mixed-content blocks in the browser. The public
|
|
36401
36481
|
* tunnel always emits `https://` regardless. */
|
|
36402
|
-
scheme: zod.z.enum(["http", "https"]).optional()
|
|
36482
|
+
scheme: zod.z.enum(["http", "https"]).optional(),
|
|
36483
|
+
/** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
|
|
36484
|
+
* an address notification links can be flagged with, not a hub
|
|
36485
|
+
* route. Default `false`: racers and rankers never see it. */
|
|
36486
|
+
includeAppLink: zod.z.boolean().optional()
|
|
36403
36487
|
}), GetConnectionEndpointsResultSchema),
|
|
36404
36488
|
/**
|
|
36405
|
-
* The
|
|
36406
|
-
*
|
|
36407
|
-
*
|
|
36408
|
-
*
|
|
36489
|
+
* The address notification links are flagged with. `baseUrl: null` means
|
|
36490
|
+
* AUTO, which is the CamStack app link (D709): viewer taps go through
|
|
36491
|
+
* `https://camstack.zentik.app/…?hub=<appLinkHub>` and open the app. Any
|
|
36492
|
+
* hub address the palette lists can be flagged instead; then viewer taps,
|
|
36493
|
+
* attachment links and action buttons all use it. Signed media and
|
|
36494
|
+
* buttons never use the app link — with it flagged (or AUTO) they keep
|
|
36495
|
+
* the ranked hub ladder (a configured origin, a connected tunnel, then
|
|
36496
|
+
* the LAN), never loopback.
|
|
36409
36497
|
*
|
|
36410
36498
|
* Why an explicit marker: the automatic ranking cannot know which of
|
|
36411
36499
|
* several reachable addresses the RECIPIENT can actually open. A link is
|
|
@@ -36819,7 +36907,7 @@ var MeshPeerSchema = zod.z.object({
|
|
|
36819
36907
|
tags: zod.z.array(zod.z.string()).readonly(),
|
|
36820
36908
|
/** True when the peer advertises itself as an exit-node candidate. */
|
|
36821
36909
|
exitNodeOption: zod.z.boolean(),
|
|
36822
|
-
/** Subnet routes the peer advertises (e.g. `["192.168.
|
|
36910
|
+
/** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
|
|
36823
36911
|
advertisedRoutes: zod.z.array(zod.z.string()).readonly(),
|
|
36824
36912
|
/** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
|
|
36825
36913
|
userLogin: zod.z.string().nullable(),
|
|
@@ -38300,7 +38388,13 @@ var OsdOverlaySchema = zod.z.object({
|
|
|
38300
38388
|
*/
|
|
38301
38389
|
availablePositions: zod.z.array(OsdPositionEnum).readonly(),
|
|
38302
38390
|
/** Firmware does not allow editing the `text` field (e.g. timestamp). */
|
|
38303
|
-
readOnly: zod.z.boolean().optional()
|
|
38391
|
+
readOnly: zod.z.boolean().optional(),
|
|
38392
|
+
/**
|
|
38393
|
+
* Firmware text ceiling for this overlay. Absent = no known limit.
|
|
38394
|
+
* The OSD manager truncates here on every write — never stores its own
|
|
38395
|
+
* copy on the binding.
|
|
38396
|
+
*/
|
|
38397
|
+
maxCharacters: zod.z.number().int().positive().optional()
|
|
38304
38398
|
});
|
|
38305
38399
|
var OsdStatusSchema = zod.z.object({ overlays: zod.z.array(OsdOverlaySchema).readonly() });
|
|
38306
38400
|
var OsdOverlayPatchSchema = zod.z.object({
|
|
@@ -38398,7 +38492,9 @@ var OsdTemplateBindingSchema = zod.z.object({
|
|
|
38398
38492
|
valuePath: zod.z.string().min(1).max(64),
|
|
38399
38493
|
/** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
|
|
38400
38494
|
displayUnit: zod.z.string().max(16).optional(),
|
|
38401
|
-
maxDecimals: zod.z.number().int().min(0).max(4).default(1)
|
|
38495
|
+
maxDecimals: zod.z.number().int().min(0).max(4).default(1),
|
|
38496
|
+
/** Raw value → display text (same shape as slot binding). Empty string hides. */
|
|
38497
|
+
valueMap: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
38402
38498
|
});
|
|
38403
38499
|
/**
|
|
38404
38500
|
* A named template: bindings (alias → leaf field) + a parser that
|
|
@@ -38448,18 +38544,11 @@ var OsdSlotBindingSchema = zod.z.object({
|
|
|
38448
38544
|
source: OsdSourceSchema,
|
|
38449
38545
|
/** `${value}` and `${unit}` are substituted; every occurrence. */
|
|
38450
38546
|
template: zod.z.string().max(96).default("${value}"),
|
|
38451
|
-
/** Truncate with an ellipsis past this length. Absent = no limit. */
|
|
38452
|
-
maxCharacters: zod.z.number().int().min(4).max(64).optional(),
|
|
38453
38547
|
/**
|
|
38454
38548
|
* Decimal places for a numeric value. `0` yields an integer — the
|
|
38455
38549
|
* documented workaround for firmwares that reject `.` in overlay text.
|
|
38456
38550
|
*/
|
|
38457
38551
|
maxDecimals: zod.z.number().int().min(0).max(4).default(1),
|
|
38458
|
-
/**
|
|
38459
|
-
* Appended via `${unit}` when no unit conversion produces a label.
|
|
38460
|
-
* Prefer `source.displayUnit` for converted device-state values.
|
|
38461
|
-
*/
|
|
38462
|
-
unitLabel: zod.z.string().max(8).optional(),
|
|
38463
38552
|
/** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
|
|
38464
38553
|
valueMap: zod.z.record(zod.z.string(), zod.z.string()).optional(),
|
|
38465
38554
|
/** Time windows in which the slot is shown. Absent = always. */
|
|
@@ -38468,9 +38557,7 @@ var OsdSlotBindingSchema = zod.z.object({
|
|
|
38468
38557
|
* Display gate, in the notification centre's condition vocabulary.
|
|
38469
38558
|
* Only the keys reported by `getConditionSupport` are accepted.
|
|
38470
38559
|
*/
|
|
38471
|
-
conditions: NcConditionsSchema.optional()
|
|
38472
|
-
/** Rendered when the gate is closed or the value unreadable. Empty = hide. */
|
|
38473
|
-
fallbackText: zod.z.string().max(64).default("")
|
|
38560
|
+
conditions: NcConditionsSchema.optional()
|
|
38474
38561
|
});
|
|
38475
38562
|
/** One camera slot, as the operator sees it: firmware truth + our binding. */
|
|
38476
38563
|
var OsdSlotViewSchema = zod.z.object({
|
|
@@ -58637,12 +58724,14 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
58637
58724
|
{
|
|
58638
58725
|
id: "addons",
|
|
58639
58726
|
family: "system",
|
|
58640
|
-
label: "Addons",
|
|
58641
|
-
kinds: [
|
|
58642
|
-
|
|
58643
|
-
|
|
58644
|
-
|
|
58645
|
-
|
|
58727
|
+
label: "Addons stopped",
|
|
58728
|
+
kinds: ["addon-crash-loop"]
|
|
58729
|
+
},
|
|
58730
|
+
{
|
|
58731
|
+
id: "addon-updates",
|
|
58732
|
+
family: "system",
|
|
58733
|
+
label: "Addon updates",
|
|
58734
|
+
kinds: ["addon-update-available", "addon-updated"]
|
|
58646
58735
|
},
|
|
58647
58736
|
{
|
|
58648
58737
|
id: "updates",
|
|
@@ -58802,13 +58891,15 @@ var STATE_SUMMARY_SECTION_VARS = [
|
|
|
58802
58891
|
},
|
|
58803
58892
|
{
|
|
58804
58893
|
name: "summaryAddons",
|
|
58805
|
-
label: "Summary: addons",
|
|
58806
|
-
example: "
|
|
58807
|
-
kinds: [
|
|
58808
|
-
|
|
58809
|
-
|
|
58810
|
-
|
|
58811
|
-
|
|
58894
|
+
label: "Summary: addons stopped",
|
|
58895
|
+
example: "hub — recorder stopped after repeated crashes",
|
|
58896
|
+
kinds: ["addon-crash-loop"]
|
|
58897
|
+
},
|
|
58898
|
+
{
|
|
58899
|
+
name: "summaryAddonUpdates",
|
|
58900
|
+
label: "Summary: addon updates",
|
|
58901
|
+
example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
|
|
58902
|
+
kinds: ["addon-update-available", "addon-updated"]
|
|
58812
58903
|
},
|
|
58813
58904
|
{
|
|
58814
58905
|
name: "summaryUpdates",
|
|
@@ -58892,6 +58983,7 @@ var STATE_SUMMARY_VARS = [
|
|
|
58892
58983
|
[...RULE_BODY_ONLY],
|
|
58893
58984
|
[...RULE_TEXT],
|
|
58894
58985
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
58986
|
+
([...RULE_TEXT], [...COMBINED_ALARM]),
|
|
58895
58987
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
58896
58988
|
([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
|
|
58897
58989
|
([...RULE_TEXT], [...CONSUMABLE]),
|
|
@@ -58932,6 +59024,8 @@ var STATE_SUMMARY_VARS = [
|
|
|
58932
59024
|
[...RULE_TEXT],
|
|
58933
59025
|
[...RULE_TEXT],
|
|
58934
59026
|
[...RULE_TEXT],
|
|
59027
|
+
[...RULE_TEXT],
|
|
59028
|
+
[...RULE_TEXT],
|
|
58935
59029
|
...STATE_SUMMARY_VARS
|
|
58936
59030
|
];
|
|
58937
59031
|
new Set([
|
|
@@ -59657,6 +59751,12 @@ Object.defineProperty(exports, "CAMERA_SWITCH_ORDER", {
|
|
|
59657
59751
|
return CAMERA_SWITCH_ORDER;
|
|
59658
59752
|
}
|
|
59659
59753
|
});
|
|
59754
|
+
Object.defineProperty(exports, "CAMSTACK_APP_LINK_ORIGIN", {
|
|
59755
|
+
enumerable: true,
|
|
59756
|
+
get: function() {
|
|
59757
|
+
return CAMSTACK_APP_LINK_ORIGIN;
|
|
59758
|
+
}
|
|
59759
|
+
});
|
|
59660
59760
|
Object.defineProperty(exports, "CAP_NAMES_WITH_STATUS", {
|
|
59661
59761
|
enumerable: true,
|
|
59662
59762
|
get: function() {
|
|
@@ -60491,6 +60591,12 @@ Object.defineProperty(exports, "parseStreamParamsFormPatch", {
|
|
|
60491
60591
|
return parseStreamParamsFormPatch;
|
|
60492
60592
|
}
|
|
60493
60593
|
});
|
|
60594
|
+
Object.defineProperty(exports, "pickAppLinkHub", {
|
|
60595
|
+
enumerable: true,
|
|
60596
|
+
get: function() {
|
|
60597
|
+
return pickAppLinkHub;
|
|
60598
|
+
}
|
|
60599
|
+
});
|
|
60494
60600
|
Object.defineProperty(exports, "planComposition", {
|
|
60495
60601
|
enumerable: true,
|
|
60496
60602
|
get: function() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
1
|
+
import { IncomingMessage, ServerResponse, OutgoingHttpHeaders } from 'node:http';
|
|
2
2
|
export interface ReverseProxyOptions {
|
|
3
3
|
/** Addon listener base, e.g. `http://127.0.0.1:54123`. */
|
|
4
4
|
readonly baseUrl: string;
|
|
@@ -14,6 +14,16 @@ export interface ReverseProxyOptions {
|
|
|
14
14
|
*/
|
|
15
15
|
readonly replayBody?: Buffer;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* The upstream's response headers, minus the CORS policy, which belongs to the
|
|
19
|
+
* EDGE (D707). The hub stamps its allowlist verdict on `clientRes` with
|
|
20
|
+
* `setHeader` before hijacking, and Node merges those into `writeHead` — so an
|
|
21
|
+
* upstream `access-control-*` header would OVERRIDE the hub's answer: the
|
|
22
|
+
* authenticated file-server's `*` reached a refused origin this way. `Vary` is
|
|
23
|
+
* merged rather than replaced, so the edge's `Origin` survives an upstream
|
|
24
|
+
* `Accept-Encoding`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function edgeOwnedResponseHeaders(upRes: IncomingMessage, clientRes: ServerResponse): OutgoingHttpHeaders;
|
|
17
27
|
/** Reverse-proxy one request to an addon data-plane listener. Fire-and-forget:
|
|
18
28
|
* the caller must `reply.hijack()` (or otherwise stop the framework from also
|
|
19
29
|
* responding) before calling this, since we drive `clientRes` directly. */
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("./dist-
|
|
3
|
+
const require_dist = require("./dist-DePB7uwU.js");
|
|
4
4
|
const require_builtins_alerts_alerts_addon = require("./builtins/alerts/alerts.addon.js");
|
|
5
5
|
require("./builtins/alerts/index.js");
|
|
6
6
|
const require_builtins_calendars_calendars_addon = require("./builtins/calendars/calendars.addon.js");
|
|
@@ -31,7 +31,7 @@ require("./builtins/system-config/index.js");
|
|
|
31
31
|
const require_builtins_winston_logging_index = require("./builtins/winston-logging/index.js");
|
|
32
32
|
const require_event_loop_stall_monitor = require("./event-loop-stall-monitor-ey4mbofz.js");
|
|
33
33
|
const require_tls$1 = require("./tls-ue_Xefmv.js");
|
|
34
|
-
const require_child_cap_dispatch = require("./child-cap-dispatch-
|
|
34
|
+
const require_child_cap_dispatch = require("./child-cap-dispatch-CRqYKHAb.js");
|
|
35
35
|
const require_resource_monitor = require("./resource-monitor-BgWGRxN5.js");
|
|
36
36
|
const require_custom_action_registry = require("./custom-action-registry-acBY3mHA.js");
|
|
37
37
|
let zod = require("zod");
|
|
@@ -410,6 +410,38 @@ function canServeDataPlane(state) {
|
|
|
410
410
|
* headers + body), so `Content-Range`, backpressure and client-disconnect are all
|
|
411
411
|
* native. No bytes are buffered; nothing crosses the cap channel.
|
|
412
412
|
*/
|
|
413
|
+
/**
|
|
414
|
+
* The upstream's response headers, minus the CORS policy, which belongs to the
|
|
415
|
+
* EDGE (D707). The hub stamps its allowlist verdict on `clientRes` with
|
|
416
|
+
* `setHeader` before hijacking, and Node merges those into `writeHead` — so an
|
|
417
|
+
* upstream `access-control-*` header would OVERRIDE the hub's answer: the
|
|
418
|
+
* authenticated file-server's `*` reached a refused origin this way. `Vary` is
|
|
419
|
+
* merged rather than replaced, so the edge's `Origin` survives an upstream
|
|
420
|
+
* `Accept-Encoding`.
|
|
421
|
+
*/
|
|
422
|
+
function edgeOwnedResponseHeaders(upRes, clientRes) {
|
|
423
|
+
const out = {};
|
|
424
|
+
for (const [name, value] of Object.entries(upRes.headers)) {
|
|
425
|
+
if (value === void 0) continue;
|
|
426
|
+
const lower = name.toLowerCase();
|
|
427
|
+
if (lower.startsWith("access-control-")) continue;
|
|
428
|
+
out[lower] = value;
|
|
429
|
+
}
|
|
430
|
+
const edgeVary = clientRes.getHeader("vary");
|
|
431
|
+
const upVary = out["vary"];
|
|
432
|
+
if (typeof edgeVary === "string" && edgeVary.length > 0 && upVary !== void 0) {
|
|
433
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
434
|
+
const merged = [];
|
|
435
|
+
for (const part of [edgeVary, String(upVary)].join(",").split(",")) {
|
|
436
|
+
const token = part.trim();
|
|
437
|
+
if (token.length === 0 || tokens.has(token.toLowerCase())) continue;
|
|
438
|
+
tokens.add(token.toLowerCase());
|
|
439
|
+
merged.push(token);
|
|
440
|
+
}
|
|
441
|
+
out["vary"] = merged.join(", ");
|
|
442
|
+
}
|
|
443
|
+
return out;
|
|
444
|
+
}
|
|
413
445
|
/** Reverse-proxy one request to an addon data-plane listener. Fire-and-forget:
|
|
414
446
|
* the caller must `reply.hijack()` (or otherwise stop the framework from also
|
|
415
447
|
* responding) before calling this, since we drive `clientRes` directly. */
|
|
@@ -430,17 +462,11 @@ function proxyToUpstream(opts) {
|
|
|
430
462
|
path: upstreamPath,
|
|
431
463
|
headers
|
|
432
464
|
}, (upRes) => {
|
|
433
|
-
clientRes.writeHead(upRes.statusCode ?? 502,
|
|
434
|
-
"access-control-allow-origin": "*",
|
|
435
|
-
...upRes.headers
|
|
436
|
-
});
|
|
465
|
+
clientRes.writeHead(upRes.statusCode ?? 502, edgeOwnedResponseHeaders(upRes, clientRes));
|
|
437
466
|
upRes.pipe(clientRes);
|
|
438
467
|
});
|
|
439
468
|
upstream.on("error", (err) => {
|
|
440
|
-
if (!clientRes.headersSent) clientRes.writeHead(502, {
|
|
441
|
-
"content-type": "application/json",
|
|
442
|
-
"access-control-allow-origin": "*"
|
|
443
|
-
});
|
|
469
|
+
if (!clientRes.headersSent) clientRes.writeHead(502, { "content-type": "application/json" });
|
|
444
470
|
clientRes.end(JSON.stringify({
|
|
445
471
|
error: "data-plane upstream error",
|
|
446
472
|
detail: String(err)
|
|
@@ -7746,10 +7772,26 @@ function createDoorSettingsView(addonId, door, sections) {
|
|
|
7746
7772
|
//#region src/kernel/config-schema.ts
|
|
7747
7773
|
/** Default data directory — used when CAMSTACK_DATA env var is not set */
|
|
7748
7774
|
var DEFAULT_DATA_PATH = "camstack-data";
|
|
7775
|
+
/**
|
|
7776
|
+
* A list of browser origins from config.yaml (a YAML list) or from the
|
|
7777
|
+
* `CAMSTACK_CORS_ORIGINS` env var (one comma-separated string — env carries
|
|
7778
|
+
* only strings). Validation of each entry is the CORS policy's job
|
|
7779
|
+
* (`server/backend/src/api/cors/cors-policy.ts`), which logs what it rejects;
|
|
7780
|
+
* the schema only fixes the SHAPE so a typo cannot stop the hub from booting.
|
|
7781
|
+
*/
|
|
7782
|
+
var originListSchema = zod.z.preprocess((value) => typeof value === "string" ? value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) : value, zod.z.array(zod.z.string()));
|
|
7749
7783
|
var serverSchema = zod.z.object({
|
|
7750
7784
|
port: zod.z.number().default(4443),
|
|
7751
7785
|
host: zod.z.string().default("0.0.0.0"),
|
|
7752
|
-
dataPath: zod.z.string().default(DEFAULT_DATA_PATH)
|
|
7786
|
+
dataPath: zod.z.string().default(DEFAULT_DATA_PATH),
|
|
7787
|
+
/**
|
|
7788
|
+
* EXTRA browser origins allowed to call this hub cross-origin (D707), on
|
|
7789
|
+
* top of the built-ins: the hub's own origin, the hosted viewer PWA and the
|
|
7790
|
+
* native app's opaque `null` origin. Bootstrap-owned on purpose — widening
|
|
7791
|
+
* who may read hub responses in a browser is a security decision, and the
|
|
7792
|
+
* policy is live from the first request, before the settings store exists.
|
|
7793
|
+
*/
|
|
7794
|
+
corsOrigins: originListSchema.default([])
|
|
7753
7795
|
});
|
|
7754
7796
|
var authSchema = zod.z.object({
|
|
7755
7797
|
jwtSecret: zod.z.string().nullable().default(null),
|
|
@@ -10214,7 +10256,8 @@ var ENV_VAR_MAP = {
|
|
|
10214
10256
|
CAMSTACK_DATA: "server.dataPath",
|
|
10215
10257
|
CAMSTACK_JWT_SECRET: "auth.jwtSecret",
|
|
10216
10258
|
CAMSTACK_ADMIN_USER: "auth.adminUsername",
|
|
10217
|
-
CAMSTACK_ADMIN_PASS: "auth.adminPassword"
|
|
10259
|
+
CAMSTACK_ADMIN_PASS: "auth.adminPassword",
|
|
10260
|
+
CAMSTACK_CORS_ORIGINS: "server.corsOrigins"
|
|
10218
10261
|
};
|
|
10219
10262
|
/** config.yaml, env overrides applied, parsed. */
|
|
10220
10263
|
function loadBootstrapConfig(configPath) {
|