@camstack/addon-ai 0.4.17 → 0.4.18
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.js +119 -13
- package/dist/addon.mjs +119 -13
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -5964,6 +5964,13 @@ var BaseAddon = class {
|
|
|
5964
5964
|
_readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
|
|
5965
5965
|
/** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
|
|
5966
5966
|
_registeredCapNames = [];
|
|
5967
|
+
/**
|
|
5968
|
+
* True only after `readAddonStore` actually answered. Constructor
|
|
5969
|
+
* defaults look like stored config when the store is down — a forked
|
|
5970
|
+
* addon that auto-starts from those defaults (cloudflare-tunnel quick
|
|
5971
|
+
* mode, 2026-08-25) is not "the operator chose this".
|
|
5972
|
+
*/
|
|
5973
|
+
settingsStoreReady = false;
|
|
5967
5974
|
/** Default config values. Provided via constructor. */
|
|
5968
5975
|
defaults;
|
|
5969
5976
|
constructor(defaults) {
|
|
@@ -6364,7 +6371,9 @@ var BaseAddon = class {
|
|
|
6364
6371
|
];
|
|
6365
6372
|
let lastErr;
|
|
6366
6373
|
for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
|
|
6367
|
-
|
|
6374
|
+
const stored = await settings.readAddonStore() ?? {};
|
|
6375
|
+
this.settingsStoreReady = true;
|
|
6376
|
+
return stored;
|
|
6368
6377
|
} catch (err) {
|
|
6369
6378
|
lastErr = err;
|
|
6370
6379
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -6372,6 +6381,7 @@ var BaseAddon = class {
|
|
|
6372
6381
|
if (attempt === delaysMs.length) break;
|
|
6373
6382
|
await new Promise((r) => setTimeout(r, delaysMs[attempt]));
|
|
6374
6383
|
}
|
|
6384
|
+
this.settingsStoreReady = false;
|
|
6375
6385
|
this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
|
|
6376
6386
|
return {};
|
|
6377
6387
|
}
|
|
@@ -8318,6 +8328,12 @@ var ModelVariantGroupSchema = object({
|
|
|
8318
8328
|
*/
|
|
8319
8329
|
resolution: number$1().int().positive().optional()
|
|
8320
8330
|
});
|
|
8331
|
+
var ModelProviderIdSchema = _enum([
|
|
8332
|
+
"camstack",
|
|
8333
|
+
"frigate",
|
|
8334
|
+
"scrypted",
|
|
8335
|
+
"custom"
|
|
8336
|
+
]);
|
|
8321
8337
|
var ModelCatalogEntrySchema = object({
|
|
8322
8338
|
id: string(),
|
|
8323
8339
|
name: string(),
|
|
@@ -8415,6 +8431,12 @@ var ModelCatalogEntrySchema = object({
|
|
|
8415
8431
|
*/
|
|
8416
8432
|
group: ModelVariantGroupSchema.optional(),
|
|
8417
8433
|
/**
|
|
8434
|
+
* Catalog source for the pipeline stepper's provider-first picker. Absent on
|
|
8435
|
+
* built-in CamStack entries (treated as `camstack`) and on registry rows
|
|
8436
|
+
* persisted before this field existed (`inferModelProvider` fills those).
|
|
8437
|
+
*/
|
|
8438
|
+
provider: ModelProviderIdSchema.optional(),
|
|
8439
|
+
/**
|
|
8418
8440
|
* Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
|
|
8419
8441
|
* applies (Frigate / COCO public catalog). Set on a custom model whose raw
|
|
8420
8442
|
* labels already ARE the CamStack macros (Scrypted identity map).
|
|
@@ -14669,12 +14691,15 @@ var NcOccupancyConditionSchema = object({
|
|
|
14669
14691
|
* there is no second switch that can disagree with the first and every rule
|
|
14670
14692
|
* authored before the decision migrates for free (`audioModeOf`):
|
|
14671
14693
|
*
|
|
14672
|
-
* - **LABEL mode — `labels` present.** The rule fires
|
|
14673
|
-
*
|
|
14674
|
-
* `hitPercent` and `samplingSeconds` are ignored
|
|
14675
|
-
*
|
|
14676
|
-
*
|
|
14677
|
-
*
|
|
14694
|
+
* - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
|
|
14695
|
+
* labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
|
|
14696
|
+
* `hitPercent` and `samplingSeconds` are still ignored — a percentage of
|
|
14697
|
+
* frames is the wrong question for a classifier that labels 1–3 frames
|
|
14698
|
+
* per episode. The count window is the brake that drops a single-frame
|
|
14699
|
+
* false positive; the rule's own `throttle` cooldown is the other. The
|
|
14700
|
+
* per-label confidence floor is the analyzer's (`classificationMinScore`,
|
|
14701
|
+
* per device) — a label only reaches this condition if the classifier was
|
|
14702
|
+
* already confident enough. `confirmHits: 1` restores first-frame fire.
|
|
14678
14703
|
* - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
|
|
14679
14704
|
* the condition: at least `hitPercent`% of the samples over
|
|
14680
14705
|
* `samplingSeconds` must be at or above `dbThreshold` dBFS (see
|
|
@@ -14701,14 +14726,22 @@ var NcOccupancyConditionSchema = object({
|
|
|
14701
14726
|
* an operator who typed `dog` mean the same thing.
|
|
14702
14727
|
*/
|
|
14703
14728
|
var NcAudioConditionSchema = object({
|
|
14704
|
-
/** LABEL MODE: audio macro labels. Present ⇒
|
|
14729
|
+
/** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
|
|
14705
14730
|
labels: array(string().min(1)).min(1).optional(),
|
|
14706
14731
|
/** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
|
|
14707
14732
|
dbThreshold: number$1().min(-96).max(0).optional(),
|
|
14708
14733
|
/** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
|
|
14709
14734
|
hitPercent: number$1().int().min(1).max(100).default(60),
|
|
14710
14735
|
/** LEVEL MODE ONLY: length of the sampling window in seconds. */
|
|
14711
|
-
samplingSeconds: number$1().int().min(1).max(300).default(10)
|
|
14736
|
+
samplingSeconds: number$1().int().min(1).max(300).default(10),
|
|
14737
|
+
/**
|
|
14738
|
+
* LABEL MODE: how many labelled frames must land inside
|
|
14739
|
+
* {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
|
|
14740
|
+
* Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
|
|
14741
|
+
*/
|
|
14742
|
+
confirmHits: number$1().int().min(1).max(20).optional(),
|
|
14743
|
+
/** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
|
|
14744
|
+
confirmWindowSec: number$1().int().min(1).max(60).optional()
|
|
14712
14745
|
});
|
|
14713
14746
|
/**
|
|
14714
14747
|
* Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
|
|
@@ -17082,6 +17115,46 @@ var RecentTracksPageSchema = object({
|
|
|
17082
17115
|
/** Cursor for the next page, or null when this page is the last. */
|
|
17083
17116
|
nextCursor: string().nullable()
|
|
17084
17117
|
});
|
|
17118
|
+
var LIST_GROUPS_DEFAULT_LIMIT = 40;
|
|
17119
|
+
var LIST_GROUPS_MAX_LIMIT = 100;
|
|
17120
|
+
var AnalyticsGroupRecordSchema = object({
|
|
17121
|
+
id: string(),
|
|
17122
|
+
deviceId: number$1().int(),
|
|
17123
|
+
openedAt: number$1().int(),
|
|
17124
|
+
closedAt: number$1().int(),
|
|
17125
|
+
timestamp: number$1().int(),
|
|
17126
|
+
memberCount: number$1().int(),
|
|
17127
|
+
memberTrackIds: array(string()).readonly(),
|
|
17128
|
+
className: string(),
|
|
17129
|
+
classes: array(string()).readonly(),
|
|
17130
|
+
/** Relative event-media path, or null when the group has no picture yet. */
|
|
17131
|
+
mediaUrl: string().nullable(),
|
|
17132
|
+
singleton: boolean()
|
|
17133
|
+
});
|
|
17134
|
+
var AnalyticsGroupMemberSchema = object({
|
|
17135
|
+
trackId: string(),
|
|
17136
|
+
deviceId: number$1().int(),
|
|
17137
|
+
className: string(),
|
|
17138
|
+
firstSeen: number$1().int(),
|
|
17139
|
+
lastSeen: number$1().int(),
|
|
17140
|
+
mediaUrl: string().nullable()
|
|
17141
|
+
});
|
|
17142
|
+
var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
|
|
17143
|
+
var ListGroupsQueryInput = object({
|
|
17144
|
+
/** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
|
|
17145
|
+
deviceIds: array(number$1()),
|
|
17146
|
+
/** Window lower bound on `closedAt` (inclusive). */
|
|
17147
|
+
since: number$1().optional(),
|
|
17148
|
+
/** Window upper bound on `openedAt` (inclusive). */
|
|
17149
|
+
until: number$1().optional(),
|
|
17150
|
+
limit: number$1().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
|
|
17151
|
+
/** Opaque continuation cursor from a previous page's `nextCursor`. */
|
|
17152
|
+
cursor: string().optional()
|
|
17153
|
+
});
|
|
17154
|
+
var ListGroupsPageSchema = object({
|
|
17155
|
+
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
17156
|
+
nextCursor: string().nullable()
|
|
17157
|
+
});
|
|
17085
17158
|
var KeyEventQueryInput = object({
|
|
17086
17159
|
deviceId: number$1(),
|
|
17087
17160
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
@@ -17157,7 +17230,9 @@ var TrackCascadeCountsSchema = object({
|
|
|
17157
17230
|
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
17158
17231
|
plates: number$1().int(),
|
|
17159
17232
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
17160
|
-
embeddings: number$1().int()
|
|
17233
|
+
embeddings: number$1().int(),
|
|
17234
|
+
/** Group membership + group rows removed with their last member (best-effort). */
|
|
17235
|
+
groups: number$1().int()
|
|
17161
17236
|
});
|
|
17162
17237
|
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
17163
17238
|
var DiskReconcileCountsSchema = object({
|
|
@@ -17303,7 +17378,10 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
|
|
|
17303
17378
|
* stationary registry). Default false: the timeline lists passages,
|
|
17304
17379
|
* not parking records (operator decision, 2026-08-15). */
|
|
17305
17380
|
includeStationary: boolean().optional()
|
|
17306
|
-
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(
|
|
17381
|
+
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
|
|
17382
|
+
deviceId: number$1(),
|
|
17383
|
+
groupId: string().min(1)
|
|
17384
|
+
}), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number$1() }), _void(), {
|
|
17307
17385
|
kind: "mutation",
|
|
17308
17386
|
auth: "admin"
|
|
17309
17387
|
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number$1() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number$1()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
|
|
@@ -17613,7 +17691,8 @@ var PipelineModelOptionSchema = object({
|
|
|
17613
17691
|
sizeMB: number$1()
|
|
17614
17692
|
})),
|
|
17615
17693
|
group: ModelVariantGroupSchema.optional(),
|
|
17616
|
-
legacy: boolean().optional()
|
|
17694
|
+
legacy: boolean().optional(),
|
|
17695
|
+
provider: ModelProviderIdSchema.optional()
|
|
17617
17696
|
});
|
|
17618
17697
|
var ConfigFieldBridge = custom();
|
|
17619
17698
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -24197,7 +24276,12 @@ var PlateInfoSchema = object({
|
|
|
24197
24276
|
plateBbox: BoundingBoxSchema.optional(),
|
|
24198
24277
|
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
24199
24278
|
keyFrameMediaKey: string().optional(),
|
|
24200
|
-
base64: string().optional()
|
|
24279
|
+
base64: string().optional(),
|
|
24280
|
+
/**
|
|
24281
|
+
* Same crop as a data-plane URL. `getPlateByTrack` returns this and
|
|
24282
|
+
* never inlines JPEG; `listPlates` still inlines for the admin-ui.
|
|
24283
|
+
*/
|
|
24284
|
+
cropUrl: string().optional()
|
|
24201
24285
|
});
|
|
24202
24286
|
var MediaFileLiteSchema = object({
|
|
24203
24287
|
key: string(),
|
|
@@ -29667,6 +29751,12 @@ Object.freeze({
|
|
|
29667
29751
|
addonId: null,
|
|
29668
29752
|
access: "view"
|
|
29669
29753
|
},
|
|
29754
|
+
"pipelineAnalytics.getGroup": {
|
|
29755
|
+
capName: "pipeline-analytics",
|
|
29756
|
+
capScope: "device",
|
|
29757
|
+
addonId: null,
|
|
29758
|
+
access: "view"
|
|
29759
|
+
},
|
|
29670
29760
|
"pipelineAnalytics.getKeyEvents": {
|
|
29671
29761
|
capName: "pipeline-analytics",
|
|
29672
29762
|
capScope: "device",
|
|
@@ -29751,6 +29841,12 @@ Object.freeze({
|
|
|
29751
29841
|
addonId: null,
|
|
29752
29842
|
access: "view"
|
|
29753
29843
|
},
|
|
29844
|
+
"pipelineAnalytics.listGroups": {
|
|
29845
|
+
capName: "pipeline-analytics",
|
|
29846
|
+
capScope: "device",
|
|
29847
|
+
addonId: null,
|
|
29848
|
+
access: "view"
|
|
29849
|
+
},
|
|
29754
29850
|
"pipelineAnalytics.listOpsLog": {
|
|
29755
29851
|
capName: "pipeline-analytics",
|
|
29756
29852
|
capScope: "device",
|
|
@@ -33170,6 +33266,11 @@ Object.freeze({
|
|
|
33170
33266
|
form: "single",
|
|
33171
33267
|
optional: false
|
|
33172
33268
|
}],
|
|
33269
|
+
"pipelineAnalytics.getGroup": [{
|
|
33270
|
+
name: "deviceId",
|
|
33271
|
+
form: "single",
|
|
33272
|
+
optional: false
|
|
33273
|
+
}],
|
|
33173
33274
|
"pipelineAnalytics.getKeyEvents": [{
|
|
33174
33275
|
name: "deviceId",
|
|
33175
33276
|
form: "single",
|
|
@@ -33225,6 +33326,11 @@ Object.freeze({
|
|
|
33225
33326
|
form: "array",
|
|
33226
33327
|
optional: false
|
|
33227
33328
|
}],
|
|
33329
|
+
"pipelineAnalytics.listGroups": [{
|
|
33330
|
+
name: "deviceIds",
|
|
33331
|
+
form: "array",
|
|
33332
|
+
optional: false
|
|
33333
|
+
}],
|
|
33228
33334
|
"pipelineAnalytics.listOpsLog": [{
|
|
33229
33335
|
name: "deviceId",
|
|
33230
33336
|
form: "single",
|
package/dist/addon.mjs
CHANGED
|
@@ -5990,6 +5990,13 @@ var BaseAddon = class {
|
|
|
5990
5990
|
_readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
|
|
5991
5991
|
/** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
|
|
5992
5992
|
_registeredCapNames = [];
|
|
5993
|
+
/**
|
|
5994
|
+
* True only after `readAddonStore` actually answered. Constructor
|
|
5995
|
+
* defaults look like stored config when the store is down — a forked
|
|
5996
|
+
* addon that auto-starts from those defaults (cloudflare-tunnel quick
|
|
5997
|
+
* mode, 2026-08-25) is not "the operator chose this".
|
|
5998
|
+
*/
|
|
5999
|
+
settingsStoreReady = false;
|
|
5993
6000
|
/** Default config values. Provided via constructor. */
|
|
5994
6001
|
defaults;
|
|
5995
6002
|
constructor(defaults) {
|
|
@@ -6390,7 +6397,9 @@ var BaseAddon = class {
|
|
|
6390
6397
|
];
|
|
6391
6398
|
let lastErr;
|
|
6392
6399
|
for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
|
|
6393
|
-
|
|
6400
|
+
const stored = await settings.readAddonStore() ?? {};
|
|
6401
|
+
this.settingsStoreReady = true;
|
|
6402
|
+
return stored;
|
|
6394
6403
|
} catch (err) {
|
|
6395
6404
|
lastErr = err;
|
|
6396
6405
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -6398,6 +6407,7 @@ var BaseAddon = class {
|
|
|
6398
6407
|
if (attempt === delaysMs.length) break;
|
|
6399
6408
|
await new Promise((r) => setTimeout(r, delaysMs[attempt]));
|
|
6400
6409
|
}
|
|
6410
|
+
this.settingsStoreReady = false;
|
|
6401
6411
|
this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
|
|
6402
6412
|
return {};
|
|
6403
6413
|
}
|
|
@@ -8344,6 +8354,12 @@ var ModelVariantGroupSchema = object({
|
|
|
8344
8354
|
*/
|
|
8345
8355
|
resolution: number$1().int().positive().optional()
|
|
8346
8356
|
});
|
|
8357
|
+
var ModelProviderIdSchema = _enum([
|
|
8358
|
+
"camstack",
|
|
8359
|
+
"frigate",
|
|
8360
|
+
"scrypted",
|
|
8361
|
+
"custom"
|
|
8362
|
+
]);
|
|
8347
8363
|
var ModelCatalogEntrySchema = object({
|
|
8348
8364
|
id: string(),
|
|
8349
8365
|
name: string(),
|
|
@@ -8441,6 +8457,12 @@ var ModelCatalogEntrySchema = object({
|
|
|
8441
8457
|
*/
|
|
8442
8458
|
group: ModelVariantGroupSchema.optional(),
|
|
8443
8459
|
/**
|
|
8460
|
+
* Catalog source for the pipeline stepper's provider-first picker. Absent on
|
|
8461
|
+
* built-in CamStack entries (treated as `camstack`) and on registry rows
|
|
8462
|
+
* persisted before this field existed (`inferModelProvider` fills those).
|
|
8463
|
+
*/
|
|
8464
|
+
provider: ModelProviderIdSchema.optional(),
|
|
8465
|
+
/**
|
|
8444
8466
|
* Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
|
|
8445
8467
|
* applies (Frigate / COCO public catalog). Set on a custom model whose raw
|
|
8446
8468
|
* labels already ARE the CamStack macros (Scrypted identity map).
|
|
@@ -14695,12 +14717,15 @@ var NcOccupancyConditionSchema = object({
|
|
|
14695
14717
|
* there is no second switch that can disagree with the first and every rule
|
|
14696
14718
|
* authored before the decision migrates for free (`audioModeOf`):
|
|
14697
14719
|
*
|
|
14698
|
-
* - **LABEL mode — `labels` present.** The rule fires
|
|
14699
|
-
*
|
|
14700
|
-
* `hitPercent` and `samplingSeconds` are ignored
|
|
14701
|
-
*
|
|
14702
|
-
*
|
|
14703
|
-
*
|
|
14720
|
+
* - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
|
|
14721
|
+
* labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
|
|
14722
|
+
* `hitPercent` and `samplingSeconds` are still ignored — a percentage of
|
|
14723
|
+
* frames is the wrong question for a classifier that labels 1–3 frames
|
|
14724
|
+
* per episode. The count window is the brake that drops a single-frame
|
|
14725
|
+
* false positive; the rule's own `throttle` cooldown is the other. The
|
|
14726
|
+
* per-label confidence floor is the analyzer's (`classificationMinScore`,
|
|
14727
|
+
* per device) — a label only reaches this condition if the classifier was
|
|
14728
|
+
* already confident enough. `confirmHits: 1` restores first-frame fire.
|
|
14704
14729
|
* - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
|
|
14705
14730
|
* the condition: at least `hitPercent`% of the samples over
|
|
14706
14731
|
* `samplingSeconds` must be at or above `dbThreshold` dBFS (see
|
|
@@ -14727,14 +14752,22 @@ var NcOccupancyConditionSchema = object({
|
|
|
14727
14752
|
* an operator who typed `dog` mean the same thing.
|
|
14728
14753
|
*/
|
|
14729
14754
|
var NcAudioConditionSchema = object({
|
|
14730
|
-
/** LABEL MODE: audio macro labels. Present ⇒
|
|
14755
|
+
/** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
|
|
14731
14756
|
labels: array(string().min(1)).min(1).optional(),
|
|
14732
14757
|
/** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
|
|
14733
14758
|
dbThreshold: number$1().min(-96).max(0).optional(),
|
|
14734
14759
|
/** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
|
|
14735
14760
|
hitPercent: number$1().int().min(1).max(100).default(60),
|
|
14736
14761
|
/** LEVEL MODE ONLY: length of the sampling window in seconds. */
|
|
14737
|
-
samplingSeconds: number$1().int().min(1).max(300).default(10)
|
|
14762
|
+
samplingSeconds: number$1().int().min(1).max(300).default(10),
|
|
14763
|
+
/**
|
|
14764
|
+
* LABEL MODE: how many labelled frames must land inside
|
|
14765
|
+
* {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
|
|
14766
|
+
* Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
|
|
14767
|
+
*/
|
|
14768
|
+
confirmHits: number$1().int().min(1).max(20).optional(),
|
|
14769
|
+
/** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
|
|
14770
|
+
confirmWindowSec: number$1().int().min(1).max(60).optional()
|
|
14738
14771
|
});
|
|
14739
14772
|
/**
|
|
14740
14773
|
* Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
|
|
@@ -17108,6 +17141,46 @@ var RecentTracksPageSchema = object({
|
|
|
17108
17141
|
/** Cursor for the next page, or null when this page is the last. */
|
|
17109
17142
|
nextCursor: string().nullable()
|
|
17110
17143
|
});
|
|
17144
|
+
var LIST_GROUPS_DEFAULT_LIMIT = 40;
|
|
17145
|
+
var LIST_GROUPS_MAX_LIMIT = 100;
|
|
17146
|
+
var AnalyticsGroupRecordSchema = object({
|
|
17147
|
+
id: string(),
|
|
17148
|
+
deviceId: number$1().int(),
|
|
17149
|
+
openedAt: number$1().int(),
|
|
17150
|
+
closedAt: number$1().int(),
|
|
17151
|
+
timestamp: number$1().int(),
|
|
17152
|
+
memberCount: number$1().int(),
|
|
17153
|
+
memberTrackIds: array(string()).readonly(),
|
|
17154
|
+
className: string(),
|
|
17155
|
+
classes: array(string()).readonly(),
|
|
17156
|
+
/** Relative event-media path, or null when the group has no picture yet. */
|
|
17157
|
+
mediaUrl: string().nullable(),
|
|
17158
|
+
singleton: boolean()
|
|
17159
|
+
});
|
|
17160
|
+
var AnalyticsGroupMemberSchema = object({
|
|
17161
|
+
trackId: string(),
|
|
17162
|
+
deviceId: number$1().int(),
|
|
17163
|
+
className: string(),
|
|
17164
|
+
firstSeen: number$1().int(),
|
|
17165
|
+
lastSeen: number$1().int(),
|
|
17166
|
+
mediaUrl: string().nullable()
|
|
17167
|
+
});
|
|
17168
|
+
var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
|
|
17169
|
+
var ListGroupsQueryInput = object({
|
|
17170
|
+
/** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
|
|
17171
|
+
deviceIds: array(number$1()),
|
|
17172
|
+
/** Window lower bound on `closedAt` (inclusive). */
|
|
17173
|
+
since: number$1().optional(),
|
|
17174
|
+
/** Window upper bound on `openedAt` (inclusive). */
|
|
17175
|
+
until: number$1().optional(),
|
|
17176
|
+
limit: number$1().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
|
|
17177
|
+
/** Opaque continuation cursor from a previous page's `nextCursor`. */
|
|
17178
|
+
cursor: string().optional()
|
|
17179
|
+
});
|
|
17180
|
+
var ListGroupsPageSchema = object({
|
|
17181
|
+
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
17182
|
+
nextCursor: string().nullable()
|
|
17183
|
+
});
|
|
17111
17184
|
var KeyEventQueryInput = object({
|
|
17112
17185
|
deviceId: number$1(),
|
|
17113
17186
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
@@ -17183,7 +17256,9 @@ var TrackCascadeCountsSchema = object({
|
|
|
17183
17256
|
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
17184
17257
|
plates: number$1().int(),
|
|
17185
17258
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
17186
|
-
embeddings: number$1().int()
|
|
17259
|
+
embeddings: number$1().int(),
|
|
17260
|
+
/** Group membership + group rows removed with their last member (best-effort). */
|
|
17261
|
+
groups: number$1().int()
|
|
17187
17262
|
});
|
|
17188
17263
|
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
17189
17264
|
var DiskReconcileCountsSchema = object({
|
|
@@ -17329,7 +17404,10 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
|
|
|
17329
17404
|
* stationary registry). Default false: the timeline lists passages,
|
|
17330
17405
|
* not parking records (operator decision, 2026-08-15). */
|
|
17331
17406
|
includeStationary: boolean().optional()
|
|
17332
|
-
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(
|
|
17407
|
+
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
|
|
17408
|
+
deviceId: number$1(),
|
|
17409
|
+
groupId: string().min(1)
|
|
17410
|
+
}), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number$1() }), _void(), {
|
|
17333
17411
|
kind: "mutation",
|
|
17334
17412
|
auth: "admin"
|
|
17335
17413
|
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number$1() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number$1()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
|
|
@@ -17639,7 +17717,8 @@ var PipelineModelOptionSchema = object({
|
|
|
17639
17717
|
sizeMB: number$1()
|
|
17640
17718
|
})),
|
|
17641
17719
|
group: ModelVariantGroupSchema.optional(),
|
|
17642
|
-
legacy: boolean().optional()
|
|
17720
|
+
legacy: boolean().optional(),
|
|
17721
|
+
provider: ModelProviderIdSchema.optional()
|
|
17643
17722
|
});
|
|
17644
17723
|
var ConfigFieldBridge = custom();
|
|
17645
17724
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -24223,7 +24302,12 @@ var PlateInfoSchema = object({
|
|
|
24223
24302
|
plateBbox: BoundingBoxSchema.optional(),
|
|
24224
24303
|
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
24225
24304
|
keyFrameMediaKey: string().optional(),
|
|
24226
|
-
base64: string().optional()
|
|
24305
|
+
base64: string().optional(),
|
|
24306
|
+
/**
|
|
24307
|
+
* Same crop as a data-plane URL. `getPlateByTrack` returns this and
|
|
24308
|
+
* never inlines JPEG; `listPlates` still inlines for the admin-ui.
|
|
24309
|
+
*/
|
|
24310
|
+
cropUrl: string().optional()
|
|
24227
24311
|
});
|
|
24228
24312
|
var MediaFileLiteSchema = object({
|
|
24229
24313
|
key: string(),
|
|
@@ -29693,6 +29777,12 @@ Object.freeze({
|
|
|
29693
29777
|
addonId: null,
|
|
29694
29778
|
access: "view"
|
|
29695
29779
|
},
|
|
29780
|
+
"pipelineAnalytics.getGroup": {
|
|
29781
|
+
capName: "pipeline-analytics",
|
|
29782
|
+
capScope: "device",
|
|
29783
|
+
addonId: null,
|
|
29784
|
+
access: "view"
|
|
29785
|
+
},
|
|
29696
29786
|
"pipelineAnalytics.getKeyEvents": {
|
|
29697
29787
|
capName: "pipeline-analytics",
|
|
29698
29788
|
capScope: "device",
|
|
@@ -29777,6 +29867,12 @@ Object.freeze({
|
|
|
29777
29867
|
addonId: null,
|
|
29778
29868
|
access: "view"
|
|
29779
29869
|
},
|
|
29870
|
+
"pipelineAnalytics.listGroups": {
|
|
29871
|
+
capName: "pipeline-analytics",
|
|
29872
|
+
capScope: "device",
|
|
29873
|
+
addonId: null,
|
|
29874
|
+
access: "view"
|
|
29875
|
+
},
|
|
29780
29876
|
"pipelineAnalytics.listOpsLog": {
|
|
29781
29877
|
capName: "pipeline-analytics",
|
|
29782
29878
|
capScope: "device",
|
|
@@ -33196,6 +33292,11 @@ Object.freeze({
|
|
|
33196
33292
|
form: "single",
|
|
33197
33293
|
optional: false
|
|
33198
33294
|
}],
|
|
33295
|
+
"pipelineAnalytics.getGroup": [{
|
|
33296
|
+
name: "deviceId",
|
|
33297
|
+
form: "single",
|
|
33298
|
+
optional: false
|
|
33299
|
+
}],
|
|
33199
33300
|
"pipelineAnalytics.getKeyEvents": [{
|
|
33200
33301
|
name: "deviceId",
|
|
33201
33302
|
form: "single",
|
|
@@ -33251,6 +33352,11 @@ Object.freeze({
|
|
|
33251
33352
|
form: "array",
|
|
33252
33353
|
optional: false
|
|
33253
33354
|
}],
|
|
33355
|
+
"pipelineAnalytics.listGroups": [{
|
|
33356
|
+
name: "deviceIds",
|
|
33357
|
+
form: "array",
|
|
33358
|
+
optional: false
|
|
33359
|
+
}],
|
|
33254
33360
|
"pipelineAnalytics.listOpsLog": [{
|
|
33255
33361
|
name: "deviceId",
|
|
33256
33362
|
form: "single",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-ai",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.18",
|
|
4
4
|
"description": "AI addon for CamStack — the `llm` collection provider (cloud, LAN, and camstack-managed local llama.cpp profiles) plus the per-node `llm-runtime` managed executor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|