@camstack/addon-decoder-ffmpeg 1.1.11 → 1.1.13
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 -265
- package/dist/index.mjs +1118 -265
- package/package.json +1 -2
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { DecoderFrameRingSink, FrameRingReaderCache, RING_BUDGET_MB, SEGMENT_NAME_PREFIX, unlinkSegment } from "@camstack/shm-ring";
|
|
3
2
|
import { spawn } from "node:child_process";
|
|
4
|
-
import { readdirSync } from "node:fs";
|
|
5
3
|
//#region ../../node_modules/zod/v4/core/core.js
|
|
6
4
|
var _a$1;
|
|
7
5
|
function $constructor(name, initializer, params) {
|
|
@@ -4290,6 +4288,14 @@ function object(shape, params) {
|
|
|
4290
4288
|
...normalizeParams(params)
|
|
4291
4289
|
});
|
|
4292
4290
|
}
|
|
4291
|
+
function looseObject(shape, params) {
|
|
4292
|
+
return new ZodObject({
|
|
4293
|
+
type: "object",
|
|
4294
|
+
shape,
|
|
4295
|
+
catchall: unknown(),
|
|
4296
|
+
...normalizeParams(params)
|
|
4297
|
+
});
|
|
4298
|
+
}
|
|
4293
4299
|
var ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
|
|
4294
4300
|
$ZodUnion.init(inst, def);
|
|
4295
4301
|
ZodType.init(inst, def);
|
|
@@ -4631,7 +4637,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4631
4637
|
return inst;
|
|
4632
4638
|
}
|
|
4633
4639
|
//#endregion
|
|
4634
|
-
//#region ../types/dist/sleep-
|
|
4640
|
+
//#region ../types/dist/sleep-_sv7WKkq.mjs
|
|
4635
4641
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4636
4642
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4637
4643
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5041,10 +5047,51 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
5041
5047
|
EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
|
|
5042
5048
|
EventCategory["EnrichmentSceneStateChanged"] = "enrichment.scene.state-changed";
|
|
5043
5049
|
EventCategory["EnrichmentActivitySummary"] = "enrichment.activity.summary";
|
|
5050
|
+
/**
|
|
5051
|
+
* Unified track-lifecycle event: ONE category carrying `phase:
|
|
5052
|
+
* 'start' | 'update' | 'end'` with a rich, typed
|
|
5053
|
+
* `PipelineAnalyticsTrackLifecyclePayload`. Supersedes the thin
|
|
5054
|
+
* `PipelineAnalyticsTrackStarted` / `PipelineAnalyticsTrackEnded`
|
|
5055
|
+
* pair — a consumer subscribes once and branches on `phase`.
|
|
5056
|
+
*/
|
|
5057
|
+
EventCategory["PipelineAnalyticsTrackLifecycle"] = "pipeline-analytics.track-lifecycle";
|
|
5058
|
+
/**
|
|
5059
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5060
|
+
* (`phase:'start'`). Kept as a soft alias — no known subscribers.
|
|
5061
|
+
*/
|
|
5044
5062
|
EventCategory["PipelineAnalyticsTrackStarted"] = "pipeline-analytics.track-started";
|
|
5063
|
+
/**
|
|
5064
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5065
|
+
* (`phase:'end'`). Kept as a soft alias — no known subscribers.
|
|
5066
|
+
*/
|
|
5045
5067
|
EventCategory["PipelineAnalyticsTrackEnded"] = "pipeline-analytics.track-ended";
|
|
5046
5068
|
EventCategory["PipelineAnalyticsDetectionEvent"] = "pipeline-analytics.detection-event";
|
|
5047
5069
|
EventCategory["PipelineAnalyticsFrameTracked"] = "pipeline-analytics.frame-tracked";
|
|
5070
|
+
/**
|
|
5071
|
+
* Fired by `addon-post-analysis` when a parked (stationary) object appears
|
|
5072
|
+
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
5073
|
+
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
5074
|
+
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
5075
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
5076
|
+
*/
|
|
5077
|
+
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
5078
|
+
/**
|
|
5079
|
+
* Fired by `addon-post-analysis` whenever a gallery face row changes:
|
|
5080
|
+
* `kind:'buffered'` a new detected face was persisted, `'assigned'` /
|
|
5081
|
+
* `'unassigned'` its identity link changed, `'deleted'` the row was
|
|
5082
|
+
* removed. Telemetry semantics (D8): a lost event only delays a refresh,
|
|
5083
|
+
* never a correctness issue. Payload `PipelineAnalyticsFaceGalleryChangedPayload`.
|
|
5084
|
+
*/
|
|
5085
|
+
EventCategory["PipelineAnalyticsFaceGalleryChanged"] = "pipeline-analytics.face-gallery-changed";
|
|
5086
|
+
/**
|
|
5087
|
+
* Fired by `addon-post-analysis` whenever a gallery plate row changes:
|
|
5088
|
+
* `kind:'buffered'` a new read was persisted (`PlateRecognizer.onTrackEnd`),
|
|
5089
|
+
* `'assigned'` / `'unassigned'` its vehicle link changed
|
|
5090
|
+
* (`PlateGalleryProvider`), `'deleted'` the row was removed. Telemetry
|
|
5091
|
+
* semantics (D8): a lost event only delays a refresh, never a correctness
|
|
5092
|
+
* issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
|
|
5093
|
+
*/
|
|
5094
|
+
EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
|
|
5048
5095
|
EventCategory["FrigateLiveEvent"] = "frigate.live-event";
|
|
5049
5096
|
EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
|
|
5050
5097
|
/**
|
|
@@ -6885,7 +6932,8 @@ var MODEL_FORMATS = [
|
|
|
6885
6932
|
"coreml",
|
|
6886
6933
|
"openvino",
|
|
6887
6934
|
"tflite",
|
|
6888
|
-
"pt"
|
|
6935
|
+
"pt",
|
|
6936
|
+
"gguf"
|
|
6889
6937
|
];
|
|
6890
6938
|
/**
|
|
6891
6939
|
* Multi-file format payload.
|
|
@@ -6929,7 +6977,8 @@ var ModelFormatsSchema = object({
|
|
|
6929
6977
|
coreml: ModelFormatEntrySchema.optional(),
|
|
6930
6978
|
openvino: ModelFormatEntrySchema.optional(),
|
|
6931
6979
|
tflite: ModelFormatEntrySchema.optional(),
|
|
6932
|
-
pt: ModelFormatEntrySchema.optional()
|
|
6980
|
+
pt: ModelFormatEntrySchema.optional(),
|
|
6981
|
+
gguf: ModelFormatEntrySchema.optional()
|
|
6933
6982
|
});
|
|
6934
6983
|
/**
|
|
6935
6984
|
* Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
|
|
@@ -6970,6 +7019,15 @@ var ModelCatalogEntrySchema = object({
|
|
|
6970
7019
|
width: number(),
|
|
6971
7020
|
height: number()
|
|
6972
7021
|
}),
|
|
7022
|
+
/**
|
|
7023
|
+
* Channel count of the model input tensor. Omit ⇒ 3 (RGB), the default for
|
|
7024
|
+
* every detector / classifier / embedder. Set to 1 for a grayscale CTC text
|
|
7025
|
+
* recognizer (EasyOCR VGG plate-OCR: input `[N,1,H,W]`) so the preprocess
|
|
7026
|
+
* feeds a single-channel, EasyOCR-normalized tensor instead of the default
|
|
7027
|
+
* 3-channel RGB one. Threaded through `PoolModelConfig.inputChannels` to the
|
|
7028
|
+
* Python inference pool.
|
|
7029
|
+
*/
|
|
7030
|
+
inputChannels: number().int().positive().optional(),
|
|
6973
7031
|
labels: array(LabelDefinitionSchema).readonly(),
|
|
6974
7032
|
inputLayout: _enum(["nchw", "nhwc"]).optional(),
|
|
6975
7033
|
inputNormalization: _enum([
|
|
@@ -6979,6 +7037,16 @@ var ModelCatalogEntrySchema = object({
|
|
|
6979
7037
|
]).optional(),
|
|
6980
7038
|
preprocessMode: _enum(["letterbox", "resize"]).optional(),
|
|
6981
7039
|
/**
|
|
7040
|
+
* Per-MODEL postprocessor override. Absent ⇒ the step's own
|
|
7041
|
+
* `StepDefinition.postprocessor` applies (the normal case — every model in a
|
|
7042
|
+
* step shares its decode). Set it when a step hosts models with DIFFERENT raw
|
|
7043
|
+
* output layouts under one slot: e.g. object-detection is `'yolo'` by default,
|
|
7044
|
+
* but a Coral SSD MobileNet build emits the `TFLite_Detection_PostProcess`
|
|
7045
|
+
* 4-tensor layout and needs `'ssd'`. Threaded into `PoolModelConfig.postprocessor`
|
|
7046
|
+
* by the engine factory (`modelEntry.postprocessor ?? def.postprocessor`).
|
|
7047
|
+
*/
|
|
7048
|
+
postprocessor: custom().optional(),
|
|
7049
|
+
/**
|
|
6982
7050
|
* When true, the executor produces a landmark-aligned crop (similarity warp
|
|
6983
7051
|
* onto the canonical template) before this step runs, instead of a plain
|
|
6984
7052
|
* axis-aligned bbox crop. Required for face-recognition embedders (ArcFace):
|
|
@@ -10424,7 +10492,8 @@ var ModelFormatSchema$1 = _enum([
|
|
|
10424
10492
|
"coreml",
|
|
10425
10493
|
"openvino",
|
|
10426
10494
|
"tflite",
|
|
10427
|
-
"pt"
|
|
10495
|
+
"pt",
|
|
10496
|
+
"gguf"
|
|
10428
10497
|
]);
|
|
10429
10498
|
var PipelineSlotSchema = _enum([
|
|
10430
10499
|
"detector",
|
|
@@ -10518,7 +10587,8 @@ var EngineProvisioningSchema = object({
|
|
|
10518
10587
|
runtimeId: _enum([
|
|
10519
10588
|
"onnx",
|
|
10520
10589
|
"openvino",
|
|
10521
|
-
"coreml"
|
|
10590
|
+
"coreml",
|
|
10591
|
+
"edgetpu"
|
|
10522
10592
|
]).nullable(),
|
|
10523
10593
|
device: string().nullable(),
|
|
10524
10594
|
state: _enum([
|
|
@@ -10679,7 +10749,15 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
10679
10749
|
image: _instanceof(Uint8Array).optional(),
|
|
10680
10750
|
referenceImage: string().optional(),
|
|
10681
10751
|
deviceId: number().optional(),
|
|
10682
|
-
sessionId: string().optional()
|
|
10752
|
+
sessionId: string().optional(),
|
|
10753
|
+
/**
|
|
10754
|
+
* Execution plane. 'full' (default) runs the whole tree — benchmark,
|
|
10755
|
+
* reference-image, and detail-subtree calls. 'frame' is the live
|
|
10756
|
+
* per-frame dispatch: ONLY root-plane steps run; crop children
|
|
10757
|
+
* (inputClasses ≠ null) are skipped and served per-track via
|
|
10758
|
+
* pipelineRunner.runDetailSubtree (two-plane design).
|
|
10759
|
+
*/
|
|
10760
|
+
plane: _enum(["full", "frame"]).optional()
|
|
10683
10761
|
}), PipelineRunResultBridge, { kind: "mutation" }), method(object({
|
|
10684
10762
|
engine: PipelineEngineChoiceSchema.optional(),
|
|
10685
10763
|
steps: array(PipelineStepInputSchema).min(1),
|
|
@@ -10822,6 +10900,28 @@ var NativeCropResultSchema = object({
|
|
|
10822
10900
|
width: number().int().positive(),
|
|
10823
10901
|
height: number().int().positive()
|
|
10824
10902
|
});
|
|
10903
|
+
/** Parent detection context passed to `runDetailSubtree` — the crop's
|
|
10904
|
+
* originating detection, in FRAME-space coordinates. Reuses
|
|
10905
|
+
* `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
|
|
10906
|
+
* the coordinates are frame-space rather than getNativeCrop's
|
|
10907
|
+
* normalized [0,1] convention). */
|
|
10908
|
+
var DetailParentSchema = object({
|
|
10909
|
+
bbox: NativeCropBboxSchema,
|
|
10910
|
+
className: string()
|
|
10911
|
+
});
|
|
10912
|
+
/** One child-step result from `runDetailSubtree` — an embedding, label,
|
|
10913
|
+
* or refined detection produced by running the crop-subtree on a
|
|
10914
|
+
* single tracked detection. */
|
|
10915
|
+
var DetailResultSchema = object({
|
|
10916
|
+
stepId: string(),
|
|
10917
|
+
className: string(),
|
|
10918
|
+
score: number(),
|
|
10919
|
+
/** FRAME-space bbox (already mapped back from crop space). */
|
|
10920
|
+
bbox: NativeCropBboxSchema.optional(),
|
|
10921
|
+
embedding: string().optional(),
|
|
10922
|
+
label: string().optional(),
|
|
10923
|
+
alignedCropJpeg: string().optional()
|
|
10924
|
+
});
|
|
10825
10925
|
/**
|
|
10826
10926
|
* Per-camera tunable ranges + defaults. Single source of truth used
|
|
10827
10927
|
* by both the Zod data schema (validation + default fallback) and
|
|
@@ -11082,7 +11182,13 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
|
|
|
11082
11182
|
handle: FrameHandleSchema,
|
|
11083
11183
|
bbox: NativeCropBboxSchema,
|
|
11084
11184
|
maxWidth: number().int().positive().optional()
|
|
11085
|
-
}), NativeCropResultSchema.nullable())
|
|
11185
|
+
}), NativeCropResultSchema.nullable()), method(object({
|
|
11186
|
+
deviceId: number(),
|
|
11187
|
+
frameHandle: FrameHandleSchema.optional(),
|
|
11188
|
+
cropJpeg: string().optional(),
|
|
11189
|
+
parent: DetailParentSchema,
|
|
11190
|
+
steps: array(string()).optional()
|
|
11191
|
+
}), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" });
|
|
11086
11192
|
object({
|
|
11087
11193
|
detected: boolean(),
|
|
11088
11194
|
/** Ms epoch of the last detected-true observation. Null if never detected. */
|
|
@@ -11694,6 +11800,140 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
|
|
|
11694
11800
|
deviceId: number(),
|
|
11695
11801
|
status: PtzAutotrackStatusSchema
|
|
11696
11802
|
});
|
|
11803
|
+
/**
|
|
11804
|
+
* scene-monitor — device-scoped reference-region state cap. An operator marks
|
|
11805
|
+
* a rect ROI on a camera frame and names one or more states; the engine
|
|
11806
|
+
* (pipeline-analytics, designated post-processing node) reports which state is
|
|
11807
|
+
* active on an ongoing basis. Two operator-selectable check modes share every-
|
|
11808
|
+
* thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
|
|
11809
|
+
* vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
|
|
11810
|
+
*
|
|
11811
|
+
* D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) — the framework
|
|
11812
|
+
* derives the device-detail contribution; the provider carries NO hand-written
|
|
11813
|
+
* settings-contribution methods. `status.kind:'push'` — the engine pushes on
|
|
11814
|
+
* every hysteresis flip / availability change; consumers never poll.
|
|
11815
|
+
*/
|
|
11816
|
+
/** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
|
|
11817
|
+
* be added without a wire break (matching falls back to any-condition refs). */
|
|
11818
|
+
var SceneConditionSchema = string();
|
|
11819
|
+
/** One captured reference — condition-tagged, model-version-gated. `embedding`
|
|
11820
|
+
* is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
|
|
11821
|
+
var SceneReferenceSchema = object({
|
|
11822
|
+
embedding: array(number()),
|
|
11823
|
+
modelId: string(),
|
|
11824
|
+
condition: SceneConditionSchema,
|
|
11825
|
+
capturedAt: number(),
|
|
11826
|
+
thumbnailMediaId: string().optional()
|
|
11827
|
+
});
|
|
11828
|
+
var SceneMonitorStateSchema = object({
|
|
11829
|
+
id: string(),
|
|
11830
|
+
label: string(),
|
|
11831
|
+
references: array(SceneReferenceSchema),
|
|
11832
|
+
textPrompts: array(string()).optional(),
|
|
11833
|
+
referenceCount: number(),
|
|
11834
|
+
currentlyMatched: boolean()
|
|
11835
|
+
});
|
|
11836
|
+
/** Per-mode comparator params in a discriminated union so an `llm` scene never
|
|
11837
|
+
* carries similarity knobs and vice-versa. */
|
|
11838
|
+
var SceneCheckSchema = discriminatedUnion("mode", [object({
|
|
11839
|
+
mode: literal("similarity"),
|
|
11840
|
+
threshold: number().min(0).max(1),
|
|
11841
|
+
hysteresisCount: number().int().positive()
|
|
11842
|
+
}), object({
|
|
11843
|
+
mode: literal("llm"),
|
|
11844
|
+
prompt: string(),
|
|
11845
|
+
profileId: string().optional(),
|
|
11846
|
+
hysteresisCount: number().int().positive()
|
|
11847
|
+
})]);
|
|
11848
|
+
var SceneMonitorSchema = object({
|
|
11849
|
+
id: string(),
|
|
11850
|
+
label: string(),
|
|
11851
|
+
roi: MaskRectShapeSchema,
|
|
11852
|
+
enabled: boolean(),
|
|
11853
|
+
triggerMode: _enum([
|
|
11854
|
+
"periodic",
|
|
11855
|
+
"on-motion",
|
|
11856
|
+
"both"
|
|
11857
|
+
]),
|
|
11858
|
+
checkIntervalSec: number().optional(),
|
|
11859
|
+
check: SceneCheckSchema,
|
|
11860
|
+
states: array(SceneMonitorStateSchema),
|
|
11861
|
+
currentStateId: string().nullable(),
|
|
11862
|
+
lastCheckedAt: number().nullable(),
|
|
11863
|
+
lastConfidence: number().nullable(),
|
|
11864
|
+
currentCondition: SceneConditionSchema.nullable(),
|
|
11865
|
+
availability: _enum(["ok", "unavailable"]),
|
|
11866
|
+
unavailableReason: string().nullable()
|
|
11867
|
+
});
|
|
11868
|
+
var SceneMonitorStatusSchema = object({
|
|
11869
|
+
monitors: array(SceneMonitorSchema),
|
|
11870
|
+
lastFetchedAt: number()
|
|
11871
|
+
});
|
|
11872
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
|
|
11873
|
+
deviceId: number(),
|
|
11874
|
+
label: string(),
|
|
11875
|
+
roi: MaskRectShapeSchema,
|
|
11876
|
+
check: SceneCheckSchema,
|
|
11877
|
+
triggerMode: _enum([
|
|
11878
|
+
"periodic",
|
|
11879
|
+
"on-motion",
|
|
11880
|
+
"both"
|
|
11881
|
+
]).optional(),
|
|
11882
|
+
checkIntervalSec: number().optional()
|
|
11883
|
+
}), SceneMonitorSchema, {
|
|
11884
|
+
kind: "mutation",
|
|
11885
|
+
auth: "admin"
|
|
11886
|
+
}), method(object({
|
|
11887
|
+
deviceId: number(),
|
|
11888
|
+
monitorId: string(),
|
|
11889
|
+
patch: object({
|
|
11890
|
+
label: string().optional(),
|
|
11891
|
+
roi: MaskRectShapeSchema.optional(),
|
|
11892
|
+
enabled: boolean().optional(),
|
|
11893
|
+
triggerMode: _enum([
|
|
11894
|
+
"periodic",
|
|
11895
|
+
"on-motion",
|
|
11896
|
+
"both"
|
|
11897
|
+
]).optional(),
|
|
11898
|
+
checkIntervalSec: number().optional(),
|
|
11899
|
+
check: SceneCheckSchema.optional()
|
|
11900
|
+
})
|
|
11901
|
+
}), _void(), {
|
|
11902
|
+
kind: "mutation",
|
|
11903
|
+
auth: "admin"
|
|
11904
|
+
}), method(object({
|
|
11905
|
+
deviceId: number(),
|
|
11906
|
+
monitorId: string()
|
|
11907
|
+
}), _void(), {
|
|
11908
|
+
kind: "mutation",
|
|
11909
|
+
auth: "admin"
|
|
11910
|
+
}), method(object({
|
|
11911
|
+
deviceId: number(),
|
|
11912
|
+
monitorId: string(),
|
|
11913
|
+
stateId: string().optional(),
|
|
11914
|
+
label: string().optional(),
|
|
11915
|
+
condition: SceneConditionSchema.optional()
|
|
11916
|
+
}), object({
|
|
11917
|
+
stateId: string(),
|
|
11918
|
+
referenceCount: number()
|
|
11919
|
+
}), {
|
|
11920
|
+
kind: "mutation",
|
|
11921
|
+
auth: "admin"
|
|
11922
|
+
}), method(object({
|
|
11923
|
+
deviceId: number(),
|
|
11924
|
+
monitorId: string(),
|
|
11925
|
+
stateId: string(),
|
|
11926
|
+
index: number()
|
|
11927
|
+
}), _void(), {
|
|
11928
|
+
kind: "mutation",
|
|
11929
|
+
auth: "admin"
|
|
11930
|
+
}), method(object({
|
|
11931
|
+
deviceId: number(),
|
|
11932
|
+
monitorId: string()
|
|
11933
|
+
}), _void(), {
|
|
11934
|
+
kind: "mutation",
|
|
11935
|
+
auth: "admin"
|
|
11936
|
+
});
|
|
11697
11937
|
object({
|
|
11698
11938
|
/** Whether the script is currently executing. */
|
|
11699
11939
|
isRunning: boolean(),
|
|
@@ -12101,6 +12341,36 @@ var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
|
|
|
12101
12341
|
* with the per-track detail panel and live overlay. */
|
|
12102
12342
|
trackIds: array(string()).readonly()
|
|
12103
12343
|
});
|
|
12344
|
+
/**
|
|
12345
|
+
* A parked ("stationary") object surfaced alongside occupancy — an object that
|
|
12346
|
+
* settled and stopped moving. It is NO LONGER a tracked object (the tracker was
|
|
12347
|
+
* told to forget it so it stops re-spawning tracks/events), but it IS still
|
|
12348
|
+
* physically present, so it keeps counting toward `frame` occupancy and is
|
|
12349
|
+
* listed here so the UI can show it in a dedicated "Stationary" section instead
|
|
12350
|
+
* of flooding the live event feed.
|
|
12351
|
+
*/
|
|
12352
|
+
var StationaryObjectSchema = object({
|
|
12353
|
+
id: string(),
|
|
12354
|
+
className: string(),
|
|
12355
|
+
bbox: object({
|
|
12356
|
+
x: number(),
|
|
12357
|
+
y: number(),
|
|
12358
|
+
w: number(),
|
|
12359
|
+
h: number()
|
|
12360
|
+
}),
|
|
12361
|
+
frameWidth: number().int().nonnegative(),
|
|
12362
|
+
frameHeight: number().int().nonnegative(),
|
|
12363
|
+
/** When the source track was first seen. */
|
|
12364
|
+
firstSeenAt: number().int(),
|
|
12365
|
+
/** When the object was recognised as parked (promotion time). */
|
|
12366
|
+
becameStationaryAt: number().int(),
|
|
12367
|
+
/** Last frame a detection confirmed the object is still there. */
|
|
12368
|
+
lastConfirmedAt: number().int(),
|
|
12369
|
+
/** Enrichment label carried from the source track (identity / plate). */
|
|
12370
|
+
label: string().optional(),
|
|
12371
|
+
/** Native-resolution key-frame media key for the parked object's best image. */
|
|
12372
|
+
keyFrameMediaKey: string().optional()
|
|
12373
|
+
});
|
|
12104
12374
|
var CameraOccupancySnapshotSchema = object({
|
|
12105
12375
|
/** Frame timestamp of the inference result that produced this snapshot. */
|
|
12106
12376
|
ts: number().int(),
|
|
@@ -12109,10 +12379,15 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
12109
12379
|
frameHeight: number().int().nonnegative(),
|
|
12110
12380
|
/** Per-zone breakdown — one entry per defined zone (user + onboard). */
|
|
12111
12381
|
zones: array(ZoneScopeBreakdownSchema).readonly(),
|
|
12112
|
-
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12382
|
+
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12383
|
+
* INCLUDES currently-confirmed stationary objects (they are still present). */
|
|
12113
12384
|
frame: PerScopeBreakdownSchema,
|
|
12114
12385
|
/** Detections that landed outside every zone. Empty when no zones defined. */
|
|
12115
|
-
unzoned: PerScopeBreakdownSchema
|
|
12386
|
+
unzoned: PerScopeBreakdownSchema,
|
|
12387
|
+
/** Parked objects on this camera (additive — absent on legacy snapshots).
|
|
12388
|
+
* Surfaced separately so the UI shows them in a dedicated section rather
|
|
12389
|
+
* than as repeated tracks/events. */
|
|
12390
|
+
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
12116
12391
|
});
|
|
12117
12392
|
/**
|
|
12118
12393
|
* Time-series resolution. The history methods return one bucket per
|
|
@@ -12715,7 +12990,12 @@ var NotificationRuleConditionsSchema = object({
|
|
|
12715
12990
|
clipDescription: object({
|
|
12716
12991
|
text: string().min(1),
|
|
12717
12992
|
minSimilarity: number().min(0).max(1)
|
|
12718
|
-
}).optional()
|
|
12993
|
+
}).optional(),
|
|
12994
|
+
/** Match events whose recognized-entity label (face identity name or plate
|
|
12995
|
+
* vehicle name, propagated onto `event.data.label`) is one of these values.
|
|
12996
|
+
* Empty/absent → unaffected (back-compat). Enables "notify me when <named
|
|
12997
|
+
* vehicle/person> is seen". */
|
|
12998
|
+
labels: array(string()).readonly().optional()
|
|
12719
12999
|
});
|
|
12720
13000
|
var NotificationRuleTemplateSchema = object({
|
|
12721
13001
|
title: string(),
|
|
@@ -13010,11 +13290,19 @@ method(object({
|
|
|
13010
13290
|
* login page needs NO change.
|
|
13011
13291
|
*
|
|
13012
13292
|
* - `widget` — a Module-Federation widget the login page mounts (via
|
|
13013
|
-
* `loadRemoteBundle`) for an in-page ceremony.
|
|
13014
|
-
*
|
|
13015
|
-
*
|
|
13016
|
-
*
|
|
13017
|
-
*
|
|
13293
|
+
* `loadRemoteBundle`) for an in-page ceremony. `auth.listLoginMethods`
|
|
13294
|
+
* stamps a public `bundleUrl` from `addonId` + `bundle`. Generic
|
|
13295
|
+
* mechanism kept for future use; no shipped addon uses it on the login
|
|
13296
|
+
* page (the passkey ceremony below runs natively in the shell instead).
|
|
13297
|
+
*
|
|
13298
|
+
* - `passkey` — a declarative WebAuthn ceremony the shell renders
|
|
13299
|
+
* natively (`@simplewebauthn/browser` lives in `addon-admin-ui`, not in
|
|
13300
|
+
* a remotely-loaded bundle). Carries the addon's effective `rpId` /
|
|
13301
|
+
* `origin` (from its `resolveRpID()` / `resolveOrigin()`) so the shell
|
|
13302
|
+
* can gate visibility (IP-literal origin, hostname/rpId mismatch) WITHOUT
|
|
13303
|
+
* fetching any remote code pre-auth. Contribution stays unconditional —
|
|
13304
|
+
* enrollment state is never leaked pre-auth; visibility is a shell
|
|
13305
|
+
* decision.
|
|
13018
13306
|
*
|
|
13019
13307
|
* Every contribution carries a `stage`:
|
|
13020
13308
|
* - `primary` — shown on the first credentials screen (OIDC /
|
|
@@ -13028,30 +13316,45 @@ method(object({
|
|
|
13028
13316
|
*/
|
|
13029
13317
|
/** When a login method renders in the two-phase login flow. */
|
|
13030
13318
|
var LoginStageEnum = _enum(["primary", "second-factor"]);
|
|
13031
|
-
/** One login-method contribution — redirect button
|
|
13032
|
-
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13033
|
-
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13319
|
+
/** One login-method contribution — redirect button, pre-auth widget, or native passkey ceremony. */
|
|
13320
|
+
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13321
|
+
object({
|
|
13322
|
+
kind: literal("redirect"),
|
|
13323
|
+
/** Stable id within the login-method set (e.g. `auth-oidc/google`). */
|
|
13324
|
+
id: string(),
|
|
13325
|
+
/** Operator-facing button label. */
|
|
13326
|
+
label: string(),
|
|
13327
|
+
/** lucide-react icon name. */
|
|
13328
|
+
icon: string().optional(),
|
|
13329
|
+
/** Addon-owned HTTP route the button navigates to (GET). */
|
|
13330
|
+
startUrl: string(),
|
|
13331
|
+
stage: LoginStageEnum
|
|
13332
|
+
}),
|
|
13333
|
+
object({
|
|
13334
|
+
kind: literal("widget"),
|
|
13335
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
|
|
13336
|
+
id: string(),
|
|
13337
|
+
/** Owning addon id — drives the public bundle URL + the MF namespace. */
|
|
13338
|
+
addonId: string(),
|
|
13339
|
+
/** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
|
|
13340
|
+
bundle: string(),
|
|
13341
|
+
/** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
|
|
13342
|
+
remote: WidgetRemoteSchema,
|
|
13343
|
+
stage: LoginStageEnum
|
|
13344
|
+
}),
|
|
13345
|
+
object({
|
|
13346
|
+
kind: literal("passkey"),
|
|
13347
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-direct-login`). */
|
|
13348
|
+
id: string(),
|
|
13349
|
+
/** Operator-facing button label. */
|
|
13350
|
+
label: string(),
|
|
13351
|
+
stage: LoginStageEnum,
|
|
13352
|
+
/** Effective WebAuthn RP ID (`resolveRpID()`) — the shell gates visibility on it. */
|
|
13353
|
+
rpId: string(),
|
|
13354
|
+
/** Effective expected origin (`resolveOrigin()`), null when unconfigured. */
|
|
13355
|
+
origin: string().nullable()
|
|
13356
|
+
})
|
|
13357
|
+
]);
|
|
13055
13358
|
method(_void(), array(LoginMethodContributionSchema).readonly());
|
|
13056
13359
|
/**
|
|
13057
13360
|
* Orchestrator-side destination metadata. The orchestrator computes
|
|
@@ -14972,14 +15275,14 @@ var TargetKindCapsSchema = object({
|
|
|
14972
15275
|
* the union is large and not meant for runtime validation here; the exported
|
|
14973
15276
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
14974
15277
|
*/
|
|
14975
|
-
var ConfigSchemaPassthrough = unknown();
|
|
15278
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
14976
15279
|
var TargetKindSchema = object({
|
|
14977
15280
|
kind: string(),
|
|
14978
15281
|
label: string(),
|
|
14979
15282
|
icon: string(),
|
|
14980
15283
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
14981
15284
|
addonId: string(),
|
|
14982
|
-
configSchema: ConfigSchemaPassthrough,
|
|
15285
|
+
configSchema: ConfigSchemaPassthrough$1,
|
|
14983
15286
|
supportsDiscovery: boolean(),
|
|
14984
15287
|
caps: TargetKindCapsSchema
|
|
14985
15288
|
});
|
|
@@ -15032,6 +15335,298 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
15032
15335
|
enabled: boolean()
|
|
15033
15336
|
}), _void(), { kind: "mutation" });
|
|
15034
15337
|
/**
|
|
15338
|
+
* Shared LLM generate contracts — imported by BOTH `llm.cap.ts` (consumer
|
|
15339
|
+
* surface) and `llm-runtime.cap.ts` (node-side managed executor) so the two
|
|
15340
|
+
* caps stay wire-compatible without a circular cap→cap import.
|
|
15341
|
+
*
|
|
15342
|
+
* Errors are a discriminated-union RESULT, never thrown: the shape survives
|
|
15343
|
+
* every transport tier structurally, and failed calls still write usage rows.
|
|
15344
|
+
* Token counts only in v1 — no costUsd (operator decision, 2026-07-15).
|
|
15345
|
+
*/
|
|
15346
|
+
var LlmUsageSchema = object({
|
|
15347
|
+
inputTokens: number(),
|
|
15348
|
+
outputTokens: number()
|
|
15349
|
+
});
|
|
15350
|
+
var LlmErrorCodeSchema = _enum([
|
|
15351
|
+
"timeout",
|
|
15352
|
+
"rate-limited",
|
|
15353
|
+
"auth",
|
|
15354
|
+
"refusal",
|
|
15355
|
+
"bad-request",
|
|
15356
|
+
"unavailable",
|
|
15357
|
+
"no-profile",
|
|
15358
|
+
"budget-exceeded",
|
|
15359
|
+
"adapter-error"
|
|
15360
|
+
]);
|
|
15361
|
+
var LlmGenerateResultSchema = discriminatedUnion("ok", [object({
|
|
15362
|
+
ok: literal(true),
|
|
15363
|
+
text: string(),
|
|
15364
|
+
model: string(),
|
|
15365
|
+
usage: LlmUsageSchema,
|
|
15366
|
+
truncated: boolean(),
|
|
15367
|
+
latencyMs: number()
|
|
15368
|
+
}), object({
|
|
15369
|
+
ok: literal(false),
|
|
15370
|
+
code: LlmErrorCodeSchema,
|
|
15371
|
+
message: string(),
|
|
15372
|
+
retryAfterMs: number().optional()
|
|
15373
|
+
})]);
|
|
15374
|
+
/**
|
|
15375
|
+
* `Uint8Array` is the sanctioned binary convention — superjson + the UDS
|
|
15376
|
+
* MsgPack channel round-trip typed arrays (embedding-encoder.cap.ts:29,
|
|
15377
|
+
* notification-output.cap.ts:27-31 precedents).
|
|
15378
|
+
*/
|
|
15379
|
+
var LlmImageSchema = object({
|
|
15380
|
+
bytes: _instanceof(Uint8Array),
|
|
15381
|
+
mimeType: string()
|
|
15382
|
+
});
|
|
15383
|
+
var LlmGenerateBaseInputSchema = object({
|
|
15384
|
+
/** Collection routing (the notification-output posture). */
|
|
15385
|
+
addonId: string().optional(),
|
|
15386
|
+
/** Explicit profile; else the resolution chain (spec §3). */
|
|
15387
|
+
profileId: string().optional(),
|
|
15388
|
+
/** MANDATORY usage tag: 'ai-summary', 'notifier-rules', 'adhoc-ui', … */
|
|
15389
|
+
consumer: string(),
|
|
15390
|
+
system: string().optional(),
|
|
15391
|
+
/** v1: single-turn. `messages[]` is a v2 additive field. */
|
|
15392
|
+
prompt: string(),
|
|
15393
|
+
/** Structured output — adapter-mapped (response_format / forced tool / responseSchema). */
|
|
15394
|
+
jsonSchema: record(string(), unknown()).optional(),
|
|
15395
|
+
/** Per-call override of the profile default. */
|
|
15396
|
+
maxTokens: number().int().positive().optional(),
|
|
15397
|
+
temperature: number().optional()
|
|
15398
|
+
});
|
|
15399
|
+
/**
|
|
15400
|
+
* `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
|
|
15401
|
+
* on EVERY node where `addon-ai` is installed; the hub `llm` provider reaches
|
|
15402
|
+
* a specific node's runtime with `nodePin(profile.runtime.nodeId)` — normal
|
|
15403
|
+
* cap routing, zero bespoke plumbing. `internal: true`: the operator reaches
|
|
15404
|
+
* this only through the `llm` cap's methods.
|
|
15405
|
+
*
|
|
15406
|
+
* One running llama-server child per node in v1 (models are RAM-heavy).
|
|
15407
|
+
* Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
|
|
15408
|
+
* watchdog — operator decision #3).
|
|
15409
|
+
*/
|
|
15410
|
+
var ManagedModelRefSchema = discriminatedUnion("kind", [
|
|
15411
|
+
object({
|
|
15412
|
+
kind: literal("catalog"),
|
|
15413
|
+
catalogId: string()
|
|
15414
|
+
}),
|
|
15415
|
+
object({
|
|
15416
|
+
kind: literal("url"),
|
|
15417
|
+
url: string(),
|
|
15418
|
+
sha256: string().optional()
|
|
15419
|
+
}),
|
|
15420
|
+
object({
|
|
15421
|
+
kind: literal("path"),
|
|
15422
|
+
path: string()
|
|
15423
|
+
})
|
|
15424
|
+
]);
|
|
15425
|
+
var ManagedRuntimeConfigSchema = object({
|
|
15426
|
+
/** WHERE the runtime lives — hub or any agent. */
|
|
15427
|
+
nodeId: string(),
|
|
15428
|
+
/** Closed for v1; 'ollama' is a v2 candidate. */
|
|
15429
|
+
engine: _enum(["llama-cpp"]),
|
|
15430
|
+
model: ManagedModelRefSchema,
|
|
15431
|
+
contextSize: number().int().default(4096),
|
|
15432
|
+
/** 0 = CPU-only. */
|
|
15433
|
+
gpuLayers: number().int().default(0),
|
|
15434
|
+
/** Default: cpus-2, clamped ≥1 (resolved node-side). */
|
|
15435
|
+
threads: number().int().optional(),
|
|
15436
|
+
/** Concurrent slots. */
|
|
15437
|
+
parallel: number().int().default(1),
|
|
15438
|
+
/** Else lazy: first generate boots it. */
|
|
15439
|
+
autoStart: boolean().default(false),
|
|
15440
|
+
/** 0 = never; frees RAM after quiet periods. */
|
|
15441
|
+
idleStopMinutes: number().int().default(30)
|
|
15442
|
+
});
|
|
15443
|
+
var LlmRuntimeStatusSchema = object({
|
|
15444
|
+
/** Status is ALWAYS node-qualified. */
|
|
15445
|
+
nodeId: string(),
|
|
15446
|
+
state: _enum([
|
|
15447
|
+
"stopped",
|
|
15448
|
+
"downloading",
|
|
15449
|
+
"starting",
|
|
15450
|
+
"ready",
|
|
15451
|
+
"crashed",
|
|
15452
|
+
"failed"
|
|
15453
|
+
]),
|
|
15454
|
+
pid: number().optional(),
|
|
15455
|
+
port: number().optional(),
|
|
15456
|
+
modelPath: string().optional(),
|
|
15457
|
+
modelId: string().optional(),
|
|
15458
|
+
downloadProgress: number().min(0).max(1).optional(),
|
|
15459
|
+
lastError: string().optional(),
|
|
15460
|
+
crashesInWindow: number(),
|
|
15461
|
+
/** Child RSS (sampled best-effort). */
|
|
15462
|
+
memoryBytes: number().optional(),
|
|
15463
|
+
vramBytes: number().optional()
|
|
15464
|
+
});
|
|
15465
|
+
var LlmNodeModelSchema = object({
|
|
15466
|
+
file: string(),
|
|
15467
|
+
sizeBytes: number(),
|
|
15468
|
+
catalogId: string().optional(),
|
|
15469
|
+
installedAt: number().optional()
|
|
15470
|
+
});
|
|
15471
|
+
var LlmRuntimeDiskUsageSchema = object({
|
|
15472
|
+
nodeId: string(),
|
|
15473
|
+
modelsBytes: number(),
|
|
15474
|
+
freeBytes: number().optional()
|
|
15475
|
+
});
|
|
15476
|
+
method(LlmGenerateBaseInputSchema.extend({
|
|
15477
|
+
images: array(LlmImageSchema).optional(),
|
|
15478
|
+
runtime: ManagedRuntimeConfigSchema,
|
|
15479
|
+
/** The managed profile's timeout, threaded by the hub provider. */
|
|
15480
|
+
timeoutMs: number().int().positive().optional()
|
|
15481
|
+
}), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
|
|
15482
|
+
kind: "mutation",
|
|
15483
|
+
auth: "admin"
|
|
15484
|
+
}), method(object({}), _void(), {
|
|
15485
|
+
kind: "mutation",
|
|
15486
|
+
auth: "admin"
|
|
15487
|
+
}), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
|
|
15488
|
+
kind: "mutation",
|
|
15489
|
+
auth: "admin"
|
|
15490
|
+
}), method(object({ file: string() }), _void(), {
|
|
15491
|
+
kind: "mutation",
|
|
15492
|
+
auth: "admin"
|
|
15493
|
+
}), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
|
|
15494
|
+
/**
|
|
15495
|
+
* `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
|
|
15496
|
+
* methods concat-fan across providers; single-row methods route to ONE
|
|
15497
|
+
* provider by the `addonId` in the call input (the notification-output
|
|
15498
|
+
* posture, notification-output.cap.ts:215-250). Provided by `addon-ai`
|
|
15499
|
+
* (hub-placed); the cap stays open for future providers.
|
|
15500
|
+
*
|
|
15501
|
+
* Profiles are ROWS (data), not addons: one row = one usable model endpoint.
|
|
15502
|
+
* `apiKey` is a password field — providers REDACT it on read and merge on
|
|
15503
|
+
* write; a stored key NEVER round-trips to a client.
|
|
15504
|
+
*/
|
|
15505
|
+
var LlmProfileKindSchema = _enum([
|
|
15506
|
+
"openai-compatible",
|
|
15507
|
+
"openai",
|
|
15508
|
+
"anthropic",
|
|
15509
|
+
"google",
|
|
15510
|
+
"managed-local"
|
|
15511
|
+
]);
|
|
15512
|
+
var LlmProfileSchema = object({
|
|
15513
|
+
id: string(),
|
|
15514
|
+
name: string(),
|
|
15515
|
+
kind: LlmProfileKindSchema,
|
|
15516
|
+
/** Stamped by the provider — keeps the fanned catalog routable. */
|
|
15517
|
+
addonId: string(),
|
|
15518
|
+
enabled: boolean(),
|
|
15519
|
+
/** Vendor model id, or the managed runtime's loaded model. */
|
|
15520
|
+
model: string(),
|
|
15521
|
+
/** Required for openai-compatible; override for cloud kinds. */
|
|
15522
|
+
baseUrl: string().optional(),
|
|
15523
|
+
/** ConfigUISchema type:'password' — never round-trips (spec §5). */
|
|
15524
|
+
apiKey: string().optional(),
|
|
15525
|
+
supportsVision: boolean(),
|
|
15526
|
+
temperature: number().min(0).max(2).optional(),
|
|
15527
|
+
maxTokens: number().int().positive().optional(),
|
|
15528
|
+
timeoutMs: number().int().positive().default(6e4),
|
|
15529
|
+
extraHeaders: record(string(), string()).optional(),
|
|
15530
|
+
/** kind === 'managed-local' only (spec §4). */
|
|
15531
|
+
runtime: ManagedRuntimeConfigSchema.optional()
|
|
15532
|
+
});
|
|
15533
|
+
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
15534
|
+
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
15535
|
+
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
15536
|
+
var ConfigSchemaPassthrough = unknown();
|
|
15537
|
+
var LlmProfileKindDescriptorSchema = object({
|
|
15538
|
+
kind: LlmProfileKindSchema,
|
|
15539
|
+
label: string(),
|
|
15540
|
+
icon: string(),
|
|
15541
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
15542
|
+
addonId: string(),
|
|
15543
|
+
configSchema: ConfigSchemaPassthrough
|
|
15544
|
+
});
|
|
15545
|
+
var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
|
|
15546
|
+
var LlmDefaultSchema = object({
|
|
15547
|
+
selector: LlmDefaultSelectorSchema,
|
|
15548
|
+
profileId: string()
|
|
15549
|
+
});
|
|
15550
|
+
/** Server-side rollup row — getUsage never dumps raw call rows (spec §6). */
|
|
15551
|
+
var LlmUsageRollupSchema = object({
|
|
15552
|
+
day: string(),
|
|
15553
|
+
consumer: string(),
|
|
15554
|
+
profileId: string(),
|
|
15555
|
+
calls: number(),
|
|
15556
|
+
okCalls: number(),
|
|
15557
|
+
errorCalls: number(),
|
|
15558
|
+
inputTokens: number(),
|
|
15559
|
+
outputTokens: number(),
|
|
15560
|
+
avgLatencyMs: number()
|
|
15561
|
+
});
|
|
15562
|
+
/** LLM-facing view over the reused ModelCatalogEntry mechanism (spec §4.2). */
|
|
15563
|
+
var ManagedModelCatalogEntrySchema = object({
|
|
15564
|
+
id: string(),
|
|
15565
|
+
label: string(),
|
|
15566
|
+
family: string(),
|
|
15567
|
+
purpose: _enum(["text", "vision"]),
|
|
15568
|
+
url: string(),
|
|
15569
|
+
sha256: string(),
|
|
15570
|
+
sizeBytes: number(),
|
|
15571
|
+
quantization: string(),
|
|
15572
|
+
/** Load-time guidance shown in the picker. */
|
|
15573
|
+
minRamBytes: number(),
|
|
15574
|
+
contextSizeDefault: number().int(),
|
|
15575
|
+
/** Vision models: companion projector file. */
|
|
15576
|
+
mmprojUrl: string().optional()
|
|
15577
|
+
});
|
|
15578
|
+
var LlmRuntimeNodeSchema = object({
|
|
15579
|
+
nodeId: string(),
|
|
15580
|
+
reachable: boolean(),
|
|
15581
|
+
status: LlmRuntimeStatusSchema.optional(),
|
|
15582
|
+
disk: LlmRuntimeDiskUsageSchema.optional(),
|
|
15583
|
+
error: string().optional()
|
|
15584
|
+
});
|
|
15585
|
+
var GenerateVisionInputSchema = LlmGenerateBaseInputSchema.extend({ images: array(LlmImageSchema).min(1) });
|
|
15586
|
+
var ProfileRefInputSchema = object({
|
|
15587
|
+
addonId: string(),
|
|
15588
|
+
profileId: string()
|
|
15589
|
+
});
|
|
15590
|
+
method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
|
|
15591
|
+
kind: "mutation",
|
|
15592
|
+
auth: "admin"
|
|
15593
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15594
|
+
kind: "mutation",
|
|
15595
|
+
auth: "admin"
|
|
15596
|
+
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
15597
|
+
kind: "mutation",
|
|
15598
|
+
auth: "admin"
|
|
15599
|
+
}), method(ProfileRefInputSchema, array(string())), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
15600
|
+
selector: LlmDefaultSelectorSchema,
|
|
15601
|
+
profileId: string().nullable()
|
|
15602
|
+
}), _void(), {
|
|
15603
|
+
kind: "mutation",
|
|
15604
|
+
auth: "admin"
|
|
15605
|
+
}), method(object({
|
|
15606
|
+
since: number().optional(),
|
|
15607
|
+
until: number().optional(),
|
|
15608
|
+
consumer: string().optional(),
|
|
15609
|
+
profileId: string().optional()
|
|
15610
|
+
}), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
|
|
15611
|
+
nodeId: string(),
|
|
15612
|
+
model: ManagedModelRefSchema
|
|
15613
|
+
}), _void(), {
|
|
15614
|
+
kind: "mutation",
|
|
15615
|
+
auth: "admin"
|
|
15616
|
+
}), method(object({
|
|
15617
|
+
nodeId: string(),
|
|
15618
|
+
file: string()
|
|
15619
|
+
}), _void(), {
|
|
15620
|
+
kind: "mutation",
|
|
15621
|
+
auth: "admin"
|
|
15622
|
+
}), method(ProfileRefInputSchema, LlmRuntimeStatusSchema), method(ProfileRefInputSchema, LlmRuntimeStatusSchema, {
|
|
15623
|
+
kind: "mutation",
|
|
15624
|
+
auth: "admin"
|
|
15625
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15626
|
+
kind: "mutation",
|
|
15627
|
+
auth: "admin"
|
|
15628
|
+
});
|
|
15629
|
+
/**
|
|
15035
15630
|
* Zod schemas for persisted record types.
|
|
15036
15631
|
*
|
|
15037
15632
|
* These schemas serve as the single source of truth for types that are
|
|
@@ -15233,6 +15828,22 @@ var TrackSnapshotSchema = object({
|
|
|
15233
15828
|
/** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
|
|
15234
15829
|
mediaKey: string()
|
|
15235
15830
|
});
|
|
15831
|
+
/**
|
|
15832
|
+
* One audio-classification label heard on the track's camera while the
|
|
15833
|
+
* track was alive, aggregated per label. An "episode" is one persisted
|
|
15834
|
+
* audio event (the confident-classification path: score ≥ the device's
|
|
15835
|
+
* `classificationMinScore`, class-change-or-heartbeat coalesced) — NOT
|
|
15836
|
+
* one 32 ms inference chunk, so counts stay human-scaled.
|
|
15837
|
+
*/
|
|
15838
|
+
var TrackAudioLabelSchema = object({
|
|
15839
|
+
label: string(),
|
|
15840
|
+
/** Highest classification score observed across the label's episodes. */
|
|
15841
|
+
peakScore: number(),
|
|
15842
|
+
/** Number of coalesced audio-event episodes carrying this label. */
|
|
15843
|
+
count: number(),
|
|
15844
|
+
firstAt: number(),
|
|
15845
|
+
lastAt: number()
|
|
15846
|
+
});
|
|
15236
15847
|
var TrackSchema = object({
|
|
15237
15848
|
trackId: string(),
|
|
15238
15849
|
deviceId: number(),
|
|
@@ -15247,6 +15858,10 @@ var TrackSchema = object({
|
|
|
15247
15858
|
snapshots: array(TrackSnapshotSchema).readonly(),
|
|
15248
15859
|
/** Deduplicated zones the track has entered at least once. */
|
|
15249
15860
|
zonesVisited: array(string()).readonly(),
|
|
15861
|
+
/** Deduplicated set of detector classes observed for this track over its
|
|
15862
|
+
* life (a track may be reclassified, e.g. person→vehicle). Absent on
|
|
15863
|
+
* legacy rows written before class accumulation shipped. */
|
|
15864
|
+
classes: array(string()).readonly().optional(),
|
|
15250
15865
|
/** Cumulative normalized distance travelled (0..1 units = full frame width). */
|
|
15251
15866
|
totalDistance: number(),
|
|
15252
15867
|
state: TrackStateSchema,
|
|
@@ -15260,7 +15875,11 @@ var TrackSchema = object({
|
|
|
15260
15875
|
bestEventId: string().optional(),
|
|
15261
15876
|
/** Tag of the importance sub-signal that dominated the score
|
|
15262
15877
|
* (identity|dwell|proximity|class|confidence|travel|zone). */
|
|
15263
|
-
importanceReason: string().optional()
|
|
15878
|
+
importanceReason: string().optional(),
|
|
15879
|
+
/** Audio-classification labels heard on the camera during the track's
|
|
15880
|
+
* life (score ≥ device `classificationMinScore`), aggregated per label.
|
|
15881
|
+
* Absent on legacy rows / tracks with no confident audio. */
|
|
15882
|
+
audioLabels: array(TrackAudioLabelSchema).readonly().optional()
|
|
15264
15883
|
});
|
|
15265
15884
|
var BaseEventFields = {
|
|
15266
15885
|
id: string(),
|
|
@@ -15408,7 +16027,7 @@ var KeyEventSchema = object({
|
|
|
15408
16027
|
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
15409
16028
|
windowMs: number().optional()
|
|
15410
16029
|
});
|
|
15411
|
-
|
|
16030
|
+
object({
|
|
15412
16031
|
trackId: string(),
|
|
15413
16032
|
className: string(),
|
|
15414
16033
|
confidence: number(),
|
|
@@ -15416,6 +16035,23 @@ var TrackedDetectionSchema = object({
|
|
|
15416
16035
|
zones: array(string()).readonly(),
|
|
15417
16036
|
state: TrackStateSchema
|
|
15418
16037
|
});
|
|
16038
|
+
var OverlayDetectionSchema = looseObject({
|
|
16039
|
+
id: string(),
|
|
16040
|
+
kind: _enum(["first-level", "detail"]),
|
|
16041
|
+
macroClass: string(),
|
|
16042
|
+
score: number(),
|
|
16043
|
+
bbox: object({
|
|
16044
|
+
x: number(),
|
|
16045
|
+
y: number(),
|
|
16046
|
+
width: number(),
|
|
16047
|
+
height: number()
|
|
16048
|
+
}),
|
|
16049
|
+
labels: array(looseObject({
|
|
16050
|
+
label: string(),
|
|
16051
|
+
score: number()
|
|
16052
|
+
})).readonly(),
|
|
16053
|
+
parentId: string().optional()
|
|
16054
|
+
});
|
|
15419
16055
|
var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: number() });
|
|
15420
16056
|
var SearchObjectEventsInput = object({
|
|
15421
16057
|
text: string(),
|
|
@@ -15426,6 +16062,20 @@ var SearchObjectEventsInput = object({
|
|
|
15426
16062
|
limit: number().default(50),
|
|
15427
16063
|
minScore: number().min(0).max(1).default(.2)
|
|
15428
16064
|
});
|
|
16065
|
+
var TrackCascadeCountsSchema = object({
|
|
16066
|
+
/** Persisted track roots deleted (authoritative). */
|
|
16067
|
+
tracks: number().int(),
|
|
16068
|
+
/** Object events removed with their tracks (best-effort; see note above). */
|
|
16069
|
+
events: number().int(),
|
|
16070
|
+
/** Track/face/plate-owned media removed (best-effort). Never identity media. */
|
|
16071
|
+
media: number().int(),
|
|
16072
|
+
/** Unassigned (non-enrolled) face reads removed (best-effort). */
|
|
16073
|
+
faces: number().int(),
|
|
16074
|
+
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
16075
|
+
plates: number().int(),
|
|
16076
|
+
/** Per-track CLIP search vectors removed (best-effort). */
|
|
16077
|
+
embeddings: number().int()
|
|
16078
|
+
});
|
|
15429
16079
|
DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
|
|
15430
16080
|
deviceId: number(),
|
|
15431
16081
|
trackId: string()
|
|
@@ -15458,15 +16108,33 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15458
16108
|
kind: "mutation",
|
|
15459
16109
|
auth: "admin"
|
|
15460
16110
|
}), method(object({
|
|
15461
|
-
eventId: string(),
|
|
15462
|
-
kind: MediaFileKindEnum.optional()
|
|
15463
|
-
}), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
15464
16111
|
deviceId: number(),
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
}), object({
|
|
16112
|
+
cutoffMs: number()
|
|
16113
|
+
}), TrackCascadeCountsSchema, {
|
|
16114
|
+
kind: "mutation",
|
|
16115
|
+
auth: "admin"
|
|
16116
|
+
}), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
|
|
16117
|
+
kind: "mutation",
|
|
16118
|
+
auth: "admin"
|
|
16119
|
+
}), method(object({
|
|
16120
|
+
deviceId: number(),
|
|
16121
|
+
trackIds: array(string()).min(1)
|
|
16122
|
+
}), object({
|
|
16123
|
+
deleted: number().int(),
|
|
16124
|
+
failed: array(string()).readonly()
|
|
16125
|
+
}), {
|
|
16126
|
+
kind: "mutation",
|
|
16127
|
+
auth: "admin"
|
|
16128
|
+
}), method(object({
|
|
16129
|
+
eventId: string(),
|
|
16130
|
+
kind: MediaFileKindEnum.optional()
|
|
16131
|
+
}), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
16132
|
+
deviceId: number(),
|
|
16133
|
+
timestamp: number(),
|
|
16134
|
+
frameWidth: number(),
|
|
16135
|
+
frameHeight: number(),
|
|
16136
|
+
detections: array(OverlayDetectionSchema).readonly()
|
|
16137
|
+
}), object({
|
|
15470
16138
|
deviceId: number(),
|
|
15471
16139
|
trackId: string(),
|
|
15472
16140
|
className: string()
|
|
@@ -18234,6 +18902,24 @@ DeviceType.Camera, method(object({
|
|
|
18234
18902
|
deviceId: number(),
|
|
18235
18903
|
status: OsdStatusSchema
|
|
18236
18904
|
});
|
|
18905
|
+
var VehicleSchema = object({
|
|
18906
|
+
id: string(),
|
|
18907
|
+
name: string(),
|
|
18908
|
+
sampleCount: number().int().nonnegative(),
|
|
18909
|
+
coverMediaKey: string().optional(),
|
|
18910
|
+
/** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
|
|
18911
|
+
coverBase64: string().optional()
|
|
18912
|
+
});
|
|
18913
|
+
var VehicleSampleInfoSchema = object({
|
|
18914
|
+
id: string(),
|
|
18915
|
+
/** The plate text this sample enrolled (self-labeling — no embedding). */
|
|
18916
|
+
text: string(),
|
|
18917
|
+
/** OCR confidence of the enrolled read (0..1). */
|
|
18918
|
+
score: number(),
|
|
18919
|
+
addedAt: number().int(),
|
|
18920
|
+
deviceId: number().int().optional(),
|
|
18921
|
+
base64: string().optional()
|
|
18922
|
+
});
|
|
18237
18923
|
var PlateInfoSchema = object({
|
|
18238
18924
|
plateId: string(),
|
|
18239
18925
|
deviceId: number().int(),
|
|
@@ -18245,6 +18931,16 @@ var PlateInfoSchema = object({
|
|
|
18245
18931
|
score: number(),
|
|
18246
18932
|
/** True when the text was manually corrected (exempt from retention). */
|
|
18247
18933
|
corrected: boolean(),
|
|
18934
|
+
/** Recognized vehicle id when a matcher lookup named this read (SQL NULL → absent). */
|
|
18935
|
+
recognizedVehicleId: string().optional(),
|
|
18936
|
+
/** Resolved vehicle name for `recognizedVehicleId` (UI convenience). */
|
|
18937
|
+
vehicleName: string().optional(),
|
|
18938
|
+
/** True once the read was assigned to a vehicle (enrolled as a sample). */
|
|
18939
|
+
assigned: boolean(),
|
|
18940
|
+
/** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
|
|
18941
|
+
plateBbox: BoundingBoxSchema.optional(),
|
|
18942
|
+
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
18943
|
+
keyFrameMediaKey: string().optional(),
|
|
18248
18944
|
base64: string().optional()
|
|
18249
18945
|
});
|
|
18250
18946
|
var MediaFileLiteSchema = object({
|
|
@@ -18284,6 +18980,48 @@ method(object({
|
|
|
18284
18980
|
}), method(object({ plateId: string() }), _void(), {
|
|
18285
18981
|
kind: "mutation",
|
|
18286
18982
|
auth: "admin"
|
|
18983
|
+
}), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
|
|
18984
|
+
kind: "mutation",
|
|
18985
|
+
auth: "admin"
|
|
18986
|
+
}), method(object({
|
|
18987
|
+
id: string(),
|
|
18988
|
+
name: string().min(1)
|
|
18989
|
+
}), _void(), {
|
|
18990
|
+
kind: "mutation",
|
|
18991
|
+
auth: "admin"
|
|
18992
|
+
}), method(object({ id: string() }), _void(), {
|
|
18993
|
+
kind: "mutation",
|
|
18994
|
+
auth: "admin"
|
|
18995
|
+
}), method(object({ vehicleId: string() }), array(VehicleSampleInfoSchema).readonly()), method(object({
|
|
18996
|
+
vehicleId: string(),
|
|
18997
|
+
sampleId: string()
|
|
18998
|
+
}), _void(), {
|
|
18999
|
+
kind: "mutation",
|
|
19000
|
+
auth: "admin"
|
|
19001
|
+
}), method(object({
|
|
19002
|
+
plateId: string(),
|
|
19003
|
+
vehicleId: string()
|
|
19004
|
+
}), _void(), {
|
|
19005
|
+
kind: "mutation",
|
|
19006
|
+
auth: "admin"
|
|
19007
|
+
}), method(object({ plateId: string() }), _void(), {
|
|
19008
|
+
kind: "mutation",
|
|
19009
|
+
auth: "admin"
|
|
19010
|
+
}), method(object({
|
|
19011
|
+
plateIds: array(string()).min(1),
|
|
19012
|
+
vehicleId: string()
|
|
19013
|
+
}), object({
|
|
19014
|
+
assigned: number().int(),
|
|
19015
|
+
failed: array(string()).readonly()
|
|
19016
|
+
}), {
|
|
19017
|
+
kind: "mutation",
|
|
19018
|
+
auth: "admin"
|
|
19019
|
+
}), method(object({ plateIds: array(string()).min(1) }), object({
|
|
19020
|
+
unassigned: number().int(),
|
|
19021
|
+
failed: array(string()).readonly()
|
|
19022
|
+
}), {
|
|
19023
|
+
kind: "mutation",
|
|
19024
|
+
auth: "admin"
|
|
18287
19025
|
});
|
|
18288
19026
|
var ModelFormatSchema = _enum(MODEL_FORMATS);
|
|
18289
19027
|
var HwAccelBackendInputSchema = _enum([
|
|
@@ -18362,6 +19100,10 @@ var GpuInfoSchema = object({
|
|
|
18362
19100
|
memoryMB: number().optional()
|
|
18363
19101
|
});
|
|
18364
19102
|
var NpuInfoSchema = object({ type: _enum(["apple-ane", "intel-npu"]) });
|
|
19103
|
+
var CoralInfoSchema = object({
|
|
19104
|
+
type: literal("coral-edgetpu"),
|
|
19105
|
+
bus: string().optional()
|
|
19106
|
+
});
|
|
18365
19107
|
var HardwareInfoSchema = object({
|
|
18366
19108
|
platform: HardwarePlatformSchema,
|
|
18367
19109
|
arch: HardwareArchSchema,
|
|
@@ -18370,7 +19112,8 @@ var HardwareInfoSchema = object({
|
|
|
18370
19112
|
totalRAM_MB: number(),
|
|
18371
19113
|
availableRAM_MB: number(),
|
|
18372
19114
|
gpu: GpuInfoSchema.nullable(),
|
|
18373
|
-
npu: NpuInfoSchema.nullable()
|
|
19115
|
+
npu: NpuInfoSchema.nullable(),
|
|
19116
|
+
coral: CoralInfoSchema.nullable().optional()
|
|
18374
19117
|
});
|
|
18375
19118
|
var PlatformScoreSchema = object({
|
|
18376
19119
|
runtime: _enum(["node", "python"]),
|
|
@@ -20829,6 +21572,168 @@ Object.freeze({
|
|
|
20829
21572
|
addonId: null,
|
|
20830
21573
|
access: "create"
|
|
20831
21574
|
},
|
|
21575
|
+
"llm.deleteModel": {
|
|
21576
|
+
capName: "llm",
|
|
21577
|
+
capScope: "system",
|
|
21578
|
+
addonId: null,
|
|
21579
|
+
access: "delete"
|
|
21580
|
+
},
|
|
21581
|
+
"llm.deleteProfile": {
|
|
21582
|
+
capName: "llm",
|
|
21583
|
+
capScope: "system",
|
|
21584
|
+
addonId: null,
|
|
21585
|
+
access: "delete"
|
|
21586
|
+
},
|
|
21587
|
+
"llm.generate": {
|
|
21588
|
+
capName: "llm",
|
|
21589
|
+
capScope: "system",
|
|
21590
|
+
addonId: null,
|
|
21591
|
+
access: "create"
|
|
21592
|
+
},
|
|
21593
|
+
"llm.generateVision": {
|
|
21594
|
+
capName: "llm",
|
|
21595
|
+
capScope: "system",
|
|
21596
|
+
addonId: null,
|
|
21597
|
+
access: "create"
|
|
21598
|
+
},
|
|
21599
|
+
"llm.getDefaults": {
|
|
21600
|
+
capName: "llm",
|
|
21601
|
+
capScope: "system",
|
|
21602
|
+
addonId: null,
|
|
21603
|
+
access: "view"
|
|
21604
|
+
},
|
|
21605
|
+
"llm.getRuntimeStatus": {
|
|
21606
|
+
capName: "llm",
|
|
21607
|
+
capScope: "system",
|
|
21608
|
+
addonId: null,
|
|
21609
|
+
access: "view"
|
|
21610
|
+
},
|
|
21611
|
+
"llm.getUsage": {
|
|
21612
|
+
capName: "llm",
|
|
21613
|
+
capScope: "system",
|
|
21614
|
+
addonId: null,
|
|
21615
|
+
access: "view"
|
|
21616
|
+
},
|
|
21617
|
+
"llm.installModel": {
|
|
21618
|
+
capName: "llm",
|
|
21619
|
+
capScope: "system",
|
|
21620
|
+
addonId: null,
|
|
21621
|
+
access: "create"
|
|
21622
|
+
},
|
|
21623
|
+
"llm.listModelCatalog": {
|
|
21624
|
+
capName: "llm",
|
|
21625
|
+
capScope: "system",
|
|
21626
|
+
addonId: null,
|
|
21627
|
+
access: "view"
|
|
21628
|
+
},
|
|
21629
|
+
"llm.listModels": {
|
|
21630
|
+
capName: "llm",
|
|
21631
|
+
capScope: "system",
|
|
21632
|
+
addonId: null,
|
|
21633
|
+
access: "view"
|
|
21634
|
+
},
|
|
21635
|
+
"llm.listNodeModels": {
|
|
21636
|
+
capName: "llm",
|
|
21637
|
+
capScope: "system",
|
|
21638
|
+
addonId: null,
|
|
21639
|
+
access: "view"
|
|
21640
|
+
},
|
|
21641
|
+
"llm.listProfileKinds": {
|
|
21642
|
+
capName: "llm",
|
|
21643
|
+
capScope: "system",
|
|
21644
|
+
addonId: null,
|
|
21645
|
+
access: "view"
|
|
21646
|
+
},
|
|
21647
|
+
"llm.listProfiles": {
|
|
21648
|
+
capName: "llm",
|
|
21649
|
+
capScope: "system",
|
|
21650
|
+
addonId: null,
|
|
21651
|
+
access: "view"
|
|
21652
|
+
},
|
|
21653
|
+
"llm.listRuntimeNodes": {
|
|
21654
|
+
capName: "llm",
|
|
21655
|
+
capScope: "system",
|
|
21656
|
+
addonId: null,
|
|
21657
|
+
access: "view"
|
|
21658
|
+
},
|
|
21659
|
+
"llm.setDefault": {
|
|
21660
|
+
capName: "llm",
|
|
21661
|
+
capScope: "system",
|
|
21662
|
+
addonId: null,
|
|
21663
|
+
access: "create"
|
|
21664
|
+
},
|
|
21665
|
+
"llm.startRuntime": {
|
|
21666
|
+
capName: "llm",
|
|
21667
|
+
capScope: "system",
|
|
21668
|
+
addonId: null,
|
|
21669
|
+
access: "create"
|
|
21670
|
+
},
|
|
21671
|
+
"llm.stopRuntime": {
|
|
21672
|
+
capName: "llm",
|
|
21673
|
+
capScope: "system",
|
|
21674
|
+
addonId: null,
|
|
21675
|
+
access: "create"
|
|
21676
|
+
},
|
|
21677
|
+
"llm.testProfile": {
|
|
21678
|
+
capName: "llm",
|
|
21679
|
+
capScope: "system",
|
|
21680
|
+
addonId: null,
|
|
21681
|
+
access: "create"
|
|
21682
|
+
},
|
|
21683
|
+
"llm.upsertProfile": {
|
|
21684
|
+
capName: "llm",
|
|
21685
|
+
capScope: "system",
|
|
21686
|
+
addonId: null,
|
|
21687
|
+
access: "create"
|
|
21688
|
+
},
|
|
21689
|
+
"llmRuntime.complete": {
|
|
21690
|
+
capName: "llm-runtime",
|
|
21691
|
+
capScope: "system",
|
|
21692
|
+
addonId: null,
|
|
21693
|
+
access: "create"
|
|
21694
|
+
},
|
|
21695
|
+
"llmRuntime.deleteModel": {
|
|
21696
|
+
capName: "llm-runtime",
|
|
21697
|
+
capScope: "system",
|
|
21698
|
+
addonId: null,
|
|
21699
|
+
access: "delete"
|
|
21700
|
+
},
|
|
21701
|
+
"llmRuntime.ensureStarted": {
|
|
21702
|
+
capName: "llm-runtime",
|
|
21703
|
+
capScope: "system",
|
|
21704
|
+
addonId: null,
|
|
21705
|
+
access: "create"
|
|
21706
|
+
},
|
|
21707
|
+
"llmRuntime.getDiskUsage": {
|
|
21708
|
+
capName: "llm-runtime",
|
|
21709
|
+
capScope: "system",
|
|
21710
|
+
addonId: null,
|
|
21711
|
+
access: "view"
|
|
21712
|
+
},
|
|
21713
|
+
"llmRuntime.installModel": {
|
|
21714
|
+
capName: "llm-runtime",
|
|
21715
|
+
capScope: "system",
|
|
21716
|
+
addonId: null,
|
|
21717
|
+
access: "create"
|
|
21718
|
+
},
|
|
21719
|
+
"llmRuntime.listLocalModels": {
|
|
21720
|
+
capName: "llm-runtime",
|
|
21721
|
+
capScope: "system",
|
|
21722
|
+
addonId: null,
|
|
21723
|
+
access: "view"
|
|
21724
|
+
},
|
|
21725
|
+
"llmRuntime.status": {
|
|
21726
|
+
capName: "llm-runtime",
|
|
21727
|
+
capScope: "system",
|
|
21728
|
+
addonId: null,
|
|
21729
|
+
access: "view"
|
|
21730
|
+
},
|
|
21731
|
+
"llmRuntime.stop": {
|
|
21732
|
+
capName: "llm-runtime",
|
|
21733
|
+
capScope: "system",
|
|
21734
|
+
addonId: null,
|
|
21735
|
+
access: "create"
|
|
21736
|
+
},
|
|
20832
21737
|
"localNetwork.getAllowedAddresses": {
|
|
20833
21738
|
capName: "local-network",
|
|
20834
21739
|
capScope: "system",
|
|
@@ -21459,6 +22364,12 @@ Object.freeze({
|
|
|
21459
22364
|
addonId: null,
|
|
21460
22365
|
access: "delete"
|
|
21461
22366
|
},
|
|
22367
|
+
"pipelineAnalytics.deleteTracks": {
|
|
22368
|
+
capName: "pipeline-analytics",
|
|
22369
|
+
capScope: "device",
|
|
22370
|
+
addonId: null,
|
|
22371
|
+
access: "delete"
|
|
22372
|
+
},
|
|
21462
22373
|
"pipelineAnalytics.getActiveTracks": {
|
|
21463
22374
|
capName: "pipeline-analytics",
|
|
21464
22375
|
capScope: "device",
|
|
@@ -21525,12 +22436,24 @@ Object.freeze({
|
|
|
21525
22436
|
addonId: null,
|
|
21526
22437
|
access: "create"
|
|
21527
22438
|
},
|
|
22439
|
+
"pipelineAnalytics.pruneTracksBefore": {
|
|
22440
|
+
capName: "pipeline-analytics",
|
|
22441
|
+
capScope: "device",
|
|
22442
|
+
addonId: null,
|
|
22443
|
+
access: "create"
|
|
22444
|
+
},
|
|
21528
22445
|
"pipelineAnalytics.searchObjectEvents": {
|
|
21529
22446
|
capName: "pipeline-analytics",
|
|
21530
22447
|
capScope: "device",
|
|
21531
22448
|
addonId: null,
|
|
21532
22449
|
access: "view"
|
|
21533
22450
|
},
|
|
22451
|
+
"pipelineAnalytics.wipeAllAnalytics": {
|
|
22452
|
+
capName: "pipeline-analytics",
|
|
22453
|
+
capScope: "device",
|
|
22454
|
+
addonId: null,
|
|
22455
|
+
access: "delete"
|
|
22456
|
+
},
|
|
21534
22457
|
"pipelineExecutor.cacheFrameInPool": {
|
|
21535
22458
|
capName: "pipeline-executor",
|
|
21536
22459
|
capScope: "system",
|
|
@@ -22029,18 +22952,48 @@ Object.freeze({
|
|
|
22029
22952
|
addonId: null,
|
|
22030
22953
|
access: "create"
|
|
22031
22954
|
},
|
|
22955
|
+
"pipelineRunner.runDetailSubtree": {
|
|
22956
|
+
capName: "pipeline-runner",
|
|
22957
|
+
capScope: "system",
|
|
22958
|
+
addonId: null,
|
|
22959
|
+
access: "create"
|
|
22960
|
+
},
|
|
22961
|
+
"plateGallery.assignPlate": {
|
|
22962
|
+
capName: "plate-gallery",
|
|
22963
|
+
capScope: "system",
|
|
22964
|
+
addonId: null,
|
|
22965
|
+
access: "create"
|
|
22966
|
+
},
|
|
22967
|
+
"plateGallery.assignPlates": {
|
|
22968
|
+
capName: "plate-gallery",
|
|
22969
|
+
capScope: "system",
|
|
22970
|
+
addonId: null,
|
|
22971
|
+
access: "create"
|
|
22972
|
+
},
|
|
22032
22973
|
"plateGallery.correctPlateText": {
|
|
22033
22974
|
capName: "plate-gallery",
|
|
22034
22975
|
capScope: "system",
|
|
22035
22976
|
addonId: null,
|
|
22036
22977
|
access: "create"
|
|
22037
22978
|
},
|
|
22979
|
+
"plateGallery.createVehicle": {
|
|
22980
|
+
capName: "plate-gallery",
|
|
22981
|
+
capScope: "system",
|
|
22982
|
+
addonId: null,
|
|
22983
|
+
access: "create"
|
|
22984
|
+
},
|
|
22038
22985
|
"plateGallery.deletePlate": {
|
|
22039
22986
|
capName: "plate-gallery",
|
|
22040
22987
|
capScope: "system",
|
|
22041
22988
|
addonId: null,
|
|
22042
22989
|
access: "delete"
|
|
22043
22990
|
},
|
|
22991
|
+
"plateGallery.deleteVehicle": {
|
|
22992
|
+
capName: "plate-gallery",
|
|
22993
|
+
capScope: "system",
|
|
22994
|
+
addonId: null,
|
|
22995
|
+
access: "delete"
|
|
22996
|
+
},
|
|
22044
22997
|
"plateGallery.getPlateByTrack": {
|
|
22045
22998
|
capName: "plate-gallery",
|
|
22046
22999
|
capScope: "system",
|
|
@@ -22059,6 +23012,30 @@ Object.freeze({
|
|
|
22059
23012
|
addonId: null,
|
|
22060
23013
|
access: "view"
|
|
22061
23014
|
},
|
|
23015
|
+
"plateGallery.listVehicles": {
|
|
23016
|
+
capName: "plate-gallery",
|
|
23017
|
+
capScope: "system",
|
|
23018
|
+
addonId: null,
|
|
23019
|
+
access: "view"
|
|
23020
|
+
},
|
|
23021
|
+
"plateGallery.listVehicleSamples": {
|
|
23022
|
+
capName: "plate-gallery",
|
|
23023
|
+
capScope: "system",
|
|
23024
|
+
addonId: null,
|
|
23025
|
+
access: "view"
|
|
23026
|
+
},
|
|
23027
|
+
"plateGallery.removeVehicleSample": {
|
|
23028
|
+
capName: "plate-gallery",
|
|
23029
|
+
capScope: "system",
|
|
23030
|
+
addonId: null,
|
|
23031
|
+
access: "delete"
|
|
23032
|
+
},
|
|
23033
|
+
"plateGallery.renameVehicle": {
|
|
23034
|
+
capName: "plate-gallery",
|
|
23035
|
+
capScope: "system",
|
|
23036
|
+
addonId: null,
|
|
23037
|
+
access: "create"
|
|
23038
|
+
},
|
|
22062
23039
|
"plateGallery.searchPlates": {
|
|
22063
23040
|
capName: "plate-gallery",
|
|
22064
23041
|
capScope: "system",
|
|
@@ -22071,6 +23048,18 @@ Object.freeze({
|
|
|
22071
23048
|
addonId: null,
|
|
22072
23049
|
access: "view"
|
|
22073
23050
|
},
|
|
23051
|
+
"plateGallery.unassignPlate": {
|
|
23052
|
+
capName: "plate-gallery",
|
|
23053
|
+
capScope: "system",
|
|
23054
|
+
addonId: null,
|
|
23055
|
+
access: "create"
|
|
23056
|
+
},
|
|
23057
|
+
"plateGallery.unassignPlates": {
|
|
23058
|
+
capName: "plate-gallery",
|
|
23059
|
+
capScope: "system",
|
|
23060
|
+
addonId: null,
|
|
23061
|
+
access: "create"
|
|
23062
|
+
},
|
|
22074
23063
|
"platformProbe.getCapabilities": {
|
|
22075
23064
|
capName: "platform-probe",
|
|
22076
23065
|
capScope: "system",
|
|
@@ -22263,6 +23252,48 @@ Object.freeze({
|
|
|
22263
23252
|
addonId: null,
|
|
22264
23253
|
access: "create"
|
|
22265
23254
|
},
|
|
23255
|
+
"sceneMonitor.captureReference": {
|
|
23256
|
+
capName: "scene-monitor",
|
|
23257
|
+
capScope: "device",
|
|
23258
|
+
addonId: null,
|
|
23259
|
+
access: "create"
|
|
23260
|
+
},
|
|
23261
|
+
"sceneMonitor.createScene": {
|
|
23262
|
+
capName: "scene-monitor",
|
|
23263
|
+
capScope: "device",
|
|
23264
|
+
addonId: null,
|
|
23265
|
+
access: "create"
|
|
23266
|
+
},
|
|
23267
|
+
"sceneMonitor.deleteReference": {
|
|
23268
|
+
capName: "scene-monitor",
|
|
23269
|
+
capScope: "device",
|
|
23270
|
+
addonId: null,
|
|
23271
|
+
access: "delete"
|
|
23272
|
+
},
|
|
23273
|
+
"sceneMonitor.deleteScene": {
|
|
23274
|
+
capName: "scene-monitor",
|
|
23275
|
+
capScope: "device",
|
|
23276
|
+
addonId: null,
|
|
23277
|
+
access: "delete"
|
|
23278
|
+
},
|
|
23279
|
+
"sceneMonitor.listScenes": {
|
|
23280
|
+
capName: "scene-monitor",
|
|
23281
|
+
capScope: "device",
|
|
23282
|
+
addonId: null,
|
|
23283
|
+
access: "view"
|
|
23284
|
+
},
|
|
23285
|
+
"sceneMonitor.recheckNow": {
|
|
23286
|
+
capName: "scene-monitor",
|
|
23287
|
+
capScope: "device",
|
|
23288
|
+
addonId: null,
|
|
23289
|
+
access: "create"
|
|
23290
|
+
},
|
|
23291
|
+
"sceneMonitor.updateScene": {
|
|
23292
|
+
capName: "scene-monitor",
|
|
23293
|
+
capScope: "device",
|
|
23294
|
+
addonId: null,
|
|
23295
|
+
access: "create"
|
|
23296
|
+
},
|
|
22266
23297
|
"scriptRunner.run": {
|
|
22267
23298
|
capName: "script-runner",
|
|
22268
23299
|
capScope: "device",
|
|
@@ -23347,6 +24378,7 @@ Object.freeze({
|
|
|
23347
24378
|
Object.freeze({
|
|
23348
24379
|
"broker": "broker",
|
|
23349
24380
|
"device-export": "device-export",
|
|
24381
|
+
"llm": "ai",
|
|
23350
24382
|
"mesh-network": "mesh",
|
|
23351
24383
|
"mqtt-broker": "broker",
|
|
23352
24384
|
"network-access": "ingress",
|
|
@@ -23533,9 +24565,9 @@ async function resolveDecoderBackend(api, nodeId, logger) {
|
|
|
23533
24565
|
//#region src/shared/notifying-ring-buffer.ts
|
|
23534
24566
|
/**
|
|
23535
24567
|
* A {@link RingBuffer} that can notify a single blocked consumer the moment an
|
|
23536
|
-
* item commits — the server side of the `pullFrames
|
|
24568
|
+
* item commits — the server side of the `pullFrames` long-poll.
|
|
23537
24569
|
*
|
|
23538
|
-
* The decoder's `onFrame
|
|
24570
|
+
* The decoder's `onFrame` callback drives `push()`; a consumer
|
|
23539
24571
|
* that finds the ring momentarily empty calls {@link waitForItem} and is woken
|
|
23540
24572
|
* on the very next `push()` (a real commit signal) rather than spinning at
|
|
23541
24573
|
* ~1kHz on `setTimeout(…,1)`. If nothing commits it resolves after `waitMs`,
|
|
@@ -23979,8 +25011,7 @@ var FrameStreamSplitter = class {
|
|
|
23979
25011
|
//#region src/ffmpeg-decoder-session.ts
|
|
23980
25012
|
/**
|
|
23981
25013
|
* FfmpegDecoderSession — an `IDecoderSession` that decodes video in an **ffmpeg
|
|
23982
|
-
* subprocess** and
|
|
23983
|
-
* ring `decoder-nodeav` writes into.
|
|
25014
|
+
* subprocess** and delivers each decoded frame via the `onFrame` callback.
|
|
23984
25015
|
*
|
|
23985
25016
|
* Why a subprocess: on the Intel hub, node-av's in-process VA-API decode path
|
|
23986
25017
|
* SIGBUSes (uncatchable) on 8MP h264 and kills the whole decoder runner. ffmpeg
|
|
@@ -23993,15 +25024,13 @@ var FrameStreamSplitter = class {
|
|
|
23993
25024
|
* ffmpeg → `-vf <scale_vaapi=…,hwdownload,… | hwdownload,…,scale=…>` (GPU or
|
|
23994
25025
|
* CPU scale, per {@link buildFfmpegDecodeArgs})
|
|
23995
25026
|
* raw video → ffmpeg stdout (pipe:1) → a {@link FrameStreamSplitter} that fills
|
|
23996
|
-
* each frame's bytes
|
|
23997
|
-
* `
|
|
23998
|
-
*
|
|
23999
|
-
* No `Buffer.concat` accumulator, one copy per byte, none for dropped
|
|
24000
|
-
* frames.
|
|
25027
|
+
* each frame's bytes into a per-frame buffer → `DecodedFrame` via
|
|
25028
|
+
* `onFrame`. No `Buffer.concat` accumulator, one copy per byte, none
|
|
25029
|
+
* for dropped frames.
|
|
24001
25030
|
*
|
|
24002
|
-
* Output geometry: the broker does not pass source width/height
|
|
24003
|
-
*
|
|
24004
|
-
*
|
|
25031
|
+
* Output geometry: the broker does not pass source width/height, so ffmpeg picks
|
|
25032
|
+
* the height (`scale=min(iw\,maxW):-2`) and the session learns the exact output
|
|
25033
|
+
* W×H by parsing the muxer's `Output #0` stderr block
|
|
24005
25034
|
* ({@link parseFfmpegOutputDims}). Until dims are known, stdout is buffered.
|
|
24006
25035
|
*/
|
|
24007
25036
|
var noopLogger = {
|
|
@@ -24041,8 +25070,6 @@ var MAX_GOP_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
|
24041
25070
|
var FfmpegDecoderSession = class {
|
|
24042
25071
|
config;
|
|
24043
25072
|
logger;
|
|
24044
|
-
frameSink;
|
|
24045
|
-
nodeId;
|
|
24046
25073
|
renderDevice;
|
|
24047
25074
|
ffmpegPath;
|
|
24048
25075
|
/**
|
|
@@ -24092,9 +25119,6 @@ var FfmpegDecoderSession = class {
|
|
|
24092
25119
|
/** Stdout chunks received BEFORE geometry is known — replayed into the
|
|
24093
25120
|
* splitter once `frameSize` is learned. A short-lived list (no concat). */
|
|
24094
25121
|
pendingStdout = [];
|
|
24095
|
-
/** The shm ring slot the splitter is currently filling, if any — aborted on
|
|
24096
|
-
* respawn / teardown so a half-filled slot never publishes. */
|
|
24097
|
-
openSlot = null;
|
|
24098
25122
|
stderrAccum = "";
|
|
24099
25123
|
destroyed = false;
|
|
24100
25124
|
pixel;
|
|
@@ -24104,9 +25128,7 @@ var FfmpegDecoderSession = class {
|
|
|
24104
25128
|
outWidth = 0;
|
|
24105
25129
|
outHeight = 0;
|
|
24106
25130
|
frameSize = 0;
|
|
24107
|
-
frameRingSink = null;
|
|
24108
25131
|
frameCallbacks = /* @__PURE__ */ new Set();
|
|
24109
|
-
handleCallbacks = /* @__PURE__ */ new Set();
|
|
24110
25132
|
frameDropper;
|
|
24111
25133
|
inputPackets = 0;
|
|
24112
25134
|
/**
|
|
@@ -24130,8 +25152,6 @@ var FfmpegDecoderSession = class {
|
|
|
24130
25152
|
if (typeof config.deviceId === "number") sessionTags["deviceId"] = config.deviceId;
|
|
24131
25153
|
if (typeof config.tag === "string" && config.tag.length > 0) sessionTags["tag"] = config.tag;
|
|
24132
25154
|
this.logger = Object.keys(sessionTags).length > 0 ? logger.withTags(sessionTags) : logger;
|
|
24133
|
-
this.frameSink = options?.frameSink ?? "callback";
|
|
24134
|
-
this.nodeId = options?.nodeId ?? "local";
|
|
24135
25155
|
this.renderDevice = options?.renderDevice;
|
|
24136
25156
|
this.ffmpegPath = options?.ffmpegPath ?? "ffmpeg";
|
|
24137
25157
|
const seededChain = options?.hwaccelChain ?? (options?.hwaccel != null && !isSoftwareHwAccel(options.hwaccel) ? [options.hwaccel] : []);
|
|
@@ -24144,10 +25164,6 @@ var FfmpegDecoderSession = class {
|
|
|
24144
25164
|
this.frameFormat = this.pixel === "gray" ? "gray" : "rgb";
|
|
24145
25165
|
this.frameDropper = new FrameDropper(config.maxFps);
|
|
24146
25166
|
}
|
|
24147
|
-
/** Exposed so the owning addon can surface ring stats via `getShmStats`. */
|
|
24148
|
-
get frameRingSinkOrNull() {
|
|
24149
|
-
return this.frameRingSink;
|
|
24150
|
-
}
|
|
24151
25167
|
get isPullMode() {
|
|
24152
25168
|
return this.pullUrl !== null;
|
|
24153
25169
|
}
|
|
@@ -24169,26 +25185,11 @@ var FfmpegDecoderSession = class {
|
|
|
24169
25185
|
this.logger.info("ffmpeg decoder: pull-mode openStream — dialing input URL", { meta: { url } });
|
|
24170
25186
|
this.spawnFfmpeg();
|
|
24171
25187
|
}
|
|
24172
|
-
ensureFrameRingSink() {
|
|
24173
|
-
if (this.frameRingSink === null) {
|
|
24174
|
-
const seedParts = [];
|
|
24175
|
-
if (typeof this.config.deviceId === "number") seedParts.push(String(this.config.deviceId));
|
|
24176
|
-
if (typeof this.config.tag === "string" && this.config.tag.length > 0) seedParts.push(this.config.tag);
|
|
24177
|
-
const seed = seedParts.length > 0 ? seedParts.join(":") : "anon";
|
|
24178
|
-
this.frameRingSink = new DecoderFrameRingSink({
|
|
24179
|
-
seed,
|
|
24180
|
-
logger: this.logger,
|
|
24181
|
-
nodeId: this.nodeId
|
|
24182
|
-
});
|
|
24183
|
-
}
|
|
24184
|
-
return this.frameRingSink;
|
|
24185
|
-
}
|
|
24186
25188
|
spawnFfmpeg() {
|
|
24187
25189
|
if (this.destroyed) return;
|
|
24188
25190
|
this.spawnAttempted = true;
|
|
24189
25191
|
this.clearPullRedial();
|
|
24190
25192
|
this.killFfmpeg();
|
|
24191
|
-
this.abortOpenSlot();
|
|
24192
25193
|
this.splitter = null;
|
|
24193
25194
|
this.pendingStdout = [];
|
|
24194
25195
|
this.stderrAccum = "";
|
|
@@ -24234,8 +25235,7 @@ var FfmpegDecoderSession = class {
|
|
|
24234
25235
|
hwaccel: this.hwaccel,
|
|
24235
25236
|
gpuScale: this.activeGpuScaleFilter ?? "cpu",
|
|
24236
25237
|
codec: this.config.codec,
|
|
24237
|
-
format: this.frameFormat
|
|
24238
|
-
sink: this.frameSink
|
|
25238
|
+
format: this.frameFormat
|
|
24239
25239
|
} });
|
|
24240
25240
|
}
|
|
24241
25241
|
killFfmpeg() {
|
|
@@ -24373,36 +25373,10 @@ var FfmpegDecoderSession = class {
|
|
|
24373
25373
|
return {
|
|
24374
25374
|
startFrame: () => {
|
|
24375
25375
|
if (!this.frameDropper.shouldKeep()) return null;
|
|
24376
|
-
if (this.frameSink === "shm") {
|
|
24377
|
-
const reserved = this.ensureFrameRingSink().beginFrame(this.outWidth, this.outHeight, this.frameFormat);
|
|
24378
|
-
if (reserved === null) return null;
|
|
24379
|
-
this.openSlot = reserved.slot;
|
|
24380
|
-
return reserved.buffer;
|
|
24381
|
-
}
|
|
24382
25376
|
return Buffer.allocUnsafe(this.frameSize);
|
|
24383
25377
|
},
|
|
24384
25378
|
finishFrame: (target) => {
|
|
24385
25379
|
this.outputFrames++;
|
|
24386
|
-
if (this.frameSink === "shm") {
|
|
24387
|
-
const slot = this.openSlot;
|
|
24388
|
-
this.openSlot = null;
|
|
24389
|
-
const sink = this.frameRingSink;
|
|
24390
|
-
if (slot === null || sink === null) return;
|
|
24391
|
-
const handle = sink.commitFrame(slot, {
|
|
24392
|
-
width: this.outWidth,
|
|
24393
|
-
height: this.outHeight,
|
|
24394
|
-
format: this.frameFormat,
|
|
24395
|
-
pts: performance.now(),
|
|
24396
|
-
byteLength: this.frameSize
|
|
24397
|
-
});
|
|
24398
|
-
if (handle === null) return;
|
|
24399
|
-
const delivered = {
|
|
24400
|
-
handle,
|
|
24401
|
-
timestamp: Date.now()
|
|
24402
|
-
};
|
|
24403
|
-
for (const cb of this.handleCallbacks) cb(delivered);
|
|
24404
|
-
return;
|
|
24405
|
-
}
|
|
24406
25380
|
const frame = {
|
|
24407
25381
|
data: target,
|
|
24408
25382
|
width: this.outWidth,
|
|
@@ -24417,14 +25391,6 @@ var FfmpegDecoderSession = class {
|
|
|
24417
25391
|
}
|
|
24418
25392
|
};
|
|
24419
25393
|
}
|
|
24420
|
-
/** Abandon a shm slot the splitter left open (respawn / teardown mid-frame),
|
|
24421
|
-
* so a half-filled slot is never published. */
|
|
24422
|
-
abortOpenSlot() {
|
|
24423
|
-
const slot = this.openSlot;
|
|
24424
|
-
if (slot === null) return;
|
|
24425
|
-
this.openSlot = null;
|
|
24426
|
-
this.frameRingSink?.abortFrame(slot);
|
|
24427
|
-
}
|
|
24428
25394
|
pushPacket(packet) {
|
|
24429
25395
|
if (this.destroyed) return;
|
|
24430
25396
|
if (this.pullUrl !== null) return;
|
|
@@ -24478,13 +25444,6 @@ var FfmpegDecoderSession = class {
|
|
|
24478
25444
|
this.frameCallbacks.delete(callback);
|
|
24479
25445
|
};
|
|
24480
25446
|
}
|
|
24481
|
-
/** Subscribe to shared-memory frame handles — fires only in `'shm'` mode. */
|
|
24482
|
-
onFrameHandle(callback) {
|
|
24483
|
-
this.handleCallbacks.add(callback);
|
|
24484
|
-
return () => {
|
|
24485
|
-
this.handleCallbacks.delete(callback);
|
|
24486
|
-
};
|
|
24487
|
-
}
|
|
24488
25447
|
updateConfig(update) {
|
|
24489
25448
|
const needsRestart = update.scale !== void 0 && update.scale !== this.config.scale || update.outputFormat !== void 0 && update.outputFormat !== this.config.outputFormat || update.codec !== void 0 && update.codec !== this.config.codec;
|
|
24490
25449
|
this.config = {
|
|
@@ -24499,12 +25458,8 @@ var FfmpegDecoderSession = class {
|
|
|
24499
25458
|
this.destroyed = true;
|
|
24500
25459
|
this.clearPullRedial();
|
|
24501
25460
|
this.killFfmpeg();
|
|
24502
|
-
this.abortOpenSlot();
|
|
24503
25461
|
this.splitter = null;
|
|
24504
|
-
this.frameRingSink?.destroy();
|
|
24505
|
-
this.frameRingSink = null;
|
|
24506
25462
|
this.frameCallbacks.clear();
|
|
24507
|
-
this.handleCallbacks.clear();
|
|
24508
25463
|
}
|
|
24509
25464
|
getStats() {
|
|
24510
25465
|
const uptimeSec = Math.max((Date.now() - this.startTime) / 1e3, 1);
|
|
@@ -24575,71 +25530,6 @@ function probeGpuScaleFilters(ffmpegPath, logger) {
|
|
|
24575
25530
|
});
|
|
24576
25531
|
}
|
|
24577
25532
|
//#endregion
|
|
24578
|
-
//#region src/shm-orphan-purge.ts
|
|
24579
|
-
/**
|
|
24580
|
-
* Startup reclamation of orphaned shared-memory segments.
|
|
24581
|
-
*
|
|
24582
|
-
* A decoder writes frames into named `/dev/shm` segments and unlinks each one
|
|
24583
|
-
* on graceful session teardown (`DecoderFrameRingSink.destroy`). When the
|
|
24584
|
-
* decoder process dies *ungracefully* — SIGBUS, OOM-kill, or a SIGKILL during
|
|
24585
|
-
* redeploy — that teardown never runs and the segment is orphaned: it stays in
|
|
24586
|
-
* the tmpfs forever, since nothing else knows its name. Across many
|
|
24587
|
-
* crashes/redeploys these accumulate until `/dev/shm` fills, at which point the
|
|
24588
|
-
* next `mmap` write faults with an uncatchable SIGBUS and the decoder
|
|
24589
|
-
* crash-loops into its circuit breaker (which is exactly the incident this
|
|
24590
|
-
* guards against).
|
|
24591
|
-
*
|
|
24592
|
-
* The reclamation is safe *at process startup*: a freshly-booting decoder owns
|
|
24593
|
-
* no live sessions, so every pre-existing segment with its prefix is by
|
|
24594
|
-
* definition an orphan from a dead instance. `shm_unlink` only removes the
|
|
24595
|
-
* name — any consumer still holding a mapping keeps reading valid memory until
|
|
24596
|
-
* it closes (POSIX deferred reclaim + the ring seqlock), so unlinking is safe
|
|
24597
|
-
* even if a stale reader is momentarily still attached.
|
|
24598
|
-
*
|
|
24599
|
-
* POSIX-only: segments surface as files under `/dev/shm` on Linux. On platforms
|
|
24600
|
-
* without that directory (Windows, macOS) the scan finds nothing — no-op.
|
|
24601
|
-
*
|
|
24602
|
-
* NOTE: this lives inside the decoder addon (not `@camstack/shm-ring`) so it
|
|
24603
|
-
* ships in the self-contained addon bundle via `camstack deploy`, reusing the
|
|
24604
|
-
* already-deployed `unlinkSegment`; no host base-image rebuild required.
|
|
24605
|
-
*/
|
|
24606
|
-
/** Default tmpfs directory where POSIX shared-memory segments appear on Linux. */
|
|
24607
|
-
var DEFAULT_SHM_DIR = "/dev/shm";
|
|
24608
|
-
/**
|
|
24609
|
-
* Unlink every shared-memory segment whose name starts with `prefix`.
|
|
24610
|
-
*
|
|
24611
|
-
* Intended to run ONCE at decoder startup, before any session is created, to
|
|
24612
|
-
* reclaim segments orphaned by a previously-crashed instance. A per-file unlink
|
|
24613
|
-
* failure is swallowed so one stuck segment cannot block reclaiming the rest.
|
|
24614
|
-
*/
|
|
24615
|
-
function purgeOrphanSegments(prefix, options = {}) {
|
|
24616
|
-
const dir = options.dir ?? DEFAULT_SHM_DIR;
|
|
24617
|
-
const unlink = options.unlink ?? unlinkSegment;
|
|
24618
|
-
let entries;
|
|
24619
|
-
try {
|
|
24620
|
-
entries = readdirSync(dir);
|
|
24621
|
-
} catch {
|
|
24622
|
-
return {
|
|
24623
|
-
scanned: 0,
|
|
24624
|
-
removed: 0,
|
|
24625
|
-
names: []
|
|
24626
|
-
};
|
|
24627
|
-
}
|
|
24628
|
-
const names = [];
|
|
24629
|
-
for (const name of entries) {
|
|
24630
|
-
if (!name.startsWith(prefix)) continue;
|
|
24631
|
-
try {
|
|
24632
|
-
unlink(name);
|
|
24633
|
-
names.push(name);
|
|
24634
|
-
} catch {}
|
|
24635
|
-
}
|
|
24636
|
-
return {
|
|
24637
|
-
scanned: entries.length,
|
|
24638
|
-
removed: names.length,
|
|
24639
|
-
names
|
|
24640
|
-
};
|
|
24641
|
-
}
|
|
24642
|
-
//#endregion
|
|
24643
25533
|
//#region src/audio-codec/ffmpeg-audio-process.ts
|
|
24644
25534
|
/**
|
|
24645
25535
|
* Minimal ffmpeg subprocess wrapper shared by the audio decode + encode
|
|
@@ -25808,12 +26698,8 @@ var DEFAULT_DECODER_FFMPEG_ADDON_CONFIG = { ...DEFAULT_DECODER_HWACCEL_CONFIG };
|
|
|
25808
26698
|
var DecoderFfmpegAddon = class extends BaseAddon {
|
|
25809
26699
|
sessions = /* @__PURE__ */ new Map();
|
|
25810
26700
|
frameBuffers = /* @__PURE__ */ new Map();
|
|
25811
|
-
handleBuffers = /* @__PURE__ */ new Map();
|
|
25812
26701
|
unsubscribers = /* @__PURE__ */ new Map();
|
|
25813
26702
|
sessionMeta = /* @__PURE__ */ new Map();
|
|
25814
|
-
frameReaders = null;
|
|
25815
|
-
getFrameHits = 0;
|
|
25816
|
-
getFrameMisses = 0;
|
|
25817
26703
|
/**
|
|
25818
26704
|
* The ffmpeg binary every session spawns — resolved once at init via the
|
|
25819
26705
|
* node-level `ctx.deps.ensureFfmpeg()`. The SAME binary the broker / recorder
|
|
@@ -25896,12 +26782,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
25896
26782
|
return registrations;
|
|
25897
26783
|
}
|
|
25898
26784
|
this.ctx.logger.info("ffmpeg decoder addon initialized", { meta: { selectedBackend: backend } });
|
|
25899
|
-
const purged = purgeOrphanSegments(SEGMENT_NAME_PREFIX);
|
|
25900
|
-
if (purged.removed > 0) this.ctx.logger.warn("ffmpeg decoder: reclaimed orphaned shm segments at startup", { meta: {
|
|
25901
|
-
removed: purged.removed,
|
|
25902
|
-
scanned: purged.scanned
|
|
25903
|
-
} });
|
|
25904
|
-
this.frameReaders = new FrameRingReaderCache(this.ctx.logger);
|
|
25905
26785
|
this.probedGpuScaleFilters = await probeGpuScaleFilters(this.ffmpegPath, this.ctx.logger);
|
|
25906
26786
|
this.ctx.logger.info("decoder-ffmpeg: probed GPU scale filters", { meta: {
|
|
25907
26787
|
filters: [...this.probedGpuScaleFilters],
|
|
@@ -26043,15 +26923,12 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26043
26923
|
}
|
|
26044
26924
|
async createSession(config) {
|
|
26045
26925
|
const sessionId = randomUUID();
|
|
26046
|
-
const { frameSink } = config;
|
|
26047
26926
|
const nodeId = this.resolveLocalNodeId();
|
|
26048
26927
|
const hwaccelChain = await this.resolveDecodeHwaccelChain();
|
|
26049
26928
|
const availableGpuScaleFilters = new Set([...this.probedGpuScaleFilters].filter((f) => !this.unsupportedGpuScaleFilters.has(f)));
|
|
26050
26929
|
const session = new FfmpegDecoderSession(config, this.ctx.logger, {
|
|
26051
26930
|
hwaccelChain,
|
|
26052
26931
|
ffmpegPath: this.ffmpegPath,
|
|
26053
|
-
frameSink,
|
|
26054
|
-
nodeId,
|
|
26055
26932
|
availableGpuScaleFilters,
|
|
26056
26933
|
onGpuScaleUnsupported: (filter) => {
|
|
26057
26934
|
if (this.unsupportedGpuScaleFilters.has(filter)) return;
|
|
@@ -26059,7 +26936,7 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26059
26936
|
this.ctx.logger.warn("decoder-ffmpeg: GPU scaler non-functional on this build — future sessions use CPU scale", { meta: { filter } });
|
|
26060
26937
|
}
|
|
26061
26938
|
});
|
|
26062
|
-
const unsub =
|
|
26939
|
+
const unsub = this.wireCallbackSink(sessionId, session);
|
|
26063
26940
|
this.sessions.set(sessionId, session);
|
|
26064
26941
|
this.unsubscribers.set(sessionId, unsub);
|
|
26065
26942
|
this.sessionMeta.set(sessionId, {
|
|
@@ -26074,7 +26951,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26074
26951
|
sessionId,
|
|
26075
26952
|
codec: config.codec,
|
|
26076
26953
|
hwaccelChain,
|
|
26077
|
-
frameSink,
|
|
26078
26954
|
nodeId
|
|
26079
26955
|
}
|
|
26080
26956
|
});
|
|
@@ -26101,13 +26977,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26101
26977
|
ringBuffer.push(capFrame);
|
|
26102
26978
|
});
|
|
26103
26979
|
}
|
|
26104
|
-
wireShmSink(sessionId, session) {
|
|
26105
|
-
const handleRing = new NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
26106
|
-
this.handleBuffers.set(sessionId, handleRing);
|
|
26107
|
-
return session.onFrameHandle((frame) => {
|
|
26108
|
-
handleRing.push(frame.handle);
|
|
26109
|
-
});
|
|
26110
|
-
}
|
|
26111
26980
|
async destroySession(input) {
|
|
26112
26981
|
const { sessionId } = input;
|
|
26113
26982
|
const session = this.sessions.get(sessionId);
|
|
@@ -26115,12 +26984,10 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26115
26984
|
const unsub = this.unsubscribers.get(sessionId);
|
|
26116
26985
|
if (unsub) unsub();
|
|
26117
26986
|
this.frameBuffers.get(sessionId)?.cancel();
|
|
26118
|
-
this.handleBuffers.get(sessionId)?.cancel();
|
|
26119
26987
|
const deviceId = this.sessionMeta.get(sessionId)?.deviceId;
|
|
26120
26988
|
await session.destroy();
|
|
26121
26989
|
this.sessions.delete(sessionId);
|
|
26122
26990
|
this.frameBuffers.delete(sessionId);
|
|
26123
|
-
this.handleBuffers.delete(sessionId);
|
|
26124
26991
|
this.unsubscribers.delete(sessionId);
|
|
26125
26992
|
this.sessionMeta.delete(sessionId);
|
|
26126
26993
|
this.ctx.logger.info("ffmpeg: destroyed session", {
|
|
@@ -26160,12 +27027,14 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26160
27027
|
if (ringBuffer.size === 0 && input.waitMs) await ringBuffer.waitForItem(input.waitMs);
|
|
26161
27028
|
return ringBuffer.drain(input.maxCount);
|
|
26162
27029
|
}
|
|
26163
|
-
|
|
26164
|
-
|
|
26165
|
-
|
|
26166
|
-
|
|
26167
|
-
|
|
26168
|
-
|
|
27030
|
+
/**
|
|
27031
|
+
* DORMANT STUB. The shm `FrameHandle` frame ring was removed (the runner ships
|
|
27032
|
+
* inline pixels via `pullFrames` now). The cap method stays defined for wire
|
|
27033
|
+
* compat until the framework train drops it, but no session ever produces
|
|
27034
|
+
* handles — always empty.
|
|
27035
|
+
*/
|
|
27036
|
+
async pullHandles(_input) {
|
|
27037
|
+
return [];
|
|
26169
27038
|
}
|
|
26170
27039
|
async updateConfig(input) {
|
|
26171
27040
|
const session = this.sessions.get(input.sessionId);
|
|
@@ -26177,33 +27046,20 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26177
27046
|
if (!session) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|
|
26178
27047
|
return session.getStats();
|
|
26179
27048
|
}
|
|
26180
|
-
|
|
26181
|
-
|
|
26182
|
-
|
|
26183
|
-
|
|
26184
|
-
|
|
26185
|
-
|
|
26186
|
-
|
|
26187
|
-
const arrayBuf = new ArrayBuffer(frame.data.byteLength);
|
|
26188
|
-
new Uint8Array(arrayBuf).set(frame.data);
|
|
26189
|
-
return {
|
|
26190
|
-
data: new Uint8Array(arrayBuf),
|
|
26191
|
-
width: frame.width,
|
|
26192
|
-
height: frame.height,
|
|
26193
|
-
format: frame.format,
|
|
26194
|
-
timestamp: frame.timestamp
|
|
26195
|
-
};
|
|
27049
|
+
/**
|
|
27050
|
+
* DORMANT STUB. Frames are no longer retained in a shm ring this addon can
|
|
27051
|
+
* read back by handle — the decode-owning node serves pixels over its own cap
|
|
27052
|
+
* surface. Kept defined for wire compat; always resolves `null`.
|
|
27053
|
+
*/
|
|
27054
|
+
async getFrame(_input) {
|
|
27055
|
+
return null;
|
|
26196
27056
|
}
|
|
26197
|
-
|
|
26198
|
-
|
|
26199
|
-
|
|
26200
|
-
|
|
26201
|
-
|
|
26202
|
-
|
|
26203
|
-
budgetMb: RING_BUDGET_MB,
|
|
26204
|
-
getFrameHits: this.getFrameHits,
|
|
26205
|
-
getFrameMisses: this.getFrameMisses
|
|
26206
|
-
};
|
|
27057
|
+
/**
|
|
27058
|
+
* DORMANT STUB. No session writes a shm frame ring anymore, so there are no
|
|
27059
|
+
* ring stats to report. Kept defined for wire compat; always `null`.
|
|
27060
|
+
*/
|
|
27061
|
+
async getShmStats(_input) {
|
|
27062
|
+
return null;
|
|
26207
27063
|
}
|
|
26208
27064
|
async onShutdown() {
|
|
26209
27065
|
this.ctx.logger.info("ffmpeg decoder addon shutdown — destroying all sessions");
|
|
@@ -26218,11 +27074,8 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26218
27074
|
await Promise.all(destroyPromises);
|
|
26219
27075
|
this.sessions.clear();
|
|
26220
27076
|
this.frameBuffers.clear();
|
|
26221
|
-
this.handleBuffers.clear();
|
|
26222
27077
|
this.unsubscribers.clear();
|
|
26223
27078
|
this.sessionMeta.clear();
|
|
26224
|
-
this.frameReaders?.close();
|
|
26225
|
-
this.frameReaders = null;
|
|
26226
27079
|
}
|
|
26227
27080
|
};
|
|
26228
27081
|
//#endregion
|