@camstack/addon-ai 0.4.8 → 0.4.10
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 +130 -23
- package/dist/addon.mjs +130 -23
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -15,7 +15,7 @@ let node_zlib = require("node:zlib");
|
|
|
15
15
|
let node_fs_promises = require("node:fs/promises");
|
|
16
16
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
17
17
|
let node_child_process = require("node:child_process");
|
|
18
|
-
//#region ../types/dist/event-category-
|
|
18
|
+
//#region ../types/dist/event-category-XfKNtfCc.mjs
|
|
19
19
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
20
20
|
EventCategory["SystemBoot"] = "system.boot";
|
|
21
21
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -54,6 +54,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
54
54
|
EventCategory["AddonInstalled"] = "addon.installed";
|
|
55
55
|
EventCategory["AddonUninstalled"] = "addon.uninstalled";
|
|
56
56
|
EventCategory["AddonCrashed"] = "addon.crashed";
|
|
57
|
+
/**
|
|
58
|
+
* A RUNNER the D6 crash circuit-breaker gave up on — terminal.
|
|
59
|
+
*
|
|
60
|
+
* `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
|
|
61
|
+
* respawned"); this is the one that never resolves itself. Emitted exactly
|
|
62
|
+
* once per trip, by `process-service.ts`, carrying the node, the runner, the
|
|
63
|
+
* addons it hosted and the crash count that tripped the breaker.
|
|
64
|
+
*
|
|
65
|
+
* It exists because a runner marked terminally `failed` used to be SILENT:
|
|
66
|
+
* on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
|
|
67
|
+
* errors, the breaker stopped respawning it (correctly), `/health` kept
|
|
68
|
+
* returning 200, and fleet-wide recording was gone for 3h15 before the
|
|
69
|
+
* operator's phone told him. The Notification Center's system-event intake
|
|
70
|
+
* consumes this category and maps it to the `addon-crash-loop` kind.
|
|
71
|
+
*/
|
|
72
|
+
EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
|
|
57
73
|
EventCategory["AddonError"] = "addon.error";
|
|
58
74
|
EventCategory["AddonPageReady"] = "addon.page-ready";
|
|
59
75
|
EventCategory["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -7806,6 +7822,12 @@ var RecordingConfigSchema = object({
|
|
|
7806
7822
|
/** DERIVED summary of `bands`, stamped by the recorder on every save.
|
|
7807
7823
|
* Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
|
|
7808
7824
|
mode: RecordingStorageModeSchema.optional(),
|
|
7825
|
+
/**
|
|
7826
|
+
* Which assigned broker slots to record. Absent / empty = {@link
|
|
7827
|
+
* DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
|
|
7828
|
+
* camera's currently assigned slots — never `mid` unless the operator
|
|
7829
|
+
* picks it, and never a slot the broker has not assigned.
|
|
7830
|
+
*/
|
|
7809
7831
|
profiles: array(CamProfileSchema).optional(),
|
|
7810
7832
|
segmentSeconds: number$1().int().positive().optional(),
|
|
7811
7833
|
/**
|
|
@@ -7886,7 +7908,11 @@ var RelocateFootageInputSchema = object({
|
|
|
7886
7908
|
profiles: array(string()).optional(),
|
|
7887
7909
|
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
7888
7910
|
* never allowed to starve live writers. */
|
|
7889
|
-
throttleMbps: number$1().min(1).max(1e3).optional()
|
|
7911
|
+
throttleMbps: number$1().min(1).max(1e3).optional(),
|
|
7912
|
+
/** Move only segments whose startMs is >= this. Absent = the whole source
|
|
7913
|
+
* pile. Used when a full drain is too expensive and the operator only
|
|
7914
|
+
* wants the recent window on the new disk. */
|
|
7915
|
+
sinceMs: number$1().int().optional()
|
|
7890
7916
|
});
|
|
7891
7917
|
/** Internal, lease-scoped participant operation. It is intentionally separate
|
|
7892
7918
|
* from persistent recording settings: a migration never changes
|
|
@@ -14530,6 +14556,7 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14530
14556
|
"node-offline",
|
|
14531
14557
|
"node-inference-unavailable",
|
|
14532
14558
|
"detection-blind",
|
|
14559
|
+
"addon-crash-loop",
|
|
14533
14560
|
"addon-update-available",
|
|
14534
14561
|
"server-update-available",
|
|
14535
14562
|
"alarm-triggered",
|
|
@@ -17103,6 +17130,12 @@ var TrackCascadeCountsSchema = object({
|
|
|
17103
17130
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
17104
17131
|
embeddings: number$1().int()
|
|
17105
17132
|
});
|
|
17133
|
+
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
17134
|
+
var DiskReconcileCountsSchema = object({
|
|
17135
|
+
mediaDropped: number$1().int(),
|
|
17136
|
+
tracks: number$1().int(),
|
|
17137
|
+
events: number$1().int()
|
|
17138
|
+
});
|
|
17106
17139
|
/** Event-store footprint for one camera. */
|
|
17107
17140
|
var EventStoreDeviceFootprintSchema = object({
|
|
17108
17141
|
deviceId: number$1(),
|
|
@@ -17279,6 +17312,9 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
|
|
|
17279
17312
|
}), method(object({ deviceId: number$1() }), TrackCascadeCountsSchema, {
|
|
17280
17313
|
kind: "mutation",
|
|
17281
17314
|
auth: "admin"
|
|
17315
|
+
}), method(object({ deviceId: number$1() }), DiskReconcileCountsSchema, {
|
|
17316
|
+
kind: "mutation",
|
|
17317
|
+
auth: "admin"
|
|
17282
17318
|
}), method(object({
|
|
17283
17319
|
deviceId: number$1(),
|
|
17284
17320
|
trackIds: array(string()).min(1)
|
|
@@ -18789,6 +18825,24 @@ var CameraStatusDegradationSchema = object({
|
|
|
18789
18825
|
*
|
|
18790
18826
|
* See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
|
|
18791
18827
|
*/
|
|
18828
|
+
var DiskReconcileJobSchema = object({
|
|
18829
|
+
state: _enum([
|
|
18830
|
+
"idle",
|
|
18831
|
+
"running",
|
|
18832
|
+
"done",
|
|
18833
|
+
"error"
|
|
18834
|
+
]),
|
|
18835
|
+
total: number$1().int().nonnegative(),
|
|
18836
|
+
completed: number$1().int().nonnegative(),
|
|
18837
|
+
currentDeviceId: number$1().int().nullable(),
|
|
18838
|
+
failed: array(number$1().int()).readonly(),
|
|
18839
|
+
mediaDropped: number$1().int().nonnegative(),
|
|
18840
|
+
tracks: number$1().int().nonnegative(),
|
|
18841
|
+
events: number$1().int().nonnegative(),
|
|
18842
|
+
startedAtMs: number$1().int().nullable(),
|
|
18843
|
+
finishedAtMs: number$1().int().nullable(),
|
|
18844
|
+
error: string().nullable()
|
|
18845
|
+
});
|
|
18792
18846
|
var CameraStatusSchema = object({
|
|
18793
18847
|
deviceId: number$1(),
|
|
18794
18848
|
assignment: CameraAssignmentStatusSchema,
|
|
@@ -19020,7 +19074,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
|
|
|
19020
19074
|
}), CameraSwitchGroupSchema, {
|
|
19021
19075
|
kind: "mutation",
|
|
19022
19076
|
auth: "admin"
|
|
19023
|
-
}), method(object({ deviceId: number$1() }), CameraStatusSchema), method(object({ deviceIds: array(number$1()).optional() }), array(CameraStatusSchema).readonly()), method(_void(),
|
|
19077
|
+
}), method(object({ deviceId: number$1() }), CameraStatusSchema), method(object({ deviceIds: array(number$1()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
|
|
19078
|
+
kind: "mutation",
|
|
19079
|
+
auth: "admin"
|
|
19080
|
+
}), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
|
|
19024
19081
|
name: string(),
|
|
19025
19082
|
description: string().optional(),
|
|
19026
19083
|
config: CameraPipelineConfigSchema
|
|
@@ -20093,20 +20150,6 @@ var VectorStatsResultSchema = object({
|
|
|
20093
20150
|
exact: boolean()
|
|
20094
20151
|
});
|
|
20095
20152
|
method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }), method(VectorQueryInputSchema, VectorQueryResultSchema), method(VectorGetInputSchema, VectorGetResultSchema), method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorStatsInputSchema, VectorStatsResultSchema);
|
|
20096
|
-
/**
|
|
20097
|
-
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
20098
|
-
*
|
|
20099
|
-
* A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
|
|
20100
|
-
* provider per device, substitutable. The DEFAULT provider (registered by
|
|
20101
|
-
* `addon-post-analysis`, `defaultActive: true`) composes the analytics event
|
|
20102
|
-
* log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
|
|
20103
|
-
* is a time-WINDOW over existing footage, never a separate file. A camera that
|
|
20104
|
-
* exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
|
|
20105
|
-
* wrapper provider for its device and serve its own clip catalog + URLs.
|
|
20106
|
-
*
|
|
20107
|
-
* A `Clip` is purely time-based (subtree-blind): playback resolves segments by
|
|
20108
|
-
* temporal overlap, so the API never decides `continuous` vs `events`.
|
|
20109
|
-
*/
|
|
20110
20153
|
var ClipSchema = object({
|
|
20111
20154
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
20112
20155
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -20132,12 +20175,41 @@ var ClipSchema = object({
|
|
|
20132
20175
|
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
20133
20176
|
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
20134
20177
|
* mint their own stills.
|
|
20178
|
+
*
|
|
20179
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
20180
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
20181
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
20182
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
20183
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
20184
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
20135
20185
|
*/
|
|
20136
20186
|
thumbnail: string().optional(),
|
|
20137
|
-
/**
|
|
20138
|
-
*
|
|
20139
|
-
*
|
|
20187
|
+
/**
|
|
20188
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
20189
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
20190
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
20191
|
+
*
|
|
20192
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
20193
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
20194
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
20195
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
20196
|
+
* construction, so a naive midpoint lands in dead air.
|
|
20197
|
+
*/
|
|
20198
|
+
stillAtMs: number$1().optional(),
|
|
20199
|
+
/**
|
|
20200
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
20201
|
+
* first within kind (object → motion → audio), capped at
|
|
20202
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
20203
|
+
*
|
|
20204
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
20205
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
20206
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
20207
|
+
*/
|
|
20140
20208
|
eventIds: array(string()).optional(),
|
|
20209
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
20210
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
20211
|
+
* list is never mistaken for a quiet visit. */
|
|
20212
|
+
eventCount: number$1().int().nonnegative().optional(),
|
|
20141
20213
|
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
20142
20214
|
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
20143
20215
|
* bar keeps showing them via `recording.getAvailability`. */
|
|
@@ -20608,7 +20680,14 @@ var SearchResultSchema = object({
|
|
|
20608
20680
|
});
|
|
20609
20681
|
var AutoUpdateSettingsSchema = object({
|
|
20610
20682
|
channel: ChannelSchema,
|
|
20611
|
-
intervalSeconds: number$1()
|
|
20683
|
+
intervalSeconds: number$1(),
|
|
20684
|
+
/**
|
|
20685
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
20686
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
20687
|
+
* about a publish and installing it are different decisions. Clamped
|
|
20688
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
20689
|
+
*/
|
|
20690
|
+
updateCheckIntervalSeconds: number$1()
|
|
20612
20691
|
});
|
|
20613
20692
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
20614
20693
|
var RestartAddonResultSchema = unknown();
|
|
@@ -20749,7 +20828,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
20749
20828
|
auth: "admin"
|
|
20750
20829
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
20751
20830
|
channel: ChannelSchema,
|
|
20752
|
-
intervalSeconds: number$1().min(300).max(86400).optional()
|
|
20831
|
+
intervalSeconds: number$1().min(300).max(86400).optional(),
|
|
20832
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
20833
|
+
updateCheckIntervalSeconds: number$1().min(900).max(604800).optional()
|
|
20753
20834
|
}), unknown(), {
|
|
20754
20835
|
kind: "mutation",
|
|
20755
20836
|
auth: "admin"
|
|
@@ -29414,6 +29495,12 @@ Object.freeze({
|
|
|
29414
29495
|
addonId: null,
|
|
29415
29496
|
access: "create"
|
|
29416
29497
|
},
|
|
29498
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
29499
|
+
capName: "pipeline-analytics",
|
|
29500
|
+
capScope: "device",
|
|
29501
|
+
addonId: null,
|
|
29502
|
+
access: "create"
|
|
29503
|
+
},
|
|
29417
29504
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
29418
29505
|
capName: "pipeline-analytics",
|
|
29419
29506
|
capScope: "device",
|
|
@@ -29816,6 +29903,12 @@ Object.freeze({
|
|
|
29816
29903
|
addonId: null,
|
|
29817
29904
|
access: "view"
|
|
29818
29905
|
},
|
|
29906
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
29907
|
+
capName: "pipeline-orchestrator",
|
|
29908
|
+
capScope: "system",
|
|
29909
|
+
addonId: null,
|
|
29910
|
+
access: "view"
|
|
29911
|
+
},
|
|
29819
29912
|
"pipelineOrchestrator.listAgentSettings": {
|
|
29820
29913
|
capName: "pipeline-orchestrator",
|
|
29821
29914
|
capScope: "system",
|
|
@@ -29840,6 +29933,12 @@ Object.freeze({
|
|
|
29840
29933
|
addonId: null,
|
|
29841
29934
|
access: "create"
|
|
29842
29935
|
},
|
|
29936
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
29937
|
+
capName: "pipeline-orchestrator",
|
|
29938
|
+
capScope: "system",
|
|
29939
|
+
addonId: null,
|
|
29940
|
+
access: "create"
|
|
29941
|
+
},
|
|
29843
29942
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
29844
29943
|
capName: "pipeline-orchestrator",
|
|
29845
29944
|
capScope: "system",
|
|
@@ -32831,6 +32930,11 @@ Object.freeze({
|
|
|
32831
32930
|
form: "single",
|
|
32832
32931
|
optional: true
|
|
32833
32932
|
}],
|
|
32933
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
32934
|
+
name: "deviceId",
|
|
32935
|
+
form: "single",
|
|
32936
|
+
optional: false
|
|
32937
|
+
}],
|
|
32834
32938
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
32835
32939
|
name: "deviceId",
|
|
32836
32940
|
form: "single",
|
|
@@ -33896,6 +34000,8 @@ DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
|
|
|
33896
34000
|
DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
33897
34001
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
33898
34002
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
34003
|
+
var MB = 1024 * 1024;
|
|
34004
|
+
1024 * MB, 3072 * MB;
|
|
33899
34005
|
//#endregion
|
|
33900
34006
|
//#region ../../node_modules/@ai-sdk/provider/dist/index.js
|
|
33901
34007
|
var marker$2 = "vercel.ai.error";
|
|
@@ -72406,7 +72512,8 @@ function createRuntimeClient(api) {
|
|
|
72406
72512
|
* spelled as `${userId}/${conversationId}` (the collection already carries
|
|
72407
72513
|
* the `ai:chats` prefix). Isolation is structural: every read/write takes
|
|
72408
72514
|
* `userId` and refuses a row that belongs to someone else.
|
|
72409
|
-
|
|
72515
|
+
*/
|
|
72516
|
+
/**
|
|
72410
72517
|
* @durable class=audit owner=ai
|
|
72411
72518
|
* write="an operator sends a persistent test-chat turn — one row per conversation, messages[] replayed into the model each turn"
|
|
72412
72519
|
* retention="operator knob: max-age days and/or max-count per user (addon global settings). Pruned on write and on the addon's existing 24h usage sweep."
|
package/dist/addon.mjs
CHANGED
|
@@ -41,7 +41,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
41
41
|
}) : target, mod));
|
|
42
42
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
43
43
|
//#endregion
|
|
44
|
-
//#region ../types/dist/event-category-
|
|
44
|
+
//#region ../types/dist/event-category-XfKNtfCc.mjs
|
|
45
45
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
46
46
|
EventCategory["SystemBoot"] = "system.boot";
|
|
47
47
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -80,6 +80,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
80
80
|
EventCategory["AddonInstalled"] = "addon.installed";
|
|
81
81
|
EventCategory["AddonUninstalled"] = "addon.uninstalled";
|
|
82
82
|
EventCategory["AddonCrashed"] = "addon.crashed";
|
|
83
|
+
/**
|
|
84
|
+
* A RUNNER the D6 crash circuit-breaker gave up on — terminal.
|
|
85
|
+
*
|
|
86
|
+
* `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
|
|
87
|
+
* respawned"); this is the one that never resolves itself. Emitted exactly
|
|
88
|
+
* once per trip, by `process-service.ts`, carrying the node, the runner, the
|
|
89
|
+
* addons it hosted and the crash count that tripped the breaker.
|
|
90
|
+
*
|
|
91
|
+
* It exists because a runner marked terminally `failed` used to be SILENT:
|
|
92
|
+
* on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
|
|
93
|
+
* errors, the breaker stopped respawning it (correctly), `/health` kept
|
|
94
|
+
* returning 200, and fleet-wide recording was gone for 3h15 before the
|
|
95
|
+
* operator's phone told him. The Notification Center's system-event intake
|
|
96
|
+
* consumes this category and maps it to the `addon-crash-loop` kind.
|
|
97
|
+
*/
|
|
98
|
+
EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
|
|
83
99
|
EventCategory["AddonError"] = "addon.error";
|
|
84
100
|
EventCategory["AddonPageReady"] = "addon.page-ready";
|
|
85
101
|
EventCategory["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -7832,6 +7848,12 @@ var RecordingConfigSchema = object({
|
|
|
7832
7848
|
/** DERIVED summary of `bands`, stamped by the recorder on every save.
|
|
7833
7849
|
* Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
|
|
7834
7850
|
mode: RecordingStorageModeSchema.optional(),
|
|
7851
|
+
/**
|
|
7852
|
+
* Which assigned broker slots to record. Absent / empty = {@link
|
|
7853
|
+
* DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
|
|
7854
|
+
* camera's currently assigned slots — never `mid` unless the operator
|
|
7855
|
+
* picks it, and never a slot the broker has not assigned.
|
|
7856
|
+
*/
|
|
7835
7857
|
profiles: array(CamProfileSchema).optional(),
|
|
7836
7858
|
segmentSeconds: number$1().int().positive().optional(),
|
|
7837
7859
|
/**
|
|
@@ -7912,7 +7934,11 @@ var RelocateFootageInputSchema = object({
|
|
|
7912
7934
|
profiles: array(string()).optional(),
|
|
7913
7935
|
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
7914
7936
|
* never allowed to starve live writers. */
|
|
7915
|
-
throttleMbps: number$1().min(1).max(1e3).optional()
|
|
7937
|
+
throttleMbps: number$1().min(1).max(1e3).optional(),
|
|
7938
|
+
/** Move only segments whose startMs is >= this. Absent = the whole source
|
|
7939
|
+
* pile. Used when a full drain is too expensive and the operator only
|
|
7940
|
+
* wants the recent window on the new disk. */
|
|
7941
|
+
sinceMs: number$1().int().optional()
|
|
7916
7942
|
});
|
|
7917
7943
|
/** Internal, lease-scoped participant operation. It is intentionally separate
|
|
7918
7944
|
* from persistent recording settings: a migration never changes
|
|
@@ -14556,6 +14582,7 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14556
14582
|
"node-offline",
|
|
14557
14583
|
"node-inference-unavailable",
|
|
14558
14584
|
"detection-blind",
|
|
14585
|
+
"addon-crash-loop",
|
|
14559
14586
|
"addon-update-available",
|
|
14560
14587
|
"server-update-available",
|
|
14561
14588
|
"alarm-triggered",
|
|
@@ -17129,6 +17156,12 @@ var TrackCascadeCountsSchema = object({
|
|
|
17129
17156
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
17130
17157
|
embeddings: number$1().int()
|
|
17131
17158
|
});
|
|
17159
|
+
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
17160
|
+
var DiskReconcileCountsSchema = object({
|
|
17161
|
+
mediaDropped: number$1().int(),
|
|
17162
|
+
tracks: number$1().int(),
|
|
17163
|
+
events: number$1().int()
|
|
17164
|
+
});
|
|
17132
17165
|
/** Event-store footprint for one camera. */
|
|
17133
17166
|
var EventStoreDeviceFootprintSchema = object({
|
|
17134
17167
|
deviceId: number$1(),
|
|
@@ -17305,6 +17338,9 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
|
|
|
17305
17338
|
}), method(object({ deviceId: number$1() }), TrackCascadeCountsSchema, {
|
|
17306
17339
|
kind: "mutation",
|
|
17307
17340
|
auth: "admin"
|
|
17341
|
+
}), method(object({ deviceId: number$1() }), DiskReconcileCountsSchema, {
|
|
17342
|
+
kind: "mutation",
|
|
17343
|
+
auth: "admin"
|
|
17308
17344
|
}), method(object({
|
|
17309
17345
|
deviceId: number$1(),
|
|
17310
17346
|
trackIds: array(string()).min(1)
|
|
@@ -18815,6 +18851,24 @@ var CameraStatusDegradationSchema = object({
|
|
|
18815
18851
|
*
|
|
18816
18852
|
* See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
|
|
18817
18853
|
*/
|
|
18854
|
+
var DiskReconcileJobSchema = object({
|
|
18855
|
+
state: _enum([
|
|
18856
|
+
"idle",
|
|
18857
|
+
"running",
|
|
18858
|
+
"done",
|
|
18859
|
+
"error"
|
|
18860
|
+
]),
|
|
18861
|
+
total: number$1().int().nonnegative(),
|
|
18862
|
+
completed: number$1().int().nonnegative(),
|
|
18863
|
+
currentDeviceId: number$1().int().nullable(),
|
|
18864
|
+
failed: array(number$1().int()).readonly(),
|
|
18865
|
+
mediaDropped: number$1().int().nonnegative(),
|
|
18866
|
+
tracks: number$1().int().nonnegative(),
|
|
18867
|
+
events: number$1().int().nonnegative(),
|
|
18868
|
+
startedAtMs: number$1().int().nullable(),
|
|
18869
|
+
finishedAtMs: number$1().int().nullable(),
|
|
18870
|
+
error: string().nullable()
|
|
18871
|
+
});
|
|
18818
18872
|
var CameraStatusSchema = object({
|
|
18819
18873
|
deviceId: number$1(),
|
|
18820
18874
|
assignment: CameraAssignmentStatusSchema,
|
|
@@ -19046,7 +19100,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
|
|
|
19046
19100
|
}), CameraSwitchGroupSchema, {
|
|
19047
19101
|
kind: "mutation",
|
|
19048
19102
|
auth: "admin"
|
|
19049
|
-
}), method(object({ deviceId: number$1() }), CameraStatusSchema), method(object({ deviceIds: array(number$1()).optional() }), array(CameraStatusSchema).readonly()), method(_void(),
|
|
19103
|
+
}), method(object({ deviceId: number$1() }), CameraStatusSchema), method(object({ deviceIds: array(number$1()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
|
|
19104
|
+
kind: "mutation",
|
|
19105
|
+
auth: "admin"
|
|
19106
|
+
}), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
|
|
19050
19107
|
name: string(),
|
|
19051
19108
|
description: string().optional(),
|
|
19052
19109
|
config: CameraPipelineConfigSchema
|
|
@@ -20119,20 +20176,6 @@ var VectorStatsResultSchema = object({
|
|
|
20119
20176
|
exact: boolean()
|
|
20120
20177
|
});
|
|
20121
20178
|
method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }), method(VectorQueryInputSchema, VectorQueryResultSchema), method(VectorGetInputSchema, VectorGetResultSchema), method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorStatsInputSchema, VectorStatsResultSchema);
|
|
20122
|
-
/**
|
|
20123
|
-
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
20124
|
-
*
|
|
20125
|
-
* A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
|
|
20126
|
-
* provider per device, substitutable. The DEFAULT provider (registered by
|
|
20127
|
-
* `addon-post-analysis`, `defaultActive: true`) composes the analytics event
|
|
20128
|
-
* log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
|
|
20129
|
-
* is a time-WINDOW over existing footage, never a separate file. A camera that
|
|
20130
|
-
* exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
|
|
20131
|
-
* wrapper provider for its device and serve its own clip catalog + URLs.
|
|
20132
|
-
*
|
|
20133
|
-
* A `Clip` is purely time-based (subtree-blind): playback resolves segments by
|
|
20134
|
-
* temporal overlap, so the API never decides `continuous` vs `events`.
|
|
20135
|
-
*/
|
|
20136
20179
|
var ClipSchema = object({
|
|
20137
20180
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
20138
20181
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -20158,12 +20201,41 @@ var ClipSchema = object({
|
|
|
20158
20201
|
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
20159
20202
|
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
20160
20203
|
* mint their own stills.
|
|
20204
|
+
*
|
|
20205
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
20206
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
20207
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
20208
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
20209
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
20210
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
20161
20211
|
*/
|
|
20162
20212
|
thumbnail: string().optional(),
|
|
20163
|
-
/**
|
|
20164
|
-
*
|
|
20165
|
-
*
|
|
20213
|
+
/**
|
|
20214
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
20215
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
20216
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
20217
|
+
*
|
|
20218
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
20219
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
20220
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
20221
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
20222
|
+
* construction, so a naive midpoint lands in dead air.
|
|
20223
|
+
*/
|
|
20224
|
+
stillAtMs: number$1().optional(),
|
|
20225
|
+
/**
|
|
20226
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
20227
|
+
* first within kind (object → motion → audio), capped at
|
|
20228
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
20229
|
+
*
|
|
20230
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
20231
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
20232
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
20233
|
+
*/
|
|
20166
20234
|
eventIds: array(string()).optional(),
|
|
20235
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
20236
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
20237
|
+
* list is never mistaken for a quiet visit. */
|
|
20238
|
+
eventCount: number$1().int().nonnegative().optional(),
|
|
20167
20239
|
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
20168
20240
|
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
20169
20241
|
* bar keeps showing them via `recording.getAvailability`. */
|
|
@@ -20634,7 +20706,14 @@ var SearchResultSchema = object({
|
|
|
20634
20706
|
});
|
|
20635
20707
|
var AutoUpdateSettingsSchema = object({
|
|
20636
20708
|
channel: ChannelSchema,
|
|
20637
|
-
intervalSeconds: number$1()
|
|
20709
|
+
intervalSeconds: number$1(),
|
|
20710
|
+
/**
|
|
20711
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
20712
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
20713
|
+
* about a publish and installing it are different decisions. Clamped
|
|
20714
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
20715
|
+
*/
|
|
20716
|
+
updateCheckIntervalSeconds: number$1()
|
|
20638
20717
|
});
|
|
20639
20718
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
20640
20719
|
var RestartAddonResultSchema = unknown();
|
|
@@ -20775,7 +20854,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
20775
20854
|
auth: "admin"
|
|
20776
20855
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
20777
20856
|
channel: ChannelSchema,
|
|
20778
|
-
intervalSeconds: number$1().min(300).max(86400).optional()
|
|
20857
|
+
intervalSeconds: number$1().min(300).max(86400).optional(),
|
|
20858
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
20859
|
+
updateCheckIntervalSeconds: number$1().min(900).max(604800).optional()
|
|
20779
20860
|
}), unknown(), {
|
|
20780
20861
|
kind: "mutation",
|
|
20781
20862
|
auth: "admin"
|
|
@@ -29440,6 +29521,12 @@ Object.freeze({
|
|
|
29440
29521
|
addonId: null,
|
|
29441
29522
|
access: "create"
|
|
29442
29523
|
},
|
|
29524
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
29525
|
+
capName: "pipeline-analytics",
|
|
29526
|
+
capScope: "device",
|
|
29527
|
+
addonId: null,
|
|
29528
|
+
access: "create"
|
|
29529
|
+
},
|
|
29443
29530
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
29444
29531
|
capName: "pipeline-analytics",
|
|
29445
29532
|
capScope: "device",
|
|
@@ -29842,6 +29929,12 @@ Object.freeze({
|
|
|
29842
29929
|
addonId: null,
|
|
29843
29930
|
access: "view"
|
|
29844
29931
|
},
|
|
29932
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
29933
|
+
capName: "pipeline-orchestrator",
|
|
29934
|
+
capScope: "system",
|
|
29935
|
+
addonId: null,
|
|
29936
|
+
access: "view"
|
|
29937
|
+
},
|
|
29845
29938
|
"pipelineOrchestrator.listAgentSettings": {
|
|
29846
29939
|
capName: "pipeline-orchestrator",
|
|
29847
29940
|
capScope: "system",
|
|
@@ -29866,6 +29959,12 @@ Object.freeze({
|
|
|
29866
29959
|
addonId: null,
|
|
29867
29960
|
access: "create"
|
|
29868
29961
|
},
|
|
29962
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
29963
|
+
capName: "pipeline-orchestrator",
|
|
29964
|
+
capScope: "system",
|
|
29965
|
+
addonId: null,
|
|
29966
|
+
access: "create"
|
|
29967
|
+
},
|
|
29869
29968
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
29870
29969
|
capName: "pipeline-orchestrator",
|
|
29871
29970
|
capScope: "system",
|
|
@@ -32857,6 +32956,11 @@ Object.freeze({
|
|
|
32857
32956
|
form: "single",
|
|
32858
32957
|
optional: true
|
|
32859
32958
|
}],
|
|
32959
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
32960
|
+
name: "deviceId",
|
|
32961
|
+
form: "single",
|
|
32962
|
+
optional: false
|
|
32963
|
+
}],
|
|
32860
32964
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
32861
32965
|
name: "deviceId",
|
|
32862
32966
|
form: "single",
|
|
@@ -33922,6 +34026,8 @@ DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
|
|
|
33922
34026
|
DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
33923
34027
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
33924
34028
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
34029
|
+
var MB = 1024 * 1024;
|
|
34030
|
+
1024 * MB, 3072 * MB;
|
|
33925
34031
|
//#endregion
|
|
33926
34032
|
//#region ../../node_modules/@ai-sdk/provider/dist/index.js
|
|
33927
34033
|
var marker$2 = "vercel.ai.error";
|
|
@@ -72432,7 +72538,8 @@ function createRuntimeClient(api) {
|
|
|
72432
72538
|
* spelled as `${userId}/${conversationId}` (the collection already carries
|
|
72433
72539
|
* the `ai:chats` prefix). Isolation is structural: every read/write takes
|
|
72434
72540
|
* `userId` and refuses a row that belongs to someone else.
|
|
72435
|
-
|
|
72541
|
+
*/
|
|
72542
|
+
/**
|
|
72436
72543
|
* @durable class=audit owner=ai
|
|
72437
72544
|
* write="an operator sends a persistent test-chat turn — one row per conversation, messages[] replayed into the model each turn"
|
|
72438
72545
|
* retention="operator knob: max-age days and/or max-count per user (addon global settings). Pruned on write and on the addon's existing 24h usage sweep."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-ai",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.10",
|
|
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",
|