@camstack/addon-decoder-nodeav 1.1.10 → 1.1.12
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/index.js +1118 -413
- package/dist/index.mjs +1119 -402
- package/package.json +1 -2
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { DecoderFrameRingSink, FrameRingReaderCache, RING_BUDGET_MB, SEGMENT_NAME_PREFIX, makeSegmentName, unlinkSegment } from "@camstack/shm-ring";
|
|
2
1
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { readdirSync } from "node:fs";
|
|
4
2
|
//#region ../../node_modules/zod/v4/core/core.js
|
|
5
3
|
var _a$1;
|
|
6
4
|
function $constructor(name, initializer, params) {
|
|
@@ -4289,6 +4287,14 @@ function object(shape, params) {
|
|
|
4289
4287
|
...normalizeParams(params)
|
|
4290
4288
|
});
|
|
4291
4289
|
}
|
|
4290
|
+
function looseObject(shape, params) {
|
|
4291
|
+
return new ZodObject({
|
|
4292
|
+
type: "object",
|
|
4293
|
+
shape,
|
|
4294
|
+
catchall: unknown(),
|
|
4295
|
+
...normalizeParams(params)
|
|
4296
|
+
});
|
|
4297
|
+
}
|
|
4292
4298
|
var ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
|
|
4293
4299
|
$ZodUnion.init(inst, def);
|
|
4294
4300
|
ZodType.init(inst, def);
|
|
@@ -4630,7 +4636,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4630
4636
|
return inst;
|
|
4631
4637
|
}
|
|
4632
4638
|
//#endregion
|
|
4633
|
-
//#region ../types/dist/sleep-
|
|
4639
|
+
//#region ../types/dist/sleep-_sv7WKkq.mjs
|
|
4634
4640
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4635
4641
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4636
4642
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5040,10 +5046,51 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
5040
5046
|
EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
|
|
5041
5047
|
EventCategory["EnrichmentSceneStateChanged"] = "enrichment.scene.state-changed";
|
|
5042
5048
|
EventCategory["EnrichmentActivitySummary"] = "enrichment.activity.summary";
|
|
5049
|
+
/**
|
|
5050
|
+
* Unified track-lifecycle event: ONE category carrying `phase:
|
|
5051
|
+
* 'start' | 'update' | 'end'` with a rich, typed
|
|
5052
|
+
* `PipelineAnalyticsTrackLifecyclePayload`. Supersedes the thin
|
|
5053
|
+
* `PipelineAnalyticsTrackStarted` / `PipelineAnalyticsTrackEnded`
|
|
5054
|
+
* pair — a consumer subscribes once and branches on `phase`.
|
|
5055
|
+
*/
|
|
5056
|
+
EventCategory["PipelineAnalyticsTrackLifecycle"] = "pipeline-analytics.track-lifecycle";
|
|
5057
|
+
/**
|
|
5058
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5059
|
+
* (`phase:'start'`). Kept as a soft alias — no known subscribers.
|
|
5060
|
+
*/
|
|
5043
5061
|
EventCategory["PipelineAnalyticsTrackStarted"] = "pipeline-analytics.track-started";
|
|
5062
|
+
/**
|
|
5063
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5064
|
+
* (`phase:'end'`). Kept as a soft alias — no known subscribers.
|
|
5065
|
+
*/
|
|
5044
5066
|
EventCategory["PipelineAnalyticsTrackEnded"] = "pipeline-analytics.track-ended";
|
|
5045
5067
|
EventCategory["PipelineAnalyticsDetectionEvent"] = "pipeline-analytics.detection-event";
|
|
5046
5068
|
EventCategory["PipelineAnalyticsFrameTracked"] = "pipeline-analytics.frame-tracked";
|
|
5069
|
+
/**
|
|
5070
|
+
* Fired by `addon-post-analysis` when a parked (stationary) object appears
|
|
5071
|
+
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
5072
|
+
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
5073
|
+
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
5074
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
5075
|
+
*/
|
|
5076
|
+
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
5077
|
+
/**
|
|
5078
|
+
* Fired by `addon-post-analysis` whenever a gallery face row changes:
|
|
5079
|
+
* `kind:'buffered'` a new detected face was persisted, `'assigned'` /
|
|
5080
|
+
* `'unassigned'` its identity link changed, `'deleted'` the row was
|
|
5081
|
+
* removed. Telemetry semantics (D8): a lost event only delays a refresh,
|
|
5082
|
+
* never a correctness issue. Payload `PipelineAnalyticsFaceGalleryChangedPayload`.
|
|
5083
|
+
*/
|
|
5084
|
+
EventCategory["PipelineAnalyticsFaceGalleryChanged"] = "pipeline-analytics.face-gallery-changed";
|
|
5085
|
+
/**
|
|
5086
|
+
* Fired by `addon-post-analysis` whenever a gallery plate row changes:
|
|
5087
|
+
* `kind:'buffered'` a new read was persisted (`PlateRecognizer.onTrackEnd`),
|
|
5088
|
+
* `'assigned'` / `'unassigned'` its vehicle link changed
|
|
5089
|
+
* (`PlateGalleryProvider`), `'deleted'` the row was removed. Telemetry
|
|
5090
|
+
* semantics (D8): a lost event only delays a refresh, never a correctness
|
|
5091
|
+
* issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
|
|
5092
|
+
*/
|
|
5093
|
+
EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
|
|
5047
5094
|
EventCategory["FrigateLiveEvent"] = "frigate.live-event";
|
|
5048
5095
|
EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
|
|
5049
5096
|
/**
|
|
@@ -6884,7 +6931,8 @@ var MODEL_FORMATS = [
|
|
|
6884
6931
|
"coreml",
|
|
6885
6932
|
"openvino",
|
|
6886
6933
|
"tflite",
|
|
6887
|
-
"pt"
|
|
6934
|
+
"pt",
|
|
6935
|
+
"gguf"
|
|
6888
6936
|
];
|
|
6889
6937
|
/**
|
|
6890
6938
|
* Multi-file format payload.
|
|
@@ -6928,7 +6976,8 @@ var ModelFormatsSchema = object({
|
|
|
6928
6976
|
coreml: ModelFormatEntrySchema.optional(),
|
|
6929
6977
|
openvino: ModelFormatEntrySchema.optional(),
|
|
6930
6978
|
tflite: ModelFormatEntrySchema.optional(),
|
|
6931
|
-
pt: ModelFormatEntrySchema.optional()
|
|
6979
|
+
pt: ModelFormatEntrySchema.optional(),
|
|
6980
|
+
gguf: ModelFormatEntrySchema.optional()
|
|
6932
6981
|
});
|
|
6933
6982
|
/**
|
|
6934
6983
|
* Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
|
|
@@ -6969,6 +7018,15 @@ var ModelCatalogEntrySchema = object({
|
|
|
6969
7018
|
width: number(),
|
|
6970
7019
|
height: number()
|
|
6971
7020
|
}),
|
|
7021
|
+
/**
|
|
7022
|
+
* Channel count of the model input tensor. Omit ⇒ 3 (RGB), the default for
|
|
7023
|
+
* every detector / classifier / embedder. Set to 1 for a grayscale CTC text
|
|
7024
|
+
* recognizer (EasyOCR VGG plate-OCR: input `[N,1,H,W]`) so the preprocess
|
|
7025
|
+
* feeds a single-channel, EasyOCR-normalized tensor instead of the default
|
|
7026
|
+
* 3-channel RGB one. Threaded through `PoolModelConfig.inputChannels` to the
|
|
7027
|
+
* Python inference pool.
|
|
7028
|
+
*/
|
|
7029
|
+
inputChannels: number().int().positive().optional(),
|
|
6972
7030
|
labels: array(LabelDefinitionSchema).readonly(),
|
|
6973
7031
|
inputLayout: _enum(["nchw", "nhwc"]).optional(),
|
|
6974
7032
|
inputNormalization: _enum([
|
|
@@ -6978,6 +7036,16 @@ var ModelCatalogEntrySchema = object({
|
|
|
6978
7036
|
]).optional(),
|
|
6979
7037
|
preprocessMode: _enum(["letterbox", "resize"]).optional(),
|
|
6980
7038
|
/**
|
|
7039
|
+
* Per-MODEL postprocessor override. Absent ⇒ the step's own
|
|
7040
|
+
* `StepDefinition.postprocessor` applies (the normal case — every model in a
|
|
7041
|
+
* step shares its decode). Set it when a step hosts models with DIFFERENT raw
|
|
7042
|
+
* output layouts under one slot: e.g. object-detection is `'yolo'` by default,
|
|
7043
|
+
* but a Coral SSD MobileNet build emits the `TFLite_Detection_PostProcess`
|
|
7044
|
+
* 4-tensor layout and needs `'ssd'`. Threaded into `PoolModelConfig.postprocessor`
|
|
7045
|
+
* by the engine factory (`modelEntry.postprocessor ?? def.postprocessor`).
|
|
7046
|
+
*/
|
|
7047
|
+
postprocessor: custom().optional(),
|
|
7048
|
+
/**
|
|
6981
7049
|
* When true, the executor produces a landmark-aligned crop (similarity warp
|
|
6982
7050
|
* onto the canonical template) before this step runs, instead of a plain
|
|
6983
7051
|
* axis-aligned bbox crop. Required for face-recognition embedders (ArcFace):
|
|
@@ -10423,7 +10491,8 @@ var ModelFormatSchema$1 = _enum([
|
|
|
10423
10491
|
"coreml",
|
|
10424
10492
|
"openvino",
|
|
10425
10493
|
"tflite",
|
|
10426
|
-
"pt"
|
|
10494
|
+
"pt",
|
|
10495
|
+
"gguf"
|
|
10427
10496
|
]);
|
|
10428
10497
|
var PipelineSlotSchema = _enum([
|
|
10429
10498
|
"detector",
|
|
@@ -10517,7 +10586,8 @@ var EngineProvisioningSchema = object({
|
|
|
10517
10586
|
runtimeId: _enum([
|
|
10518
10587
|
"onnx",
|
|
10519
10588
|
"openvino",
|
|
10520
|
-
"coreml"
|
|
10589
|
+
"coreml",
|
|
10590
|
+
"edgetpu"
|
|
10521
10591
|
]).nullable(),
|
|
10522
10592
|
device: string().nullable(),
|
|
10523
10593
|
state: _enum([
|
|
@@ -10678,7 +10748,15 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
10678
10748
|
image: _instanceof(Uint8Array).optional(),
|
|
10679
10749
|
referenceImage: string().optional(),
|
|
10680
10750
|
deviceId: number().optional(),
|
|
10681
|
-
sessionId: string().optional()
|
|
10751
|
+
sessionId: string().optional(),
|
|
10752
|
+
/**
|
|
10753
|
+
* Execution plane. 'full' (default) runs the whole tree — benchmark,
|
|
10754
|
+
* reference-image, and detail-subtree calls. 'frame' is the live
|
|
10755
|
+
* per-frame dispatch: ONLY root-plane steps run; crop children
|
|
10756
|
+
* (inputClasses ≠ null) are skipped and served per-track via
|
|
10757
|
+
* pipelineRunner.runDetailSubtree (two-plane design).
|
|
10758
|
+
*/
|
|
10759
|
+
plane: _enum(["full", "frame"]).optional()
|
|
10682
10760
|
}), PipelineRunResultBridge, { kind: "mutation" }), method(object({
|
|
10683
10761
|
engine: PipelineEngineChoiceSchema.optional(),
|
|
10684
10762
|
steps: array(PipelineStepInputSchema).min(1),
|
|
@@ -10821,6 +10899,28 @@ var NativeCropResultSchema = object({
|
|
|
10821
10899
|
width: number().int().positive(),
|
|
10822
10900
|
height: number().int().positive()
|
|
10823
10901
|
});
|
|
10902
|
+
/** Parent detection context passed to `runDetailSubtree` — the crop's
|
|
10903
|
+
* originating detection, in FRAME-space coordinates. Reuses
|
|
10904
|
+
* `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
|
|
10905
|
+
* the coordinates are frame-space rather than getNativeCrop's
|
|
10906
|
+
* normalized [0,1] convention). */
|
|
10907
|
+
var DetailParentSchema = object({
|
|
10908
|
+
bbox: NativeCropBboxSchema,
|
|
10909
|
+
className: string()
|
|
10910
|
+
});
|
|
10911
|
+
/** One child-step result from `runDetailSubtree` — an embedding, label,
|
|
10912
|
+
* or refined detection produced by running the crop-subtree on a
|
|
10913
|
+
* single tracked detection. */
|
|
10914
|
+
var DetailResultSchema = object({
|
|
10915
|
+
stepId: string(),
|
|
10916
|
+
className: string(),
|
|
10917
|
+
score: number(),
|
|
10918
|
+
/** FRAME-space bbox (already mapped back from crop space). */
|
|
10919
|
+
bbox: NativeCropBboxSchema.optional(),
|
|
10920
|
+
embedding: string().optional(),
|
|
10921
|
+
label: string().optional(),
|
|
10922
|
+
alignedCropJpeg: string().optional()
|
|
10923
|
+
});
|
|
10824
10924
|
/**
|
|
10825
10925
|
* Per-camera tunable ranges + defaults. Single source of truth used
|
|
10826
10926
|
* by both the Zod data schema (validation + default fallback) and
|
|
@@ -11081,7 +11181,13 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
|
|
|
11081
11181
|
handle: FrameHandleSchema,
|
|
11082
11182
|
bbox: NativeCropBboxSchema,
|
|
11083
11183
|
maxWidth: number().int().positive().optional()
|
|
11084
|
-
}), NativeCropResultSchema.nullable())
|
|
11184
|
+
}), NativeCropResultSchema.nullable()), method(object({
|
|
11185
|
+
deviceId: number(),
|
|
11186
|
+
frameHandle: FrameHandleSchema.optional(),
|
|
11187
|
+
cropJpeg: string().optional(),
|
|
11188
|
+
parent: DetailParentSchema,
|
|
11189
|
+
steps: array(string()).optional()
|
|
11190
|
+
}), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" });
|
|
11085
11191
|
object({
|
|
11086
11192
|
detected: boolean(),
|
|
11087
11193
|
/** Ms epoch of the last detected-true observation. Null if never detected. */
|
|
@@ -11693,6 +11799,140 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
|
|
|
11693
11799
|
deviceId: number(),
|
|
11694
11800
|
status: PtzAutotrackStatusSchema
|
|
11695
11801
|
});
|
|
11802
|
+
/**
|
|
11803
|
+
* scene-monitor — device-scoped reference-region state cap. An operator marks
|
|
11804
|
+
* a rect ROI on a camera frame and names one or more states; the engine
|
|
11805
|
+
* (pipeline-analytics, designated post-processing node) reports which state is
|
|
11806
|
+
* active on an ongoing basis. Two operator-selectable check modes share every-
|
|
11807
|
+
* thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
|
|
11808
|
+
* vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
|
|
11809
|
+
*
|
|
11810
|
+
* D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) — the framework
|
|
11811
|
+
* derives the device-detail contribution; the provider carries NO hand-written
|
|
11812
|
+
* settings-contribution methods. `status.kind:'push'` — the engine pushes on
|
|
11813
|
+
* every hysteresis flip / availability change; consumers never poll.
|
|
11814
|
+
*/
|
|
11815
|
+
/** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
|
|
11816
|
+
* be added without a wire break (matching falls back to any-condition refs). */
|
|
11817
|
+
var SceneConditionSchema = string();
|
|
11818
|
+
/** One captured reference — condition-tagged, model-version-gated. `embedding`
|
|
11819
|
+
* is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
|
|
11820
|
+
var SceneReferenceSchema = object({
|
|
11821
|
+
embedding: array(number()),
|
|
11822
|
+
modelId: string(),
|
|
11823
|
+
condition: SceneConditionSchema,
|
|
11824
|
+
capturedAt: number(),
|
|
11825
|
+
thumbnailMediaId: string().optional()
|
|
11826
|
+
});
|
|
11827
|
+
var SceneMonitorStateSchema = object({
|
|
11828
|
+
id: string(),
|
|
11829
|
+
label: string(),
|
|
11830
|
+
references: array(SceneReferenceSchema),
|
|
11831
|
+
textPrompts: array(string()).optional(),
|
|
11832
|
+
referenceCount: number(),
|
|
11833
|
+
currentlyMatched: boolean()
|
|
11834
|
+
});
|
|
11835
|
+
/** Per-mode comparator params in a discriminated union so an `llm` scene never
|
|
11836
|
+
* carries similarity knobs and vice-versa. */
|
|
11837
|
+
var SceneCheckSchema = discriminatedUnion("mode", [object({
|
|
11838
|
+
mode: literal("similarity"),
|
|
11839
|
+
threshold: number().min(0).max(1),
|
|
11840
|
+
hysteresisCount: number().int().positive()
|
|
11841
|
+
}), object({
|
|
11842
|
+
mode: literal("llm"),
|
|
11843
|
+
prompt: string(),
|
|
11844
|
+
profileId: string().optional(),
|
|
11845
|
+
hysteresisCount: number().int().positive()
|
|
11846
|
+
})]);
|
|
11847
|
+
var SceneMonitorSchema = object({
|
|
11848
|
+
id: string(),
|
|
11849
|
+
label: string(),
|
|
11850
|
+
roi: MaskRectShapeSchema,
|
|
11851
|
+
enabled: boolean(),
|
|
11852
|
+
triggerMode: _enum([
|
|
11853
|
+
"periodic",
|
|
11854
|
+
"on-motion",
|
|
11855
|
+
"both"
|
|
11856
|
+
]),
|
|
11857
|
+
checkIntervalSec: number().optional(),
|
|
11858
|
+
check: SceneCheckSchema,
|
|
11859
|
+
states: array(SceneMonitorStateSchema),
|
|
11860
|
+
currentStateId: string().nullable(),
|
|
11861
|
+
lastCheckedAt: number().nullable(),
|
|
11862
|
+
lastConfidence: number().nullable(),
|
|
11863
|
+
currentCondition: SceneConditionSchema.nullable(),
|
|
11864
|
+
availability: _enum(["ok", "unavailable"]),
|
|
11865
|
+
unavailableReason: string().nullable()
|
|
11866
|
+
});
|
|
11867
|
+
var SceneMonitorStatusSchema = object({
|
|
11868
|
+
monitors: array(SceneMonitorSchema),
|
|
11869
|
+
lastFetchedAt: number()
|
|
11870
|
+
});
|
|
11871
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
|
|
11872
|
+
deviceId: number(),
|
|
11873
|
+
label: string(),
|
|
11874
|
+
roi: MaskRectShapeSchema,
|
|
11875
|
+
check: SceneCheckSchema,
|
|
11876
|
+
triggerMode: _enum([
|
|
11877
|
+
"periodic",
|
|
11878
|
+
"on-motion",
|
|
11879
|
+
"both"
|
|
11880
|
+
]).optional(),
|
|
11881
|
+
checkIntervalSec: number().optional()
|
|
11882
|
+
}), SceneMonitorSchema, {
|
|
11883
|
+
kind: "mutation",
|
|
11884
|
+
auth: "admin"
|
|
11885
|
+
}), method(object({
|
|
11886
|
+
deviceId: number(),
|
|
11887
|
+
monitorId: string(),
|
|
11888
|
+
patch: object({
|
|
11889
|
+
label: string().optional(),
|
|
11890
|
+
roi: MaskRectShapeSchema.optional(),
|
|
11891
|
+
enabled: boolean().optional(),
|
|
11892
|
+
triggerMode: _enum([
|
|
11893
|
+
"periodic",
|
|
11894
|
+
"on-motion",
|
|
11895
|
+
"both"
|
|
11896
|
+
]).optional(),
|
|
11897
|
+
checkIntervalSec: number().optional(),
|
|
11898
|
+
check: SceneCheckSchema.optional()
|
|
11899
|
+
})
|
|
11900
|
+
}), _void(), {
|
|
11901
|
+
kind: "mutation",
|
|
11902
|
+
auth: "admin"
|
|
11903
|
+
}), method(object({
|
|
11904
|
+
deviceId: number(),
|
|
11905
|
+
monitorId: string()
|
|
11906
|
+
}), _void(), {
|
|
11907
|
+
kind: "mutation",
|
|
11908
|
+
auth: "admin"
|
|
11909
|
+
}), method(object({
|
|
11910
|
+
deviceId: number(),
|
|
11911
|
+
monitorId: string(),
|
|
11912
|
+
stateId: string().optional(),
|
|
11913
|
+
label: string().optional(),
|
|
11914
|
+
condition: SceneConditionSchema.optional()
|
|
11915
|
+
}), object({
|
|
11916
|
+
stateId: string(),
|
|
11917
|
+
referenceCount: number()
|
|
11918
|
+
}), {
|
|
11919
|
+
kind: "mutation",
|
|
11920
|
+
auth: "admin"
|
|
11921
|
+
}), method(object({
|
|
11922
|
+
deviceId: number(),
|
|
11923
|
+
monitorId: string(),
|
|
11924
|
+
stateId: string(),
|
|
11925
|
+
index: number()
|
|
11926
|
+
}), _void(), {
|
|
11927
|
+
kind: "mutation",
|
|
11928
|
+
auth: "admin"
|
|
11929
|
+
}), method(object({
|
|
11930
|
+
deviceId: number(),
|
|
11931
|
+
monitorId: string()
|
|
11932
|
+
}), _void(), {
|
|
11933
|
+
kind: "mutation",
|
|
11934
|
+
auth: "admin"
|
|
11935
|
+
});
|
|
11696
11936
|
object({
|
|
11697
11937
|
/** Whether the script is currently executing. */
|
|
11698
11938
|
isRunning: boolean(),
|
|
@@ -12100,6 +12340,36 @@ var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
|
|
|
12100
12340
|
* with the per-track detail panel and live overlay. */
|
|
12101
12341
|
trackIds: array(string()).readonly()
|
|
12102
12342
|
});
|
|
12343
|
+
/**
|
|
12344
|
+
* A parked ("stationary") object surfaced alongside occupancy — an object that
|
|
12345
|
+
* settled and stopped moving. It is NO LONGER a tracked object (the tracker was
|
|
12346
|
+
* told to forget it so it stops re-spawning tracks/events), but it IS still
|
|
12347
|
+
* physically present, so it keeps counting toward `frame` occupancy and is
|
|
12348
|
+
* listed here so the UI can show it in a dedicated "Stationary" section instead
|
|
12349
|
+
* of flooding the live event feed.
|
|
12350
|
+
*/
|
|
12351
|
+
var StationaryObjectSchema = object({
|
|
12352
|
+
id: string(),
|
|
12353
|
+
className: string(),
|
|
12354
|
+
bbox: object({
|
|
12355
|
+
x: number(),
|
|
12356
|
+
y: number(),
|
|
12357
|
+
w: number(),
|
|
12358
|
+
h: number()
|
|
12359
|
+
}),
|
|
12360
|
+
frameWidth: number().int().nonnegative(),
|
|
12361
|
+
frameHeight: number().int().nonnegative(),
|
|
12362
|
+
/** When the source track was first seen. */
|
|
12363
|
+
firstSeenAt: number().int(),
|
|
12364
|
+
/** When the object was recognised as parked (promotion time). */
|
|
12365
|
+
becameStationaryAt: number().int(),
|
|
12366
|
+
/** Last frame a detection confirmed the object is still there. */
|
|
12367
|
+
lastConfirmedAt: number().int(),
|
|
12368
|
+
/** Enrichment label carried from the source track (identity / plate). */
|
|
12369
|
+
label: string().optional(),
|
|
12370
|
+
/** Native-resolution key-frame media key for the parked object's best image. */
|
|
12371
|
+
keyFrameMediaKey: string().optional()
|
|
12372
|
+
});
|
|
12103
12373
|
var CameraOccupancySnapshotSchema = object({
|
|
12104
12374
|
/** Frame timestamp of the inference result that produced this snapshot. */
|
|
12105
12375
|
ts: number().int(),
|
|
@@ -12108,10 +12378,15 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
12108
12378
|
frameHeight: number().int().nonnegative(),
|
|
12109
12379
|
/** Per-zone breakdown — one entry per defined zone (user + onboard). */
|
|
12110
12380
|
zones: array(ZoneScopeBreakdownSchema).readonly(),
|
|
12111
|
-
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12381
|
+
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12382
|
+
* INCLUDES currently-confirmed stationary objects (they are still present). */
|
|
12112
12383
|
frame: PerScopeBreakdownSchema,
|
|
12113
12384
|
/** Detections that landed outside every zone. Empty when no zones defined. */
|
|
12114
|
-
unzoned: PerScopeBreakdownSchema
|
|
12385
|
+
unzoned: PerScopeBreakdownSchema,
|
|
12386
|
+
/** Parked objects on this camera (additive — absent on legacy snapshots).
|
|
12387
|
+
* Surfaced separately so the UI shows them in a dedicated section rather
|
|
12388
|
+
* than as repeated tracks/events. */
|
|
12389
|
+
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
12115
12390
|
});
|
|
12116
12391
|
/**
|
|
12117
12392
|
* Time-series resolution. The history methods return one bucket per
|
|
@@ -12714,7 +12989,12 @@ var NotificationRuleConditionsSchema = object({
|
|
|
12714
12989
|
clipDescription: object({
|
|
12715
12990
|
text: string().min(1),
|
|
12716
12991
|
minSimilarity: number().min(0).max(1)
|
|
12717
|
-
}).optional()
|
|
12992
|
+
}).optional(),
|
|
12993
|
+
/** Match events whose recognized-entity label (face identity name or plate
|
|
12994
|
+
* vehicle name, propagated onto `event.data.label`) is one of these values.
|
|
12995
|
+
* Empty/absent → unaffected (back-compat). Enables "notify me when <named
|
|
12996
|
+
* vehicle/person> is seen". */
|
|
12997
|
+
labels: array(string()).readonly().optional()
|
|
12718
12998
|
});
|
|
12719
12999
|
var NotificationRuleTemplateSchema = object({
|
|
12720
13000
|
title: string(),
|
|
@@ -13009,11 +13289,19 @@ method(object({
|
|
|
13009
13289
|
* login page needs NO change.
|
|
13010
13290
|
*
|
|
13011
13291
|
* - `widget` — a Module-Federation widget the login page mounts (via
|
|
13012
|
-
* `loadRemoteBundle`) for an in-page ceremony.
|
|
13013
|
-
*
|
|
13014
|
-
*
|
|
13015
|
-
*
|
|
13016
|
-
*
|
|
13292
|
+
* `loadRemoteBundle`) for an in-page ceremony. `auth.listLoginMethods`
|
|
13293
|
+
* stamps a public `bundleUrl` from `addonId` + `bundle`. Generic
|
|
13294
|
+
* mechanism kept for future use; no shipped addon uses it on the login
|
|
13295
|
+
* page (the passkey ceremony below runs natively in the shell instead).
|
|
13296
|
+
*
|
|
13297
|
+
* - `passkey` — a declarative WebAuthn ceremony the shell renders
|
|
13298
|
+
* natively (`@simplewebauthn/browser` lives in `addon-admin-ui`, not in
|
|
13299
|
+
* a remotely-loaded bundle). Carries the addon's effective `rpId` /
|
|
13300
|
+
* `origin` (from its `resolveRpID()` / `resolveOrigin()`) so the shell
|
|
13301
|
+
* can gate visibility (IP-literal origin, hostname/rpId mismatch) WITHOUT
|
|
13302
|
+
* fetching any remote code pre-auth. Contribution stays unconditional —
|
|
13303
|
+
* enrollment state is never leaked pre-auth; visibility is a shell
|
|
13304
|
+
* decision.
|
|
13017
13305
|
*
|
|
13018
13306
|
* Every contribution carries a `stage`:
|
|
13019
13307
|
* - `primary` — shown on the first credentials screen (OIDC /
|
|
@@ -13027,30 +13315,45 @@ method(object({
|
|
|
13027
13315
|
*/
|
|
13028
13316
|
/** When a login method renders in the two-phase login flow. */
|
|
13029
13317
|
var LoginStageEnum = _enum(["primary", "second-factor"]);
|
|
13030
|
-
/** One login-method contribution — redirect button
|
|
13031
|
-
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13318
|
+
/** One login-method contribution — redirect button, pre-auth widget, or native passkey ceremony. */
|
|
13319
|
+
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13320
|
+
object({
|
|
13321
|
+
kind: literal("redirect"),
|
|
13322
|
+
/** Stable id within the login-method set (e.g. `auth-oidc/google`). */
|
|
13323
|
+
id: string(),
|
|
13324
|
+
/** Operator-facing button label. */
|
|
13325
|
+
label: string(),
|
|
13326
|
+
/** lucide-react icon name. */
|
|
13327
|
+
icon: string().optional(),
|
|
13328
|
+
/** Addon-owned HTTP route the button navigates to (GET). */
|
|
13329
|
+
startUrl: string(),
|
|
13330
|
+
stage: LoginStageEnum
|
|
13331
|
+
}),
|
|
13332
|
+
object({
|
|
13333
|
+
kind: literal("widget"),
|
|
13334
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
|
|
13335
|
+
id: string(),
|
|
13336
|
+
/** Owning addon id — drives the public bundle URL + the MF namespace. */
|
|
13337
|
+
addonId: string(),
|
|
13338
|
+
/** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
|
|
13339
|
+
bundle: string(),
|
|
13340
|
+
/** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
|
|
13341
|
+
remote: WidgetRemoteSchema,
|
|
13342
|
+
stage: LoginStageEnum
|
|
13343
|
+
}),
|
|
13344
|
+
object({
|
|
13345
|
+
kind: literal("passkey"),
|
|
13346
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-direct-login`). */
|
|
13347
|
+
id: string(),
|
|
13348
|
+
/** Operator-facing button label. */
|
|
13349
|
+
label: string(),
|
|
13350
|
+
stage: LoginStageEnum,
|
|
13351
|
+
/** Effective WebAuthn RP ID (`resolveRpID()`) — the shell gates visibility on it. */
|
|
13352
|
+
rpId: string(),
|
|
13353
|
+
/** Effective expected origin (`resolveOrigin()`), null when unconfigured. */
|
|
13354
|
+
origin: string().nullable()
|
|
13355
|
+
})
|
|
13356
|
+
]);
|
|
13054
13357
|
method(_void(), array(LoginMethodContributionSchema).readonly());
|
|
13055
13358
|
/**
|
|
13056
13359
|
* Orchestrator-side destination metadata. The orchestrator computes
|
|
@@ -14971,14 +15274,14 @@ var TargetKindCapsSchema = object({
|
|
|
14971
15274
|
* the union is large and not meant for runtime validation here; the exported
|
|
14972
15275
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
14973
15276
|
*/
|
|
14974
|
-
var ConfigSchemaPassthrough = unknown();
|
|
15277
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
14975
15278
|
var TargetKindSchema = object({
|
|
14976
15279
|
kind: string(),
|
|
14977
15280
|
label: string(),
|
|
14978
15281
|
icon: string(),
|
|
14979
15282
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
14980
15283
|
addonId: string(),
|
|
14981
|
-
configSchema: ConfigSchemaPassthrough,
|
|
15284
|
+
configSchema: ConfigSchemaPassthrough$1,
|
|
14982
15285
|
supportsDiscovery: boolean(),
|
|
14983
15286
|
caps: TargetKindCapsSchema
|
|
14984
15287
|
});
|
|
@@ -15031,6 +15334,298 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
15031
15334
|
enabled: boolean()
|
|
15032
15335
|
}), _void(), { kind: "mutation" });
|
|
15033
15336
|
/**
|
|
15337
|
+
* Shared LLM generate contracts — imported by BOTH `llm.cap.ts` (consumer
|
|
15338
|
+
* surface) and `llm-runtime.cap.ts` (node-side managed executor) so the two
|
|
15339
|
+
* caps stay wire-compatible without a circular cap→cap import.
|
|
15340
|
+
*
|
|
15341
|
+
* Errors are a discriminated-union RESULT, never thrown: the shape survives
|
|
15342
|
+
* every transport tier structurally, and failed calls still write usage rows.
|
|
15343
|
+
* Token counts only in v1 — no costUsd (operator decision, 2026-07-15).
|
|
15344
|
+
*/
|
|
15345
|
+
var LlmUsageSchema = object({
|
|
15346
|
+
inputTokens: number(),
|
|
15347
|
+
outputTokens: number()
|
|
15348
|
+
});
|
|
15349
|
+
var LlmErrorCodeSchema = _enum([
|
|
15350
|
+
"timeout",
|
|
15351
|
+
"rate-limited",
|
|
15352
|
+
"auth",
|
|
15353
|
+
"refusal",
|
|
15354
|
+
"bad-request",
|
|
15355
|
+
"unavailable",
|
|
15356
|
+
"no-profile",
|
|
15357
|
+
"budget-exceeded",
|
|
15358
|
+
"adapter-error"
|
|
15359
|
+
]);
|
|
15360
|
+
var LlmGenerateResultSchema = discriminatedUnion("ok", [object({
|
|
15361
|
+
ok: literal(true),
|
|
15362
|
+
text: string(),
|
|
15363
|
+
model: string(),
|
|
15364
|
+
usage: LlmUsageSchema,
|
|
15365
|
+
truncated: boolean(),
|
|
15366
|
+
latencyMs: number()
|
|
15367
|
+
}), object({
|
|
15368
|
+
ok: literal(false),
|
|
15369
|
+
code: LlmErrorCodeSchema,
|
|
15370
|
+
message: string(),
|
|
15371
|
+
retryAfterMs: number().optional()
|
|
15372
|
+
})]);
|
|
15373
|
+
/**
|
|
15374
|
+
* `Uint8Array` is the sanctioned binary convention — superjson + the UDS
|
|
15375
|
+
* MsgPack channel round-trip typed arrays (embedding-encoder.cap.ts:29,
|
|
15376
|
+
* notification-output.cap.ts:27-31 precedents).
|
|
15377
|
+
*/
|
|
15378
|
+
var LlmImageSchema = object({
|
|
15379
|
+
bytes: _instanceof(Uint8Array),
|
|
15380
|
+
mimeType: string()
|
|
15381
|
+
});
|
|
15382
|
+
var LlmGenerateBaseInputSchema = object({
|
|
15383
|
+
/** Collection routing (the notification-output posture). */
|
|
15384
|
+
addonId: string().optional(),
|
|
15385
|
+
/** Explicit profile; else the resolution chain (spec §3). */
|
|
15386
|
+
profileId: string().optional(),
|
|
15387
|
+
/** MANDATORY usage tag: 'ai-summary', 'notifier-rules', 'adhoc-ui', … */
|
|
15388
|
+
consumer: string(),
|
|
15389
|
+
system: string().optional(),
|
|
15390
|
+
/** v1: single-turn. `messages[]` is a v2 additive field. */
|
|
15391
|
+
prompt: string(),
|
|
15392
|
+
/** Structured output — adapter-mapped (response_format / forced tool / responseSchema). */
|
|
15393
|
+
jsonSchema: record(string(), unknown()).optional(),
|
|
15394
|
+
/** Per-call override of the profile default. */
|
|
15395
|
+
maxTokens: number().int().positive().optional(),
|
|
15396
|
+
temperature: number().optional()
|
|
15397
|
+
});
|
|
15398
|
+
/**
|
|
15399
|
+
* `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
|
|
15400
|
+
* on EVERY node where `addon-ai` is installed; the hub `llm` provider reaches
|
|
15401
|
+
* a specific node's runtime with `nodePin(profile.runtime.nodeId)` — normal
|
|
15402
|
+
* cap routing, zero bespoke plumbing. `internal: true`: the operator reaches
|
|
15403
|
+
* this only through the `llm` cap's methods.
|
|
15404
|
+
*
|
|
15405
|
+
* One running llama-server child per node in v1 (models are RAM-heavy).
|
|
15406
|
+
* Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
|
|
15407
|
+
* watchdog — operator decision #3).
|
|
15408
|
+
*/
|
|
15409
|
+
var ManagedModelRefSchema = discriminatedUnion("kind", [
|
|
15410
|
+
object({
|
|
15411
|
+
kind: literal("catalog"),
|
|
15412
|
+
catalogId: string()
|
|
15413
|
+
}),
|
|
15414
|
+
object({
|
|
15415
|
+
kind: literal("url"),
|
|
15416
|
+
url: string(),
|
|
15417
|
+
sha256: string().optional()
|
|
15418
|
+
}),
|
|
15419
|
+
object({
|
|
15420
|
+
kind: literal("path"),
|
|
15421
|
+
path: string()
|
|
15422
|
+
})
|
|
15423
|
+
]);
|
|
15424
|
+
var ManagedRuntimeConfigSchema = object({
|
|
15425
|
+
/** WHERE the runtime lives — hub or any agent. */
|
|
15426
|
+
nodeId: string(),
|
|
15427
|
+
/** Closed for v1; 'ollama' is a v2 candidate. */
|
|
15428
|
+
engine: _enum(["llama-cpp"]),
|
|
15429
|
+
model: ManagedModelRefSchema,
|
|
15430
|
+
contextSize: number().int().default(4096),
|
|
15431
|
+
/** 0 = CPU-only. */
|
|
15432
|
+
gpuLayers: number().int().default(0),
|
|
15433
|
+
/** Default: cpus-2, clamped ≥1 (resolved node-side). */
|
|
15434
|
+
threads: number().int().optional(),
|
|
15435
|
+
/** Concurrent slots. */
|
|
15436
|
+
parallel: number().int().default(1),
|
|
15437
|
+
/** Else lazy: first generate boots it. */
|
|
15438
|
+
autoStart: boolean().default(false),
|
|
15439
|
+
/** 0 = never; frees RAM after quiet periods. */
|
|
15440
|
+
idleStopMinutes: number().int().default(30)
|
|
15441
|
+
});
|
|
15442
|
+
var LlmRuntimeStatusSchema = object({
|
|
15443
|
+
/** Status is ALWAYS node-qualified. */
|
|
15444
|
+
nodeId: string(),
|
|
15445
|
+
state: _enum([
|
|
15446
|
+
"stopped",
|
|
15447
|
+
"downloading",
|
|
15448
|
+
"starting",
|
|
15449
|
+
"ready",
|
|
15450
|
+
"crashed",
|
|
15451
|
+
"failed"
|
|
15452
|
+
]),
|
|
15453
|
+
pid: number().optional(),
|
|
15454
|
+
port: number().optional(),
|
|
15455
|
+
modelPath: string().optional(),
|
|
15456
|
+
modelId: string().optional(),
|
|
15457
|
+
downloadProgress: number().min(0).max(1).optional(),
|
|
15458
|
+
lastError: string().optional(),
|
|
15459
|
+
crashesInWindow: number(),
|
|
15460
|
+
/** Child RSS (sampled best-effort). */
|
|
15461
|
+
memoryBytes: number().optional(),
|
|
15462
|
+
vramBytes: number().optional()
|
|
15463
|
+
});
|
|
15464
|
+
var LlmNodeModelSchema = object({
|
|
15465
|
+
file: string(),
|
|
15466
|
+
sizeBytes: number(),
|
|
15467
|
+
catalogId: string().optional(),
|
|
15468
|
+
installedAt: number().optional()
|
|
15469
|
+
});
|
|
15470
|
+
var LlmRuntimeDiskUsageSchema = object({
|
|
15471
|
+
nodeId: string(),
|
|
15472
|
+
modelsBytes: number(),
|
|
15473
|
+
freeBytes: number().optional()
|
|
15474
|
+
});
|
|
15475
|
+
method(LlmGenerateBaseInputSchema.extend({
|
|
15476
|
+
images: array(LlmImageSchema).optional(),
|
|
15477
|
+
runtime: ManagedRuntimeConfigSchema,
|
|
15478
|
+
/** The managed profile's timeout, threaded by the hub provider. */
|
|
15479
|
+
timeoutMs: number().int().positive().optional()
|
|
15480
|
+
}), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
|
|
15481
|
+
kind: "mutation",
|
|
15482
|
+
auth: "admin"
|
|
15483
|
+
}), method(object({}), _void(), {
|
|
15484
|
+
kind: "mutation",
|
|
15485
|
+
auth: "admin"
|
|
15486
|
+
}), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
|
|
15487
|
+
kind: "mutation",
|
|
15488
|
+
auth: "admin"
|
|
15489
|
+
}), method(object({ file: string() }), _void(), {
|
|
15490
|
+
kind: "mutation",
|
|
15491
|
+
auth: "admin"
|
|
15492
|
+
}), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
|
|
15493
|
+
/**
|
|
15494
|
+
* `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
|
|
15495
|
+
* methods concat-fan across providers; single-row methods route to ONE
|
|
15496
|
+
* provider by the `addonId` in the call input (the notification-output
|
|
15497
|
+
* posture, notification-output.cap.ts:215-250). Provided by `addon-ai`
|
|
15498
|
+
* (hub-placed); the cap stays open for future providers.
|
|
15499
|
+
*
|
|
15500
|
+
* Profiles are ROWS (data), not addons: one row = one usable model endpoint.
|
|
15501
|
+
* `apiKey` is a password field — providers REDACT it on read and merge on
|
|
15502
|
+
* write; a stored key NEVER round-trips to a client.
|
|
15503
|
+
*/
|
|
15504
|
+
var LlmProfileKindSchema = _enum([
|
|
15505
|
+
"openai-compatible",
|
|
15506
|
+
"openai",
|
|
15507
|
+
"anthropic",
|
|
15508
|
+
"google",
|
|
15509
|
+
"managed-local"
|
|
15510
|
+
]);
|
|
15511
|
+
var LlmProfileSchema = object({
|
|
15512
|
+
id: string(),
|
|
15513
|
+
name: string(),
|
|
15514
|
+
kind: LlmProfileKindSchema,
|
|
15515
|
+
/** Stamped by the provider — keeps the fanned catalog routable. */
|
|
15516
|
+
addonId: string(),
|
|
15517
|
+
enabled: boolean(),
|
|
15518
|
+
/** Vendor model id, or the managed runtime's loaded model. */
|
|
15519
|
+
model: string(),
|
|
15520
|
+
/** Required for openai-compatible; override for cloud kinds. */
|
|
15521
|
+
baseUrl: string().optional(),
|
|
15522
|
+
/** ConfigUISchema type:'password' — never round-trips (spec §5). */
|
|
15523
|
+
apiKey: string().optional(),
|
|
15524
|
+
supportsVision: boolean(),
|
|
15525
|
+
temperature: number().min(0).max(2).optional(),
|
|
15526
|
+
maxTokens: number().int().positive().optional(),
|
|
15527
|
+
timeoutMs: number().int().positive().default(6e4),
|
|
15528
|
+
extraHeaders: record(string(), string()).optional(),
|
|
15529
|
+
/** kind === 'managed-local' only (spec §4). */
|
|
15530
|
+
runtime: ManagedRuntimeConfigSchema.optional()
|
|
15531
|
+
});
|
|
15532
|
+
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
15533
|
+
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
15534
|
+
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
15535
|
+
var ConfigSchemaPassthrough = unknown();
|
|
15536
|
+
var LlmProfileKindDescriptorSchema = object({
|
|
15537
|
+
kind: LlmProfileKindSchema,
|
|
15538
|
+
label: string(),
|
|
15539
|
+
icon: string(),
|
|
15540
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
15541
|
+
addonId: string(),
|
|
15542
|
+
configSchema: ConfigSchemaPassthrough
|
|
15543
|
+
});
|
|
15544
|
+
var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
|
|
15545
|
+
var LlmDefaultSchema = object({
|
|
15546
|
+
selector: LlmDefaultSelectorSchema,
|
|
15547
|
+
profileId: string()
|
|
15548
|
+
});
|
|
15549
|
+
/** Server-side rollup row — getUsage never dumps raw call rows (spec §6). */
|
|
15550
|
+
var LlmUsageRollupSchema = object({
|
|
15551
|
+
day: string(),
|
|
15552
|
+
consumer: string(),
|
|
15553
|
+
profileId: string(),
|
|
15554
|
+
calls: number(),
|
|
15555
|
+
okCalls: number(),
|
|
15556
|
+
errorCalls: number(),
|
|
15557
|
+
inputTokens: number(),
|
|
15558
|
+
outputTokens: number(),
|
|
15559
|
+
avgLatencyMs: number()
|
|
15560
|
+
});
|
|
15561
|
+
/** LLM-facing view over the reused ModelCatalogEntry mechanism (spec §4.2). */
|
|
15562
|
+
var ManagedModelCatalogEntrySchema = object({
|
|
15563
|
+
id: string(),
|
|
15564
|
+
label: string(),
|
|
15565
|
+
family: string(),
|
|
15566
|
+
purpose: _enum(["text", "vision"]),
|
|
15567
|
+
url: string(),
|
|
15568
|
+
sha256: string(),
|
|
15569
|
+
sizeBytes: number(),
|
|
15570
|
+
quantization: string(),
|
|
15571
|
+
/** Load-time guidance shown in the picker. */
|
|
15572
|
+
minRamBytes: number(),
|
|
15573
|
+
contextSizeDefault: number().int(),
|
|
15574
|
+
/** Vision models: companion projector file. */
|
|
15575
|
+
mmprojUrl: string().optional()
|
|
15576
|
+
});
|
|
15577
|
+
var LlmRuntimeNodeSchema = object({
|
|
15578
|
+
nodeId: string(),
|
|
15579
|
+
reachable: boolean(),
|
|
15580
|
+
status: LlmRuntimeStatusSchema.optional(),
|
|
15581
|
+
disk: LlmRuntimeDiskUsageSchema.optional(),
|
|
15582
|
+
error: string().optional()
|
|
15583
|
+
});
|
|
15584
|
+
var GenerateVisionInputSchema = LlmGenerateBaseInputSchema.extend({ images: array(LlmImageSchema).min(1) });
|
|
15585
|
+
var ProfileRefInputSchema = object({
|
|
15586
|
+
addonId: string(),
|
|
15587
|
+
profileId: string()
|
|
15588
|
+
});
|
|
15589
|
+
method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
|
|
15590
|
+
kind: "mutation",
|
|
15591
|
+
auth: "admin"
|
|
15592
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15593
|
+
kind: "mutation",
|
|
15594
|
+
auth: "admin"
|
|
15595
|
+
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
15596
|
+
kind: "mutation",
|
|
15597
|
+
auth: "admin"
|
|
15598
|
+
}), method(ProfileRefInputSchema, array(string())), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
15599
|
+
selector: LlmDefaultSelectorSchema,
|
|
15600
|
+
profileId: string().nullable()
|
|
15601
|
+
}), _void(), {
|
|
15602
|
+
kind: "mutation",
|
|
15603
|
+
auth: "admin"
|
|
15604
|
+
}), method(object({
|
|
15605
|
+
since: number().optional(),
|
|
15606
|
+
until: number().optional(),
|
|
15607
|
+
consumer: string().optional(),
|
|
15608
|
+
profileId: string().optional()
|
|
15609
|
+
}), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
|
|
15610
|
+
nodeId: string(),
|
|
15611
|
+
model: ManagedModelRefSchema
|
|
15612
|
+
}), _void(), {
|
|
15613
|
+
kind: "mutation",
|
|
15614
|
+
auth: "admin"
|
|
15615
|
+
}), method(object({
|
|
15616
|
+
nodeId: string(),
|
|
15617
|
+
file: string()
|
|
15618
|
+
}), _void(), {
|
|
15619
|
+
kind: "mutation",
|
|
15620
|
+
auth: "admin"
|
|
15621
|
+
}), method(ProfileRefInputSchema, LlmRuntimeStatusSchema), method(ProfileRefInputSchema, LlmRuntimeStatusSchema, {
|
|
15622
|
+
kind: "mutation",
|
|
15623
|
+
auth: "admin"
|
|
15624
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15625
|
+
kind: "mutation",
|
|
15626
|
+
auth: "admin"
|
|
15627
|
+
});
|
|
15628
|
+
/**
|
|
15034
15629
|
* Zod schemas for persisted record types.
|
|
15035
15630
|
*
|
|
15036
15631
|
* These schemas serve as the single source of truth for types that are
|
|
@@ -15232,6 +15827,22 @@ var TrackSnapshotSchema = object({
|
|
|
15232
15827
|
/** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
|
|
15233
15828
|
mediaKey: string()
|
|
15234
15829
|
});
|
|
15830
|
+
/**
|
|
15831
|
+
* One audio-classification label heard on the track's camera while the
|
|
15832
|
+
* track was alive, aggregated per label. An "episode" is one persisted
|
|
15833
|
+
* audio event (the confident-classification path: score ≥ the device's
|
|
15834
|
+
* `classificationMinScore`, class-change-or-heartbeat coalesced) — NOT
|
|
15835
|
+
* one 32 ms inference chunk, so counts stay human-scaled.
|
|
15836
|
+
*/
|
|
15837
|
+
var TrackAudioLabelSchema = object({
|
|
15838
|
+
label: string(),
|
|
15839
|
+
/** Highest classification score observed across the label's episodes. */
|
|
15840
|
+
peakScore: number(),
|
|
15841
|
+
/** Number of coalesced audio-event episodes carrying this label. */
|
|
15842
|
+
count: number(),
|
|
15843
|
+
firstAt: number(),
|
|
15844
|
+
lastAt: number()
|
|
15845
|
+
});
|
|
15235
15846
|
var TrackSchema = object({
|
|
15236
15847
|
trackId: string(),
|
|
15237
15848
|
deviceId: number(),
|
|
@@ -15246,6 +15857,10 @@ var TrackSchema = object({
|
|
|
15246
15857
|
snapshots: array(TrackSnapshotSchema).readonly(),
|
|
15247
15858
|
/** Deduplicated zones the track has entered at least once. */
|
|
15248
15859
|
zonesVisited: array(string()).readonly(),
|
|
15860
|
+
/** Deduplicated set of detector classes observed for this track over its
|
|
15861
|
+
* life (a track may be reclassified, e.g. person→vehicle). Absent on
|
|
15862
|
+
* legacy rows written before class accumulation shipped. */
|
|
15863
|
+
classes: array(string()).readonly().optional(),
|
|
15249
15864
|
/** Cumulative normalized distance travelled (0..1 units = full frame width). */
|
|
15250
15865
|
totalDistance: number(),
|
|
15251
15866
|
state: TrackStateSchema,
|
|
@@ -15259,7 +15874,11 @@ var TrackSchema = object({
|
|
|
15259
15874
|
bestEventId: string().optional(),
|
|
15260
15875
|
/** Tag of the importance sub-signal that dominated the score
|
|
15261
15876
|
* (identity|dwell|proximity|class|confidence|travel|zone). */
|
|
15262
|
-
importanceReason: string().optional()
|
|
15877
|
+
importanceReason: string().optional(),
|
|
15878
|
+
/** Audio-classification labels heard on the camera during the track's
|
|
15879
|
+
* life (score ≥ device `classificationMinScore`), aggregated per label.
|
|
15880
|
+
* Absent on legacy rows / tracks with no confident audio. */
|
|
15881
|
+
audioLabels: array(TrackAudioLabelSchema).readonly().optional()
|
|
15263
15882
|
});
|
|
15264
15883
|
var BaseEventFields = {
|
|
15265
15884
|
id: string(),
|
|
@@ -15407,7 +16026,7 @@ var KeyEventSchema = object({
|
|
|
15407
16026
|
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
15408
16027
|
windowMs: number().optional()
|
|
15409
16028
|
});
|
|
15410
|
-
|
|
16029
|
+
object({
|
|
15411
16030
|
trackId: string(),
|
|
15412
16031
|
className: string(),
|
|
15413
16032
|
confidence: number(),
|
|
@@ -15415,6 +16034,23 @@ var TrackedDetectionSchema = object({
|
|
|
15415
16034
|
zones: array(string()).readonly(),
|
|
15416
16035
|
state: TrackStateSchema
|
|
15417
16036
|
});
|
|
16037
|
+
var OverlayDetectionSchema = looseObject({
|
|
16038
|
+
id: string(),
|
|
16039
|
+
kind: _enum(["first-level", "detail"]),
|
|
16040
|
+
macroClass: string(),
|
|
16041
|
+
score: number(),
|
|
16042
|
+
bbox: object({
|
|
16043
|
+
x: number(),
|
|
16044
|
+
y: number(),
|
|
16045
|
+
width: number(),
|
|
16046
|
+
height: number()
|
|
16047
|
+
}),
|
|
16048
|
+
labels: array(looseObject({
|
|
16049
|
+
label: string(),
|
|
16050
|
+
score: number()
|
|
16051
|
+
})).readonly(),
|
|
16052
|
+
parentId: string().optional()
|
|
16053
|
+
});
|
|
15418
16054
|
var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: number() });
|
|
15419
16055
|
var SearchObjectEventsInput = object({
|
|
15420
16056
|
text: string(),
|
|
@@ -15425,6 +16061,20 @@ var SearchObjectEventsInput = object({
|
|
|
15425
16061
|
limit: number().default(50),
|
|
15426
16062
|
minScore: number().min(0).max(1).default(.2)
|
|
15427
16063
|
});
|
|
16064
|
+
var TrackCascadeCountsSchema = object({
|
|
16065
|
+
/** Persisted track roots deleted (authoritative). */
|
|
16066
|
+
tracks: number().int(),
|
|
16067
|
+
/** Object events removed with their tracks (best-effort; see note above). */
|
|
16068
|
+
events: number().int(),
|
|
16069
|
+
/** Track/face/plate-owned media removed (best-effort). Never identity media. */
|
|
16070
|
+
media: number().int(),
|
|
16071
|
+
/** Unassigned (non-enrolled) face reads removed (best-effort). */
|
|
16072
|
+
faces: number().int(),
|
|
16073
|
+
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
16074
|
+
plates: number().int(),
|
|
16075
|
+
/** Per-track CLIP search vectors removed (best-effort). */
|
|
16076
|
+
embeddings: number().int()
|
|
16077
|
+
});
|
|
15428
16078
|
DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
|
|
15429
16079
|
deviceId: number(),
|
|
15430
16080
|
trackId: string()
|
|
@@ -15457,15 +16107,33 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15457
16107
|
kind: "mutation",
|
|
15458
16108
|
auth: "admin"
|
|
15459
16109
|
}), method(object({
|
|
15460
|
-
eventId: string(),
|
|
15461
|
-
kind: MediaFileKindEnum.optional()
|
|
15462
|
-
}), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
15463
16110
|
deviceId: number(),
|
|
15464
|
-
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
}), object({
|
|
16111
|
+
cutoffMs: number()
|
|
16112
|
+
}), TrackCascadeCountsSchema, {
|
|
16113
|
+
kind: "mutation",
|
|
16114
|
+
auth: "admin"
|
|
16115
|
+
}), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
|
|
16116
|
+
kind: "mutation",
|
|
16117
|
+
auth: "admin"
|
|
16118
|
+
}), method(object({
|
|
16119
|
+
deviceId: number(),
|
|
16120
|
+
trackIds: array(string()).min(1)
|
|
16121
|
+
}), object({
|
|
16122
|
+
deleted: number().int(),
|
|
16123
|
+
failed: array(string()).readonly()
|
|
16124
|
+
}), {
|
|
16125
|
+
kind: "mutation",
|
|
16126
|
+
auth: "admin"
|
|
16127
|
+
}), method(object({
|
|
16128
|
+
eventId: string(),
|
|
16129
|
+
kind: MediaFileKindEnum.optional()
|
|
16130
|
+
}), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
16131
|
+
deviceId: number(),
|
|
16132
|
+
timestamp: number(),
|
|
16133
|
+
frameWidth: number(),
|
|
16134
|
+
frameHeight: number(),
|
|
16135
|
+
detections: array(OverlayDetectionSchema).readonly()
|
|
16136
|
+
}), object({
|
|
15469
16137
|
deviceId: number(),
|
|
15470
16138
|
trackId: string(),
|
|
15471
16139
|
className: string()
|
|
@@ -18233,6 +18901,24 @@ DeviceType.Camera, method(object({
|
|
|
18233
18901
|
deviceId: number(),
|
|
18234
18902
|
status: OsdStatusSchema
|
|
18235
18903
|
});
|
|
18904
|
+
var VehicleSchema = object({
|
|
18905
|
+
id: string(),
|
|
18906
|
+
name: string(),
|
|
18907
|
+
sampleCount: number().int().nonnegative(),
|
|
18908
|
+
coverMediaKey: string().optional(),
|
|
18909
|
+
/** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
|
|
18910
|
+
coverBase64: string().optional()
|
|
18911
|
+
});
|
|
18912
|
+
var VehicleSampleInfoSchema = object({
|
|
18913
|
+
id: string(),
|
|
18914
|
+
/** The plate text this sample enrolled (self-labeling — no embedding). */
|
|
18915
|
+
text: string(),
|
|
18916
|
+
/** OCR confidence of the enrolled read (0..1). */
|
|
18917
|
+
score: number(),
|
|
18918
|
+
addedAt: number().int(),
|
|
18919
|
+
deviceId: number().int().optional(),
|
|
18920
|
+
base64: string().optional()
|
|
18921
|
+
});
|
|
18236
18922
|
var PlateInfoSchema = object({
|
|
18237
18923
|
plateId: string(),
|
|
18238
18924
|
deviceId: number().int(),
|
|
@@ -18244,6 +18930,16 @@ var PlateInfoSchema = object({
|
|
|
18244
18930
|
score: number(),
|
|
18245
18931
|
/** True when the text was manually corrected (exempt from retention). */
|
|
18246
18932
|
corrected: boolean(),
|
|
18933
|
+
/** Recognized vehicle id when a matcher lookup named this read (SQL NULL → absent). */
|
|
18934
|
+
recognizedVehicleId: string().optional(),
|
|
18935
|
+
/** Resolved vehicle name for `recognizedVehicleId` (UI convenience). */
|
|
18936
|
+
vehicleName: string().optional(),
|
|
18937
|
+
/** True once the read was assigned to a vehicle (enrolled as a sample). */
|
|
18938
|
+
assigned: boolean(),
|
|
18939
|
+
/** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
|
|
18940
|
+
plateBbox: BoundingBoxSchema.optional(),
|
|
18941
|
+
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
18942
|
+
keyFrameMediaKey: string().optional(),
|
|
18247
18943
|
base64: string().optional()
|
|
18248
18944
|
});
|
|
18249
18945
|
var MediaFileLiteSchema = object({
|
|
@@ -18283,6 +18979,48 @@ method(object({
|
|
|
18283
18979
|
}), method(object({ plateId: string() }), _void(), {
|
|
18284
18980
|
kind: "mutation",
|
|
18285
18981
|
auth: "admin"
|
|
18982
|
+
}), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
|
|
18983
|
+
kind: "mutation",
|
|
18984
|
+
auth: "admin"
|
|
18985
|
+
}), method(object({
|
|
18986
|
+
id: string(),
|
|
18987
|
+
name: string().min(1)
|
|
18988
|
+
}), _void(), {
|
|
18989
|
+
kind: "mutation",
|
|
18990
|
+
auth: "admin"
|
|
18991
|
+
}), method(object({ id: string() }), _void(), {
|
|
18992
|
+
kind: "mutation",
|
|
18993
|
+
auth: "admin"
|
|
18994
|
+
}), method(object({ vehicleId: string() }), array(VehicleSampleInfoSchema).readonly()), method(object({
|
|
18995
|
+
vehicleId: string(),
|
|
18996
|
+
sampleId: string()
|
|
18997
|
+
}), _void(), {
|
|
18998
|
+
kind: "mutation",
|
|
18999
|
+
auth: "admin"
|
|
19000
|
+
}), method(object({
|
|
19001
|
+
plateId: string(),
|
|
19002
|
+
vehicleId: string()
|
|
19003
|
+
}), _void(), {
|
|
19004
|
+
kind: "mutation",
|
|
19005
|
+
auth: "admin"
|
|
19006
|
+
}), method(object({ plateId: string() }), _void(), {
|
|
19007
|
+
kind: "mutation",
|
|
19008
|
+
auth: "admin"
|
|
19009
|
+
}), method(object({
|
|
19010
|
+
plateIds: array(string()).min(1),
|
|
19011
|
+
vehicleId: string()
|
|
19012
|
+
}), object({
|
|
19013
|
+
assigned: number().int(),
|
|
19014
|
+
failed: array(string()).readonly()
|
|
19015
|
+
}), {
|
|
19016
|
+
kind: "mutation",
|
|
19017
|
+
auth: "admin"
|
|
19018
|
+
}), method(object({ plateIds: array(string()).min(1) }), object({
|
|
19019
|
+
unassigned: number().int(),
|
|
19020
|
+
failed: array(string()).readonly()
|
|
19021
|
+
}), {
|
|
19022
|
+
kind: "mutation",
|
|
19023
|
+
auth: "admin"
|
|
18286
19024
|
});
|
|
18287
19025
|
var ModelFormatSchema = _enum(MODEL_FORMATS);
|
|
18288
19026
|
var HwAccelBackendInputSchema = _enum([
|
|
@@ -18361,6 +19099,10 @@ var GpuInfoSchema = object({
|
|
|
18361
19099
|
memoryMB: number().optional()
|
|
18362
19100
|
});
|
|
18363
19101
|
var NpuInfoSchema = object({ type: _enum(["apple-ane", "intel-npu"]) });
|
|
19102
|
+
var CoralInfoSchema = object({
|
|
19103
|
+
type: literal("coral-edgetpu"),
|
|
19104
|
+
bus: string().optional()
|
|
19105
|
+
});
|
|
18364
19106
|
var HardwareInfoSchema = object({
|
|
18365
19107
|
platform: HardwarePlatformSchema,
|
|
18366
19108
|
arch: HardwareArchSchema,
|
|
@@ -18369,7 +19111,8 @@ var HardwareInfoSchema = object({
|
|
|
18369
19111
|
totalRAM_MB: number(),
|
|
18370
19112
|
availableRAM_MB: number(),
|
|
18371
19113
|
gpu: GpuInfoSchema.nullable(),
|
|
18372
|
-
npu: NpuInfoSchema.nullable()
|
|
19114
|
+
npu: NpuInfoSchema.nullable(),
|
|
19115
|
+
coral: CoralInfoSchema.nullable().optional()
|
|
18373
19116
|
});
|
|
18374
19117
|
var PlatformScoreSchema = object({
|
|
18375
19118
|
runtime: _enum(["node", "python"]),
|
|
@@ -20828,6 +21571,168 @@ Object.freeze({
|
|
|
20828
21571
|
addonId: null,
|
|
20829
21572
|
access: "create"
|
|
20830
21573
|
},
|
|
21574
|
+
"llm.deleteModel": {
|
|
21575
|
+
capName: "llm",
|
|
21576
|
+
capScope: "system",
|
|
21577
|
+
addonId: null,
|
|
21578
|
+
access: "delete"
|
|
21579
|
+
},
|
|
21580
|
+
"llm.deleteProfile": {
|
|
21581
|
+
capName: "llm",
|
|
21582
|
+
capScope: "system",
|
|
21583
|
+
addonId: null,
|
|
21584
|
+
access: "delete"
|
|
21585
|
+
},
|
|
21586
|
+
"llm.generate": {
|
|
21587
|
+
capName: "llm",
|
|
21588
|
+
capScope: "system",
|
|
21589
|
+
addonId: null,
|
|
21590
|
+
access: "create"
|
|
21591
|
+
},
|
|
21592
|
+
"llm.generateVision": {
|
|
21593
|
+
capName: "llm",
|
|
21594
|
+
capScope: "system",
|
|
21595
|
+
addonId: null,
|
|
21596
|
+
access: "create"
|
|
21597
|
+
},
|
|
21598
|
+
"llm.getDefaults": {
|
|
21599
|
+
capName: "llm",
|
|
21600
|
+
capScope: "system",
|
|
21601
|
+
addonId: null,
|
|
21602
|
+
access: "view"
|
|
21603
|
+
},
|
|
21604
|
+
"llm.getRuntimeStatus": {
|
|
21605
|
+
capName: "llm",
|
|
21606
|
+
capScope: "system",
|
|
21607
|
+
addonId: null,
|
|
21608
|
+
access: "view"
|
|
21609
|
+
},
|
|
21610
|
+
"llm.getUsage": {
|
|
21611
|
+
capName: "llm",
|
|
21612
|
+
capScope: "system",
|
|
21613
|
+
addonId: null,
|
|
21614
|
+
access: "view"
|
|
21615
|
+
},
|
|
21616
|
+
"llm.installModel": {
|
|
21617
|
+
capName: "llm",
|
|
21618
|
+
capScope: "system",
|
|
21619
|
+
addonId: null,
|
|
21620
|
+
access: "create"
|
|
21621
|
+
},
|
|
21622
|
+
"llm.listModelCatalog": {
|
|
21623
|
+
capName: "llm",
|
|
21624
|
+
capScope: "system",
|
|
21625
|
+
addonId: null,
|
|
21626
|
+
access: "view"
|
|
21627
|
+
},
|
|
21628
|
+
"llm.listModels": {
|
|
21629
|
+
capName: "llm",
|
|
21630
|
+
capScope: "system",
|
|
21631
|
+
addonId: null,
|
|
21632
|
+
access: "view"
|
|
21633
|
+
},
|
|
21634
|
+
"llm.listNodeModels": {
|
|
21635
|
+
capName: "llm",
|
|
21636
|
+
capScope: "system",
|
|
21637
|
+
addonId: null,
|
|
21638
|
+
access: "view"
|
|
21639
|
+
},
|
|
21640
|
+
"llm.listProfileKinds": {
|
|
21641
|
+
capName: "llm",
|
|
21642
|
+
capScope: "system",
|
|
21643
|
+
addonId: null,
|
|
21644
|
+
access: "view"
|
|
21645
|
+
},
|
|
21646
|
+
"llm.listProfiles": {
|
|
21647
|
+
capName: "llm",
|
|
21648
|
+
capScope: "system",
|
|
21649
|
+
addonId: null,
|
|
21650
|
+
access: "view"
|
|
21651
|
+
},
|
|
21652
|
+
"llm.listRuntimeNodes": {
|
|
21653
|
+
capName: "llm",
|
|
21654
|
+
capScope: "system",
|
|
21655
|
+
addonId: null,
|
|
21656
|
+
access: "view"
|
|
21657
|
+
},
|
|
21658
|
+
"llm.setDefault": {
|
|
21659
|
+
capName: "llm",
|
|
21660
|
+
capScope: "system",
|
|
21661
|
+
addonId: null,
|
|
21662
|
+
access: "create"
|
|
21663
|
+
},
|
|
21664
|
+
"llm.startRuntime": {
|
|
21665
|
+
capName: "llm",
|
|
21666
|
+
capScope: "system",
|
|
21667
|
+
addonId: null,
|
|
21668
|
+
access: "create"
|
|
21669
|
+
},
|
|
21670
|
+
"llm.stopRuntime": {
|
|
21671
|
+
capName: "llm",
|
|
21672
|
+
capScope: "system",
|
|
21673
|
+
addonId: null,
|
|
21674
|
+
access: "create"
|
|
21675
|
+
},
|
|
21676
|
+
"llm.testProfile": {
|
|
21677
|
+
capName: "llm",
|
|
21678
|
+
capScope: "system",
|
|
21679
|
+
addonId: null,
|
|
21680
|
+
access: "create"
|
|
21681
|
+
},
|
|
21682
|
+
"llm.upsertProfile": {
|
|
21683
|
+
capName: "llm",
|
|
21684
|
+
capScope: "system",
|
|
21685
|
+
addonId: null,
|
|
21686
|
+
access: "create"
|
|
21687
|
+
},
|
|
21688
|
+
"llmRuntime.complete": {
|
|
21689
|
+
capName: "llm-runtime",
|
|
21690
|
+
capScope: "system",
|
|
21691
|
+
addonId: null,
|
|
21692
|
+
access: "create"
|
|
21693
|
+
},
|
|
21694
|
+
"llmRuntime.deleteModel": {
|
|
21695
|
+
capName: "llm-runtime",
|
|
21696
|
+
capScope: "system",
|
|
21697
|
+
addonId: null,
|
|
21698
|
+
access: "delete"
|
|
21699
|
+
},
|
|
21700
|
+
"llmRuntime.ensureStarted": {
|
|
21701
|
+
capName: "llm-runtime",
|
|
21702
|
+
capScope: "system",
|
|
21703
|
+
addonId: null,
|
|
21704
|
+
access: "create"
|
|
21705
|
+
},
|
|
21706
|
+
"llmRuntime.getDiskUsage": {
|
|
21707
|
+
capName: "llm-runtime",
|
|
21708
|
+
capScope: "system",
|
|
21709
|
+
addonId: null,
|
|
21710
|
+
access: "view"
|
|
21711
|
+
},
|
|
21712
|
+
"llmRuntime.installModel": {
|
|
21713
|
+
capName: "llm-runtime",
|
|
21714
|
+
capScope: "system",
|
|
21715
|
+
addonId: null,
|
|
21716
|
+
access: "create"
|
|
21717
|
+
},
|
|
21718
|
+
"llmRuntime.listLocalModels": {
|
|
21719
|
+
capName: "llm-runtime",
|
|
21720
|
+
capScope: "system",
|
|
21721
|
+
addonId: null,
|
|
21722
|
+
access: "view"
|
|
21723
|
+
},
|
|
21724
|
+
"llmRuntime.status": {
|
|
21725
|
+
capName: "llm-runtime",
|
|
21726
|
+
capScope: "system",
|
|
21727
|
+
addonId: null,
|
|
21728
|
+
access: "view"
|
|
21729
|
+
},
|
|
21730
|
+
"llmRuntime.stop": {
|
|
21731
|
+
capName: "llm-runtime",
|
|
21732
|
+
capScope: "system",
|
|
21733
|
+
addonId: null,
|
|
21734
|
+
access: "create"
|
|
21735
|
+
},
|
|
20831
21736
|
"localNetwork.getAllowedAddresses": {
|
|
20832
21737
|
capName: "local-network",
|
|
20833
21738
|
capScope: "system",
|
|
@@ -21458,6 +22363,12 @@ Object.freeze({
|
|
|
21458
22363
|
addonId: null,
|
|
21459
22364
|
access: "delete"
|
|
21460
22365
|
},
|
|
22366
|
+
"pipelineAnalytics.deleteTracks": {
|
|
22367
|
+
capName: "pipeline-analytics",
|
|
22368
|
+
capScope: "device",
|
|
22369
|
+
addonId: null,
|
|
22370
|
+
access: "delete"
|
|
22371
|
+
},
|
|
21461
22372
|
"pipelineAnalytics.getActiveTracks": {
|
|
21462
22373
|
capName: "pipeline-analytics",
|
|
21463
22374
|
capScope: "device",
|
|
@@ -21524,12 +22435,24 @@ Object.freeze({
|
|
|
21524
22435
|
addonId: null,
|
|
21525
22436
|
access: "create"
|
|
21526
22437
|
},
|
|
22438
|
+
"pipelineAnalytics.pruneTracksBefore": {
|
|
22439
|
+
capName: "pipeline-analytics",
|
|
22440
|
+
capScope: "device",
|
|
22441
|
+
addonId: null,
|
|
22442
|
+
access: "create"
|
|
22443
|
+
},
|
|
21527
22444
|
"pipelineAnalytics.searchObjectEvents": {
|
|
21528
22445
|
capName: "pipeline-analytics",
|
|
21529
22446
|
capScope: "device",
|
|
21530
22447
|
addonId: null,
|
|
21531
22448
|
access: "view"
|
|
21532
22449
|
},
|
|
22450
|
+
"pipelineAnalytics.wipeAllAnalytics": {
|
|
22451
|
+
capName: "pipeline-analytics",
|
|
22452
|
+
capScope: "device",
|
|
22453
|
+
addonId: null,
|
|
22454
|
+
access: "delete"
|
|
22455
|
+
},
|
|
21533
22456
|
"pipelineExecutor.cacheFrameInPool": {
|
|
21534
22457
|
capName: "pipeline-executor",
|
|
21535
22458
|
capScope: "system",
|
|
@@ -22028,18 +22951,48 @@ Object.freeze({
|
|
|
22028
22951
|
addonId: null,
|
|
22029
22952
|
access: "create"
|
|
22030
22953
|
},
|
|
22954
|
+
"pipelineRunner.runDetailSubtree": {
|
|
22955
|
+
capName: "pipeline-runner",
|
|
22956
|
+
capScope: "system",
|
|
22957
|
+
addonId: null,
|
|
22958
|
+
access: "create"
|
|
22959
|
+
},
|
|
22960
|
+
"plateGallery.assignPlate": {
|
|
22961
|
+
capName: "plate-gallery",
|
|
22962
|
+
capScope: "system",
|
|
22963
|
+
addonId: null,
|
|
22964
|
+
access: "create"
|
|
22965
|
+
},
|
|
22966
|
+
"plateGallery.assignPlates": {
|
|
22967
|
+
capName: "plate-gallery",
|
|
22968
|
+
capScope: "system",
|
|
22969
|
+
addonId: null,
|
|
22970
|
+
access: "create"
|
|
22971
|
+
},
|
|
22031
22972
|
"plateGallery.correctPlateText": {
|
|
22032
22973
|
capName: "plate-gallery",
|
|
22033
22974
|
capScope: "system",
|
|
22034
22975
|
addonId: null,
|
|
22035
22976
|
access: "create"
|
|
22036
22977
|
},
|
|
22978
|
+
"plateGallery.createVehicle": {
|
|
22979
|
+
capName: "plate-gallery",
|
|
22980
|
+
capScope: "system",
|
|
22981
|
+
addonId: null,
|
|
22982
|
+
access: "create"
|
|
22983
|
+
},
|
|
22037
22984
|
"plateGallery.deletePlate": {
|
|
22038
22985
|
capName: "plate-gallery",
|
|
22039
22986
|
capScope: "system",
|
|
22040
22987
|
addonId: null,
|
|
22041
22988
|
access: "delete"
|
|
22042
22989
|
},
|
|
22990
|
+
"plateGallery.deleteVehicle": {
|
|
22991
|
+
capName: "plate-gallery",
|
|
22992
|
+
capScope: "system",
|
|
22993
|
+
addonId: null,
|
|
22994
|
+
access: "delete"
|
|
22995
|
+
},
|
|
22043
22996
|
"plateGallery.getPlateByTrack": {
|
|
22044
22997
|
capName: "plate-gallery",
|
|
22045
22998
|
capScope: "system",
|
|
@@ -22058,6 +23011,30 @@ Object.freeze({
|
|
|
22058
23011
|
addonId: null,
|
|
22059
23012
|
access: "view"
|
|
22060
23013
|
},
|
|
23014
|
+
"plateGallery.listVehicles": {
|
|
23015
|
+
capName: "plate-gallery",
|
|
23016
|
+
capScope: "system",
|
|
23017
|
+
addonId: null,
|
|
23018
|
+
access: "view"
|
|
23019
|
+
},
|
|
23020
|
+
"plateGallery.listVehicleSamples": {
|
|
23021
|
+
capName: "plate-gallery",
|
|
23022
|
+
capScope: "system",
|
|
23023
|
+
addonId: null,
|
|
23024
|
+
access: "view"
|
|
23025
|
+
},
|
|
23026
|
+
"plateGallery.removeVehicleSample": {
|
|
23027
|
+
capName: "plate-gallery",
|
|
23028
|
+
capScope: "system",
|
|
23029
|
+
addonId: null,
|
|
23030
|
+
access: "delete"
|
|
23031
|
+
},
|
|
23032
|
+
"plateGallery.renameVehicle": {
|
|
23033
|
+
capName: "plate-gallery",
|
|
23034
|
+
capScope: "system",
|
|
23035
|
+
addonId: null,
|
|
23036
|
+
access: "create"
|
|
23037
|
+
},
|
|
22061
23038
|
"plateGallery.searchPlates": {
|
|
22062
23039
|
capName: "plate-gallery",
|
|
22063
23040
|
capScope: "system",
|
|
@@ -22070,6 +23047,18 @@ Object.freeze({
|
|
|
22070
23047
|
addonId: null,
|
|
22071
23048
|
access: "view"
|
|
22072
23049
|
},
|
|
23050
|
+
"plateGallery.unassignPlate": {
|
|
23051
|
+
capName: "plate-gallery",
|
|
23052
|
+
capScope: "system",
|
|
23053
|
+
addonId: null,
|
|
23054
|
+
access: "create"
|
|
23055
|
+
},
|
|
23056
|
+
"plateGallery.unassignPlates": {
|
|
23057
|
+
capName: "plate-gallery",
|
|
23058
|
+
capScope: "system",
|
|
23059
|
+
addonId: null,
|
|
23060
|
+
access: "create"
|
|
23061
|
+
},
|
|
22073
23062
|
"platformProbe.getCapabilities": {
|
|
22074
23063
|
capName: "platform-probe",
|
|
22075
23064
|
capScope: "system",
|
|
@@ -22262,6 +23251,48 @@ Object.freeze({
|
|
|
22262
23251
|
addonId: null,
|
|
22263
23252
|
access: "create"
|
|
22264
23253
|
},
|
|
23254
|
+
"sceneMonitor.captureReference": {
|
|
23255
|
+
capName: "scene-monitor",
|
|
23256
|
+
capScope: "device",
|
|
23257
|
+
addonId: null,
|
|
23258
|
+
access: "create"
|
|
23259
|
+
},
|
|
23260
|
+
"sceneMonitor.createScene": {
|
|
23261
|
+
capName: "scene-monitor",
|
|
23262
|
+
capScope: "device",
|
|
23263
|
+
addonId: null,
|
|
23264
|
+
access: "create"
|
|
23265
|
+
},
|
|
23266
|
+
"sceneMonitor.deleteReference": {
|
|
23267
|
+
capName: "scene-monitor",
|
|
23268
|
+
capScope: "device",
|
|
23269
|
+
addonId: null,
|
|
23270
|
+
access: "delete"
|
|
23271
|
+
},
|
|
23272
|
+
"sceneMonitor.deleteScene": {
|
|
23273
|
+
capName: "scene-monitor",
|
|
23274
|
+
capScope: "device",
|
|
23275
|
+
addonId: null,
|
|
23276
|
+
access: "delete"
|
|
23277
|
+
},
|
|
23278
|
+
"sceneMonitor.listScenes": {
|
|
23279
|
+
capName: "scene-monitor",
|
|
23280
|
+
capScope: "device",
|
|
23281
|
+
addonId: null,
|
|
23282
|
+
access: "view"
|
|
23283
|
+
},
|
|
23284
|
+
"sceneMonitor.recheckNow": {
|
|
23285
|
+
capName: "scene-monitor",
|
|
23286
|
+
capScope: "device",
|
|
23287
|
+
addonId: null,
|
|
23288
|
+
access: "create"
|
|
23289
|
+
},
|
|
23290
|
+
"sceneMonitor.updateScene": {
|
|
23291
|
+
capName: "scene-monitor",
|
|
23292
|
+
capScope: "device",
|
|
23293
|
+
addonId: null,
|
|
23294
|
+
access: "create"
|
|
23295
|
+
},
|
|
22265
23296
|
"scriptRunner.run": {
|
|
22266
23297
|
capName: "script-runner",
|
|
22267
23298
|
capScope: "device",
|
|
@@ -23346,6 +24377,7 @@ Object.freeze({
|
|
|
23346
24377
|
Object.freeze({
|
|
23347
24378
|
"broker": "broker",
|
|
23348
24379
|
"device-export": "device-export",
|
|
24380
|
+
"llm": "ai",
|
|
23349
24381
|
"mesh-network": "mesh",
|
|
23350
24382
|
"mqtt-broker": "broker",
|
|
23351
24383
|
"network-access": "ingress",
|
|
@@ -23480,77 +24512,12 @@ function rankDecodeHwAccels(preferred, supportedMethods) {
|
|
|
23480
24512
|
return candidates.sort((a, b) => decodeHwAccelRankIndex(a) - decodeHwAccelRankIndex(b));
|
|
23481
24513
|
}
|
|
23482
24514
|
//#endregion
|
|
23483
|
-
//#region src/shm-orphan-purge.ts
|
|
23484
|
-
/**
|
|
23485
|
-
* Startup reclamation of orphaned shared-memory segments.
|
|
23486
|
-
*
|
|
23487
|
-
* A decoder writes frames into named `/dev/shm` segments and unlinks each one
|
|
23488
|
-
* on graceful session teardown (`DecoderFrameRingSink.destroy`). When the
|
|
23489
|
-
* decoder process dies *ungracefully* — SIGBUS, OOM-kill, or a SIGKILL during
|
|
23490
|
-
* redeploy — that teardown never runs and the segment is orphaned: it stays in
|
|
23491
|
-
* the tmpfs forever, since nothing else knows its name. Across many
|
|
23492
|
-
* crashes/redeploys these accumulate until `/dev/shm` fills, at which point the
|
|
23493
|
-
* next `mmap` write faults with an uncatchable SIGBUS and the decoder
|
|
23494
|
-
* crash-loops into its circuit breaker (which is exactly the incident this
|
|
23495
|
-
* guards against).
|
|
23496
|
-
*
|
|
23497
|
-
* The reclamation is safe *at process startup*: a freshly-booting decoder owns
|
|
23498
|
-
* no live sessions, so every pre-existing segment with its prefix is by
|
|
23499
|
-
* definition an orphan from a dead instance. `shm_unlink` only removes the
|
|
23500
|
-
* name — any consumer still holding a mapping keeps reading valid memory until
|
|
23501
|
-
* it closes (POSIX deferred reclaim + the ring seqlock), so unlinking is safe
|
|
23502
|
-
* even if a stale reader is momentarily still attached.
|
|
23503
|
-
*
|
|
23504
|
-
* POSIX-only: segments surface as files under `/dev/shm` on Linux. On platforms
|
|
23505
|
-
* without that directory (Windows, macOS) the scan finds nothing — no-op.
|
|
23506
|
-
*
|
|
23507
|
-
* NOTE: this lives inside the decoder addon (not `@camstack/shm-ring`) so it
|
|
23508
|
-
* ships in the self-contained addon bundle via `camstack deploy`, reusing the
|
|
23509
|
-
* already-deployed `unlinkSegment`; no host base-image rebuild required.
|
|
23510
|
-
*/
|
|
23511
|
-
/** Default tmpfs directory where POSIX shared-memory segments appear on Linux. */
|
|
23512
|
-
var DEFAULT_SHM_DIR = "/dev/shm";
|
|
23513
|
-
/**
|
|
23514
|
-
* Unlink every shared-memory segment whose name starts with `prefix`.
|
|
23515
|
-
*
|
|
23516
|
-
* Intended to run ONCE at decoder startup, before any session is created, to
|
|
23517
|
-
* reclaim segments orphaned by a previously-crashed instance. A per-file unlink
|
|
23518
|
-
* failure is swallowed so one stuck segment cannot block reclaiming the rest.
|
|
23519
|
-
*/
|
|
23520
|
-
function purgeOrphanSegments(prefix, options = {}) {
|
|
23521
|
-
const dir = options.dir ?? DEFAULT_SHM_DIR;
|
|
23522
|
-
const unlink = options.unlink ?? unlinkSegment;
|
|
23523
|
-
let entries;
|
|
23524
|
-
try {
|
|
23525
|
-
entries = readdirSync(dir);
|
|
23526
|
-
} catch {
|
|
23527
|
-
return {
|
|
23528
|
-
scanned: 0,
|
|
23529
|
-
removed: 0,
|
|
23530
|
-
names: []
|
|
23531
|
-
};
|
|
23532
|
-
}
|
|
23533
|
-
const names = [];
|
|
23534
|
-
for (const name of entries) {
|
|
23535
|
-
if (!name.startsWith(prefix)) continue;
|
|
23536
|
-
try {
|
|
23537
|
-
unlink(name);
|
|
23538
|
-
names.push(name);
|
|
23539
|
-
} catch {}
|
|
23540
|
-
}
|
|
23541
|
-
return {
|
|
23542
|
-
scanned: entries.length,
|
|
23543
|
-
removed: names.length,
|
|
23544
|
-
names
|
|
23545
|
-
};
|
|
23546
|
-
}
|
|
23547
|
-
//#endregion
|
|
23548
24515
|
//#region src/shared/notifying-ring-buffer.ts
|
|
23549
24516
|
/**
|
|
23550
24517
|
* A {@link RingBuffer} that can notify a single blocked consumer the moment an
|
|
23551
|
-
* item commits — the server side of the `pullFrames
|
|
24518
|
+
* item commits — the server side of the `pullFrames` long-poll.
|
|
23552
24519
|
*
|
|
23553
|
-
* The decoder's `onFrame
|
|
24520
|
+
* The decoder's `onFrame` callback drives `push()`; a consumer
|
|
23554
24521
|
* that finds the ring momentarily empty calls {@link waitForItem} and is woken
|
|
23555
24522
|
* on the very next `push()` (a real commit signal) rather than spinning at
|
|
23556
24523
|
* ~1kHz on `setTimeout(…,1)`. If nothing commits it resolves after `waitMs`,
|
|
@@ -23888,19 +24855,7 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
23888
24855
|
config;
|
|
23889
24856
|
logger;
|
|
23890
24857
|
frameCallbacks = /* @__PURE__ */ new Set();
|
|
23891
|
-
handleCallbacks = /* @__PURE__ */ new Set();
|
|
23892
24858
|
destroyed = false;
|
|
23893
|
-
/**
|
|
23894
|
-
* Frame delivery mode (see {@link DecoderFrameSink}). `'shm'` lazily
|
|
23895
|
-
* constructs `frameRingSink` and routes every decoded frame through it.
|
|
23896
|
-
*/
|
|
23897
|
-
frameSink;
|
|
23898
|
-
/**
|
|
23899
|
-
* The shared-memory ring writer for this stream. Created lazily on the
|
|
23900
|
-
* first decoded frame in `'shm'` mode (the segment cannot be sized until
|
|
23901
|
-
* the output geometry is known) and torn down in `destroy`.
|
|
23902
|
-
*/
|
|
23903
|
-
frameRingSink = null;
|
|
23904
24859
|
parser = null;
|
|
23905
24860
|
codecCtx = null;
|
|
23906
24861
|
scaler = null;
|
|
@@ -24021,8 +24976,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24021
24976
|
startTime = Date.now();
|
|
24022
24977
|
hwaccelPref;
|
|
24023
24978
|
hwaccelResolver;
|
|
24024
|
-
/** Cluster node id stamped into every `FrameHandle` the `'shm'` sink emits. */
|
|
24025
|
-
nodeId;
|
|
24026
24979
|
/** The backend that actually initialised successfully — `'none'` = software fallback. */
|
|
24027
24980
|
activeHwAccel = "none";
|
|
24028
24981
|
hwDevice = null;
|
|
@@ -24054,35 +25007,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24054
25007
|
this.outputMode = NodeAvDecoderSession.resolveOutputMode(config.outputFormat);
|
|
24055
25008
|
this.hwaccelPref = options?.hwaccel ?? "auto";
|
|
24056
25009
|
this.hwaccelResolver = options?.hwaccelResolver ?? null;
|
|
24057
|
-
this.frameSink = options?.frameSink ?? "callback";
|
|
24058
|
-
this.nodeId = options?.nodeId ?? "local";
|
|
24059
|
-
}
|
|
24060
|
-
/**
|
|
24061
|
-
* The shared-memory ring sink for this stream, or `null` before the first
|
|
24062
|
-
* `'shm'`-mode frame lazily creates it. Exposed so the owning addon can
|
|
24063
|
-
* surface ring stats via `decoder.getShmStats`.
|
|
24064
|
-
*/
|
|
24065
|
-
get frameRingSinkOrNull() {
|
|
24066
|
-
return this.frameRingSink;
|
|
24067
|
-
}
|
|
24068
|
-
/**
|
|
24069
|
-
* Lazily build the shared-memory ring sink for this stream. The segment
|
|
24070
|
-
* itself is still created lazily by the sink on its first `writeFrame`,
|
|
24071
|
-
* once the decoded geometry is known.
|
|
24072
|
-
*/
|
|
24073
|
-
ensureFrameRingSink() {
|
|
24074
|
-
if (this.frameRingSink === null) {
|
|
24075
|
-
const seedParts = [];
|
|
24076
|
-
if (typeof this.config.deviceId === "number") seedParts.push(String(this.config.deviceId));
|
|
24077
|
-
if (typeof this.config.tag === "string" && this.config.tag.length > 0) seedParts.push(this.config.tag);
|
|
24078
|
-
const seed = seedParts.length > 0 ? seedParts.join(":") : "anon";
|
|
24079
|
-
this.frameRingSink = new DecoderFrameRingSink({
|
|
24080
|
-
seed,
|
|
24081
|
-
logger: this.logger,
|
|
24082
|
-
nodeId: this.nodeId
|
|
24083
|
-
});
|
|
24084
|
-
}
|
|
24085
|
-
return this.frameRingSink;
|
|
24086
25010
|
}
|
|
24087
25011
|
/**
|
|
24088
25012
|
* Resolve the backend preference list and try each one against
|
|
@@ -24706,10 +25630,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24706
25630
|
}
|
|
24707
25631
|
if (!this.dstFrame || !this.scaler) return;
|
|
24708
25632
|
const decodeStart = performance.now();
|
|
24709
|
-
if (this.frameSink === "shm" && this.outputMode !== "jpeg") {
|
|
24710
|
-
this.scaleIntoRingSlot(frame, decodeStart);
|
|
24711
|
-
return;
|
|
24712
|
-
}
|
|
24713
25633
|
try {
|
|
24714
25634
|
this.dstFrame.makeWritable();
|
|
24715
25635
|
this.scaler.scaleFrameSync(this.dstFrame, frame);
|
|
@@ -24754,100 +25674,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24754
25674
|
else this.emitRawFrame(rawBuf, "gray", decodeStart);
|
|
24755
25675
|
}
|
|
24756
25676
|
/**
|
|
24757
|
-
* Scale a decoded frame **directly into a shared-memory ring slot** — the
|
|
24758
|
-
* zero write-side copy path (Phase 5 / D9 Task 7c).
|
|
24759
|
-
*
|
|
24760
|
-
* `beginFrame` reserves the slot (its seqlock open / odd — readers skip it);
|
|
24761
|
-
* `SoftwareScaleContext.scaleSync` (the low-level `sws_scale` mapping) writes
|
|
24762
|
-
* the packed pixels straight into the slot buffer with `linesize = width ×
|
|
24763
|
-
* bpp` — no linesize padding, so there is nothing to strip and nothing to
|
|
24764
|
-
* memcpy; `commitFrame` writes the metadata, closes the seqlock and publishes
|
|
24765
|
-
* the slot. The decoded source planes feed `scaleSync` as `srcSlice` +
|
|
24766
|
-
* `srcStride` (`frame.data` / `frame.linesize`).
|
|
24767
|
-
*
|
|
24768
|
-
* Used only for raw output formats (rgb/bgr/gray). The jpeg path keeps the
|
|
24769
|
-
* `dstFrame` + sharp encode route because sharp must consume an RGB buffer
|
|
24770
|
-
* before the final (variable-length) jpeg bytes exist.
|
|
24771
|
-
*/
|
|
24772
|
-
scaleIntoRingSlot(frame, decodeStart) {
|
|
24773
|
-
if (!this.scaler) return;
|
|
24774
|
-
const format = this.outputMode === "gray" ? "gray" : "rgb";
|
|
24775
|
-
const channels = this.outputMode === "gray" ? 1 : 3;
|
|
24776
|
-
const sink = this.ensureFrameRingSink();
|
|
24777
|
-
const reserved = sink.beginFrame(this.outWidth, this.outHeight, format);
|
|
24778
|
-
if (reserved === null) {
|
|
24779
|
-
this.droppedFrames++;
|
|
24780
|
-
return;
|
|
24781
|
-
}
|
|
24782
|
-
const srcPlanes = frame.data;
|
|
24783
|
-
if (!srcPlanes || srcPlanes.length === 0) {
|
|
24784
|
-
sink.abortFrame(reserved.slot);
|
|
24785
|
-
this.droppedFrames++;
|
|
24786
|
-
return;
|
|
24787
|
-
}
|
|
24788
|
-
const dstStride = this.outWidth * channels;
|
|
24789
|
-
const needed = dstStride * this.outHeight;
|
|
24790
|
-
if (reserved.buffer.length < needed) {
|
|
24791
|
-
this.logger.warn("node-av: ring slot too small for scaled frame — dropping", { meta: {
|
|
24792
|
-
needed,
|
|
24793
|
-
slotBytes: reserved.buffer.length,
|
|
24794
|
-
width: this.outWidth,
|
|
24795
|
-
height: this.outHeight
|
|
24796
|
-
} });
|
|
24797
|
-
sink.abortFrame(reserved.slot);
|
|
24798
|
-
this.droppedFrames++;
|
|
24799
|
-
return;
|
|
24800
|
-
}
|
|
24801
|
-
try {
|
|
24802
|
-
const srcStrides = Array.from(frame.linesize).slice(0, srcPlanes.length);
|
|
24803
|
-
this.scaler.scaleSync(Array.from(srcPlanes), srcStrides, 0, frame.height, [reserved.buffer], [dstStride]);
|
|
24804
|
-
} catch (err) {
|
|
24805
|
-
this.logger.warn("node-av scale-into-slot error", { meta: { error: errMsg(err) } });
|
|
24806
|
-
sink.abortFrame(reserved.slot);
|
|
24807
|
-
this.droppedFrames++;
|
|
24808
|
-
return;
|
|
24809
|
-
}
|
|
24810
|
-
const decodeMs = performance.now() - decodeStart;
|
|
24811
|
-
this.totalDecodeTimeMs += decodeMs;
|
|
24812
|
-
this.outputFrames++;
|
|
24813
|
-
this.lastEmitTime = performance.now();
|
|
24814
|
-
if (!this.firstFrameLogged) {
|
|
24815
|
-
this.firstFrameLogged = true;
|
|
24816
|
-
this.logger.info("node-av: scaled directly into shm ring slot", { meta: {
|
|
24817
|
-
phase: "frame-debug",
|
|
24818
|
-
width: this.outWidth,
|
|
24819
|
-
height: this.outHeight,
|
|
24820
|
-
dstStride,
|
|
24821
|
-
format
|
|
24822
|
-
} });
|
|
24823
|
-
}
|
|
24824
|
-
if (this.outputFrames === 1 || this.outputFrames % 500 === 0) this.logger.info("node-av frame emitted", { meta: {
|
|
24825
|
-
frameNumber: this.outputFrames,
|
|
24826
|
-
width: this.outWidth,
|
|
24827
|
-
height: this.outHeight,
|
|
24828
|
-
format,
|
|
24829
|
-
decodeMs,
|
|
24830
|
-
sink: "shm",
|
|
24831
|
-
subs: this.handleCallbacks.size
|
|
24832
|
-
} });
|
|
24833
|
-
const handle = sink.commitFrame(reserved.slot, {
|
|
24834
|
-
width: this.outWidth,
|
|
24835
|
-
height: this.outHeight,
|
|
24836
|
-
format,
|
|
24837
|
-
pts: performance.now(),
|
|
24838
|
-
byteLength: dstStride * this.outHeight
|
|
24839
|
-
});
|
|
24840
|
-
if (handle === null) {
|
|
24841
|
-
this.droppedFrames++;
|
|
24842
|
-
return;
|
|
24843
|
-
}
|
|
24844
|
-
const delivered = {
|
|
24845
|
-
handle,
|
|
24846
|
-
timestamp: Date.now()
|
|
24847
|
-
};
|
|
24848
|
-
for (const cb of this.handleCallbacks) cb(delivered);
|
|
24849
|
-
}
|
|
24850
|
-
/**
|
|
24851
25677
|
* Extract packed pixel buffer from a decoded frame.
|
|
24852
25678
|
* FFmpeg's av_frame_get_buffer() may pad each row to alignment (32/64 bytes).
|
|
24853
25679
|
* Sharp and WASM consumers expect tightly-packed rows (stride = width * channels).
|
|
@@ -24906,13 +25732,8 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24906
25732
|
height: this.outHeight,
|
|
24907
25733
|
format,
|
|
24908
25734
|
decodeMs,
|
|
24909
|
-
|
|
24910
|
-
subs: this.frameSink === "shm" ? this.handleCallbacks.size : this.frameCallbacks.size
|
|
25735
|
+
subs: this.frameCallbacks.size
|
|
24911
25736
|
} });
|
|
24912
|
-
if (this.frameSink === "shm") {
|
|
24913
|
-
this.emitShmFrame(data, format);
|
|
24914
|
-
return;
|
|
24915
|
-
}
|
|
24916
25737
|
const decodedFrame = {
|
|
24917
25738
|
data,
|
|
24918
25739
|
width: this.outWidth,
|
|
@@ -24922,47 +25743,12 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24922
25743
|
};
|
|
24923
25744
|
for (const cb of this.frameCallbacks) cb(decodedFrame);
|
|
24924
25745
|
}
|
|
24925
|
-
/**
|
|
24926
|
-
* Write a decoded frame into this stream's shared-memory ring and deliver
|
|
24927
|
-
* the resulting `FrameHandle` to `onFrameHandle` subscribers. No pixel
|
|
24928
|
-
* `Buffer` is handed to callbacks — consumers open the segment and read the
|
|
24929
|
-
* pixels zero-copy via a `FrameRingReader`.
|
|
24930
|
-
*/
|
|
24931
|
-
emitShmFrame(data, format) {
|
|
24932
|
-
const handle = this.ensureFrameRingSink().writeFrame(data, {
|
|
24933
|
-
width: this.outWidth,
|
|
24934
|
-
height: this.outHeight,
|
|
24935
|
-
format,
|
|
24936
|
-
pts: performance.now(),
|
|
24937
|
-
byteLength: data.byteLength
|
|
24938
|
-
});
|
|
24939
|
-
if (handle === null) {
|
|
24940
|
-
this.droppedFrames++;
|
|
24941
|
-
return;
|
|
24942
|
-
}
|
|
24943
|
-
const frame = {
|
|
24944
|
-
handle,
|
|
24945
|
-
timestamp: Date.now()
|
|
24946
|
-
};
|
|
24947
|
-
for (const cb of this.handleCallbacks) cb(frame);
|
|
24948
|
-
}
|
|
24949
25746
|
onFrame(callback) {
|
|
24950
25747
|
this.frameCallbacks.add(callback);
|
|
24951
25748
|
return () => {
|
|
24952
25749
|
this.frameCallbacks.delete(callback);
|
|
24953
25750
|
};
|
|
24954
25751
|
}
|
|
24955
|
-
/**
|
|
24956
|
-
* Subscribe to shared-memory frame handles. Fires only when the session was
|
|
24957
|
-
* created with `frameSink: 'shm'`; in the legacy `'callback'` mode no
|
|
24958
|
-
* handles are produced and this subscription never fires.
|
|
24959
|
-
*/
|
|
24960
|
-
onFrameHandle(callback) {
|
|
24961
|
-
this.handleCallbacks.add(callback);
|
|
24962
|
-
return () => {
|
|
24963
|
-
this.handleCallbacks.delete(callback);
|
|
24964
|
-
};
|
|
24965
|
-
}
|
|
24966
25752
|
updateConfig(update) {
|
|
24967
25753
|
const prevFormat = this.config.outputFormat;
|
|
24968
25754
|
this.config = {
|
|
@@ -25000,7 +25786,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
25000
25786
|
if (this.destroyed) return;
|
|
25001
25787
|
this.destroyed = true;
|
|
25002
25788
|
this.frameCallbacks.clear();
|
|
25003
|
-
this.handleCallbacks.clear();
|
|
25004
25789
|
this.pullActive = false;
|
|
25005
25790
|
if (this.pullRedialTimer) {
|
|
25006
25791
|
clearTimeout(this.pullRedialTimer);
|
|
@@ -25016,8 +25801,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
25016
25801
|
if (!this.pullHwContext.isDisposed) this.pullHwContext.dispose();
|
|
25017
25802
|
this.pullHwContext = null;
|
|
25018
25803
|
}
|
|
25019
|
-
this.frameRingSink?.destroy();
|
|
25020
|
-
this.frameRingSink = null;
|
|
25021
25804
|
this.dstFrame?.[Symbol.dispose]?.();
|
|
25022
25805
|
this.avFrame?.[Symbol.dispose]?.();
|
|
25023
25806
|
this.avPacket?.[Symbol.dispose]?.();
|
|
@@ -25845,27 +26628,14 @@ var FRAME_BUFFER_CAPACITY = 32;
|
|
|
25845
26628
|
var DecoderNodeAvAddon = class extends BaseAddon {
|
|
25846
26629
|
/**
|
|
25847
26630
|
* Sessions are stored as the concrete `NodeAvDecoderSession` (the only type
|
|
25848
|
-
* this addon ever creates)
|
|
25849
|
-
* ring sink — `IDecoderSession` does not expose it.
|
|
26631
|
+
* this addon ever creates).
|
|
25850
26632
|
*/
|
|
25851
26633
|
sessions = /* @__PURE__ */ new Map();
|
|
25852
|
-
/** Pixel-frame buffers
|
|
26634
|
+
/** Pixel-frame buffers drained by `pullFrames`. */
|
|
25853
26635
|
frameBuffers = /* @__PURE__ */ new Map();
|
|
25854
|
-
/** `FrameHandle` buffers — populated only for `frameSink: 'shm'` sessions. */
|
|
25855
|
-
handleBuffers = /* @__PURE__ */ new Map();
|
|
25856
26636
|
unsubscribers = /* @__PURE__ */ new Map();
|
|
25857
26637
|
sessionMeta = /* @__PURE__ */ new Map();
|
|
25858
26638
|
/**
|
|
25859
|
-
* Per-shm-segment `FrameRingReader` cache backing `getFrame`. Opens each
|
|
25860
|
-
* named segment once and reuses the reader for every later handle on it;
|
|
25861
|
-
* built in `onInitialize` (it needs the scoped logger) and closed in
|
|
25862
|
-
* `onShutdown`.
|
|
25863
|
-
*/
|
|
25864
|
-
frameReaders = null;
|
|
25865
|
-
/** Running `getFrame` hit/miss counters surfaced via `getShmStats`. */
|
|
25866
|
-
getFrameHits = 0;
|
|
25867
|
-
getFrameMisses = 0;
|
|
25868
|
-
/**
|
|
25869
26639
|
* node-av audio-codec provider — registered independently of the video
|
|
25870
26640
|
* decoder backend gate whenever node-av loads on this node, so this single
|
|
25871
26641
|
* addon provides both the `decoder` and `audio-codec` caps.
|
|
@@ -25930,12 +26700,6 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
25930
26700
|
return registrations;
|
|
25931
26701
|
}
|
|
25932
26702
|
this.ctx.logger.info("node-av decoder addon initialized", { meta: { selectedBackend: backend } });
|
|
25933
|
-
const purged = purgeOrphanSegments(SEGMENT_NAME_PREFIX);
|
|
25934
|
-
if (purged.removed > 0) this.ctx.logger.warn("node-av decoder: reclaimed orphaned shm segments at startup", { meta: {
|
|
25935
|
-
removed: purged.removed,
|
|
25936
|
-
scanned: purged.scanned
|
|
25937
|
-
} });
|
|
25938
|
-
this.frameReaders = new FrameRingReaderCache(this.ctx.logger);
|
|
25939
26703
|
if (!this.config.probedBestHwaccel) this.reprobeHwaccel().catch((err) => {
|
|
25940
26704
|
this.ctx.logger.warn("nodeav: auto-reprobe hwaccel failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
25941
26705
|
});
|
|
@@ -26002,11 +26766,11 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26002
26766
|
}
|
|
26003
26767
|
/**
|
|
26004
26768
|
* The cluster node id of this decoder — the Moleculer nodeID (`hub`,
|
|
26005
|
-
* `dev-agent-0`, …), not the addon id.
|
|
26006
|
-
*
|
|
26007
|
-
*
|
|
26008
|
-
*
|
|
26009
|
-
*
|
|
26769
|
+
* `dev-agent-0`, …), not the addon id. Returned from `createSession` so the
|
|
26770
|
+
* caller can route session-scoped calls to the node that owns the session.
|
|
26771
|
+
* A hierarchical broker nodeID (`hub/...`) is collapsed to the
|
|
26772
|
+
* cluster-visible parent; in-process boot is left as-is. Mirrors the
|
|
26773
|
+
* resolution `PipelineRunnerAddon.onInitialize` performs.
|
|
26010
26774
|
*/
|
|
26011
26775
|
resolveLocalNodeId() {
|
|
26012
26776
|
const raw = this.ctx.kernel.localNodeId ?? this.ctx.id;
|
|
@@ -26015,15 +26779,12 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26015
26779
|
async createSession(config) {
|
|
26016
26780
|
const sessionId = randomUUID();
|
|
26017
26781
|
const hwaccel = await this.resolveHwAccelPref();
|
|
26018
|
-
const { frameSink } = config;
|
|
26019
26782
|
const nodeId = this.resolveLocalNodeId();
|
|
26020
26783
|
const session = new NodeAvDecoderSession(config, this.ctx.logger, {
|
|
26021
26784
|
hwaccel,
|
|
26022
|
-
hwaccelResolver: this.ctx.kernel.hwaccel
|
|
26023
|
-
frameSink,
|
|
26024
|
-
nodeId
|
|
26785
|
+
hwaccelResolver: this.ctx.kernel.hwaccel
|
|
26025
26786
|
});
|
|
26026
|
-
const unsub =
|
|
26787
|
+
const unsub = this.wireCallbackSink(sessionId, session);
|
|
26027
26788
|
this.sessions.set(sessionId, session);
|
|
26028
26789
|
this.unsubscribers.set(sessionId, unsub);
|
|
26029
26790
|
this.sessionMeta.set(sessionId, {
|
|
@@ -26038,7 +26799,6 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26038
26799
|
sessionId,
|
|
26039
26800
|
codec: config.codec,
|
|
26040
26801
|
hwaccelPref: hwaccel,
|
|
26041
|
-
frameSink,
|
|
26042
26802
|
nodeId
|
|
26043
26803
|
}
|
|
26044
26804
|
});
|
|
@@ -26069,18 +26829,6 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26069
26829
|
ringBuffer.push(capFrame);
|
|
26070
26830
|
});
|
|
26071
26831
|
}
|
|
26072
|
-
/**
|
|
26073
|
-
* Subscribe a `'shm'` session's `FrameHandle`s into a per-session ring
|
|
26074
|
-
* buffer drained by `pullHandles`. No pixel bytes cross the cap boundary
|
|
26075
|
-
* — the broker opens the named segment and reads the pixels zero-copy.
|
|
26076
|
-
*/
|
|
26077
|
-
wireShmSink(sessionId, session) {
|
|
26078
|
-
const handleRing = new NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
26079
|
-
this.handleBuffers.set(sessionId, handleRing);
|
|
26080
|
-
return session.onFrameHandle((frame) => {
|
|
26081
|
-
handleRing.push(frame.handle);
|
|
26082
|
-
});
|
|
26083
|
-
}
|
|
26084
26832
|
async destroySession(input) {
|
|
26085
26833
|
const { sessionId } = input;
|
|
26086
26834
|
const session = this.sessions.get(sessionId);
|
|
@@ -26088,12 +26836,10 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26088
26836
|
const unsub = this.unsubscribers.get(sessionId);
|
|
26089
26837
|
if (unsub) unsub();
|
|
26090
26838
|
this.frameBuffers.get(sessionId)?.cancel();
|
|
26091
|
-
this.handleBuffers.get(sessionId)?.cancel();
|
|
26092
26839
|
const deviceId = this.sessionMeta.get(sessionId)?.deviceId;
|
|
26093
26840
|
await session.destroy();
|
|
26094
26841
|
this.sessions.delete(sessionId);
|
|
26095
26842
|
this.frameBuffers.delete(sessionId);
|
|
26096
|
-
this.handleBuffers.delete(sessionId);
|
|
26097
26843
|
this.unsubscribers.delete(sessionId);
|
|
26098
26844
|
this.sessionMeta.delete(sessionId);
|
|
26099
26845
|
this.ctx.logger.info("node-av: destroyed session", {
|
|
@@ -26133,12 +26879,14 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26133
26879
|
if (ringBuffer.size === 0 && input.waitMs) await ringBuffer.waitForItem(input.waitMs);
|
|
26134
26880
|
return ringBuffer.drain(input.maxCount);
|
|
26135
26881
|
}
|
|
26136
|
-
|
|
26137
|
-
|
|
26138
|
-
|
|
26139
|
-
|
|
26140
|
-
|
|
26141
|
-
|
|
26882
|
+
/**
|
|
26883
|
+
* DORMANT — the shm `FrameHandle` frame plane is retired. Every session now
|
|
26884
|
+
* delivers pixels over the callback path drained by `pullFrames`, so no
|
|
26885
|
+
* handles are ever produced. Kept to satisfy the (still-defined) cap method;
|
|
26886
|
+
* always returns an empty list. Removal rides a later framework train.
|
|
26887
|
+
*/
|
|
26888
|
+
async pullHandles() {
|
|
26889
|
+
return [];
|
|
26142
26890
|
}
|
|
26143
26891
|
async updateConfig(input) {
|
|
26144
26892
|
const session = this.sessions.get(input.sessionId);
|
|
@@ -26151,50 +26899,22 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26151
26899
|
return session.getStats();
|
|
26152
26900
|
}
|
|
26153
26901
|
/**
|
|
26154
|
-
*
|
|
26155
|
-
*
|
|
26156
|
-
*
|
|
26157
|
-
*
|
|
26158
|
-
*
|
|
26159
|
-
* The reader hands back a `Buffer` view over its **reusable scratch
|
|
26160
|
-
* buffer**, only valid until the next `read` on the same reader; the
|
|
26161
|
-
* cap caller may keep the frame across reads, so the pixels are copied
|
|
26162
|
-
* into a fresh detached `ArrayBuffer` here. This matches the existing
|
|
26163
|
-
* callback-path copy in `wireCallbackSink`.
|
|
26902
|
+
* DORMANT — shm handle read-back is retired (the decoder no longer owns a
|
|
26903
|
+
* `/dev/shm` ring). Kept to satisfy the (still-defined) cap method; always
|
|
26904
|
+
* returns `null`. Full-frame retrieval by handle now flows through the
|
|
26905
|
+
* runner's RAM-backed `pipelineRunner.getNativeCrop`. Removal rides a later
|
|
26906
|
+
* framework train.
|
|
26164
26907
|
*/
|
|
26165
|
-
async getFrame(
|
|
26166
|
-
|
|
26167
|
-
if (!frame) {
|
|
26168
|
-
this.getFrameMisses += 1;
|
|
26169
|
-
return null;
|
|
26170
|
-
}
|
|
26171
|
-
this.getFrameHits += 1;
|
|
26172
|
-
const arrayBuf = new ArrayBuffer(frame.data.byteLength);
|
|
26173
|
-
new Uint8Array(arrayBuf).set(frame.data);
|
|
26174
|
-
return {
|
|
26175
|
-
data: new Uint8Array(arrayBuf),
|
|
26176
|
-
width: frame.width,
|
|
26177
|
-
height: frame.height,
|
|
26178
|
-
format: frame.format,
|
|
26179
|
-
timestamp: frame.timestamp
|
|
26180
|
-
};
|
|
26908
|
+
async getFrame() {
|
|
26909
|
+
return null;
|
|
26181
26910
|
}
|
|
26182
26911
|
/**
|
|
26183
|
-
* shm ring
|
|
26184
|
-
*
|
|
26185
|
-
*
|
|
26186
|
-
* shm ring has not yet been armed by a first decoded frame.
|
|
26912
|
+
* DORMANT — no shm ring exists to report stats for. Kept to satisfy the
|
|
26913
|
+
* (still-defined) cap method; always returns `null`. Removal rides a later
|
|
26914
|
+
* framework train.
|
|
26187
26915
|
*/
|
|
26188
|
-
async getShmStats(
|
|
26189
|
-
|
|
26190
|
-
if (!stats) return null;
|
|
26191
|
-
return {
|
|
26192
|
-
sessionId: input.sessionId,
|
|
26193
|
-
...stats,
|
|
26194
|
-
budgetMb: RING_BUDGET_MB,
|
|
26195
|
-
getFrameHits: this.getFrameHits,
|
|
26196
|
-
getFrameMisses: this.getFrameMisses
|
|
26197
|
-
};
|
|
26916
|
+
async getShmStats() {
|
|
26917
|
+
return null;
|
|
26198
26918
|
}
|
|
26199
26919
|
async onShutdown() {
|
|
26200
26920
|
this.audioProvider?.stop();
|
|
@@ -26209,12 +26929,9 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26209
26929
|
await Promise.all(destroyPromises);
|
|
26210
26930
|
this.sessions.clear();
|
|
26211
26931
|
this.frameBuffers.clear();
|
|
26212
|
-
this.handleBuffers.clear();
|
|
26213
26932
|
this.unsubscribers.clear();
|
|
26214
26933
|
this.sessionMeta.clear();
|
|
26215
|
-
this.frameReaders?.close();
|
|
26216
|
-
this.frameReaders = null;
|
|
26217
26934
|
}
|
|
26218
26935
|
};
|
|
26219
26936
|
//#endregion
|
|
26220
|
-
export {
|
|
26937
|
+
export { DecoderNodeAvAddon, DecoderNodeAvAddon as default, NodeAvAudioCodecProvider, NodeAvAudioDecodeSession, NodeAvAudioEncodeSession, NodeAvDecoderSession, loadNodeAvRuntime, peekNodeAvRuntime };
|