@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.js
CHANGED
|
@@ -3,9 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
let node_crypto = require("node:crypto");
|
|
6
|
-
let _camstack_shm_ring = require("@camstack/shm-ring");
|
|
7
6
|
let node_child_process = require("node:child_process");
|
|
8
|
-
let node_fs = require("node:fs");
|
|
9
7
|
//#region ../../node_modules/zod/v4/core/core.js
|
|
10
8
|
var _a$1;
|
|
11
9
|
function $constructor(name, initializer, params) {
|
|
@@ -4294,6 +4292,14 @@ function object(shape, params) {
|
|
|
4294
4292
|
...normalizeParams(params)
|
|
4295
4293
|
});
|
|
4296
4294
|
}
|
|
4295
|
+
function looseObject(shape, params) {
|
|
4296
|
+
return new ZodObject({
|
|
4297
|
+
type: "object",
|
|
4298
|
+
shape,
|
|
4299
|
+
catchall: unknown(),
|
|
4300
|
+
...normalizeParams(params)
|
|
4301
|
+
});
|
|
4302
|
+
}
|
|
4297
4303
|
var ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
|
|
4298
4304
|
$ZodUnion.init(inst, def);
|
|
4299
4305
|
ZodType.init(inst, def);
|
|
@@ -4635,7 +4641,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4635
4641
|
return inst;
|
|
4636
4642
|
}
|
|
4637
4643
|
//#endregion
|
|
4638
|
-
//#region ../types/dist/sleep-
|
|
4644
|
+
//#region ../types/dist/sleep-_sv7WKkq.mjs
|
|
4639
4645
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4640
4646
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4641
4647
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5045,10 +5051,51 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
5045
5051
|
EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
|
|
5046
5052
|
EventCategory["EnrichmentSceneStateChanged"] = "enrichment.scene.state-changed";
|
|
5047
5053
|
EventCategory["EnrichmentActivitySummary"] = "enrichment.activity.summary";
|
|
5054
|
+
/**
|
|
5055
|
+
* Unified track-lifecycle event: ONE category carrying `phase:
|
|
5056
|
+
* 'start' | 'update' | 'end'` with a rich, typed
|
|
5057
|
+
* `PipelineAnalyticsTrackLifecyclePayload`. Supersedes the thin
|
|
5058
|
+
* `PipelineAnalyticsTrackStarted` / `PipelineAnalyticsTrackEnded`
|
|
5059
|
+
* pair — a consumer subscribes once and branches on `phase`.
|
|
5060
|
+
*/
|
|
5061
|
+
EventCategory["PipelineAnalyticsTrackLifecycle"] = "pipeline-analytics.track-lifecycle";
|
|
5062
|
+
/**
|
|
5063
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5064
|
+
* (`phase:'start'`). Kept as a soft alias — no known subscribers.
|
|
5065
|
+
*/
|
|
5048
5066
|
EventCategory["PipelineAnalyticsTrackStarted"] = "pipeline-analytics.track-started";
|
|
5067
|
+
/**
|
|
5068
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5069
|
+
* (`phase:'end'`). Kept as a soft alias — no known subscribers.
|
|
5070
|
+
*/
|
|
5049
5071
|
EventCategory["PipelineAnalyticsTrackEnded"] = "pipeline-analytics.track-ended";
|
|
5050
5072
|
EventCategory["PipelineAnalyticsDetectionEvent"] = "pipeline-analytics.detection-event";
|
|
5051
5073
|
EventCategory["PipelineAnalyticsFrameTracked"] = "pipeline-analytics.frame-tracked";
|
|
5074
|
+
/**
|
|
5075
|
+
* Fired by `addon-post-analysis` when a parked (stationary) object appears
|
|
5076
|
+
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
5077
|
+
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
5078
|
+
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
5079
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
5080
|
+
*/
|
|
5081
|
+
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
5082
|
+
/**
|
|
5083
|
+
* Fired by `addon-post-analysis` whenever a gallery face row changes:
|
|
5084
|
+
* `kind:'buffered'` a new detected face was persisted, `'assigned'` /
|
|
5085
|
+
* `'unassigned'` its identity link changed, `'deleted'` the row was
|
|
5086
|
+
* removed. Telemetry semantics (D8): a lost event only delays a refresh,
|
|
5087
|
+
* never a correctness issue. Payload `PipelineAnalyticsFaceGalleryChangedPayload`.
|
|
5088
|
+
*/
|
|
5089
|
+
EventCategory["PipelineAnalyticsFaceGalleryChanged"] = "pipeline-analytics.face-gallery-changed";
|
|
5090
|
+
/**
|
|
5091
|
+
* Fired by `addon-post-analysis` whenever a gallery plate row changes:
|
|
5092
|
+
* `kind:'buffered'` a new read was persisted (`PlateRecognizer.onTrackEnd`),
|
|
5093
|
+
* `'assigned'` / `'unassigned'` its vehicle link changed
|
|
5094
|
+
* (`PlateGalleryProvider`), `'deleted'` the row was removed. Telemetry
|
|
5095
|
+
* semantics (D8): a lost event only delays a refresh, never a correctness
|
|
5096
|
+
* issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
|
|
5097
|
+
*/
|
|
5098
|
+
EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
|
|
5052
5099
|
EventCategory["FrigateLiveEvent"] = "frigate.live-event";
|
|
5053
5100
|
EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
|
|
5054
5101
|
/**
|
|
@@ -6889,7 +6936,8 @@ var MODEL_FORMATS = [
|
|
|
6889
6936
|
"coreml",
|
|
6890
6937
|
"openvino",
|
|
6891
6938
|
"tflite",
|
|
6892
|
-
"pt"
|
|
6939
|
+
"pt",
|
|
6940
|
+
"gguf"
|
|
6893
6941
|
];
|
|
6894
6942
|
/**
|
|
6895
6943
|
* Multi-file format payload.
|
|
@@ -6933,7 +6981,8 @@ var ModelFormatsSchema = object({
|
|
|
6933
6981
|
coreml: ModelFormatEntrySchema.optional(),
|
|
6934
6982
|
openvino: ModelFormatEntrySchema.optional(),
|
|
6935
6983
|
tflite: ModelFormatEntrySchema.optional(),
|
|
6936
|
-
pt: ModelFormatEntrySchema.optional()
|
|
6984
|
+
pt: ModelFormatEntrySchema.optional(),
|
|
6985
|
+
gguf: ModelFormatEntrySchema.optional()
|
|
6937
6986
|
});
|
|
6938
6987
|
/**
|
|
6939
6988
|
* Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
|
|
@@ -6974,6 +7023,15 @@ var ModelCatalogEntrySchema = object({
|
|
|
6974
7023
|
width: number(),
|
|
6975
7024
|
height: number()
|
|
6976
7025
|
}),
|
|
7026
|
+
/**
|
|
7027
|
+
* Channel count of the model input tensor. Omit ⇒ 3 (RGB), the default for
|
|
7028
|
+
* every detector / classifier / embedder. Set to 1 for a grayscale CTC text
|
|
7029
|
+
* recognizer (EasyOCR VGG plate-OCR: input `[N,1,H,W]`) so the preprocess
|
|
7030
|
+
* feeds a single-channel, EasyOCR-normalized tensor instead of the default
|
|
7031
|
+
* 3-channel RGB one. Threaded through `PoolModelConfig.inputChannels` to the
|
|
7032
|
+
* Python inference pool.
|
|
7033
|
+
*/
|
|
7034
|
+
inputChannels: number().int().positive().optional(),
|
|
6977
7035
|
labels: array(LabelDefinitionSchema).readonly(),
|
|
6978
7036
|
inputLayout: _enum(["nchw", "nhwc"]).optional(),
|
|
6979
7037
|
inputNormalization: _enum([
|
|
@@ -6983,6 +7041,16 @@ var ModelCatalogEntrySchema = object({
|
|
|
6983
7041
|
]).optional(),
|
|
6984
7042
|
preprocessMode: _enum(["letterbox", "resize"]).optional(),
|
|
6985
7043
|
/**
|
|
7044
|
+
* Per-MODEL postprocessor override. Absent ⇒ the step's own
|
|
7045
|
+
* `StepDefinition.postprocessor` applies (the normal case — every model in a
|
|
7046
|
+
* step shares its decode). Set it when a step hosts models with DIFFERENT raw
|
|
7047
|
+
* output layouts under one slot: e.g. object-detection is `'yolo'` by default,
|
|
7048
|
+
* but a Coral SSD MobileNet build emits the `TFLite_Detection_PostProcess`
|
|
7049
|
+
* 4-tensor layout and needs `'ssd'`. Threaded into `PoolModelConfig.postprocessor`
|
|
7050
|
+
* by the engine factory (`modelEntry.postprocessor ?? def.postprocessor`).
|
|
7051
|
+
*/
|
|
7052
|
+
postprocessor: custom().optional(),
|
|
7053
|
+
/**
|
|
6986
7054
|
* When true, the executor produces a landmark-aligned crop (similarity warp
|
|
6987
7055
|
* onto the canonical template) before this step runs, instead of a plain
|
|
6988
7056
|
* axis-aligned bbox crop. Required for face-recognition embedders (ArcFace):
|
|
@@ -10428,7 +10496,8 @@ var ModelFormatSchema$1 = _enum([
|
|
|
10428
10496
|
"coreml",
|
|
10429
10497
|
"openvino",
|
|
10430
10498
|
"tflite",
|
|
10431
|
-
"pt"
|
|
10499
|
+
"pt",
|
|
10500
|
+
"gguf"
|
|
10432
10501
|
]);
|
|
10433
10502
|
var PipelineSlotSchema = _enum([
|
|
10434
10503
|
"detector",
|
|
@@ -10522,7 +10591,8 @@ var EngineProvisioningSchema = object({
|
|
|
10522
10591
|
runtimeId: _enum([
|
|
10523
10592
|
"onnx",
|
|
10524
10593
|
"openvino",
|
|
10525
|
-
"coreml"
|
|
10594
|
+
"coreml",
|
|
10595
|
+
"edgetpu"
|
|
10526
10596
|
]).nullable(),
|
|
10527
10597
|
device: string().nullable(),
|
|
10528
10598
|
state: _enum([
|
|
@@ -10683,7 +10753,15 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
10683
10753
|
image: _instanceof(Uint8Array).optional(),
|
|
10684
10754
|
referenceImage: string().optional(),
|
|
10685
10755
|
deviceId: number().optional(),
|
|
10686
|
-
sessionId: string().optional()
|
|
10756
|
+
sessionId: string().optional(),
|
|
10757
|
+
/**
|
|
10758
|
+
* Execution plane. 'full' (default) runs the whole tree — benchmark,
|
|
10759
|
+
* reference-image, and detail-subtree calls. 'frame' is the live
|
|
10760
|
+
* per-frame dispatch: ONLY root-plane steps run; crop children
|
|
10761
|
+
* (inputClasses ≠ null) are skipped and served per-track via
|
|
10762
|
+
* pipelineRunner.runDetailSubtree (two-plane design).
|
|
10763
|
+
*/
|
|
10764
|
+
plane: _enum(["full", "frame"]).optional()
|
|
10687
10765
|
}), PipelineRunResultBridge, { kind: "mutation" }), method(object({
|
|
10688
10766
|
engine: PipelineEngineChoiceSchema.optional(),
|
|
10689
10767
|
steps: array(PipelineStepInputSchema).min(1),
|
|
@@ -10826,6 +10904,28 @@ var NativeCropResultSchema = object({
|
|
|
10826
10904
|
width: number().int().positive(),
|
|
10827
10905
|
height: number().int().positive()
|
|
10828
10906
|
});
|
|
10907
|
+
/** Parent detection context passed to `runDetailSubtree` — the crop's
|
|
10908
|
+
* originating detection, in FRAME-space coordinates. Reuses
|
|
10909
|
+
* `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
|
|
10910
|
+
* the coordinates are frame-space rather than getNativeCrop's
|
|
10911
|
+
* normalized [0,1] convention). */
|
|
10912
|
+
var DetailParentSchema = object({
|
|
10913
|
+
bbox: NativeCropBboxSchema,
|
|
10914
|
+
className: string()
|
|
10915
|
+
});
|
|
10916
|
+
/** One child-step result from `runDetailSubtree` — an embedding, label,
|
|
10917
|
+
* or refined detection produced by running the crop-subtree on a
|
|
10918
|
+
* single tracked detection. */
|
|
10919
|
+
var DetailResultSchema = object({
|
|
10920
|
+
stepId: string(),
|
|
10921
|
+
className: string(),
|
|
10922
|
+
score: number(),
|
|
10923
|
+
/** FRAME-space bbox (already mapped back from crop space). */
|
|
10924
|
+
bbox: NativeCropBboxSchema.optional(),
|
|
10925
|
+
embedding: string().optional(),
|
|
10926
|
+
label: string().optional(),
|
|
10927
|
+
alignedCropJpeg: string().optional()
|
|
10928
|
+
});
|
|
10829
10929
|
/**
|
|
10830
10930
|
* Per-camera tunable ranges + defaults. Single source of truth used
|
|
10831
10931
|
* by both the Zod data schema (validation + default fallback) and
|
|
@@ -11086,7 +11186,13 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
|
|
|
11086
11186
|
handle: FrameHandleSchema,
|
|
11087
11187
|
bbox: NativeCropBboxSchema,
|
|
11088
11188
|
maxWidth: number().int().positive().optional()
|
|
11089
|
-
}), NativeCropResultSchema.nullable())
|
|
11189
|
+
}), NativeCropResultSchema.nullable()), method(object({
|
|
11190
|
+
deviceId: number(),
|
|
11191
|
+
frameHandle: FrameHandleSchema.optional(),
|
|
11192
|
+
cropJpeg: string().optional(),
|
|
11193
|
+
parent: DetailParentSchema,
|
|
11194
|
+
steps: array(string()).optional()
|
|
11195
|
+
}), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" });
|
|
11090
11196
|
object({
|
|
11091
11197
|
detected: boolean(),
|
|
11092
11198
|
/** Ms epoch of the last detected-true observation. Null if never detected. */
|
|
@@ -11698,6 +11804,140 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
|
|
|
11698
11804
|
deviceId: number(),
|
|
11699
11805
|
status: PtzAutotrackStatusSchema
|
|
11700
11806
|
});
|
|
11807
|
+
/**
|
|
11808
|
+
* scene-monitor — device-scoped reference-region state cap. An operator marks
|
|
11809
|
+
* a rect ROI on a camera frame and names one or more states; the engine
|
|
11810
|
+
* (pipeline-analytics, designated post-processing node) reports which state is
|
|
11811
|
+
* active on an ongoing basis. Two operator-selectable check modes share every-
|
|
11812
|
+
* thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
|
|
11813
|
+
* vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
|
|
11814
|
+
*
|
|
11815
|
+
* D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) — the framework
|
|
11816
|
+
* derives the device-detail contribution; the provider carries NO hand-written
|
|
11817
|
+
* settings-contribution methods. `status.kind:'push'` — the engine pushes on
|
|
11818
|
+
* every hysteresis flip / availability change; consumers never poll.
|
|
11819
|
+
*/
|
|
11820
|
+
/** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
|
|
11821
|
+
* be added without a wire break (matching falls back to any-condition refs). */
|
|
11822
|
+
var SceneConditionSchema = string();
|
|
11823
|
+
/** One captured reference — condition-tagged, model-version-gated. `embedding`
|
|
11824
|
+
* is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
|
|
11825
|
+
var SceneReferenceSchema = object({
|
|
11826
|
+
embedding: array(number()),
|
|
11827
|
+
modelId: string(),
|
|
11828
|
+
condition: SceneConditionSchema,
|
|
11829
|
+
capturedAt: number(),
|
|
11830
|
+
thumbnailMediaId: string().optional()
|
|
11831
|
+
});
|
|
11832
|
+
var SceneMonitorStateSchema = object({
|
|
11833
|
+
id: string(),
|
|
11834
|
+
label: string(),
|
|
11835
|
+
references: array(SceneReferenceSchema),
|
|
11836
|
+
textPrompts: array(string()).optional(),
|
|
11837
|
+
referenceCount: number(),
|
|
11838
|
+
currentlyMatched: boolean()
|
|
11839
|
+
});
|
|
11840
|
+
/** Per-mode comparator params in a discriminated union so an `llm` scene never
|
|
11841
|
+
* carries similarity knobs and vice-versa. */
|
|
11842
|
+
var SceneCheckSchema = discriminatedUnion("mode", [object({
|
|
11843
|
+
mode: literal("similarity"),
|
|
11844
|
+
threshold: number().min(0).max(1),
|
|
11845
|
+
hysteresisCount: number().int().positive()
|
|
11846
|
+
}), object({
|
|
11847
|
+
mode: literal("llm"),
|
|
11848
|
+
prompt: string(),
|
|
11849
|
+
profileId: string().optional(),
|
|
11850
|
+
hysteresisCount: number().int().positive()
|
|
11851
|
+
})]);
|
|
11852
|
+
var SceneMonitorSchema = object({
|
|
11853
|
+
id: string(),
|
|
11854
|
+
label: string(),
|
|
11855
|
+
roi: MaskRectShapeSchema,
|
|
11856
|
+
enabled: boolean(),
|
|
11857
|
+
triggerMode: _enum([
|
|
11858
|
+
"periodic",
|
|
11859
|
+
"on-motion",
|
|
11860
|
+
"both"
|
|
11861
|
+
]),
|
|
11862
|
+
checkIntervalSec: number().optional(),
|
|
11863
|
+
check: SceneCheckSchema,
|
|
11864
|
+
states: array(SceneMonitorStateSchema),
|
|
11865
|
+
currentStateId: string().nullable(),
|
|
11866
|
+
lastCheckedAt: number().nullable(),
|
|
11867
|
+
lastConfidence: number().nullable(),
|
|
11868
|
+
currentCondition: SceneConditionSchema.nullable(),
|
|
11869
|
+
availability: _enum(["ok", "unavailable"]),
|
|
11870
|
+
unavailableReason: string().nullable()
|
|
11871
|
+
});
|
|
11872
|
+
var SceneMonitorStatusSchema = object({
|
|
11873
|
+
monitors: array(SceneMonitorSchema),
|
|
11874
|
+
lastFetchedAt: number()
|
|
11875
|
+
});
|
|
11876
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
|
|
11877
|
+
deviceId: number(),
|
|
11878
|
+
label: string(),
|
|
11879
|
+
roi: MaskRectShapeSchema,
|
|
11880
|
+
check: SceneCheckSchema,
|
|
11881
|
+
triggerMode: _enum([
|
|
11882
|
+
"periodic",
|
|
11883
|
+
"on-motion",
|
|
11884
|
+
"both"
|
|
11885
|
+
]).optional(),
|
|
11886
|
+
checkIntervalSec: number().optional()
|
|
11887
|
+
}), SceneMonitorSchema, {
|
|
11888
|
+
kind: "mutation",
|
|
11889
|
+
auth: "admin"
|
|
11890
|
+
}), method(object({
|
|
11891
|
+
deviceId: number(),
|
|
11892
|
+
monitorId: string(),
|
|
11893
|
+
patch: object({
|
|
11894
|
+
label: string().optional(),
|
|
11895
|
+
roi: MaskRectShapeSchema.optional(),
|
|
11896
|
+
enabled: boolean().optional(),
|
|
11897
|
+
triggerMode: _enum([
|
|
11898
|
+
"periodic",
|
|
11899
|
+
"on-motion",
|
|
11900
|
+
"both"
|
|
11901
|
+
]).optional(),
|
|
11902
|
+
checkIntervalSec: number().optional(),
|
|
11903
|
+
check: SceneCheckSchema.optional()
|
|
11904
|
+
})
|
|
11905
|
+
}), _void(), {
|
|
11906
|
+
kind: "mutation",
|
|
11907
|
+
auth: "admin"
|
|
11908
|
+
}), method(object({
|
|
11909
|
+
deviceId: number(),
|
|
11910
|
+
monitorId: string()
|
|
11911
|
+
}), _void(), {
|
|
11912
|
+
kind: "mutation",
|
|
11913
|
+
auth: "admin"
|
|
11914
|
+
}), method(object({
|
|
11915
|
+
deviceId: number(),
|
|
11916
|
+
monitorId: string(),
|
|
11917
|
+
stateId: string().optional(),
|
|
11918
|
+
label: string().optional(),
|
|
11919
|
+
condition: SceneConditionSchema.optional()
|
|
11920
|
+
}), object({
|
|
11921
|
+
stateId: string(),
|
|
11922
|
+
referenceCount: number()
|
|
11923
|
+
}), {
|
|
11924
|
+
kind: "mutation",
|
|
11925
|
+
auth: "admin"
|
|
11926
|
+
}), method(object({
|
|
11927
|
+
deviceId: number(),
|
|
11928
|
+
monitorId: string(),
|
|
11929
|
+
stateId: string(),
|
|
11930
|
+
index: number()
|
|
11931
|
+
}), _void(), {
|
|
11932
|
+
kind: "mutation",
|
|
11933
|
+
auth: "admin"
|
|
11934
|
+
}), method(object({
|
|
11935
|
+
deviceId: number(),
|
|
11936
|
+
monitorId: string()
|
|
11937
|
+
}), _void(), {
|
|
11938
|
+
kind: "mutation",
|
|
11939
|
+
auth: "admin"
|
|
11940
|
+
});
|
|
11701
11941
|
object({
|
|
11702
11942
|
/** Whether the script is currently executing. */
|
|
11703
11943
|
isRunning: boolean(),
|
|
@@ -12105,6 +12345,36 @@ var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
|
|
|
12105
12345
|
* with the per-track detail panel and live overlay. */
|
|
12106
12346
|
trackIds: array(string()).readonly()
|
|
12107
12347
|
});
|
|
12348
|
+
/**
|
|
12349
|
+
* A parked ("stationary") object surfaced alongside occupancy — an object that
|
|
12350
|
+
* settled and stopped moving. It is NO LONGER a tracked object (the tracker was
|
|
12351
|
+
* told to forget it so it stops re-spawning tracks/events), but it IS still
|
|
12352
|
+
* physically present, so it keeps counting toward `frame` occupancy and is
|
|
12353
|
+
* listed here so the UI can show it in a dedicated "Stationary" section instead
|
|
12354
|
+
* of flooding the live event feed.
|
|
12355
|
+
*/
|
|
12356
|
+
var StationaryObjectSchema = object({
|
|
12357
|
+
id: string(),
|
|
12358
|
+
className: string(),
|
|
12359
|
+
bbox: object({
|
|
12360
|
+
x: number(),
|
|
12361
|
+
y: number(),
|
|
12362
|
+
w: number(),
|
|
12363
|
+
h: number()
|
|
12364
|
+
}),
|
|
12365
|
+
frameWidth: number().int().nonnegative(),
|
|
12366
|
+
frameHeight: number().int().nonnegative(),
|
|
12367
|
+
/** When the source track was first seen. */
|
|
12368
|
+
firstSeenAt: number().int(),
|
|
12369
|
+
/** When the object was recognised as parked (promotion time). */
|
|
12370
|
+
becameStationaryAt: number().int(),
|
|
12371
|
+
/** Last frame a detection confirmed the object is still there. */
|
|
12372
|
+
lastConfirmedAt: number().int(),
|
|
12373
|
+
/** Enrichment label carried from the source track (identity / plate). */
|
|
12374
|
+
label: string().optional(),
|
|
12375
|
+
/** Native-resolution key-frame media key for the parked object's best image. */
|
|
12376
|
+
keyFrameMediaKey: string().optional()
|
|
12377
|
+
});
|
|
12108
12378
|
var CameraOccupancySnapshotSchema = object({
|
|
12109
12379
|
/** Frame timestamp of the inference result that produced this snapshot. */
|
|
12110
12380
|
ts: number().int(),
|
|
@@ -12113,10 +12383,15 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
12113
12383
|
frameHeight: number().int().nonnegative(),
|
|
12114
12384
|
/** Per-zone breakdown — one entry per defined zone (user + onboard). */
|
|
12115
12385
|
zones: array(ZoneScopeBreakdownSchema).readonly(),
|
|
12116
|
-
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12386
|
+
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12387
|
+
* INCLUDES currently-confirmed stationary objects (they are still present). */
|
|
12117
12388
|
frame: PerScopeBreakdownSchema,
|
|
12118
12389
|
/** Detections that landed outside every zone. Empty when no zones defined. */
|
|
12119
|
-
unzoned: PerScopeBreakdownSchema
|
|
12390
|
+
unzoned: PerScopeBreakdownSchema,
|
|
12391
|
+
/** Parked objects on this camera (additive — absent on legacy snapshots).
|
|
12392
|
+
* Surfaced separately so the UI shows them in a dedicated section rather
|
|
12393
|
+
* than as repeated tracks/events. */
|
|
12394
|
+
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
12120
12395
|
});
|
|
12121
12396
|
/**
|
|
12122
12397
|
* Time-series resolution. The history methods return one bucket per
|
|
@@ -12719,7 +12994,12 @@ var NotificationRuleConditionsSchema = object({
|
|
|
12719
12994
|
clipDescription: object({
|
|
12720
12995
|
text: string().min(1),
|
|
12721
12996
|
minSimilarity: number().min(0).max(1)
|
|
12722
|
-
}).optional()
|
|
12997
|
+
}).optional(),
|
|
12998
|
+
/** Match events whose recognized-entity label (face identity name or plate
|
|
12999
|
+
* vehicle name, propagated onto `event.data.label`) is one of these values.
|
|
13000
|
+
* Empty/absent → unaffected (back-compat). Enables "notify me when <named
|
|
13001
|
+
* vehicle/person> is seen". */
|
|
13002
|
+
labels: array(string()).readonly().optional()
|
|
12723
13003
|
});
|
|
12724
13004
|
var NotificationRuleTemplateSchema = object({
|
|
12725
13005
|
title: string(),
|
|
@@ -13014,11 +13294,19 @@ method(object({
|
|
|
13014
13294
|
* login page needs NO change.
|
|
13015
13295
|
*
|
|
13016
13296
|
* - `widget` — a Module-Federation widget the login page mounts (via
|
|
13017
|
-
* `loadRemoteBundle`) for an in-page ceremony.
|
|
13018
|
-
*
|
|
13019
|
-
*
|
|
13020
|
-
*
|
|
13021
|
-
*
|
|
13297
|
+
* `loadRemoteBundle`) for an in-page ceremony. `auth.listLoginMethods`
|
|
13298
|
+
* stamps a public `bundleUrl` from `addonId` + `bundle`. Generic
|
|
13299
|
+
* mechanism kept for future use; no shipped addon uses it on the login
|
|
13300
|
+
* page (the passkey ceremony below runs natively in the shell instead).
|
|
13301
|
+
*
|
|
13302
|
+
* - `passkey` — a declarative WebAuthn ceremony the shell renders
|
|
13303
|
+
* natively (`@simplewebauthn/browser` lives in `addon-admin-ui`, not in
|
|
13304
|
+
* a remotely-loaded bundle). Carries the addon's effective `rpId` /
|
|
13305
|
+
* `origin` (from its `resolveRpID()` / `resolveOrigin()`) so the shell
|
|
13306
|
+
* can gate visibility (IP-literal origin, hostname/rpId mismatch) WITHOUT
|
|
13307
|
+
* fetching any remote code pre-auth. Contribution stays unconditional —
|
|
13308
|
+
* enrollment state is never leaked pre-auth; visibility is a shell
|
|
13309
|
+
* decision.
|
|
13022
13310
|
*
|
|
13023
13311
|
* Every contribution carries a `stage`:
|
|
13024
13312
|
* - `primary` — shown on the first credentials screen (OIDC /
|
|
@@ -13032,30 +13320,45 @@ method(object({
|
|
|
13032
13320
|
*/
|
|
13033
13321
|
/** When a login method renders in the two-phase login flow. */
|
|
13034
13322
|
var LoginStageEnum = _enum(["primary", "second-factor"]);
|
|
13035
|
-
/** One login-method contribution — redirect button
|
|
13036
|
-
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13323
|
+
/** One login-method contribution — redirect button, pre-auth widget, or native passkey ceremony. */
|
|
13324
|
+
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13325
|
+
object({
|
|
13326
|
+
kind: literal("redirect"),
|
|
13327
|
+
/** Stable id within the login-method set (e.g. `auth-oidc/google`). */
|
|
13328
|
+
id: string(),
|
|
13329
|
+
/** Operator-facing button label. */
|
|
13330
|
+
label: string(),
|
|
13331
|
+
/** lucide-react icon name. */
|
|
13332
|
+
icon: string().optional(),
|
|
13333
|
+
/** Addon-owned HTTP route the button navigates to (GET). */
|
|
13334
|
+
startUrl: string(),
|
|
13335
|
+
stage: LoginStageEnum
|
|
13336
|
+
}),
|
|
13337
|
+
object({
|
|
13338
|
+
kind: literal("widget"),
|
|
13339
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
|
|
13340
|
+
id: string(),
|
|
13341
|
+
/** Owning addon id — drives the public bundle URL + the MF namespace. */
|
|
13342
|
+
addonId: string(),
|
|
13343
|
+
/** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
|
|
13344
|
+
bundle: string(),
|
|
13345
|
+
/** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
|
|
13346
|
+
remote: WidgetRemoteSchema,
|
|
13347
|
+
stage: LoginStageEnum
|
|
13348
|
+
}),
|
|
13349
|
+
object({
|
|
13350
|
+
kind: literal("passkey"),
|
|
13351
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-direct-login`). */
|
|
13352
|
+
id: string(),
|
|
13353
|
+
/** Operator-facing button label. */
|
|
13354
|
+
label: string(),
|
|
13355
|
+
stage: LoginStageEnum,
|
|
13356
|
+
/** Effective WebAuthn RP ID (`resolveRpID()`) — the shell gates visibility on it. */
|
|
13357
|
+
rpId: string(),
|
|
13358
|
+
/** Effective expected origin (`resolveOrigin()`), null when unconfigured. */
|
|
13359
|
+
origin: string().nullable()
|
|
13360
|
+
})
|
|
13361
|
+
]);
|
|
13059
13362
|
method(_void(), array(LoginMethodContributionSchema).readonly());
|
|
13060
13363
|
/**
|
|
13061
13364
|
* Orchestrator-side destination metadata. The orchestrator computes
|
|
@@ -14976,14 +15279,14 @@ var TargetKindCapsSchema = object({
|
|
|
14976
15279
|
* the union is large and not meant for runtime validation here; the exported
|
|
14977
15280
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
14978
15281
|
*/
|
|
14979
|
-
var ConfigSchemaPassthrough = unknown();
|
|
15282
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
14980
15283
|
var TargetKindSchema = object({
|
|
14981
15284
|
kind: string(),
|
|
14982
15285
|
label: string(),
|
|
14983
15286
|
icon: string(),
|
|
14984
15287
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
14985
15288
|
addonId: string(),
|
|
14986
|
-
configSchema: ConfigSchemaPassthrough,
|
|
15289
|
+
configSchema: ConfigSchemaPassthrough$1,
|
|
14987
15290
|
supportsDiscovery: boolean(),
|
|
14988
15291
|
caps: TargetKindCapsSchema
|
|
14989
15292
|
});
|
|
@@ -15036,6 +15339,298 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
15036
15339
|
enabled: boolean()
|
|
15037
15340
|
}), _void(), { kind: "mutation" });
|
|
15038
15341
|
/**
|
|
15342
|
+
* Shared LLM generate contracts — imported by BOTH `llm.cap.ts` (consumer
|
|
15343
|
+
* surface) and `llm-runtime.cap.ts` (node-side managed executor) so the two
|
|
15344
|
+
* caps stay wire-compatible without a circular cap→cap import.
|
|
15345
|
+
*
|
|
15346
|
+
* Errors are a discriminated-union RESULT, never thrown: the shape survives
|
|
15347
|
+
* every transport tier structurally, and failed calls still write usage rows.
|
|
15348
|
+
* Token counts only in v1 — no costUsd (operator decision, 2026-07-15).
|
|
15349
|
+
*/
|
|
15350
|
+
var LlmUsageSchema = object({
|
|
15351
|
+
inputTokens: number(),
|
|
15352
|
+
outputTokens: number()
|
|
15353
|
+
});
|
|
15354
|
+
var LlmErrorCodeSchema = _enum([
|
|
15355
|
+
"timeout",
|
|
15356
|
+
"rate-limited",
|
|
15357
|
+
"auth",
|
|
15358
|
+
"refusal",
|
|
15359
|
+
"bad-request",
|
|
15360
|
+
"unavailable",
|
|
15361
|
+
"no-profile",
|
|
15362
|
+
"budget-exceeded",
|
|
15363
|
+
"adapter-error"
|
|
15364
|
+
]);
|
|
15365
|
+
var LlmGenerateResultSchema = discriminatedUnion("ok", [object({
|
|
15366
|
+
ok: literal(true),
|
|
15367
|
+
text: string(),
|
|
15368
|
+
model: string(),
|
|
15369
|
+
usage: LlmUsageSchema,
|
|
15370
|
+
truncated: boolean(),
|
|
15371
|
+
latencyMs: number()
|
|
15372
|
+
}), object({
|
|
15373
|
+
ok: literal(false),
|
|
15374
|
+
code: LlmErrorCodeSchema,
|
|
15375
|
+
message: string(),
|
|
15376
|
+
retryAfterMs: number().optional()
|
|
15377
|
+
})]);
|
|
15378
|
+
/**
|
|
15379
|
+
* `Uint8Array` is the sanctioned binary convention — superjson + the UDS
|
|
15380
|
+
* MsgPack channel round-trip typed arrays (embedding-encoder.cap.ts:29,
|
|
15381
|
+
* notification-output.cap.ts:27-31 precedents).
|
|
15382
|
+
*/
|
|
15383
|
+
var LlmImageSchema = object({
|
|
15384
|
+
bytes: _instanceof(Uint8Array),
|
|
15385
|
+
mimeType: string()
|
|
15386
|
+
});
|
|
15387
|
+
var LlmGenerateBaseInputSchema = object({
|
|
15388
|
+
/** Collection routing (the notification-output posture). */
|
|
15389
|
+
addonId: string().optional(),
|
|
15390
|
+
/** Explicit profile; else the resolution chain (spec §3). */
|
|
15391
|
+
profileId: string().optional(),
|
|
15392
|
+
/** MANDATORY usage tag: 'ai-summary', 'notifier-rules', 'adhoc-ui', … */
|
|
15393
|
+
consumer: string(),
|
|
15394
|
+
system: string().optional(),
|
|
15395
|
+
/** v1: single-turn. `messages[]` is a v2 additive field. */
|
|
15396
|
+
prompt: string(),
|
|
15397
|
+
/** Structured output — adapter-mapped (response_format / forced tool / responseSchema). */
|
|
15398
|
+
jsonSchema: record(string(), unknown()).optional(),
|
|
15399
|
+
/** Per-call override of the profile default. */
|
|
15400
|
+
maxTokens: number().int().positive().optional(),
|
|
15401
|
+
temperature: number().optional()
|
|
15402
|
+
});
|
|
15403
|
+
/**
|
|
15404
|
+
* `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
|
|
15405
|
+
* on EVERY node where `addon-ai` is installed; the hub `llm` provider reaches
|
|
15406
|
+
* a specific node's runtime with `nodePin(profile.runtime.nodeId)` — normal
|
|
15407
|
+
* cap routing, zero bespoke plumbing. `internal: true`: the operator reaches
|
|
15408
|
+
* this only through the `llm` cap's methods.
|
|
15409
|
+
*
|
|
15410
|
+
* One running llama-server child per node in v1 (models are RAM-heavy).
|
|
15411
|
+
* Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
|
|
15412
|
+
* watchdog — operator decision #3).
|
|
15413
|
+
*/
|
|
15414
|
+
var ManagedModelRefSchema = discriminatedUnion("kind", [
|
|
15415
|
+
object({
|
|
15416
|
+
kind: literal("catalog"),
|
|
15417
|
+
catalogId: string()
|
|
15418
|
+
}),
|
|
15419
|
+
object({
|
|
15420
|
+
kind: literal("url"),
|
|
15421
|
+
url: string(),
|
|
15422
|
+
sha256: string().optional()
|
|
15423
|
+
}),
|
|
15424
|
+
object({
|
|
15425
|
+
kind: literal("path"),
|
|
15426
|
+
path: string()
|
|
15427
|
+
})
|
|
15428
|
+
]);
|
|
15429
|
+
var ManagedRuntimeConfigSchema = object({
|
|
15430
|
+
/** WHERE the runtime lives — hub or any agent. */
|
|
15431
|
+
nodeId: string(),
|
|
15432
|
+
/** Closed for v1; 'ollama' is a v2 candidate. */
|
|
15433
|
+
engine: _enum(["llama-cpp"]),
|
|
15434
|
+
model: ManagedModelRefSchema,
|
|
15435
|
+
contextSize: number().int().default(4096),
|
|
15436
|
+
/** 0 = CPU-only. */
|
|
15437
|
+
gpuLayers: number().int().default(0),
|
|
15438
|
+
/** Default: cpus-2, clamped ≥1 (resolved node-side). */
|
|
15439
|
+
threads: number().int().optional(),
|
|
15440
|
+
/** Concurrent slots. */
|
|
15441
|
+
parallel: number().int().default(1),
|
|
15442
|
+
/** Else lazy: first generate boots it. */
|
|
15443
|
+
autoStart: boolean().default(false),
|
|
15444
|
+
/** 0 = never; frees RAM after quiet periods. */
|
|
15445
|
+
idleStopMinutes: number().int().default(30)
|
|
15446
|
+
});
|
|
15447
|
+
var LlmRuntimeStatusSchema = object({
|
|
15448
|
+
/** Status is ALWAYS node-qualified. */
|
|
15449
|
+
nodeId: string(),
|
|
15450
|
+
state: _enum([
|
|
15451
|
+
"stopped",
|
|
15452
|
+
"downloading",
|
|
15453
|
+
"starting",
|
|
15454
|
+
"ready",
|
|
15455
|
+
"crashed",
|
|
15456
|
+
"failed"
|
|
15457
|
+
]),
|
|
15458
|
+
pid: number().optional(),
|
|
15459
|
+
port: number().optional(),
|
|
15460
|
+
modelPath: string().optional(),
|
|
15461
|
+
modelId: string().optional(),
|
|
15462
|
+
downloadProgress: number().min(0).max(1).optional(),
|
|
15463
|
+
lastError: string().optional(),
|
|
15464
|
+
crashesInWindow: number(),
|
|
15465
|
+
/** Child RSS (sampled best-effort). */
|
|
15466
|
+
memoryBytes: number().optional(),
|
|
15467
|
+
vramBytes: number().optional()
|
|
15468
|
+
});
|
|
15469
|
+
var LlmNodeModelSchema = object({
|
|
15470
|
+
file: string(),
|
|
15471
|
+
sizeBytes: number(),
|
|
15472
|
+
catalogId: string().optional(),
|
|
15473
|
+
installedAt: number().optional()
|
|
15474
|
+
});
|
|
15475
|
+
var LlmRuntimeDiskUsageSchema = object({
|
|
15476
|
+
nodeId: string(),
|
|
15477
|
+
modelsBytes: number(),
|
|
15478
|
+
freeBytes: number().optional()
|
|
15479
|
+
});
|
|
15480
|
+
method(LlmGenerateBaseInputSchema.extend({
|
|
15481
|
+
images: array(LlmImageSchema).optional(),
|
|
15482
|
+
runtime: ManagedRuntimeConfigSchema,
|
|
15483
|
+
/** The managed profile's timeout, threaded by the hub provider. */
|
|
15484
|
+
timeoutMs: number().int().positive().optional()
|
|
15485
|
+
}), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
|
|
15486
|
+
kind: "mutation",
|
|
15487
|
+
auth: "admin"
|
|
15488
|
+
}), method(object({}), _void(), {
|
|
15489
|
+
kind: "mutation",
|
|
15490
|
+
auth: "admin"
|
|
15491
|
+
}), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
|
|
15492
|
+
kind: "mutation",
|
|
15493
|
+
auth: "admin"
|
|
15494
|
+
}), method(object({ file: string() }), _void(), {
|
|
15495
|
+
kind: "mutation",
|
|
15496
|
+
auth: "admin"
|
|
15497
|
+
}), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
|
|
15498
|
+
/**
|
|
15499
|
+
* `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
|
|
15500
|
+
* methods concat-fan across providers; single-row methods route to ONE
|
|
15501
|
+
* provider by the `addonId` in the call input (the notification-output
|
|
15502
|
+
* posture, notification-output.cap.ts:215-250). Provided by `addon-ai`
|
|
15503
|
+
* (hub-placed); the cap stays open for future providers.
|
|
15504
|
+
*
|
|
15505
|
+
* Profiles are ROWS (data), not addons: one row = one usable model endpoint.
|
|
15506
|
+
* `apiKey` is a password field — providers REDACT it on read and merge on
|
|
15507
|
+
* write; a stored key NEVER round-trips to a client.
|
|
15508
|
+
*/
|
|
15509
|
+
var LlmProfileKindSchema = _enum([
|
|
15510
|
+
"openai-compatible",
|
|
15511
|
+
"openai",
|
|
15512
|
+
"anthropic",
|
|
15513
|
+
"google",
|
|
15514
|
+
"managed-local"
|
|
15515
|
+
]);
|
|
15516
|
+
var LlmProfileSchema = object({
|
|
15517
|
+
id: string(),
|
|
15518
|
+
name: string(),
|
|
15519
|
+
kind: LlmProfileKindSchema,
|
|
15520
|
+
/** Stamped by the provider — keeps the fanned catalog routable. */
|
|
15521
|
+
addonId: string(),
|
|
15522
|
+
enabled: boolean(),
|
|
15523
|
+
/** Vendor model id, or the managed runtime's loaded model. */
|
|
15524
|
+
model: string(),
|
|
15525
|
+
/** Required for openai-compatible; override for cloud kinds. */
|
|
15526
|
+
baseUrl: string().optional(),
|
|
15527
|
+
/** ConfigUISchema type:'password' — never round-trips (spec §5). */
|
|
15528
|
+
apiKey: string().optional(),
|
|
15529
|
+
supportsVision: boolean(),
|
|
15530
|
+
temperature: number().min(0).max(2).optional(),
|
|
15531
|
+
maxTokens: number().int().positive().optional(),
|
|
15532
|
+
timeoutMs: number().int().positive().default(6e4),
|
|
15533
|
+
extraHeaders: record(string(), string()).optional(),
|
|
15534
|
+
/** kind === 'managed-local' only (spec §4). */
|
|
15535
|
+
runtime: ManagedRuntimeConfigSchema.optional()
|
|
15536
|
+
});
|
|
15537
|
+
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
15538
|
+
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
15539
|
+
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
15540
|
+
var ConfigSchemaPassthrough = unknown();
|
|
15541
|
+
var LlmProfileKindDescriptorSchema = object({
|
|
15542
|
+
kind: LlmProfileKindSchema,
|
|
15543
|
+
label: string(),
|
|
15544
|
+
icon: string(),
|
|
15545
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
15546
|
+
addonId: string(),
|
|
15547
|
+
configSchema: ConfigSchemaPassthrough
|
|
15548
|
+
});
|
|
15549
|
+
var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
|
|
15550
|
+
var LlmDefaultSchema = object({
|
|
15551
|
+
selector: LlmDefaultSelectorSchema,
|
|
15552
|
+
profileId: string()
|
|
15553
|
+
});
|
|
15554
|
+
/** Server-side rollup row — getUsage never dumps raw call rows (spec §6). */
|
|
15555
|
+
var LlmUsageRollupSchema = object({
|
|
15556
|
+
day: string(),
|
|
15557
|
+
consumer: string(),
|
|
15558
|
+
profileId: string(),
|
|
15559
|
+
calls: number(),
|
|
15560
|
+
okCalls: number(),
|
|
15561
|
+
errorCalls: number(),
|
|
15562
|
+
inputTokens: number(),
|
|
15563
|
+
outputTokens: number(),
|
|
15564
|
+
avgLatencyMs: number()
|
|
15565
|
+
});
|
|
15566
|
+
/** LLM-facing view over the reused ModelCatalogEntry mechanism (spec §4.2). */
|
|
15567
|
+
var ManagedModelCatalogEntrySchema = object({
|
|
15568
|
+
id: string(),
|
|
15569
|
+
label: string(),
|
|
15570
|
+
family: string(),
|
|
15571
|
+
purpose: _enum(["text", "vision"]),
|
|
15572
|
+
url: string(),
|
|
15573
|
+
sha256: string(),
|
|
15574
|
+
sizeBytes: number(),
|
|
15575
|
+
quantization: string(),
|
|
15576
|
+
/** Load-time guidance shown in the picker. */
|
|
15577
|
+
minRamBytes: number(),
|
|
15578
|
+
contextSizeDefault: number().int(),
|
|
15579
|
+
/** Vision models: companion projector file. */
|
|
15580
|
+
mmprojUrl: string().optional()
|
|
15581
|
+
});
|
|
15582
|
+
var LlmRuntimeNodeSchema = object({
|
|
15583
|
+
nodeId: string(),
|
|
15584
|
+
reachable: boolean(),
|
|
15585
|
+
status: LlmRuntimeStatusSchema.optional(),
|
|
15586
|
+
disk: LlmRuntimeDiskUsageSchema.optional(),
|
|
15587
|
+
error: string().optional()
|
|
15588
|
+
});
|
|
15589
|
+
var GenerateVisionInputSchema = LlmGenerateBaseInputSchema.extend({ images: array(LlmImageSchema).min(1) });
|
|
15590
|
+
var ProfileRefInputSchema = object({
|
|
15591
|
+
addonId: string(),
|
|
15592
|
+
profileId: string()
|
|
15593
|
+
});
|
|
15594
|
+
method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
|
|
15595
|
+
kind: "mutation",
|
|
15596
|
+
auth: "admin"
|
|
15597
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15598
|
+
kind: "mutation",
|
|
15599
|
+
auth: "admin"
|
|
15600
|
+
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
15601
|
+
kind: "mutation",
|
|
15602
|
+
auth: "admin"
|
|
15603
|
+
}), method(ProfileRefInputSchema, array(string())), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
15604
|
+
selector: LlmDefaultSelectorSchema,
|
|
15605
|
+
profileId: string().nullable()
|
|
15606
|
+
}), _void(), {
|
|
15607
|
+
kind: "mutation",
|
|
15608
|
+
auth: "admin"
|
|
15609
|
+
}), method(object({
|
|
15610
|
+
since: number().optional(),
|
|
15611
|
+
until: number().optional(),
|
|
15612
|
+
consumer: string().optional(),
|
|
15613
|
+
profileId: string().optional()
|
|
15614
|
+
}), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
|
|
15615
|
+
nodeId: string(),
|
|
15616
|
+
model: ManagedModelRefSchema
|
|
15617
|
+
}), _void(), {
|
|
15618
|
+
kind: "mutation",
|
|
15619
|
+
auth: "admin"
|
|
15620
|
+
}), method(object({
|
|
15621
|
+
nodeId: string(),
|
|
15622
|
+
file: string()
|
|
15623
|
+
}), _void(), {
|
|
15624
|
+
kind: "mutation",
|
|
15625
|
+
auth: "admin"
|
|
15626
|
+
}), method(ProfileRefInputSchema, LlmRuntimeStatusSchema), method(ProfileRefInputSchema, LlmRuntimeStatusSchema, {
|
|
15627
|
+
kind: "mutation",
|
|
15628
|
+
auth: "admin"
|
|
15629
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15630
|
+
kind: "mutation",
|
|
15631
|
+
auth: "admin"
|
|
15632
|
+
});
|
|
15633
|
+
/**
|
|
15039
15634
|
* Zod schemas for persisted record types.
|
|
15040
15635
|
*
|
|
15041
15636
|
* These schemas serve as the single source of truth for types that are
|
|
@@ -15237,6 +15832,22 @@ var TrackSnapshotSchema = object({
|
|
|
15237
15832
|
/** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
|
|
15238
15833
|
mediaKey: string()
|
|
15239
15834
|
});
|
|
15835
|
+
/**
|
|
15836
|
+
* One audio-classification label heard on the track's camera while the
|
|
15837
|
+
* track was alive, aggregated per label. An "episode" is one persisted
|
|
15838
|
+
* audio event (the confident-classification path: score ≥ the device's
|
|
15839
|
+
* `classificationMinScore`, class-change-or-heartbeat coalesced) — NOT
|
|
15840
|
+
* one 32 ms inference chunk, so counts stay human-scaled.
|
|
15841
|
+
*/
|
|
15842
|
+
var TrackAudioLabelSchema = object({
|
|
15843
|
+
label: string(),
|
|
15844
|
+
/** Highest classification score observed across the label's episodes. */
|
|
15845
|
+
peakScore: number(),
|
|
15846
|
+
/** Number of coalesced audio-event episodes carrying this label. */
|
|
15847
|
+
count: number(),
|
|
15848
|
+
firstAt: number(),
|
|
15849
|
+
lastAt: number()
|
|
15850
|
+
});
|
|
15240
15851
|
var TrackSchema = object({
|
|
15241
15852
|
trackId: string(),
|
|
15242
15853
|
deviceId: number(),
|
|
@@ -15251,6 +15862,10 @@ var TrackSchema = object({
|
|
|
15251
15862
|
snapshots: array(TrackSnapshotSchema).readonly(),
|
|
15252
15863
|
/** Deduplicated zones the track has entered at least once. */
|
|
15253
15864
|
zonesVisited: array(string()).readonly(),
|
|
15865
|
+
/** Deduplicated set of detector classes observed for this track over its
|
|
15866
|
+
* life (a track may be reclassified, e.g. person→vehicle). Absent on
|
|
15867
|
+
* legacy rows written before class accumulation shipped. */
|
|
15868
|
+
classes: array(string()).readonly().optional(),
|
|
15254
15869
|
/** Cumulative normalized distance travelled (0..1 units = full frame width). */
|
|
15255
15870
|
totalDistance: number(),
|
|
15256
15871
|
state: TrackStateSchema,
|
|
@@ -15264,7 +15879,11 @@ var TrackSchema = object({
|
|
|
15264
15879
|
bestEventId: string().optional(),
|
|
15265
15880
|
/** Tag of the importance sub-signal that dominated the score
|
|
15266
15881
|
* (identity|dwell|proximity|class|confidence|travel|zone). */
|
|
15267
|
-
importanceReason: string().optional()
|
|
15882
|
+
importanceReason: string().optional(),
|
|
15883
|
+
/** Audio-classification labels heard on the camera during the track's
|
|
15884
|
+
* life (score ≥ device `classificationMinScore`), aggregated per label.
|
|
15885
|
+
* Absent on legacy rows / tracks with no confident audio. */
|
|
15886
|
+
audioLabels: array(TrackAudioLabelSchema).readonly().optional()
|
|
15268
15887
|
});
|
|
15269
15888
|
var BaseEventFields = {
|
|
15270
15889
|
id: string(),
|
|
@@ -15412,7 +16031,7 @@ var KeyEventSchema = object({
|
|
|
15412
16031
|
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
15413
16032
|
windowMs: number().optional()
|
|
15414
16033
|
});
|
|
15415
|
-
|
|
16034
|
+
object({
|
|
15416
16035
|
trackId: string(),
|
|
15417
16036
|
className: string(),
|
|
15418
16037
|
confidence: number(),
|
|
@@ -15420,6 +16039,23 @@ var TrackedDetectionSchema = object({
|
|
|
15420
16039
|
zones: array(string()).readonly(),
|
|
15421
16040
|
state: TrackStateSchema
|
|
15422
16041
|
});
|
|
16042
|
+
var OverlayDetectionSchema = looseObject({
|
|
16043
|
+
id: string(),
|
|
16044
|
+
kind: _enum(["first-level", "detail"]),
|
|
16045
|
+
macroClass: string(),
|
|
16046
|
+
score: number(),
|
|
16047
|
+
bbox: object({
|
|
16048
|
+
x: number(),
|
|
16049
|
+
y: number(),
|
|
16050
|
+
width: number(),
|
|
16051
|
+
height: number()
|
|
16052
|
+
}),
|
|
16053
|
+
labels: array(looseObject({
|
|
16054
|
+
label: string(),
|
|
16055
|
+
score: number()
|
|
16056
|
+
})).readonly(),
|
|
16057
|
+
parentId: string().optional()
|
|
16058
|
+
});
|
|
15423
16059
|
var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: number() });
|
|
15424
16060
|
var SearchObjectEventsInput = object({
|
|
15425
16061
|
text: string(),
|
|
@@ -15430,6 +16066,20 @@ var SearchObjectEventsInput = object({
|
|
|
15430
16066
|
limit: number().default(50),
|
|
15431
16067
|
minScore: number().min(0).max(1).default(.2)
|
|
15432
16068
|
});
|
|
16069
|
+
var TrackCascadeCountsSchema = object({
|
|
16070
|
+
/** Persisted track roots deleted (authoritative). */
|
|
16071
|
+
tracks: number().int(),
|
|
16072
|
+
/** Object events removed with their tracks (best-effort; see note above). */
|
|
16073
|
+
events: number().int(),
|
|
16074
|
+
/** Track/face/plate-owned media removed (best-effort). Never identity media. */
|
|
16075
|
+
media: number().int(),
|
|
16076
|
+
/** Unassigned (non-enrolled) face reads removed (best-effort). */
|
|
16077
|
+
faces: number().int(),
|
|
16078
|
+
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
16079
|
+
plates: number().int(),
|
|
16080
|
+
/** Per-track CLIP search vectors removed (best-effort). */
|
|
16081
|
+
embeddings: number().int()
|
|
16082
|
+
});
|
|
15433
16083
|
DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
|
|
15434
16084
|
deviceId: number(),
|
|
15435
16085
|
trackId: string()
|
|
@@ -15462,15 +16112,33 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15462
16112
|
kind: "mutation",
|
|
15463
16113
|
auth: "admin"
|
|
15464
16114
|
}), method(object({
|
|
15465
|
-
eventId: string(),
|
|
15466
|
-
kind: MediaFileKindEnum.optional()
|
|
15467
|
-
}), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
15468
16115
|
deviceId: number(),
|
|
15469
|
-
|
|
15470
|
-
|
|
15471
|
-
|
|
15472
|
-
|
|
15473
|
-
}), object({
|
|
16116
|
+
cutoffMs: number()
|
|
16117
|
+
}), TrackCascadeCountsSchema, {
|
|
16118
|
+
kind: "mutation",
|
|
16119
|
+
auth: "admin"
|
|
16120
|
+
}), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
|
|
16121
|
+
kind: "mutation",
|
|
16122
|
+
auth: "admin"
|
|
16123
|
+
}), method(object({
|
|
16124
|
+
deviceId: number(),
|
|
16125
|
+
trackIds: array(string()).min(1)
|
|
16126
|
+
}), object({
|
|
16127
|
+
deleted: number().int(),
|
|
16128
|
+
failed: array(string()).readonly()
|
|
16129
|
+
}), {
|
|
16130
|
+
kind: "mutation",
|
|
16131
|
+
auth: "admin"
|
|
16132
|
+
}), method(object({
|
|
16133
|
+
eventId: string(),
|
|
16134
|
+
kind: MediaFileKindEnum.optional()
|
|
16135
|
+
}), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
16136
|
+
deviceId: number(),
|
|
16137
|
+
timestamp: number(),
|
|
16138
|
+
frameWidth: number(),
|
|
16139
|
+
frameHeight: number(),
|
|
16140
|
+
detections: array(OverlayDetectionSchema).readonly()
|
|
16141
|
+
}), object({
|
|
15474
16142
|
deviceId: number(),
|
|
15475
16143
|
trackId: string(),
|
|
15476
16144
|
className: string()
|
|
@@ -18238,6 +18906,24 @@ DeviceType.Camera, method(object({
|
|
|
18238
18906
|
deviceId: number(),
|
|
18239
18907
|
status: OsdStatusSchema
|
|
18240
18908
|
});
|
|
18909
|
+
var VehicleSchema = object({
|
|
18910
|
+
id: string(),
|
|
18911
|
+
name: string(),
|
|
18912
|
+
sampleCount: number().int().nonnegative(),
|
|
18913
|
+
coverMediaKey: string().optional(),
|
|
18914
|
+
/** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
|
|
18915
|
+
coverBase64: string().optional()
|
|
18916
|
+
});
|
|
18917
|
+
var VehicleSampleInfoSchema = object({
|
|
18918
|
+
id: string(),
|
|
18919
|
+
/** The plate text this sample enrolled (self-labeling — no embedding). */
|
|
18920
|
+
text: string(),
|
|
18921
|
+
/** OCR confidence of the enrolled read (0..1). */
|
|
18922
|
+
score: number(),
|
|
18923
|
+
addedAt: number().int(),
|
|
18924
|
+
deviceId: number().int().optional(),
|
|
18925
|
+
base64: string().optional()
|
|
18926
|
+
});
|
|
18241
18927
|
var PlateInfoSchema = object({
|
|
18242
18928
|
plateId: string(),
|
|
18243
18929
|
deviceId: number().int(),
|
|
@@ -18249,6 +18935,16 @@ var PlateInfoSchema = object({
|
|
|
18249
18935
|
score: number(),
|
|
18250
18936
|
/** True when the text was manually corrected (exempt from retention). */
|
|
18251
18937
|
corrected: boolean(),
|
|
18938
|
+
/** Recognized vehicle id when a matcher lookup named this read (SQL NULL → absent). */
|
|
18939
|
+
recognizedVehicleId: string().optional(),
|
|
18940
|
+
/** Resolved vehicle name for `recognizedVehicleId` (UI convenience). */
|
|
18941
|
+
vehicleName: string().optional(),
|
|
18942
|
+
/** True once the read was assigned to a vehicle (enrolled as a sample). */
|
|
18943
|
+
assigned: boolean(),
|
|
18944
|
+
/** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
|
|
18945
|
+
plateBbox: BoundingBoxSchema.optional(),
|
|
18946
|
+
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
18947
|
+
keyFrameMediaKey: string().optional(),
|
|
18252
18948
|
base64: string().optional()
|
|
18253
18949
|
});
|
|
18254
18950
|
var MediaFileLiteSchema = object({
|
|
@@ -18288,6 +18984,48 @@ method(object({
|
|
|
18288
18984
|
}), method(object({ plateId: string() }), _void(), {
|
|
18289
18985
|
kind: "mutation",
|
|
18290
18986
|
auth: "admin"
|
|
18987
|
+
}), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
|
|
18988
|
+
kind: "mutation",
|
|
18989
|
+
auth: "admin"
|
|
18990
|
+
}), method(object({
|
|
18991
|
+
id: string(),
|
|
18992
|
+
name: string().min(1)
|
|
18993
|
+
}), _void(), {
|
|
18994
|
+
kind: "mutation",
|
|
18995
|
+
auth: "admin"
|
|
18996
|
+
}), method(object({ id: string() }), _void(), {
|
|
18997
|
+
kind: "mutation",
|
|
18998
|
+
auth: "admin"
|
|
18999
|
+
}), method(object({ vehicleId: string() }), array(VehicleSampleInfoSchema).readonly()), method(object({
|
|
19000
|
+
vehicleId: string(),
|
|
19001
|
+
sampleId: string()
|
|
19002
|
+
}), _void(), {
|
|
19003
|
+
kind: "mutation",
|
|
19004
|
+
auth: "admin"
|
|
19005
|
+
}), method(object({
|
|
19006
|
+
plateId: string(),
|
|
19007
|
+
vehicleId: string()
|
|
19008
|
+
}), _void(), {
|
|
19009
|
+
kind: "mutation",
|
|
19010
|
+
auth: "admin"
|
|
19011
|
+
}), method(object({ plateId: string() }), _void(), {
|
|
19012
|
+
kind: "mutation",
|
|
19013
|
+
auth: "admin"
|
|
19014
|
+
}), method(object({
|
|
19015
|
+
plateIds: array(string()).min(1),
|
|
19016
|
+
vehicleId: string()
|
|
19017
|
+
}), object({
|
|
19018
|
+
assigned: number().int(),
|
|
19019
|
+
failed: array(string()).readonly()
|
|
19020
|
+
}), {
|
|
19021
|
+
kind: "mutation",
|
|
19022
|
+
auth: "admin"
|
|
19023
|
+
}), method(object({ plateIds: array(string()).min(1) }), object({
|
|
19024
|
+
unassigned: number().int(),
|
|
19025
|
+
failed: array(string()).readonly()
|
|
19026
|
+
}), {
|
|
19027
|
+
kind: "mutation",
|
|
19028
|
+
auth: "admin"
|
|
18291
19029
|
});
|
|
18292
19030
|
var ModelFormatSchema = _enum(MODEL_FORMATS);
|
|
18293
19031
|
var HwAccelBackendInputSchema = _enum([
|
|
@@ -18366,6 +19104,10 @@ var GpuInfoSchema = object({
|
|
|
18366
19104
|
memoryMB: number().optional()
|
|
18367
19105
|
});
|
|
18368
19106
|
var NpuInfoSchema = object({ type: _enum(["apple-ane", "intel-npu"]) });
|
|
19107
|
+
var CoralInfoSchema = object({
|
|
19108
|
+
type: literal("coral-edgetpu"),
|
|
19109
|
+
bus: string().optional()
|
|
19110
|
+
});
|
|
18369
19111
|
var HardwareInfoSchema = object({
|
|
18370
19112
|
platform: HardwarePlatformSchema,
|
|
18371
19113
|
arch: HardwareArchSchema,
|
|
@@ -18374,7 +19116,8 @@ var HardwareInfoSchema = object({
|
|
|
18374
19116
|
totalRAM_MB: number(),
|
|
18375
19117
|
availableRAM_MB: number(),
|
|
18376
19118
|
gpu: GpuInfoSchema.nullable(),
|
|
18377
|
-
npu: NpuInfoSchema.nullable()
|
|
19119
|
+
npu: NpuInfoSchema.nullable(),
|
|
19120
|
+
coral: CoralInfoSchema.nullable().optional()
|
|
18378
19121
|
});
|
|
18379
19122
|
var PlatformScoreSchema = object({
|
|
18380
19123
|
runtime: _enum(["node", "python"]),
|
|
@@ -20833,6 +21576,168 @@ Object.freeze({
|
|
|
20833
21576
|
addonId: null,
|
|
20834
21577
|
access: "create"
|
|
20835
21578
|
},
|
|
21579
|
+
"llm.deleteModel": {
|
|
21580
|
+
capName: "llm",
|
|
21581
|
+
capScope: "system",
|
|
21582
|
+
addonId: null,
|
|
21583
|
+
access: "delete"
|
|
21584
|
+
},
|
|
21585
|
+
"llm.deleteProfile": {
|
|
21586
|
+
capName: "llm",
|
|
21587
|
+
capScope: "system",
|
|
21588
|
+
addonId: null,
|
|
21589
|
+
access: "delete"
|
|
21590
|
+
},
|
|
21591
|
+
"llm.generate": {
|
|
21592
|
+
capName: "llm",
|
|
21593
|
+
capScope: "system",
|
|
21594
|
+
addonId: null,
|
|
21595
|
+
access: "create"
|
|
21596
|
+
},
|
|
21597
|
+
"llm.generateVision": {
|
|
21598
|
+
capName: "llm",
|
|
21599
|
+
capScope: "system",
|
|
21600
|
+
addonId: null,
|
|
21601
|
+
access: "create"
|
|
21602
|
+
},
|
|
21603
|
+
"llm.getDefaults": {
|
|
21604
|
+
capName: "llm",
|
|
21605
|
+
capScope: "system",
|
|
21606
|
+
addonId: null,
|
|
21607
|
+
access: "view"
|
|
21608
|
+
},
|
|
21609
|
+
"llm.getRuntimeStatus": {
|
|
21610
|
+
capName: "llm",
|
|
21611
|
+
capScope: "system",
|
|
21612
|
+
addonId: null,
|
|
21613
|
+
access: "view"
|
|
21614
|
+
},
|
|
21615
|
+
"llm.getUsage": {
|
|
21616
|
+
capName: "llm",
|
|
21617
|
+
capScope: "system",
|
|
21618
|
+
addonId: null,
|
|
21619
|
+
access: "view"
|
|
21620
|
+
},
|
|
21621
|
+
"llm.installModel": {
|
|
21622
|
+
capName: "llm",
|
|
21623
|
+
capScope: "system",
|
|
21624
|
+
addonId: null,
|
|
21625
|
+
access: "create"
|
|
21626
|
+
},
|
|
21627
|
+
"llm.listModelCatalog": {
|
|
21628
|
+
capName: "llm",
|
|
21629
|
+
capScope: "system",
|
|
21630
|
+
addonId: null,
|
|
21631
|
+
access: "view"
|
|
21632
|
+
},
|
|
21633
|
+
"llm.listModels": {
|
|
21634
|
+
capName: "llm",
|
|
21635
|
+
capScope: "system",
|
|
21636
|
+
addonId: null,
|
|
21637
|
+
access: "view"
|
|
21638
|
+
},
|
|
21639
|
+
"llm.listNodeModels": {
|
|
21640
|
+
capName: "llm",
|
|
21641
|
+
capScope: "system",
|
|
21642
|
+
addonId: null,
|
|
21643
|
+
access: "view"
|
|
21644
|
+
},
|
|
21645
|
+
"llm.listProfileKinds": {
|
|
21646
|
+
capName: "llm",
|
|
21647
|
+
capScope: "system",
|
|
21648
|
+
addonId: null,
|
|
21649
|
+
access: "view"
|
|
21650
|
+
},
|
|
21651
|
+
"llm.listProfiles": {
|
|
21652
|
+
capName: "llm",
|
|
21653
|
+
capScope: "system",
|
|
21654
|
+
addonId: null,
|
|
21655
|
+
access: "view"
|
|
21656
|
+
},
|
|
21657
|
+
"llm.listRuntimeNodes": {
|
|
21658
|
+
capName: "llm",
|
|
21659
|
+
capScope: "system",
|
|
21660
|
+
addonId: null,
|
|
21661
|
+
access: "view"
|
|
21662
|
+
},
|
|
21663
|
+
"llm.setDefault": {
|
|
21664
|
+
capName: "llm",
|
|
21665
|
+
capScope: "system",
|
|
21666
|
+
addonId: null,
|
|
21667
|
+
access: "create"
|
|
21668
|
+
},
|
|
21669
|
+
"llm.startRuntime": {
|
|
21670
|
+
capName: "llm",
|
|
21671
|
+
capScope: "system",
|
|
21672
|
+
addonId: null,
|
|
21673
|
+
access: "create"
|
|
21674
|
+
},
|
|
21675
|
+
"llm.stopRuntime": {
|
|
21676
|
+
capName: "llm",
|
|
21677
|
+
capScope: "system",
|
|
21678
|
+
addonId: null,
|
|
21679
|
+
access: "create"
|
|
21680
|
+
},
|
|
21681
|
+
"llm.testProfile": {
|
|
21682
|
+
capName: "llm",
|
|
21683
|
+
capScope: "system",
|
|
21684
|
+
addonId: null,
|
|
21685
|
+
access: "create"
|
|
21686
|
+
},
|
|
21687
|
+
"llm.upsertProfile": {
|
|
21688
|
+
capName: "llm",
|
|
21689
|
+
capScope: "system",
|
|
21690
|
+
addonId: null,
|
|
21691
|
+
access: "create"
|
|
21692
|
+
},
|
|
21693
|
+
"llmRuntime.complete": {
|
|
21694
|
+
capName: "llm-runtime",
|
|
21695
|
+
capScope: "system",
|
|
21696
|
+
addonId: null,
|
|
21697
|
+
access: "create"
|
|
21698
|
+
},
|
|
21699
|
+
"llmRuntime.deleteModel": {
|
|
21700
|
+
capName: "llm-runtime",
|
|
21701
|
+
capScope: "system",
|
|
21702
|
+
addonId: null,
|
|
21703
|
+
access: "delete"
|
|
21704
|
+
},
|
|
21705
|
+
"llmRuntime.ensureStarted": {
|
|
21706
|
+
capName: "llm-runtime",
|
|
21707
|
+
capScope: "system",
|
|
21708
|
+
addonId: null,
|
|
21709
|
+
access: "create"
|
|
21710
|
+
},
|
|
21711
|
+
"llmRuntime.getDiskUsage": {
|
|
21712
|
+
capName: "llm-runtime",
|
|
21713
|
+
capScope: "system",
|
|
21714
|
+
addonId: null,
|
|
21715
|
+
access: "view"
|
|
21716
|
+
},
|
|
21717
|
+
"llmRuntime.installModel": {
|
|
21718
|
+
capName: "llm-runtime",
|
|
21719
|
+
capScope: "system",
|
|
21720
|
+
addonId: null,
|
|
21721
|
+
access: "create"
|
|
21722
|
+
},
|
|
21723
|
+
"llmRuntime.listLocalModels": {
|
|
21724
|
+
capName: "llm-runtime",
|
|
21725
|
+
capScope: "system",
|
|
21726
|
+
addonId: null,
|
|
21727
|
+
access: "view"
|
|
21728
|
+
},
|
|
21729
|
+
"llmRuntime.status": {
|
|
21730
|
+
capName: "llm-runtime",
|
|
21731
|
+
capScope: "system",
|
|
21732
|
+
addonId: null,
|
|
21733
|
+
access: "view"
|
|
21734
|
+
},
|
|
21735
|
+
"llmRuntime.stop": {
|
|
21736
|
+
capName: "llm-runtime",
|
|
21737
|
+
capScope: "system",
|
|
21738
|
+
addonId: null,
|
|
21739
|
+
access: "create"
|
|
21740
|
+
},
|
|
20836
21741
|
"localNetwork.getAllowedAddresses": {
|
|
20837
21742
|
capName: "local-network",
|
|
20838
21743
|
capScope: "system",
|
|
@@ -21463,6 +22368,12 @@ Object.freeze({
|
|
|
21463
22368
|
addonId: null,
|
|
21464
22369
|
access: "delete"
|
|
21465
22370
|
},
|
|
22371
|
+
"pipelineAnalytics.deleteTracks": {
|
|
22372
|
+
capName: "pipeline-analytics",
|
|
22373
|
+
capScope: "device",
|
|
22374
|
+
addonId: null,
|
|
22375
|
+
access: "delete"
|
|
22376
|
+
},
|
|
21466
22377
|
"pipelineAnalytics.getActiveTracks": {
|
|
21467
22378
|
capName: "pipeline-analytics",
|
|
21468
22379
|
capScope: "device",
|
|
@@ -21529,12 +22440,24 @@ Object.freeze({
|
|
|
21529
22440
|
addonId: null,
|
|
21530
22441
|
access: "create"
|
|
21531
22442
|
},
|
|
22443
|
+
"pipelineAnalytics.pruneTracksBefore": {
|
|
22444
|
+
capName: "pipeline-analytics",
|
|
22445
|
+
capScope: "device",
|
|
22446
|
+
addonId: null,
|
|
22447
|
+
access: "create"
|
|
22448
|
+
},
|
|
21532
22449
|
"pipelineAnalytics.searchObjectEvents": {
|
|
21533
22450
|
capName: "pipeline-analytics",
|
|
21534
22451
|
capScope: "device",
|
|
21535
22452
|
addonId: null,
|
|
21536
22453
|
access: "view"
|
|
21537
22454
|
},
|
|
22455
|
+
"pipelineAnalytics.wipeAllAnalytics": {
|
|
22456
|
+
capName: "pipeline-analytics",
|
|
22457
|
+
capScope: "device",
|
|
22458
|
+
addonId: null,
|
|
22459
|
+
access: "delete"
|
|
22460
|
+
},
|
|
21538
22461
|
"pipelineExecutor.cacheFrameInPool": {
|
|
21539
22462
|
capName: "pipeline-executor",
|
|
21540
22463
|
capScope: "system",
|
|
@@ -22033,18 +22956,48 @@ Object.freeze({
|
|
|
22033
22956
|
addonId: null,
|
|
22034
22957
|
access: "create"
|
|
22035
22958
|
},
|
|
22959
|
+
"pipelineRunner.runDetailSubtree": {
|
|
22960
|
+
capName: "pipeline-runner",
|
|
22961
|
+
capScope: "system",
|
|
22962
|
+
addonId: null,
|
|
22963
|
+
access: "create"
|
|
22964
|
+
},
|
|
22965
|
+
"plateGallery.assignPlate": {
|
|
22966
|
+
capName: "plate-gallery",
|
|
22967
|
+
capScope: "system",
|
|
22968
|
+
addonId: null,
|
|
22969
|
+
access: "create"
|
|
22970
|
+
},
|
|
22971
|
+
"plateGallery.assignPlates": {
|
|
22972
|
+
capName: "plate-gallery",
|
|
22973
|
+
capScope: "system",
|
|
22974
|
+
addonId: null,
|
|
22975
|
+
access: "create"
|
|
22976
|
+
},
|
|
22036
22977
|
"plateGallery.correctPlateText": {
|
|
22037
22978
|
capName: "plate-gallery",
|
|
22038
22979
|
capScope: "system",
|
|
22039
22980
|
addonId: null,
|
|
22040
22981
|
access: "create"
|
|
22041
22982
|
},
|
|
22983
|
+
"plateGallery.createVehicle": {
|
|
22984
|
+
capName: "plate-gallery",
|
|
22985
|
+
capScope: "system",
|
|
22986
|
+
addonId: null,
|
|
22987
|
+
access: "create"
|
|
22988
|
+
},
|
|
22042
22989
|
"plateGallery.deletePlate": {
|
|
22043
22990
|
capName: "plate-gallery",
|
|
22044
22991
|
capScope: "system",
|
|
22045
22992
|
addonId: null,
|
|
22046
22993
|
access: "delete"
|
|
22047
22994
|
},
|
|
22995
|
+
"plateGallery.deleteVehicle": {
|
|
22996
|
+
capName: "plate-gallery",
|
|
22997
|
+
capScope: "system",
|
|
22998
|
+
addonId: null,
|
|
22999
|
+
access: "delete"
|
|
23000
|
+
},
|
|
22048
23001
|
"plateGallery.getPlateByTrack": {
|
|
22049
23002
|
capName: "plate-gallery",
|
|
22050
23003
|
capScope: "system",
|
|
@@ -22063,6 +23016,30 @@ Object.freeze({
|
|
|
22063
23016
|
addonId: null,
|
|
22064
23017
|
access: "view"
|
|
22065
23018
|
},
|
|
23019
|
+
"plateGallery.listVehicles": {
|
|
23020
|
+
capName: "plate-gallery",
|
|
23021
|
+
capScope: "system",
|
|
23022
|
+
addonId: null,
|
|
23023
|
+
access: "view"
|
|
23024
|
+
},
|
|
23025
|
+
"plateGallery.listVehicleSamples": {
|
|
23026
|
+
capName: "plate-gallery",
|
|
23027
|
+
capScope: "system",
|
|
23028
|
+
addonId: null,
|
|
23029
|
+
access: "view"
|
|
23030
|
+
},
|
|
23031
|
+
"plateGallery.removeVehicleSample": {
|
|
23032
|
+
capName: "plate-gallery",
|
|
23033
|
+
capScope: "system",
|
|
23034
|
+
addonId: null,
|
|
23035
|
+
access: "delete"
|
|
23036
|
+
},
|
|
23037
|
+
"plateGallery.renameVehicle": {
|
|
23038
|
+
capName: "plate-gallery",
|
|
23039
|
+
capScope: "system",
|
|
23040
|
+
addonId: null,
|
|
23041
|
+
access: "create"
|
|
23042
|
+
},
|
|
22066
23043
|
"plateGallery.searchPlates": {
|
|
22067
23044
|
capName: "plate-gallery",
|
|
22068
23045
|
capScope: "system",
|
|
@@ -22075,6 +23052,18 @@ Object.freeze({
|
|
|
22075
23052
|
addonId: null,
|
|
22076
23053
|
access: "view"
|
|
22077
23054
|
},
|
|
23055
|
+
"plateGallery.unassignPlate": {
|
|
23056
|
+
capName: "plate-gallery",
|
|
23057
|
+
capScope: "system",
|
|
23058
|
+
addonId: null,
|
|
23059
|
+
access: "create"
|
|
23060
|
+
},
|
|
23061
|
+
"plateGallery.unassignPlates": {
|
|
23062
|
+
capName: "plate-gallery",
|
|
23063
|
+
capScope: "system",
|
|
23064
|
+
addonId: null,
|
|
23065
|
+
access: "create"
|
|
23066
|
+
},
|
|
22078
23067
|
"platformProbe.getCapabilities": {
|
|
22079
23068
|
capName: "platform-probe",
|
|
22080
23069
|
capScope: "system",
|
|
@@ -22267,6 +23256,48 @@ Object.freeze({
|
|
|
22267
23256
|
addonId: null,
|
|
22268
23257
|
access: "create"
|
|
22269
23258
|
},
|
|
23259
|
+
"sceneMonitor.captureReference": {
|
|
23260
|
+
capName: "scene-monitor",
|
|
23261
|
+
capScope: "device",
|
|
23262
|
+
addonId: null,
|
|
23263
|
+
access: "create"
|
|
23264
|
+
},
|
|
23265
|
+
"sceneMonitor.createScene": {
|
|
23266
|
+
capName: "scene-monitor",
|
|
23267
|
+
capScope: "device",
|
|
23268
|
+
addonId: null,
|
|
23269
|
+
access: "create"
|
|
23270
|
+
},
|
|
23271
|
+
"sceneMonitor.deleteReference": {
|
|
23272
|
+
capName: "scene-monitor",
|
|
23273
|
+
capScope: "device",
|
|
23274
|
+
addonId: null,
|
|
23275
|
+
access: "delete"
|
|
23276
|
+
},
|
|
23277
|
+
"sceneMonitor.deleteScene": {
|
|
23278
|
+
capName: "scene-monitor",
|
|
23279
|
+
capScope: "device",
|
|
23280
|
+
addonId: null,
|
|
23281
|
+
access: "delete"
|
|
23282
|
+
},
|
|
23283
|
+
"sceneMonitor.listScenes": {
|
|
23284
|
+
capName: "scene-monitor",
|
|
23285
|
+
capScope: "device",
|
|
23286
|
+
addonId: null,
|
|
23287
|
+
access: "view"
|
|
23288
|
+
},
|
|
23289
|
+
"sceneMonitor.recheckNow": {
|
|
23290
|
+
capName: "scene-monitor",
|
|
23291
|
+
capScope: "device",
|
|
23292
|
+
addonId: null,
|
|
23293
|
+
access: "create"
|
|
23294
|
+
},
|
|
23295
|
+
"sceneMonitor.updateScene": {
|
|
23296
|
+
capName: "scene-monitor",
|
|
23297
|
+
capScope: "device",
|
|
23298
|
+
addonId: null,
|
|
23299
|
+
access: "create"
|
|
23300
|
+
},
|
|
22270
23301
|
"scriptRunner.run": {
|
|
22271
23302
|
capName: "script-runner",
|
|
22272
23303
|
capScope: "device",
|
|
@@ -23351,6 +24382,7 @@ Object.freeze({
|
|
|
23351
24382
|
Object.freeze({
|
|
23352
24383
|
"broker": "broker",
|
|
23353
24384
|
"device-export": "device-export",
|
|
24385
|
+
"llm": "ai",
|
|
23354
24386
|
"mesh-network": "mesh",
|
|
23355
24387
|
"mqtt-broker": "broker",
|
|
23356
24388
|
"network-access": "ingress",
|
|
@@ -23537,9 +24569,9 @@ async function resolveDecoderBackend(api, nodeId, logger) {
|
|
|
23537
24569
|
//#region src/shared/notifying-ring-buffer.ts
|
|
23538
24570
|
/**
|
|
23539
24571
|
* A {@link RingBuffer} that can notify a single blocked consumer the moment an
|
|
23540
|
-
* item commits — the server side of the `pullFrames
|
|
24572
|
+
* item commits — the server side of the `pullFrames` long-poll.
|
|
23541
24573
|
*
|
|
23542
|
-
* The decoder's `onFrame
|
|
24574
|
+
* The decoder's `onFrame` callback drives `push()`; a consumer
|
|
23543
24575
|
* that finds the ring momentarily empty calls {@link waitForItem} and is woken
|
|
23544
24576
|
* on the very next `push()` (a real commit signal) rather than spinning at
|
|
23545
24577
|
* ~1kHz on `setTimeout(…,1)`. If nothing commits it resolves after `waitMs`,
|
|
@@ -23983,8 +25015,7 @@ var FrameStreamSplitter = class {
|
|
|
23983
25015
|
//#region src/ffmpeg-decoder-session.ts
|
|
23984
25016
|
/**
|
|
23985
25017
|
* FfmpegDecoderSession — an `IDecoderSession` that decodes video in an **ffmpeg
|
|
23986
|
-
* subprocess** and
|
|
23987
|
-
* ring `decoder-nodeav` writes into.
|
|
25018
|
+
* subprocess** and delivers each decoded frame via the `onFrame` callback.
|
|
23988
25019
|
*
|
|
23989
25020
|
* Why a subprocess: on the Intel hub, node-av's in-process VA-API decode path
|
|
23990
25021
|
* SIGBUSes (uncatchable) on 8MP h264 and kills the whole decoder runner. ffmpeg
|
|
@@ -23997,15 +25028,13 @@ var FrameStreamSplitter = class {
|
|
|
23997
25028
|
* ffmpeg → `-vf <scale_vaapi=…,hwdownload,… | hwdownload,…,scale=…>` (GPU or
|
|
23998
25029
|
* CPU scale, per {@link buildFfmpegDecodeArgs})
|
|
23999
25030
|
* raw video → ffmpeg stdout (pipe:1) → a {@link FrameStreamSplitter} that fills
|
|
24000
|
-
* each frame's bytes
|
|
24001
|
-
* `
|
|
24002
|
-
*
|
|
24003
|
-
* No `Buffer.concat` accumulator, one copy per byte, none for dropped
|
|
24004
|
-
* frames.
|
|
25031
|
+
* each frame's bytes into a per-frame buffer → `DecodedFrame` via
|
|
25032
|
+
* `onFrame`. No `Buffer.concat` accumulator, one copy per byte, none
|
|
25033
|
+
* for dropped frames.
|
|
24005
25034
|
*
|
|
24006
|
-
* Output geometry: the broker does not pass source width/height
|
|
24007
|
-
*
|
|
24008
|
-
*
|
|
25035
|
+
* Output geometry: the broker does not pass source width/height, so ffmpeg picks
|
|
25036
|
+
* the height (`scale=min(iw\,maxW):-2`) and the session learns the exact output
|
|
25037
|
+
* W×H by parsing the muxer's `Output #0` stderr block
|
|
24009
25038
|
* ({@link parseFfmpegOutputDims}). Until dims are known, stdout is buffered.
|
|
24010
25039
|
*/
|
|
24011
25040
|
var noopLogger = {
|
|
@@ -24045,8 +25074,6 @@ var MAX_GOP_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
|
24045
25074
|
var FfmpegDecoderSession = class {
|
|
24046
25075
|
config;
|
|
24047
25076
|
logger;
|
|
24048
|
-
frameSink;
|
|
24049
|
-
nodeId;
|
|
24050
25077
|
renderDevice;
|
|
24051
25078
|
ffmpegPath;
|
|
24052
25079
|
/**
|
|
@@ -24096,9 +25123,6 @@ var FfmpegDecoderSession = class {
|
|
|
24096
25123
|
/** Stdout chunks received BEFORE geometry is known — replayed into the
|
|
24097
25124
|
* splitter once `frameSize` is learned. A short-lived list (no concat). */
|
|
24098
25125
|
pendingStdout = [];
|
|
24099
|
-
/** The shm ring slot the splitter is currently filling, if any — aborted on
|
|
24100
|
-
* respawn / teardown so a half-filled slot never publishes. */
|
|
24101
|
-
openSlot = null;
|
|
24102
25126
|
stderrAccum = "";
|
|
24103
25127
|
destroyed = false;
|
|
24104
25128
|
pixel;
|
|
@@ -24108,9 +25132,7 @@ var FfmpegDecoderSession = class {
|
|
|
24108
25132
|
outWidth = 0;
|
|
24109
25133
|
outHeight = 0;
|
|
24110
25134
|
frameSize = 0;
|
|
24111
|
-
frameRingSink = null;
|
|
24112
25135
|
frameCallbacks = /* @__PURE__ */ new Set();
|
|
24113
|
-
handleCallbacks = /* @__PURE__ */ new Set();
|
|
24114
25136
|
frameDropper;
|
|
24115
25137
|
inputPackets = 0;
|
|
24116
25138
|
/**
|
|
@@ -24134,8 +25156,6 @@ var FfmpegDecoderSession = class {
|
|
|
24134
25156
|
if (typeof config.deviceId === "number") sessionTags["deviceId"] = config.deviceId;
|
|
24135
25157
|
if (typeof config.tag === "string" && config.tag.length > 0) sessionTags["tag"] = config.tag;
|
|
24136
25158
|
this.logger = Object.keys(sessionTags).length > 0 ? logger.withTags(sessionTags) : logger;
|
|
24137
|
-
this.frameSink = options?.frameSink ?? "callback";
|
|
24138
|
-
this.nodeId = options?.nodeId ?? "local";
|
|
24139
25159
|
this.renderDevice = options?.renderDevice;
|
|
24140
25160
|
this.ffmpegPath = options?.ffmpegPath ?? "ffmpeg";
|
|
24141
25161
|
const seededChain = options?.hwaccelChain ?? (options?.hwaccel != null && !isSoftwareHwAccel(options.hwaccel) ? [options.hwaccel] : []);
|
|
@@ -24148,10 +25168,6 @@ var FfmpegDecoderSession = class {
|
|
|
24148
25168
|
this.frameFormat = this.pixel === "gray" ? "gray" : "rgb";
|
|
24149
25169
|
this.frameDropper = new FrameDropper(config.maxFps);
|
|
24150
25170
|
}
|
|
24151
|
-
/** Exposed so the owning addon can surface ring stats via `getShmStats`. */
|
|
24152
|
-
get frameRingSinkOrNull() {
|
|
24153
|
-
return this.frameRingSink;
|
|
24154
|
-
}
|
|
24155
25171
|
get isPullMode() {
|
|
24156
25172
|
return this.pullUrl !== null;
|
|
24157
25173
|
}
|
|
@@ -24173,26 +25189,11 @@ var FfmpegDecoderSession = class {
|
|
|
24173
25189
|
this.logger.info("ffmpeg decoder: pull-mode openStream — dialing input URL", { meta: { url } });
|
|
24174
25190
|
this.spawnFfmpeg();
|
|
24175
25191
|
}
|
|
24176
|
-
ensureFrameRingSink() {
|
|
24177
|
-
if (this.frameRingSink === null) {
|
|
24178
|
-
const seedParts = [];
|
|
24179
|
-
if (typeof this.config.deviceId === "number") seedParts.push(String(this.config.deviceId));
|
|
24180
|
-
if (typeof this.config.tag === "string" && this.config.tag.length > 0) seedParts.push(this.config.tag);
|
|
24181
|
-
const seed = seedParts.length > 0 ? seedParts.join(":") : "anon";
|
|
24182
|
-
this.frameRingSink = new _camstack_shm_ring.DecoderFrameRingSink({
|
|
24183
|
-
seed,
|
|
24184
|
-
logger: this.logger,
|
|
24185
|
-
nodeId: this.nodeId
|
|
24186
|
-
});
|
|
24187
|
-
}
|
|
24188
|
-
return this.frameRingSink;
|
|
24189
|
-
}
|
|
24190
25192
|
spawnFfmpeg() {
|
|
24191
25193
|
if (this.destroyed) return;
|
|
24192
25194
|
this.spawnAttempted = true;
|
|
24193
25195
|
this.clearPullRedial();
|
|
24194
25196
|
this.killFfmpeg();
|
|
24195
|
-
this.abortOpenSlot();
|
|
24196
25197
|
this.splitter = null;
|
|
24197
25198
|
this.pendingStdout = [];
|
|
24198
25199
|
this.stderrAccum = "";
|
|
@@ -24238,8 +25239,7 @@ var FfmpegDecoderSession = class {
|
|
|
24238
25239
|
hwaccel: this.hwaccel,
|
|
24239
25240
|
gpuScale: this.activeGpuScaleFilter ?? "cpu",
|
|
24240
25241
|
codec: this.config.codec,
|
|
24241
|
-
format: this.frameFormat
|
|
24242
|
-
sink: this.frameSink
|
|
25242
|
+
format: this.frameFormat
|
|
24243
25243
|
} });
|
|
24244
25244
|
}
|
|
24245
25245
|
killFfmpeg() {
|
|
@@ -24377,36 +25377,10 @@ var FfmpegDecoderSession = class {
|
|
|
24377
25377
|
return {
|
|
24378
25378
|
startFrame: () => {
|
|
24379
25379
|
if (!this.frameDropper.shouldKeep()) return null;
|
|
24380
|
-
if (this.frameSink === "shm") {
|
|
24381
|
-
const reserved = this.ensureFrameRingSink().beginFrame(this.outWidth, this.outHeight, this.frameFormat);
|
|
24382
|
-
if (reserved === null) return null;
|
|
24383
|
-
this.openSlot = reserved.slot;
|
|
24384
|
-
return reserved.buffer;
|
|
24385
|
-
}
|
|
24386
25380
|
return Buffer.allocUnsafe(this.frameSize);
|
|
24387
25381
|
},
|
|
24388
25382
|
finishFrame: (target) => {
|
|
24389
25383
|
this.outputFrames++;
|
|
24390
|
-
if (this.frameSink === "shm") {
|
|
24391
|
-
const slot = this.openSlot;
|
|
24392
|
-
this.openSlot = null;
|
|
24393
|
-
const sink = this.frameRingSink;
|
|
24394
|
-
if (slot === null || sink === null) return;
|
|
24395
|
-
const handle = sink.commitFrame(slot, {
|
|
24396
|
-
width: this.outWidth,
|
|
24397
|
-
height: this.outHeight,
|
|
24398
|
-
format: this.frameFormat,
|
|
24399
|
-
pts: performance.now(),
|
|
24400
|
-
byteLength: this.frameSize
|
|
24401
|
-
});
|
|
24402
|
-
if (handle === null) return;
|
|
24403
|
-
const delivered = {
|
|
24404
|
-
handle,
|
|
24405
|
-
timestamp: Date.now()
|
|
24406
|
-
};
|
|
24407
|
-
for (const cb of this.handleCallbacks) cb(delivered);
|
|
24408
|
-
return;
|
|
24409
|
-
}
|
|
24410
25384
|
const frame = {
|
|
24411
25385
|
data: target,
|
|
24412
25386
|
width: this.outWidth,
|
|
@@ -24421,14 +25395,6 @@ var FfmpegDecoderSession = class {
|
|
|
24421
25395
|
}
|
|
24422
25396
|
};
|
|
24423
25397
|
}
|
|
24424
|
-
/** Abandon a shm slot the splitter left open (respawn / teardown mid-frame),
|
|
24425
|
-
* so a half-filled slot is never published. */
|
|
24426
|
-
abortOpenSlot() {
|
|
24427
|
-
const slot = this.openSlot;
|
|
24428
|
-
if (slot === null) return;
|
|
24429
|
-
this.openSlot = null;
|
|
24430
|
-
this.frameRingSink?.abortFrame(slot);
|
|
24431
|
-
}
|
|
24432
25398
|
pushPacket(packet) {
|
|
24433
25399
|
if (this.destroyed) return;
|
|
24434
25400
|
if (this.pullUrl !== null) return;
|
|
@@ -24482,13 +25448,6 @@ var FfmpegDecoderSession = class {
|
|
|
24482
25448
|
this.frameCallbacks.delete(callback);
|
|
24483
25449
|
};
|
|
24484
25450
|
}
|
|
24485
|
-
/** Subscribe to shared-memory frame handles — fires only in `'shm'` mode. */
|
|
24486
|
-
onFrameHandle(callback) {
|
|
24487
|
-
this.handleCallbacks.add(callback);
|
|
24488
|
-
return () => {
|
|
24489
|
-
this.handleCallbacks.delete(callback);
|
|
24490
|
-
};
|
|
24491
|
-
}
|
|
24492
25451
|
updateConfig(update) {
|
|
24493
25452
|
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;
|
|
24494
25453
|
this.config = {
|
|
@@ -24503,12 +25462,8 @@ var FfmpegDecoderSession = class {
|
|
|
24503
25462
|
this.destroyed = true;
|
|
24504
25463
|
this.clearPullRedial();
|
|
24505
25464
|
this.killFfmpeg();
|
|
24506
|
-
this.abortOpenSlot();
|
|
24507
25465
|
this.splitter = null;
|
|
24508
|
-
this.frameRingSink?.destroy();
|
|
24509
|
-
this.frameRingSink = null;
|
|
24510
25466
|
this.frameCallbacks.clear();
|
|
24511
|
-
this.handleCallbacks.clear();
|
|
24512
25467
|
}
|
|
24513
25468
|
getStats() {
|
|
24514
25469
|
const uptimeSec = Math.max((Date.now() - this.startTime) / 1e3, 1);
|
|
@@ -24579,71 +25534,6 @@ function probeGpuScaleFilters(ffmpegPath, logger) {
|
|
|
24579
25534
|
});
|
|
24580
25535
|
}
|
|
24581
25536
|
//#endregion
|
|
24582
|
-
//#region src/shm-orphan-purge.ts
|
|
24583
|
-
/**
|
|
24584
|
-
* Startup reclamation of orphaned shared-memory segments.
|
|
24585
|
-
*
|
|
24586
|
-
* A decoder writes frames into named `/dev/shm` segments and unlinks each one
|
|
24587
|
-
* on graceful session teardown (`DecoderFrameRingSink.destroy`). When the
|
|
24588
|
-
* decoder process dies *ungracefully* — SIGBUS, OOM-kill, or a SIGKILL during
|
|
24589
|
-
* redeploy — that teardown never runs and the segment is orphaned: it stays in
|
|
24590
|
-
* the tmpfs forever, since nothing else knows its name. Across many
|
|
24591
|
-
* crashes/redeploys these accumulate until `/dev/shm` fills, at which point the
|
|
24592
|
-
* next `mmap` write faults with an uncatchable SIGBUS and the decoder
|
|
24593
|
-
* crash-loops into its circuit breaker (which is exactly the incident this
|
|
24594
|
-
* guards against).
|
|
24595
|
-
*
|
|
24596
|
-
* The reclamation is safe *at process startup*: a freshly-booting decoder owns
|
|
24597
|
-
* no live sessions, so every pre-existing segment with its prefix is by
|
|
24598
|
-
* definition an orphan from a dead instance. `shm_unlink` only removes the
|
|
24599
|
-
* name — any consumer still holding a mapping keeps reading valid memory until
|
|
24600
|
-
* it closes (POSIX deferred reclaim + the ring seqlock), so unlinking is safe
|
|
24601
|
-
* even if a stale reader is momentarily still attached.
|
|
24602
|
-
*
|
|
24603
|
-
* POSIX-only: segments surface as files under `/dev/shm` on Linux. On platforms
|
|
24604
|
-
* without that directory (Windows, macOS) the scan finds nothing — no-op.
|
|
24605
|
-
*
|
|
24606
|
-
* NOTE: this lives inside the decoder addon (not `@camstack/shm-ring`) so it
|
|
24607
|
-
* ships in the self-contained addon bundle via `camstack deploy`, reusing the
|
|
24608
|
-
* already-deployed `unlinkSegment`; no host base-image rebuild required.
|
|
24609
|
-
*/
|
|
24610
|
-
/** Default tmpfs directory where POSIX shared-memory segments appear on Linux. */
|
|
24611
|
-
var DEFAULT_SHM_DIR = "/dev/shm";
|
|
24612
|
-
/**
|
|
24613
|
-
* Unlink every shared-memory segment whose name starts with `prefix`.
|
|
24614
|
-
*
|
|
24615
|
-
* Intended to run ONCE at decoder startup, before any session is created, to
|
|
24616
|
-
* reclaim segments orphaned by a previously-crashed instance. A per-file unlink
|
|
24617
|
-
* failure is swallowed so one stuck segment cannot block reclaiming the rest.
|
|
24618
|
-
*/
|
|
24619
|
-
function purgeOrphanSegments(prefix, options = {}) {
|
|
24620
|
-
const dir = options.dir ?? DEFAULT_SHM_DIR;
|
|
24621
|
-
const unlink = options.unlink ?? _camstack_shm_ring.unlinkSegment;
|
|
24622
|
-
let entries;
|
|
24623
|
-
try {
|
|
24624
|
-
entries = (0, node_fs.readdirSync)(dir);
|
|
24625
|
-
} catch {
|
|
24626
|
-
return {
|
|
24627
|
-
scanned: 0,
|
|
24628
|
-
removed: 0,
|
|
24629
|
-
names: []
|
|
24630
|
-
};
|
|
24631
|
-
}
|
|
24632
|
-
const names = [];
|
|
24633
|
-
for (const name of entries) {
|
|
24634
|
-
if (!name.startsWith(prefix)) continue;
|
|
24635
|
-
try {
|
|
24636
|
-
unlink(name);
|
|
24637
|
-
names.push(name);
|
|
24638
|
-
} catch {}
|
|
24639
|
-
}
|
|
24640
|
-
return {
|
|
24641
|
-
scanned: entries.length,
|
|
24642
|
-
removed: names.length,
|
|
24643
|
-
names
|
|
24644
|
-
};
|
|
24645
|
-
}
|
|
24646
|
-
//#endregion
|
|
24647
25537
|
//#region src/audio-codec/ffmpeg-audio-process.ts
|
|
24648
25538
|
/**
|
|
24649
25539
|
* Minimal ffmpeg subprocess wrapper shared by the audio decode + encode
|
|
@@ -25812,12 +26702,8 @@ var DEFAULT_DECODER_FFMPEG_ADDON_CONFIG = { ...DEFAULT_DECODER_HWACCEL_CONFIG };
|
|
|
25812
26702
|
var DecoderFfmpegAddon = class extends BaseAddon {
|
|
25813
26703
|
sessions = /* @__PURE__ */ new Map();
|
|
25814
26704
|
frameBuffers = /* @__PURE__ */ new Map();
|
|
25815
|
-
handleBuffers = /* @__PURE__ */ new Map();
|
|
25816
26705
|
unsubscribers = /* @__PURE__ */ new Map();
|
|
25817
26706
|
sessionMeta = /* @__PURE__ */ new Map();
|
|
25818
|
-
frameReaders = null;
|
|
25819
|
-
getFrameHits = 0;
|
|
25820
|
-
getFrameMisses = 0;
|
|
25821
26707
|
/**
|
|
25822
26708
|
* The ffmpeg binary every session spawns — resolved once at init via the
|
|
25823
26709
|
* node-level `ctx.deps.ensureFfmpeg()`. The SAME binary the broker / recorder
|
|
@@ -25900,12 +26786,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
25900
26786
|
return registrations;
|
|
25901
26787
|
}
|
|
25902
26788
|
this.ctx.logger.info("ffmpeg decoder addon initialized", { meta: { selectedBackend: backend } });
|
|
25903
|
-
const purged = purgeOrphanSegments(_camstack_shm_ring.SEGMENT_NAME_PREFIX);
|
|
25904
|
-
if (purged.removed > 0) this.ctx.logger.warn("ffmpeg decoder: reclaimed orphaned shm segments at startup", { meta: {
|
|
25905
|
-
removed: purged.removed,
|
|
25906
|
-
scanned: purged.scanned
|
|
25907
|
-
} });
|
|
25908
|
-
this.frameReaders = new _camstack_shm_ring.FrameRingReaderCache(this.ctx.logger);
|
|
25909
26789
|
this.probedGpuScaleFilters = await probeGpuScaleFilters(this.ffmpegPath, this.ctx.logger);
|
|
25910
26790
|
this.ctx.logger.info("decoder-ffmpeg: probed GPU scale filters", { meta: {
|
|
25911
26791
|
filters: [...this.probedGpuScaleFilters],
|
|
@@ -26047,15 +26927,12 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26047
26927
|
}
|
|
26048
26928
|
async createSession(config) {
|
|
26049
26929
|
const sessionId = (0, node_crypto.randomUUID)();
|
|
26050
|
-
const { frameSink } = config;
|
|
26051
26930
|
const nodeId = this.resolveLocalNodeId();
|
|
26052
26931
|
const hwaccelChain = await this.resolveDecodeHwaccelChain();
|
|
26053
26932
|
const availableGpuScaleFilters = new Set([...this.probedGpuScaleFilters].filter((f) => !this.unsupportedGpuScaleFilters.has(f)));
|
|
26054
26933
|
const session = new FfmpegDecoderSession(config, this.ctx.logger, {
|
|
26055
26934
|
hwaccelChain,
|
|
26056
26935
|
ffmpegPath: this.ffmpegPath,
|
|
26057
|
-
frameSink,
|
|
26058
|
-
nodeId,
|
|
26059
26936
|
availableGpuScaleFilters,
|
|
26060
26937
|
onGpuScaleUnsupported: (filter) => {
|
|
26061
26938
|
if (this.unsupportedGpuScaleFilters.has(filter)) return;
|
|
@@ -26063,7 +26940,7 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26063
26940
|
this.ctx.logger.warn("decoder-ffmpeg: GPU scaler non-functional on this build — future sessions use CPU scale", { meta: { filter } });
|
|
26064
26941
|
}
|
|
26065
26942
|
});
|
|
26066
|
-
const unsub =
|
|
26943
|
+
const unsub = this.wireCallbackSink(sessionId, session);
|
|
26067
26944
|
this.sessions.set(sessionId, session);
|
|
26068
26945
|
this.unsubscribers.set(sessionId, unsub);
|
|
26069
26946
|
this.sessionMeta.set(sessionId, {
|
|
@@ -26078,7 +26955,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26078
26955
|
sessionId,
|
|
26079
26956
|
codec: config.codec,
|
|
26080
26957
|
hwaccelChain,
|
|
26081
|
-
frameSink,
|
|
26082
26958
|
nodeId
|
|
26083
26959
|
}
|
|
26084
26960
|
});
|
|
@@ -26105,13 +26981,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26105
26981
|
ringBuffer.push(capFrame);
|
|
26106
26982
|
});
|
|
26107
26983
|
}
|
|
26108
|
-
wireShmSink(sessionId, session) {
|
|
26109
|
-
const handleRing = new NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
26110
|
-
this.handleBuffers.set(sessionId, handleRing);
|
|
26111
|
-
return session.onFrameHandle((frame) => {
|
|
26112
|
-
handleRing.push(frame.handle);
|
|
26113
|
-
});
|
|
26114
|
-
}
|
|
26115
26984
|
async destroySession(input) {
|
|
26116
26985
|
const { sessionId } = input;
|
|
26117
26986
|
const session = this.sessions.get(sessionId);
|
|
@@ -26119,12 +26988,10 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26119
26988
|
const unsub = this.unsubscribers.get(sessionId);
|
|
26120
26989
|
if (unsub) unsub();
|
|
26121
26990
|
this.frameBuffers.get(sessionId)?.cancel();
|
|
26122
|
-
this.handleBuffers.get(sessionId)?.cancel();
|
|
26123
26991
|
const deviceId = this.sessionMeta.get(sessionId)?.deviceId;
|
|
26124
26992
|
await session.destroy();
|
|
26125
26993
|
this.sessions.delete(sessionId);
|
|
26126
26994
|
this.frameBuffers.delete(sessionId);
|
|
26127
|
-
this.handleBuffers.delete(sessionId);
|
|
26128
26995
|
this.unsubscribers.delete(sessionId);
|
|
26129
26996
|
this.sessionMeta.delete(sessionId);
|
|
26130
26997
|
this.ctx.logger.info("ffmpeg: destroyed session", {
|
|
@@ -26164,12 +27031,14 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26164
27031
|
if (ringBuffer.size === 0 && input.waitMs) await ringBuffer.waitForItem(input.waitMs);
|
|
26165
27032
|
return ringBuffer.drain(input.maxCount);
|
|
26166
27033
|
}
|
|
26167
|
-
|
|
26168
|
-
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26172
|
-
|
|
27034
|
+
/**
|
|
27035
|
+
* DORMANT STUB. The shm `FrameHandle` frame ring was removed (the runner ships
|
|
27036
|
+
* inline pixels via `pullFrames` now). The cap method stays defined for wire
|
|
27037
|
+
* compat until the framework train drops it, but no session ever produces
|
|
27038
|
+
* handles — always empty.
|
|
27039
|
+
*/
|
|
27040
|
+
async pullHandles(_input) {
|
|
27041
|
+
return [];
|
|
26173
27042
|
}
|
|
26174
27043
|
async updateConfig(input) {
|
|
26175
27044
|
const session = this.sessions.get(input.sessionId);
|
|
@@ -26181,33 +27050,20 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26181
27050
|
if (!session) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|
|
26182
27051
|
return session.getStats();
|
|
26183
27052
|
}
|
|
26184
|
-
|
|
26185
|
-
|
|
26186
|
-
|
|
26187
|
-
|
|
26188
|
-
|
|
26189
|
-
|
|
26190
|
-
|
|
26191
|
-
const arrayBuf = new ArrayBuffer(frame.data.byteLength);
|
|
26192
|
-
new Uint8Array(arrayBuf).set(frame.data);
|
|
26193
|
-
return {
|
|
26194
|
-
data: new Uint8Array(arrayBuf),
|
|
26195
|
-
width: frame.width,
|
|
26196
|
-
height: frame.height,
|
|
26197
|
-
format: frame.format,
|
|
26198
|
-
timestamp: frame.timestamp
|
|
26199
|
-
};
|
|
27053
|
+
/**
|
|
27054
|
+
* DORMANT STUB. Frames are no longer retained in a shm ring this addon can
|
|
27055
|
+
* read back by handle — the decode-owning node serves pixels over its own cap
|
|
27056
|
+
* surface. Kept defined for wire compat; always resolves `null`.
|
|
27057
|
+
*/
|
|
27058
|
+
async getFrame(_input) {
|
|
27059
|
+
return null;
|
|
26200
27060
|
}
|
|
26201
|
-
|
|
26202
|
-
|
|
26203
|
-
|
|
26204
|
-
|
|
26205
|
-
|
|
26206
|
-
|
|
26207
|
-
budgetMb: _camstack_shm_ring.RING_BUDGET_MB,
|
|
26208
|
-
getFrameHits: this.getFrameHits,
|
|
26209
|
-
getFrameMisses: this.getFrameMisses
|
|
26210
|
-
};
|
|
27061
|
+
/**
|
|
27062
|
+
* DORMANT STUB. No session writes a shm frame ring anymore, so there are no
|
|
27063
|
+
* ring stats to report. Kept defined for wire compat; always `null`.
|
|
27064
|
+
*/
|
|
27065
|
+
async getShmStats(_input) {
|
|
27066
|
+
return null;
|
|
26211
27067
|
}
|
|
26212
27068
|
async onShutdown() {
|
|
26213
27069
|
this.ctx.logger.info("ffmpeg decoder addon shutdown — destroying all sessions");
|
|
@@ -26222,11 +27078,8 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26222
27078
|
await Promise.all(destroyPromises);
|
|
26223
27079
|
this.sessions.clear();
|
|
26224
27080
|
this.frameBuffers.clear();
|
|
26225
|
-
this.handleBuffers.clear();
|
|
26226
27081
|
this.unsubscribers.clear();
|
|
26227
27082
|
this.sessionMeta.clear();
|
|
26228
|
-
this.frameReaders?.close();
|
|
26229
|
-
this.frameReaders = null;
|
|
26230
27083
|
}
|
|
26231
27084
|
};
|
|
26232
27085
|
//#endregion
|