@camstack/addon-cloudflare 1.2.26 → 1.2.27
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/{dist-CbCaiEpw.js → dist-B3KpK67G.js} +119 -13
- package/dist/{dist-DnohT9Id.mjs → dist-BgYOkp3_.mjs} +119 -13
- package/dist/tunnel/cloudflare-tunnel.addon.js +29 -3
- package/dist/tunnel/cloudflare-tunnel.addon.mjs +29 -3
- package/dist/turn/cloudflare-turn.addon.js +1 -1
- package/dist/turn/cloudflare-turn.addon.mjs +1 -1
- package/package.json +1 -1
|
@@ -5800,6 +5800,13 @@ var BaseAddon = class {
|
|
|
5800
5800
|
_readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
|
|
5801
5801
|
/** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
|
|
5802
5802
|
_registeredCapNames = [];
|
|
5803
|
+
/**
|
|
5804
|
+
* True only after `readAddonStore` actually answered. Constructor
|
|
5805
|
+
* defaults look like stored config when the store is down — a forked
|
|
5806
|
+
* addon that auto-starts from those defaults (cloudflare-tunnel quick
|
|
5807
|
+
* mode, 2026-08-25) is not "the operator chose this".
|
|
5808
|
+
*/
|
|
5809
|
+
settingsStoreReady = false;
|
|
5803
5810
|
/** Default config values. Provided via constructor. */
|
|
5804
5811
|
defaults;
|
|
5805
5812
|
constructor(defaults) {
|
|
@@ -6200,7 +6207,9 @@ var BaseAddon = class {
|
|
|
6200
6207
|
];
|
|
6201
6208
|
let lastErr;
|
|
6202
6209
|
for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
|
|
6203
|
-
|
|
6210
|
+
const stored = await settings.readAddonStore() ?? {};
|
|
6211
|
+
this.settingsStoreReady = true;
|
|
6212
|
+
return stored;
|
|
6204
6213
|
} catch (err) {
|
|
6205
6214
|
lastErr = err;
|
|
6206
6215
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -6208,6 +6217,7 @@ var BaseAddon = class {
|
|
|
6208
6217
|
if (attempt === delaysMs.length) break;
|
|
6209
6218
|
await new Promise((r) => setTimeout(r, delaysMs[attempt]));
|
|
6210
6219
|
}
|
|
6220
|
+
this.settingsStoreReady = false;
|
|
6211
6221
|
this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
|
|
6212
6222
|
return {};
|
|
6213
6223
|
}
|
|
@@ -8095,6 +8105,12 @@ var ModelVariantGroupSchema = object({
|
|
|
8095
8105
|
*/
|
|
8096
8106
|
resolution: number().int().positive().optional()
|
|
8097
8107
|
});
|
|
8108
|
+
var ModelProviderIdSchema = _enum([
|
|
8109
|
+
"camstack",
|
|
8110
|
+
"frigate",
|
|
8111
|
+
"scrypted",
|
|
8112
|
+
"custom"
|
|
8113
|
+
]);
|
|
8098
8114
|
var ModelCatalogEntrySchema = object({
|
|
8099
8115
|
id: string(),
|
|
8100
8116
|
name: string(),
|
|
@@ -8192,6 +8208,12 @@ var ModelCatalogEntrySchema = object({
|
|
|
8192
8208
|
*/
|
|
8193
8209
|
group: ModelVariantGroupSchema.optional(),
|
|
8194
8210
|
/**
|
|
8211
|
+
* Catalog source for the pipeline stepper's provider-first picker. Absent on
|
|
8212
|
+
* built-in CamStack entries (treated as `camstack`) and on registry rows
|
|
8213
|
+
* persisted before this field existed (`inferModelProvider` fills those).
|
|
8214
|
+
*/
|
|
8215
|
+
provider: ModelProviderIdSchema.optional(),
|
|
8216
|
+
/**
|
|
8195
8217
|
* Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
|
|
8196
8218
|
* applies (Frigate / COCO public catalog). Set on a custom model whose raw
|
|
8197
8219
|
* labels already ARE the CamStack macros (Scrypted identity map).
|
|
@@ -14404,12 +14426,15 @@ var NcOccupancyConditionSchema = object({
|
|
|
14404
14426
|
* there is no second switch that can disagree with the first and every rule
|
|
14405
14427
|
* authored before the decision migrates for free (`audioModeOf`):
|
|
14406
14428
|
*
|
|
14407
|
-
* - **LABEL mode — `labels` present.** The rule fires
|
|
14408
|
-
*
|
|
14409
|
-
* `hitPercent` and `samplingSeconds` are ignored
|
|
14410
|
-
*
|
|
14411
|
-
*
|
|
14412
|
-
*
|
|
14429
|
+
* - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
|
|
14430
|
+
* labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
|
|
14431
|
+
* `hitPercent` and `samplingSeconds` are still ignored — a percentage of
|
|
14432
|
+
* frames is the wrong question for a classifier that labels 1–3 frames
|
|
14433
|
+
* per episode. The count window is the brake that drops a single-frame
|
|
14434
|
+
* false positive; the rule's own `throttle` cooldown is the other. The
|
|
14435
|
+
* per-label confidence floor is the analyzer's (`classificationMinScore`,
|
|
14436
|
+
* per device) — a label only reaches this condition if the classifier was
|
|
14437
|
+
* already confident enough. `confirmHits: 1` restores first-frame fire.
|
|
14413
14438
|
* - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
|
|
14414
14439
|
* the condition: at least `hitPercent`% of the samples over
|
|
14415
14440
|
* `samplingSeconds` must be at or above `dbThreshold` dBFS (see
|
|
@@ -14436,14 +14461,22 @@ var NcOccupancyConditionSchema = object({
|
|
|
14436
14461
|
* an operator who typed `dog` mean the same thing.
|
|
14437
14462
|
*/
|
|
14438
14463
|
var NcAudioConditionSchema = object({
|
|
14439
|
-
/** LABEL MODE: audio macro labels. Present ⇒
|
|
14464
|
+
/** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
|
|
14440
14465
|
labels: array(string().min(1)).min(1).optional(),
|
|
14441
14466
|
/** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
|
|
14442
14467
|
dbThreshold: number().min(-96).max(0).optional(),
|
|
14443
14468
|
/** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
|
|
14444
14469
|
hitPercent: number().int().min(1).max(100).default(60),
|
|
14445
14470
|
/** LEVEL MODE ONLY: length of the sampling window in seconds. */
|
|
14446
|
-
samplingSeconds: number().int().min(1).max(300).default(10)
|
|
14471
|
+
samplingSeconds: number().int().min(1).max(300).default(10),
|
|
14472
|
+
/**
|
|
14473
|
+
* LABEL MODE: how many labelled frames must land inside
|
|
14474
|
+
* {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
|
|
14475
|
+
* Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
|
|
14476
|
+
*/
|
|
14477
|
+
confirmHits: number().int().min(1).max(20).optional(),
|
|
14478
|
+
/** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
|
|
14479
|
+
confirmWindowSec: number().int().min(1).max(60).optional()
|
|
14447
14480
|
});
|
|
14448
14481
|
/**
|
|
14449
14482
|
* Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
|
|
@@ -16817,6 +16850,46 @@ var RecentTracksPageSchema = object({
|
|
|
16817
16850
|
/** Cursor for the next page, or null when this page is the last. */
|
|
16818
16851
|
nextCursor: string().nullable()
|
|
16819
16852
|
});
|
|
16853
|
+
var LIST_GROUPS_DEFAULT_LIMIT = 40;
|
|
16854
|
+
var LIST_GROUPS_MAX_LIMIT = 100;
|
|
16855
|
+
var AnalyticsGroupRecordSchema = object({
|
|
16856
|
+
id: string(),
|
|
16857
|
+
deviceId: number().int(),
|
|
16858
|
+
openedAt: number().int(),
|
|
16859
|
+
closedAt: number().int(),
|
|
16860
|
+
timestamp: number().int(),
|
|
16861
|
+
memberCount: number().int(),
|
|
16862
|
+
memberTrackIds: array(string()).readonly(),
|
|
16863
|
+
className: string(),
|
|
16864
|
+
classes: array(string()).readonly(),
|
|
16865
|
+
/** Relative event-media path, or null when the group has no picture yet. */
|
|
16866
|
+
mediaUrl: string().nullable(),
|
|
16867
|
+
singleton: boolean()
|
|
16868
|
+
});
|
|
16869
|
+
var AnalyticsGroupMemberSchema = object({
|
|
16870
|
+
trackId: string(),
|
|
16871
|
+
deviceId: number().int(),
|
|
16872
|
+
className: string(),
|
|
16873
|
+
firstSeen: number().int(),
|
|
16874
|
+
lastSeen: number().int(),
|
|
16875
|
+
mediaUrl: string().nullable()
|
|
16876
|
+
});
|
|
16877
|
+
var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
|
|
16878
|
+
var ListGroupsQueryInput = object({
|
|
16879
|
+
/** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
|
|
16880
|
+
deviceIds: array(number()),
|
|
16881
|
+
/** Window lower bound on `closedAt` (inclusive). */
|
|
16882
|
+
since: number().optional(),
|
|
16883
|
+
/** Window upper bound on `openedAt` (inclusive). */
|
|
16884
|
+
until: number().optional(),
|
|
16885
|
+
limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
|
|
16886
|
+
/** Opaque continuation cursor from a previous page's `nextCursor`. */
|
|
16887
|
+
cursor: string().optional()
|
|
16888
|
+
});
|
|
16889
|
+
var ListGroupsPageSchema = object({
|
|
16890
|
+
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
16891
|
+
nextCursor: string().nullable()
|
|
16892
|
+
});
|
|
16820
16893
|
var KeyEventQueryInput = object({
|
|
16821
16894
|
deviceId: number(),
|
|
16822
16895
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
@@ -16892,7 +16965,9 @@ var TrackCascadeCountsSchema = object({
|
|
|
16892
16965
|
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
16893
16966
|
plates: number().int(),
|
|
16894
16967
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
16895
|
-
embeddings: number().int()
|
|
16968
|
+
embeddings: number().int(),
|
|
16969
|
+
/** Group membership + group rows removed with their last member (best-effort). */
|
|
16970
|
+
groups: number().int()
|
|
16896
16971
|
});
|
|
16897
16972
|
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
16898
16973
|
var DiskReconcileCountsSchema = object({
|
|
@@ -17038,7 +17113,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
17038
17113
|
* stationary registry). Default false: the timeline lists passages,
|
|
17039
17114
|
* not parking records (operator decision, 2026-08-15). */
|
|
17040
17115
|
includeStationary: boolean().optional()
|
|
17041
|
-
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(
|
|
17116
|
+
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
|
|
17117
|
+
deviceId: number(),
|
|
17118
|
+
groupId: string().min(1)
|
|
17119
|
+
}), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
|
|
17042
17120
|
kind: "mutation",
|
|
17043
17121
|
auth: "admin"
|
|
17044
17122
|
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
|
|
@@ -17348,7 +17426,8 @@ var PipelineModelOptionSchema = object({
|
|
|
17348
17426
|
sizeMB: number()
|
|
17349
17427
|
})),
|
|
17350
17428
|
group: ModelVariantGroupSchema.optional(),
|
|
17351
|
-
legacy: boolean().optional()
|
|
17429
|
+
legacy: boolean().optional(),
|
|
17430
|
+
provider: ModelProviderIdSchema.optional()
|
|
17352
17431
|
});
|
|
17353
17432
|
var ConfigFieldBridge = custom();
|
|
17354
17433
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -23955,7 +24034,12 @@ var PlateInfoSchema = object({
|
|
|
23955
24034
|
plateBbox: BoundingBoxSchema.optional(),
|
|
23956
24035
|
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
23957
24036
|
keyFrameMediaKey: string().optional(),
|
|
23958
|
-
base64: string().optional()
|
|
24037
|
+
base64: string().optional(),
|
|
24038
|
+
/**
|
|
24039
|
+
* Same crop as a data-plane URL. `getPlateByTrack` returns this and
|
|
24040
|
+
* never inlines JPEG; `listPlates` still inlines for the admin-ui.
|
|
24041
|
+
*/
|
|
24042
|
+
cropUrl: string().optional()
|
|
23959
24043
|
});
|
|
23960
24044
|
var MediaFileLiteSchema = object({
|
|
23961
24045
|
key: string(),
|
|
@@ -29425,6 +29509,12 @@ Object.freeze({
|
|
|
29425
29509
|
addonId: null,
|
|
29426
29510
|
access: "view"
|
|
29427
29511
|
},
|
|
29512
|
+
"pipelineAnalytics.getGroup": {
|
|
29513
|
+
capName: "pipeline-analytics",
|
|
29514
|
+
capScope: "device",
|
|
29515
|
+
addonId: null,
|
|
29516
|
+
access: "view"
|
|
29517
|
+
},
|
|
29428
29518
|
"pipelineAnalytics.getKeyEvents": {
|
|
29429
29519
|
capName: "pipeline-analytics",
|
|
29430
29520
|
capScope: "device",
|
|
@@ -29509,6 +29599,12 @@ Object.freeze({
|
|
|
29509
29599
|
addonId: null,
|
|
29510
29600
|
access: "view"
|
|
29511
29601
|
},
|
|
29602
|
+
"pipelineAnalytics.listGroups": {
|
|
29603
|
+
capName: "pipeline-analytics",
|
|
29604
|
+
capScope: "device",
|
|
29605
|
+
addonId: null,
|
|
29606
|
+
access: "view"
|
|
29607
|
+
},
|
|
29512
29608
|
"pipelineAnalytics.listOpsLog": {
|
|
29513
29609
|
capName: "pipeline-analytics",
|
|
29514
29610
|
capScope: "device",
|
|
@@ -32928,6 +33024,11 @@ Object.freeze({
|
|
|
32928
33024
|
form: "single",
|
|
32929
33025
|
optional: false
|
|
32930
33026
|
}],
|
|
33027
|
+
"pipelineAnalytics.getGroup": [{
|
|
33028
|
+
name: "deviceId",
|
|
33029
|
+
form: "single",
|
|
33030
|
+
optional: false
|
|
33031
|
+
}],
|
|
32931
33032
|
"pipelineAnalytics.getKeyEvents": [{
|
|
32932
33033
|
name: "deviceId",
|
|
32933
33034
|
form: "single",
|
|
@@ -32983,6 +33084,11 @@ Object.freeze({
|
|
|
32983
33084
|
form: "array",
|
|
32984
33085
|
optional: false
|
|
32985
33086
|
}],
|
|
33087
|
+
"pipelineAnalytics.listGroups": [{
|
|
33088
|
+
name: "deviceIds",
|
|
33089
|
+
form: "array",
|
|
33090
|
+
optional: false
|
|
33091
|
+
}],
|
|
32986
33092
|
"pipelineAnalytics.listOpsLog": [{
|
|
32987
33093
|
name: "deviceId",
|
|
32988
33094
|
form: "single",
|
|
@@ -5800,6 +5800,13 @@ var BaseAddon = class {
|
|
|
5800
5800
|
_readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
|
|
5801
5801
|
/** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
|
|
5802
5802
|
_registeredCapNames = [];
|
|
5803
|
+
/**
|
|
5804
|
+
* True only after `readAddonStore` actually answered. Constructor
|
|
5805
|
+
* defaults look like stored config when the store is down — a forked
|
|
5806
|
+
* addon that auto-starts from those defaults (cloudflare-tunnel quick
|
|
5807
|
+
* mode, 2026-08-25) is not "the operator chose this".
|
|
5808
|
+
*/
|
|
5809
|
+
settingsStoreReady = false;
|
|
5803
5810
|
/** Default config values. Provided via constructor. */
|
|
5804
5811
|
defaults;
|
|
5805
5812
|
constructor(defaults) {
|
|
@@ -6200,7 +6207,9 @@ var BaseAddon = class {
|
|
|
6200
6207
|
];
|
|
6201
6208
|
let lastErr;
|
|
6202
6209
|
for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
|
|
6203
|
-
|
|
6210
|
+
const stored = await settings.readAddonStore() ?? {};
|
|
6211
|
+
this.settingsStoreReady = true;
|
|
6212
|
+
return stored;
|
|
6204
6213
|
} catch (err) {
|
|
6205
6214
|
lastErr = err;
|
|
6206
6215
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -6208,6 +6217,7 @@ var BaseAddon = class {
|
|
|
6208
6217
|
if (attempt === delaysMs.length) break;
|
|
6209
6218
|
await new Promise((r) => setTimeout(r, delaysMs[attempt]));
|
|
6210
6219
|
}
|
|
6220
|
+
this.settingsStoreReady = false;
|
|
6211
6221
|
this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
|
|
6212
6222
|
return {};
|
|
6213
6223
|
}
|
|
@@ -8095,6 +8105,12 @@ var ModelVariantGroupSchema = object({
|
|
|
8095
8105
|
*/
|
|
8096
8106
|
resolution: number().int().positive().optional()
|
|
8097
8107
|
});
|
|
8108
|
+
var ModelProviderIdSchema = _enum([
|
|
8109
|
+
"camstack",
|
|
8110
|
+
"frigate",
|
|
8111
|
+
"scrypted",
|
|
8112
|
+
"custom"
|
|
8113
|
+
]);
|
|
8098
8114
|
var ModelCatalogEntrySchema = object({
|
|
8099
8115
|
id: string(),
|
|
8100
8116
|
name: string(),
|
|
@@ -8192,6 +8208,12 @@ var ModelCatalogEntrySchema = object({
|
|
|
8192
8208
|
*/
|
|
8193
8209
|
group: ModelVariantGroupSchema.optional(),
|
|
8194
8210
|
/**
|
|
8211
|
+
* Catalog source for the pipeline stepper's provider-first picker. Absent on
|
|
8212
|
+
* built-in CamStack entries (treated as `camstack`) and on registry rows
|
|
8213
|
+
* persisted before this field existed (`inferModelProvider` fills those).
|
|
8214
|
+
*/
|
|
8215
|
+
provider: ModelProviderIdSchema.optional(),
|
|
8216
|
+
/**
|
|
8195
8217
|
* Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
|
|
8196
8218
|
* applies (Frigate / COCO public catalog). Set on a custom model whose raw
|
|
8197
8219
|
* labels already ARE the CamStack macros (Scrypted identity map).
|
|
@@ -14404,12 +14426,15 @@ var NcOccupancyConditionSchema = object({
|
|
|
14404
14426
|
* there is no second switch that can disagree with the first and every rule
|
|
14405
14427
|
* authored before the decision migrates for free (`audioModeOf`):
|
|
14406
14428
|
*
|
|
14407
|
-
* - **LABEL mode — `labels` present.** The rule fires
|
|
14408
|
-
*
|
|
14409
|
-
* `hitPercent` and `samplingSeconds` are ignored
|
|
14410
|
-
*
|
|
14411
|
-
*
|
|
14412
|
-
*
|
|
14429
|
+
* - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
|
|
14430
|
+
* labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
|
|
14431
|
+
* `hitPercent` and `samplingSeconds` are still ignored — a percentage of
|
|
14432
|
+
* frames is the wrong question for a classifier that labels 1–3 frames
|
|
14433
|
+
* per episode. The count window is the brake that drops a single-frame
|
|
14434
|
+
* false positive; the rule's own `throttle` cooldown is the other. The
|
|
14435
|
+
* per-label confidence floor is the analyzer's (`classificationMinScore`,
|
|
14436
|
+
* per device) — a label only reaches this condition if the classifier was
|
|
14437
|
+
* already confident enough. `confirmHits: 1` restores first-frame fire.
|
|
14413
14438
|
* - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
|
|
14414
14439
|
* the condition: at least `hitPercent`% of the samples over
|
|
14415
14440
|
* `samplingSeconds` must be at or above `dbThreshold` dBFS (see
|
|
@@ -14436,14 +14461,22 @@ var NcOccupancyConditionSchema = object({
|
|
|
14436
14461
|
* an operator who typed `dog` mean the same thing.
|
|
14437
14462
|
*/
|
|
14438
14463
|
var NcAudioConditionSchema = object({
|
|
14439
|
-
/** LABEL MODE: audio macro labels. Present ⇒
|
|
14464
|
+
/** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
|
|
14440
14465
|
labels: array(string().min(1)).min(1).optional(),
|
|
14441
14466
|
/** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
|
|
14442
14467
|
dbThreshold: number().min(-96).max(0).optional(),
|
|
14443
14468
|
/** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
|
|
14444
14469
|
hitPercent: number().int().min(1).max(100).default(60),
|
|
14445
14470
|
/** LEVEL MODE ONLY: length of the sampling window in seconds. */
|
|
14446
|
-
samplingSeconds: number().int().min(1).max(300).default(10)
|
|
14471
|
+
samplingSeconds: number().int().min(1).max(300).default(10),
|
|
14472
|
+
/**
|
|
14473
|
+
* LABEL MODE: how many labelled frames must land inside
|
|
14474
|
+
* {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
|
|
14475
|
+
* Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
|
|
14476
|
+
*/
|
|
14477
|
+
confirmHits: number().int().min(1).max(20).optional(),
|
|
14478
|
+
/** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
|
|
14479
|
+
confirmWindowSec: number().int().min(1).max(60).optional()
|
|
14447
14480
|
});
|
|
14448
14481
|
/**
|
|
14449
14482
|
* Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
|
|
@@ -16817,6 +16850,46 @@ var RecentTracksPageSchema = object({
|
|
|
16817
16850
|
/** Cursor for the next page, or null when this page is the last. */
|
|
16818
16851
|
nextCursor: string().nullable()
|
|
16819
16852
|
});
|
|
16853
|
+
var LIST_GROUPS_DEFAULT_LIMIT = 40;
|
|
16854
|
+
var LIST_GROUPS_MAX_LIMIT = 100;
|
|
16855
|
+
var AnalyticsGroupRecordSchema = object({
|
|
16856
|
+
id: string(),
|
|
16857
|
+
deviceId: number().int(),
|
|
16858
|
+
openedAt: number().int(),
|
|
16859
|
+
closedAt: number().int(),
|
|
16860
|
+
timestamp: number().int(),
|
|
16861
|
+
memberCount: number().int(),
|
|
16862
|
+
memberTrackIds: array(string()).readonly(),
|
|
16863
|
+
className: string(),
|
|
16864
|
+
classes: array(string()).readonly(),
|
|
16865
|
+
/** Relative event-media path, or null when the group has no picture yet. */
|
|
16866
|
+
mediaUrl: string().nullable(),
|
|
16867
|
+
singleton: boolean()
|
|
16868
|
+
});
|
|
16869
|
+
var AnalyticsGroupMemberSchema = object({
|
|
16870
|
+
trackId: string(),
|
|
16871
|
+
deviceId: number().int(),
|
|
16872
|
+
className: string(),
|
|
16873
|
+
firstSeen: number().int(),
|
|
16874
|
+
lastSeen: number().int(),
|
|
16875
|
+
mediaUrl: string().nullable()
|
|
16876
|
+
});
|
|
16877
|
+
var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
|
|
16878
|
+
var ListGroupsQueryInput = object({
|
|
16879
|
+
/** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
|
|
16880
|
+
deviceIds: array(number()),
|
|
16881
|
+
/** Window lower bound on `closedAt` (inclusive). */
|
|
16882
|
+
since: number().optional(),
|
|
16883
|
+
/** Window upper bound on `openedAt` (inclusive). */
|
|
16884
|
+
until: number().optional(),
|
|
16885
|
+
limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
|
|
16886
|
+
/** Opaque continuation cursor from a previous page's `nextCursor`. */
|
|
16887
|
+
cursor: string().optional()
|
|
16888
|
+
});
|
|
16889
|
+
var ListGroupsPageSchema = object({
|
|
16890
|
+
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
16891
|
+
nextCursor: string().nullable()
|
|
16892
|
+
});
|
|
16820
16893
|
var KeyEventQueryInput = object({
|
|
16821
16894
|
deviceId: number(),
|
|
16822
16895
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
@@ -16892,7 +16965,9 @@ var TrackCascadeCountsSchema = object({
|
|
|
16892
16965
|
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
16893
16966
|
plates: number().int(),
|
|
16894
16967
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
16895
|
-
embeddings: number().int()
|
|
16968
|
+
embeddings: number().int(),
|
|
16969
|
+
/** Group membership + group rows removed with their last member (best-effort). */
|
|
16970
|
+
groups: number().int()
|
|
16896
16971
|
});
|
|
16897
16972
|
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
16898
16973
|
var DiskReconcileCountsSchema = object({
|
|
@@ -17038,7 +17113,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
17038
17113
|
* stationary registry). Default false: the timeline lists passages,
|
|
17039
17114
|
* not parking records (operator decision, 2026-08-15). */
|
|
17040
17115
|
includeStationary: boolean().optional()
|
|
17041
|
-
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(
|
|
17116
|
+
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
|
|
17117
|
+
deviceId: number(),
|
|
17118
|
+
groupId: string().min(1)
|
|
17119
|
+
}), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
|
|
17042
17120
|
kind: "mutation",
|
|
17043
17121
|
auth: "admin"
|
|
17044
17122
|
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
|
|
@@ -17348,7 +17426,8 @@ var PipelineModelOptionSchema = object({
|
|
|
17348
17426
|
sizeMB: number()
|
|
17349
17427
|
})),
|
|
17350
17428
|
group: ModelVariantGroupSchema.optional(),
|
|
17351
|
-
legacy: boolean().optional()
|
|
17429
|
+
legacy: boolean().optional(),
|
|
17430
|
+
provider: ModelProviderIdSchema.optional()
|
|
17352
17431
|
});
|
|
17353
17432
|
var ConfigFieldBridge = custom();
|
|
17354
17433
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -23955,7 +24034,12 @@ var PlateInfoSchema = object({
|
|
|
23955
24034
|
plateBbox: BoundingBoxSchema.optional(),
|
|
23956
24035
|
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
23957
24036
|
keyFrameMediaKey: string().optional(),
|
|
23958
|
-
base64: string().optional()
|
|
24037
|
+
base64: string().optional(),
|
|
24038
|
+
/**
|
|
24039
|
+
* Same crop as a data-plane URL. `getPlateByTrack` returns this and
|
|
24040
|
+
* never inlines JPEG; `listPlates` still inlines for the admin-ui.
|
|
24041
|
+
*/
|
|
24042
|
+
cropUrl: string().optional()
|
|
23959
24043
|
});
|
|
23960
24044
|
var MediaFileLiteSchema = object({
|
|
23961
24045
|
key: string(),
|
|
@@ -29425,6 +29509,12 @@ Object.freeze({
|
|
|
29425
29509
|
addonId: null,
|
|
29426
29510
|
access: "view"
|
|
29427
29511
|
},
|
|
29512
|
+
"pipelineAnalytics.getGroup": {
|
|
29513
|
+
capName: "pipeline-analytics",
|
|
29514
|
+
capScope: "device",
|
|
29515
|
+
addonId: null,
|
|
29516
|
+
access: "view"
|
|
29517
|
+
},
|
|
29428
29518
|
"pipelineAnalytics.getKeyEvents": {
|
|
29429
29519
|
capName: "pipeline-analytics",
|
|
29430
29520
|
capScope: "device",
|
|
@@ -29509,6 +29599,12 @@ Object.freeze({
|
|
|
29509
29599
|
addonId: null,
|
|
29510
29600
|
access: "view"
|
|
29511
29601
|
},
|
|
29602
|
+
"pipelineAnalytics.listGroups": {
|
|
29603
|
+
capName: "pipeline-analytics",
|
|
29604
|
+
capScope: "device",
|
|
29605
|
+
addonId: null,
|
|
29606
|
+
access: "view"
|
|
29607
|
+
},
|
|
29512
29608
|
"pipelineAnalytics.listOpsLog": {
|
|
29513
29609
|
capName: "pipeline-analytics",
|
|
29514
29610
|
capScope: "device",
|
|
@@ -32928,6 +33024,11 @@ Object.freeze({
|
|
|
32928
33024
|
form: "single",
|
|
32929
33025
|
optional: false
|
|
32930
33026
|
}],
|
|
33027
|
+
"pipelineAnalytics.getGroup": [{
|
|
33028
|
+
name: "deviceId",
|
|
33029
|
+
form: "single",
|
|
33030
|
+
optional: false
|
|
33031
|
+
}],
|
|
32931
33032
|
"pipelineAnalytics.getKeyEvents": [{
|
|
32932
33033
|
name: "deviceId",
|
|
32933
33034
|
form: "single",
|
|
@@ -32983,6 +33084,11 @@ Object.freeze({
|
|
|
32983
33084
|
form: "array",
|
|
32984
33085
|
optional: false
|
|
32985
33086
|
}],
|
|
33087
|
+
"pipelineAnalytics.listGroups": [{
|
|
33088
|
+
name: "deviceIds",
|
|
33089
|
+
form: "array",
|
|
33090
|
+
optional: false
|
|
33091
|
+
}],
|
|
32986
33092
|
"pipelineAnalytics.listOpsLog": [{
|
|
32987
33093
|
name: "deviceId",
|
|
32988
33094
|
form: "single",
|
|
@@ -21,7 +21,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
|
-
const require_dist = require("../dist-
|
|
24
|
+
const require_dist = require("../dist-B3KpK67G.js");
|
|
25
25
|
let node_crypto = require("node:crypto");
|
|
26
26
|
let node_path = require("node:path");
|
|
27
27
|
node_path = __toESM(node_path);
|
|
@@ -758,6 +758,24 @@ var cloudflareTunnelActions = require_dist.defineCustomActions({
|
|
|
758
758
|
})).readonly() }))
|
|
759
759
|
});
|
|
760
760
|
//#endregion
|
|
761
|
+
//#region src/tunnel/auto-start.ts
|
|
762
|
+
/**
|
|
763
|
+
* Whether cloudflare-tunnel may spawn `cloudflared` at boot / config-reload.
|
|
764
|
+
*
|
|
765
|
+
* Constructor defaults are `mode: 'quick'`. A settings store that has not
|
|
766
|
+
* answered therefore looks exactly like "the operator wants a trycloudflare
|
|
767
|
+
* URL". Auto-starting that is how a named hostname (the one the notification
|
|
768
|
+
* centre still mints) went Cloudflare 530: the named tunnel never connected.
|
|
769
|
+
*
|
|
770
|
+
* An empty store that DID answer is a real choice — a fresh install still
|
|
771
|
+
* auto-starts quick. Only "never answered" is refused.
|
|
772
|
+
*/
|
|
773
|
+
function shouldAutoStartTunnel(input) {
|
|
774
|
+
if (!input.settingsStoreReady) return false;
|
|
775
|
+
if (input.mode === "quick") return true;
|
|
776
|
+
return input.hasCustomToken && input.hasCustomHostname;
|
|
777
|
+
}
|
|
778
|
+
//#endregion
|
|
761
779
|
//#region src/tunnel/cloudflare-tunnel.addon.ts
|
|
762
780
|
/**
|
|
763
781
|
* Cloudflare Tunnel — exposes CamStack via Cloudflare's network.
|
|
@@ -882,6 +900,10 @@ var CloudflareTunnelAddon = class extends require_dist.BaseAddon {
|
|
|
882
900
|
}
|
|
883
901
|
});
|
|
884
902
|
});
|
|
903
|
+
else if (!this.settingsStoreReady) this.ctx.logger.warn("boot auto-start skipped — settings store did not answer; defaults are not an operator choice", { tags: {
|
|
904
|
+
topic: "tunnel",
|
|
905
|
+
phase: "auto-start-skipped"
|
|
906
|
+
} });
|
|
885
907
|
return {
|
|
886
908
|
providers: [{
|
|
887
909
|
capability: require_dist.networkAccessCapability,
|
|
@@ -903,8 +925,12 @@ var CloudflareTunnelAddon = class extends require_dist.BaseAddon {
|
|
|
903
925
|
* auto-start and by `onConfigChanged` to decide whether a change
|
|
904
926
|
* warrants a respawn even when no instance was previously running. */
|
|
905
927
|
isRunCapable() {
|
|
906
|
-
|
|
907
|
-
|
|
928
|
+
return shouldAutoStartTunnel({
|
|
929
|
+
settingsStoreReady: this.settingsStoreReady,
|
|
930
|
+
mode: this.config.mode,
|
|
931
|
+
hasCustomToken: !!this.config.customTunnelToken,
|
|
932
|
+
hasCustomHostname: !!this.config.customHostname
|
|
933
|
+
});
|
|
908
934
|
}
|
|
909
935
|
async onShutdown() {
|
|
910
936
|
if (this.service) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as BaseAddon, c as boolean, d as number, f as object, m as EventCategory, n as defineCustomActions, o as _enum, p as string, r as networkAccessCapability, s as array, t as customAction, u as literal } from "../dist-
|
|
1
|
+
import { a as BaseAddon, c as boolean, d as number, f as object, m as EventCategory, n as defineCustomActions, o as _enum, p as string, r as networkAccessCapability, s as array, t as customAction, u as literal } from "../dist-BgYOkp3_.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
@@ -731,6 +731,24 @@ var cloudflareTunnelActions = defineCustomActions({
|
|
|
731
731
|
})).readonly() }))
|
|
732
732
|
});
|
|
733
733
|
//#endregion
|
|
734
|
+
//#region src/tunnel/auto-start.ts
|
|
735
|
+
/**
|
|
736
|
+
* Whether cloudflare-tunnel may spawn `cloudflared` at boot / config-reload.
|
|
737
|
+
*
|
|
738
|
+
* Constructor defaults are `mode: 'quick'`. A settings store that has not
|
|
739
|
+
* answered therefore looks exactly like "the operator wants a trycloudflare
|
|
740
|
+
* URL". Auto-starting that is how a named hostname (the one the notification
|
|
741
|
+
* centre still mints) went Cloudflare 530: the named tunnel never connected.
|
|
742
|
+
*
|
|
743
|
+
* An empty store that DID answer is a real choice — a fresh install still
|
|
744
|
+
* auto-starts quick. Only "never answered" is refused.
|
|
745
|
+
*/
|
|
746
|
+
function shouldAutoStartTunnel(input) {
|
|
747
|
+
if (!input.settingsStoreReady) return false;
|
|
748
|
+
if (input.mode === "quick") return true;
|
|
749
|
+
return input.hasCustomToken && input.hasCustomHostname;
|
|
750
|
+
}
|
|
751
|
+
//#endregion
|
|
734
752
|
//#region src/tunnel/cloudflare-tunnel.addon.ts
|
|
735
753
|
/**
|
|
736
754
|
* Cloudflare Tunnel — exposes CamStack via Cloudflare's network.
|
|
@@ -855,6 +873,10 @@ var CloudflareTunnelAddon = class extends BaseAddon {
|
|
|
855
873
|
}
|
|
856
874
|
});
|
|
857
875
|
});
|
|
876
|
+
else if (!this.settingsStoreReady) this.ctx.logger.warn("boot auto-start skipped — settings store did not answer; defaults are not an operator choice", { tags: {
|
|
877
|
+
topic: "tunnel",
|
|
878
|
+
phase: "auto-start-skipped"
|
|
879
|
+
} });
|
|
858
880
|
return {
|
|
859
881
|
providers: [{
|
|
860
882
|
capability: networkAccessCapability,
|
|
@@ -876,8 +898,12 @@ var CloudflareTunnelAddon = class extends BaseAddon {
|
|
|
876
898
|
* auto-start and by `onConfigChanged` to decide whether a change
|
|
877
899
|
* warrants a respawn even when no instance was previously running. */
|
|
878
900
|
isRunCapable() {
|
|
879
|
-
|
|
880
|
-
|
|
901
|
+
return shouldAutoStartTunnel({
|
|
902
|
+
settingsStoreReady: this.settingsStoreReady,
|
|
903
|
+
mode: this.config.mode,
|
|
904
|
+
hasCustomToken: !!this.config.customTunnelToken,
|
|
905
|
+
hasCustomHostname: !!this.config.customHostname
|
|
906
|
+
});
|
|
881
907
|
}
|
|
882
908
|
async onShutdown() {
|
|
883
909
|
if (this.service) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_dist = require("../dist-
|
|
2
|
+
const require_dist = require("../dist-B3KpK67G.js");
|
|
3
3
|
//#region src/turn/cloudflare-turn.ts
|
|
4
4
|
/**
|
|
5
5
|
* Cloudflare returns ICE servers in several flavours depending on which
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as BaseAddon, c as boolean, d as number, f as object, i as turnProviderCapability, l as discriminatedUnion, n as defineCustomActions, p as string, t as customAction, u as literal } from "../dist-
|
|
1
|
+
import { a as BaseAddon, c as boolean, d as number, f as object, i as turnProviderCapability, l as discriminatedUnion, n as defineCustomActions, p as string, t as customAction, u as literal } from "../dist-BgYOkp3_.mjs";
|
|
2
2
|
//#region src/turn/cloudflare-turn.ts
|
|
3
3
|
/**
|
|
4
4
|
* Cloudflare returns ICE servers in several flavours depending on which
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-cloudflare",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.27",
|
|
4
4
|
"description": "Cloudflare bundle — Tunnel (network-access) + TURN relay (turn-provider). Multi-entry npm package shipping 2 addons under a single bundle.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|