@camstack/addon-decoder-nodeav 1.1.11 → 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1067 -404
- package/dist/index.mjs +1068 -393
- package/package.json +1 -2
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { DecoderFrameRingSink, FrameRingReaderCache, RING_BUDGET_MB, SEGMENT_NAME_PREFIX, makeSegmentName, unlinkSegment } from "@camstack/shm-ring";
|
|
2
1
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { readdirSync } from "node:fs";
|
|
4
2
|
//#region ../../node_modules/zod/v4/core/core.js
|
|
5
3
|
var _a$1;
|
|
6
4
|
function $constructor(name, initializer, params) {
|
|
@@ -4289,6 +4287,14 @@ function object(shape, params) {
|
|
|
4289
4287
|
...normalizeParams(params)
|
|
4290
4288
|
});
|
|
4291
4289
|
}
|
|
4290
|
+
function looseObject(shape, params) {
|
|
4291
|
+
return new ZodObject({
|
|
4292
|
+
type: "object",
|
|
4293
|
+
shape,
|
|
4294
|
+
catchall: unknown(),
|
|
4295
|
+
...normalizeParams(params)
|
|
4296
|
+
});
|
|
4297
|
+
}
|
|
4292
4298
|
var ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
|
|
4293
4299
|
$ZodUnion.init(inst, def);
|
|
4294
4300
|
ZodType.init(inst, def);
|
|
@@ -4630,7 +4636,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4630
4636
|
return inst;
|
|
4631
4637
|
}
|
|
4632
4638
|
//#endregion
|
|
4633
|
-
//#region ../types/dist/sleep-
|
|
4639
|
+
//#region ../types/dist/sleep-_sv7WKkq.mjs
|
|
4634
4640
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4635
4641
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4636
4642
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5040,10 +5046,51 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
5040
5046
|
EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
|
|
5041
5047
|
EventCategory["EnrichmentSceneStateChanged"] = "enrichment.scene.state-changed";
|
|
5042
5048
|
EventCategory["EnrichmentActivitySummary"] = "enrichment.activity.summary";
|
|
5049
|
+
/**
|
|
5050
|
+
* Unified track-lifecycle event: ONE category carrying `phase:
|
|
5051
|
+
* 'start' | 'update' | 'end'` with a rich, typed
|
|
5052
|
+
* `PipelineAnalyticsTrackLifecyclePayload`. Supersedes the thin
|
|
5053
|
+
* `PipelineAnalyticsTrackStarted` / `PipelineAnalyticsTrackEnded`
|
|
5054
|
+
* pair — a consumer subscribes once and branches on `phase`.
|
|
5055
|
+
*/
|
|
5056
|
+
EventCategory["PipelineAnalyticsTrackLifecycle"] = "pipeline-analytics.track-lifecycle";
|
|
5057
|
+
/**
|
|
5058
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5059
|
+
* (`phase:'start'`). Kept as a soft alias — no known subscribers.
|
|
5060
|
+
*/
|
|
5043
5061
|
EventCategory["PipelineAnalyticsTrackStarted"] = "pipeline-analytics.track-started";
|
|
5062
|
+
/**
|
|
5063
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5064
|
+
* (`phase:'end'`). Kept as a soft alias — no known subscribers.
|
|
5065
|
+
*/
|
|
5044
5066
|
EventCategory["PipelineAnalyticsTrackEnded"] = "pipeline-analytics.track-ended";
|
|
5045
5067
|
EventCategory["PipelineAnalyticsDetectionEvent"] = "pipeline-analytics.detection-event";
|
|
5046
5068
|
EventCategory["PipelineAnalyticsFrameTracked"] = "pipeline-analytics.frame-tracked";
|
|
5069
|
+
/**
|
|
5070
|
+
* Fired by `addon-post-analysis` when a parked (stationary) object appears
|
|
5071
|
+
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
5072
|
+
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
5073
|
+
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
5074
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
5075
|
+
*/
|
|
5076
|
+
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
5077
|
+
/**
|
|
5078
|
+
* Fired by `addon-post-analysis` whenever a gallery face row changes:
|
|
5079
|
+
* `kind:'buffered'` a new detected face was persisted, `'assigned'` /
|
|
5080
|
+
* `'unassigned'` its identity link changed, `'deleted'` the row was
|
|
5081
|
+
* removed. Telemetry semantics (D8): a lost event only delays a refresh,
|
|
5082
|
+
* never a correctness issue. Payload `PipelineAnalyticsFaceGalleryChangedPayload`.
|
|
5083
|
+
*/
|
|
5084
|
+
EventCategory["PipelineAnalyticsFaceGalleryChanged"] = "pipeline-analytics.face-gallery-changed";
|
|
5085
|
+
/**
|
|
5086
|
+
* Fired by `addon-post-analysis` whenever a gallery plate row changes:
|
|
5087
|
+
* `kind:'buffered'` a new read was persisted (`PlateRecognizer.onTrackEnd`),
|
|
5088
|
+
* `'assigned'` / `'unassigned'` its vehicle link changed
|
|
5089
|
+
* (`PlateGalleryProvider`), `'deleted'` the row was removed. Telemetry
|
|
5090
|
+
* semantics (D8): a lost event only delays a refresh, never a correctness
|
|
5091
|
+
* issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
|
|
5092
|
+
*/
|
|
5093
|
+
EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
|
|
5047
5094
|
EventCategory["FrigateLiveEvent"] = "frigate.live-event";
|
|
5048
5095
|
EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
|
|
5049
5096
|
/**
|
|
@@ -6884,7 +6931,8 @@ var MODEL_FORMATS = [
|
|
|
6884
6931
|
"coreml",
|
|
6885
6932
|
"openvino",
|
|
6886
6933
|
"tflite",
|
|
6887
|
-
"pt"
|
|
6934
|
+
"pt",
|
|
6935
|
+
"gguf"
|
|
6888
6936
|
];
|
|
6889
6937
|
/**
|
|
6890
6938
|
* Multi-file format payload.
|
|
@@ -6928,7 +6976,8 @@ var ModelFormatsSchema = object({
|
|
|
6928
6976
|
coreml: ModelFormatEntrySchema.optional(),
|
|
6929
6977
|
openvino: ModelFormatEntrySchema.optional(),
|
|
6930
6978
|
tflite: ModelFormatEntrySchema.optional(),
|
|
6931
|
-
pt: ModelFormatEntrySchema.optional()
|
|
6979
|
+
pt: ModelFormatEntrySchema.optional(),
|
|
6980
|
+
gguf: ModelFormatEntrySchema.optional()
|
|
6932
6981
|
});
|
|
6933
6982
|
/**
|
|
6934
6983
|
* Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
|
|
@@ -6969,6 +7018,15 @@ var ModelCatalogEntrySchema = object({
|
|
|
6969
7018
|
width: number(),
|
|
6970
7019
|
height: number()
|
|
6971
7020
|
}),
|
|
7021
|
+
/**
|
|
7022
|
+
* Channel count of the model input tensor. Omit ⇒ 3 (RGB), the default for
|
|
7023
|
+
* every detector / classifier / embedder. Set to 1 for a grayscale CTC text
|
|
7024
|
+
* recognizer (EasyOCR VGG plate-OCR: input `[N,1,H,W]`) so the preprocess
|
|
7025
|
+
* feeds a single-channel, EasyOCR-normalized tensor instead of the default
|
|
7026
|
+
* 3-channel RGB one. Threaded through `PoolModelConfig.inputChannels` to the
|
|
7027
|
+
* Python inference pool.
|
|
7028
|
+
*/
|
|
7029
|
+
inputChannels: number().int().positive().optional(),
|
|
6972
7030
|
labels: array(LabelDefinitionSchema).readonly(),
|
|
6973
7031
|
inputLayout: _enum(["nchw", "nhwc"]).optional(),
|
|
6974
7032
|
inputNormalization: _enum([
|
|
@@ -6978,6 +7036,16 @@ var ModelCatalogEntrySchema = object({
|
|
|
6978
7036
|
]).optional(),
|
|
6979
7037
|
preprocessMode: _enum(["letterbox", "resize"]).optional(),
|
|
6980
7038
|
/**
|
|
7039
|
+
* Per-MODEL postprocessor override. Absent ⇒ the step's own
|
|
7040
|
+
* `StepDefinition.postprocessor` applies (the normal case — every model in a
|
|
7041
|
+
* step shares its decode). Set it when a step hosts models with DIFFERENT raw
|
|
7042
|
+
* output layouts under one slot: e.g. object-detection is `'yolo'` by default,
|
|
7043
|
+
* but a Coral SSD MobileNet build emits the `TFLite_Detection_PostProcess`
|
|
7044
|
+
* 4-tensor layout and needs `'ssd'`. Threaded into `PoolModelConfig.postprocessor`
|
|
7045
|
+
* by the engine factory (`modelEntry.postprocessor ?? def.postprocessor`).
|
|
7046
|
+
*/
|
|
7047
|
+
postprocessor: custom().optional(),
|
|
7048
|
+
/**
|
|
6981
7049
|
* When true, the executor produces a landmark-aligned crop (similarity warp
|
|
6982
7050
|
* onto the canonical template) before this step runs, instead of a plain
|
|
6983
7051
|
* axis-aligned bbox crop. Required for face-recognition embedders (ArcFace):
|
|
@@ -10423,7 +10491,8 @@ var ModelFormatSchema$1 = _enum([
|
|
|
10423
10491
|
"coreml",
|
|
10424
10492
|
"openvino",
|
|
10425
10493
|
"tflite",
|
|
10426
|
-
"pt"
|
|
10494
|
+
"pt",
|
|
10495
|
+
"gguf"
|
|
10427
10496
|
]);
|
|
10428
10497
|
var PipelineSlotSchema = _enum([
|
|
10429
10498
|
"detector",
|
|
@@ -10517,7 +10586,8 @@ var EngineProvisioningSchema = object({
|
|
|
10517
10586
|
runtimeId: _enum([
|
|
10518
10587
|
"onnx",
|
|
10519
10588
|
"openvino",
|
|
10520
|
-
"coreml"
|
|
10589
|
+
"coreml",
|
|
10590
|
+
"edgetpu"
|
|
10521
10591
|
]).nullable(),
|
|
10522
10592
|
device: string().nullable(),
|
|
10523
10593
|
state: _enum([
|
|
@@ -11729,6 +11799,140 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
|
|
|
11729
11799
|
deviceId: number(),
|
|
11730
11800
|
status: PtzAutotrackStatusSchema
|
|
11731
11801
|
});
|
|
11802
|
+
/**
|
|
11803
|
+
* scene-monitor — device-scoped reference-region state cap. An operator marks
|
|
11804
|
+
* a rect ROI on a camera frame and names one or more states; the engine
|
|
11805
|
+
* (pipeline-analytics, designated post-processing node) reports which state is
|
|
11806
|
+
* active on an ongoing basis. Two operator-selectable check modes share every-
|
|
11807
|
+
* thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
|
|
11808
|
+
* vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
|
|
11809
|
+
*
|
|
11810
|
+
* D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) — the framework
|
|
11811
|
+
* derives the device-detail contribution; the provider carries NO hand-written
|
|
11812
|
+
* settings-contribution methods. `status.kind:'push'` — the engine pushes on
|
|
11813
|
+
* every hysteresis flip / availability change; consumers never poll.
|
|
11814
|
+
*/
|
|
11815
|
+
/** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
|
|
11816
|
+
* be added without a wire break (matching falls back to any-condition refs). */
|
|
11817
|
+
var SceneConditionSchema = string();
|
|
11818
|
+
/** One captured reference — condition-tagged, model-version-gated. `embedding`
|
|
11819
|
+
* is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
|
|
11820
|
+
var SceneReferenceSchema = object({
|
|
11821
|
+
embedding: array(number()),
|
|
11822
|
+
modelId: string(),
|
|
11823
|
+
condition: SceneConditionSchema,
|
|
11824
|
+
capturedAt: number(),
|
|
11825
|
+
thumbnailMediaId: string().optional()
|
|
11826
|
+
});
|
|
11827
|
+
var SceneMonitorStateSchema = object({
|
|
11828
|
+
id: string(),
|
|
11829
|
+
label: string(),
|
|
11830
|
+
references: array(SceneReferenceSchema),
|
|
11831
|
+
textPrompts: array(string()).optional(),
|
|
11832
|
+
referenceCount: number(),
|
|
11833
|
+
currentlyMatched: boolean()
|
|
11834
|
+
});
|
|
11835
|
+
/** Per-mode comparator params in a discriminated union so an `llm` scene never
|
|
11836
|
+
* carries similarity knobs and vice-versa. */
|
|
11837
|
+
var SceneCheckSchema = discriminatedUnion("mode", [object({
|
|
11838
|
+
mode: literal("similarity"),
|
|
11839
|
+
threshold: number().min(0).max(1),
|
|
11840
|
+
hysteresisCount: number().int().positive()
|
|
11841
|
+
}), object({
|
|
11842
|
+
mode: literal("llm"),
|
|
11843
|
+
prompt: string(),
|
|
11844
|
+
profileId: string().optional(),
|
|
11845
|
+
hysteresisCount: number().int().positive()
|
|
11846
|
+
})]);
|
|
11847
|
+
var SceneMonitorSchema = object({
|
|
11848
|
+
id: string(),
|
|
11849
|
+
label: string(),
|
|
11850
|
+
roi: MaskRectShapeSchema,
|
|
11851
|
+
enabled: boolean(),
|
|
11852
|
+
triggerMode: _enum([
|
|
11853
|
+
"periodic",
|
|
11854
|
+
"on-motion",
|
|
11855
|
+
"both"
|
|
11856
|
+
]),
|
|
11857
|
+
checkIntervalSec: number().optional(),
|
|
11858
|
+
check: SceneCheckSchema,
|
|
11859
|
+
states: array(SceneMonitorStateSchema),
|
|
11860
|
+
currentStateId: string().nullable(),
|
|
11861
|
+
lastCheckedAt: number().nullable(),
|
|
11862
|
+
lastConfidence: number().nullable(),
|
|
11863
|
+
currentCondition: SceneConditionSchema.nullable(),
|
|
11864
|
+
availability: _enum(["ok", "unavailable"]),
|
|
11865
|
+
unavailableReason: string().nullable()
|
|
11866
|
+
});
|
|
11867
|
+
var SceneMonitorStatusSchema = object({
|
|
11868
|
+
monitors: array(SceneMonitorSchema),
|
|
11869
|
+
lastFetchedAt: number()
|
|
11870
|
+
});
|
|
11871
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
|
|
11872
|
+
deviceId: number(),
|
|
11873
|
+
label: string(),
|
|
11874
|
+
roi: MaskRectShapeSchema,
|
|
11875
|
+
check: SceneCheckSchema,
|
|
11876
|
+
triggerMode: _enum([
|
|
11877
|
+
"periodic",
|
|
11878
|
+
"on-motion",
|
|
11879
|
+
"both"
|
|
11880
|
+
]).optional(),
|
|
11881
|
+
checkIntervalSec: number().optional()
|
|
11882
|
+
}), SceneMonitorSchema, {
|
|
11883
|
+
kind: "mutation",
|
|
11884
|
+
auth: "admin"
|
|
11885
|
+
}), method(object({
|
|
11886
|
+
deviceId: number(),
|
|
11887
|
+
monitorId: string(),
|
|
11888
|
+
patch: object({
|
|
11889
|
+
label: string().optional(),
|
|
11890
|
+
roi: MaskRectShapeSchema.optional(),
|
|
11891
|
+
enabled: boolean().optional(),
|
|
11892
|
+
triggerMode: _enum([
|
|
11893
|
+
"periodic",
|
|
11894
|
+
"on-motion",
|
|
11895
|
+
"both"
|
|
11896
|
+
]).optional(),
|
|
11897
|
+
checkIntervalSec: number().optional(),
|
|
11898
|
+
check: SceneCheckSchema.optional()
|
|
11899
|
+
})
|
|
11900
|
+
}), _void(), {
|
|
11901
|
+
kind: "mutation",
|
|
11902
|
+
auth: "admin"
|
|
11903
|
+
}), method(object({
|
|
11904
|
+
deviceId: number(),
|
|
11905
|
+
monitorId: string()
|
|
11906
|
+
}), _void(), {
|
|
11907
|
+
kind: "mutation",
|
|
11908
|
+
auth: "admin"
|
|
11909
|
+
}), method(object({
|
|
11910
|
+
deviceId: number(),
|
|
11911
|
+
monitorId: string(),
|
|
11912
|
+
stateId: string().optional(),
|
|
11913
|
+
label: string().optional(),
|
|
11914
|
+
condition: SceneConditionSchema.optional()
|
|
11915
|
+
}), object({
|
|
11916
|
+
stateId: string(),
|
|
11917
|
+
referenceCount: number()
|
|
11918
|
+
}), {
|
|
11919
|
+
kind: "mutation",
|
|
11920
|
+
auth: "admin"
|
|
11921
|
+
}), method(object({
|
|
11922
|
+
deviceId: number(),
|
|
11923
|
+
monitorId: string(),
|
|
11924
|
+
stateId: string(),
|
|
11925
|
+
index: number()
|
|
11926
|
+
}), _void(), {
|
|
11927
|
+
kind: "mutation",
|
|
11928
|
+
auth: "admin"
|
|
11929
|
+
}), method(object({
|
|
11930
|
+
deviceId: number(),
|
|
11931
|
+
monitorId: string()
|
|
11932
|
+
}), _void(), {
|
|
11933
|
+
kind: "mutation",
|
|
11934
|
+
auth: "admin"
|
|
11935
|
+
});
|
|
11732
11936
|
object({
|
|
11733
11937
|
/** Whether the script is currently executing. */
|
|
11734
11938
|
isRunning: boolean(),
|
|
@@ -12136,6 +12340,36 @@ var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
|
|
|
12136
12340
|
* with the per-track detail panel and live overlay. */
|
|
12137
12341
|
trackIds: array(string()).readonly()
|
|
12138
12342
|
});
|
|
12343
|
+
/**
|
|
12344
|
+
* A parked ("stationary") object surfaced alongside occupancy — an object that
|
|
12345
|
+
* settled and stopped moving. It is NO LONGER a tracked object (the tracker was
|
|
12346
|
+
* told to forget it so it stops re-spawning tracks/events), but it IS still
|
|
12347
|
+
* physically present, so it keeps counting toward `frame` occupancy and is
|
|
12348
|
+
* listed here so the UI can show it in a dedicated "Stationary" section instead
|
|
12349
|
+
* of flooding the live event feed.
|
|
12350
|
+
*/
|
|
12351
|
+
var StationaryObjectSchema = object({
|
|
12352
|
+
id: string(),
|
|
12353
|
+
className: string(),
|
|
12354
|
+
bbox: object({
|
|
12355
|
+
x: number(),
|
|
12356
|
+
y: number(),
|
|
12357
|
+
w: number(),
|
|
12358
|
+
h: number()
|
|
12359
|
+
}),
|
|
12360
|
+
frameWidth: number().int().nonnegative(),
|
|
12361
|
+
frameHeight: number().int().nonnegative(),
|
|
12362
|
+
/** When the source track was first seen. */
|
|
12363
|
+
firstSeenAt: number().int(),
|
|
12364
|
+
/** When the object was recognised as parked (promotion time). */
|
|
12365
|
+
becameStationaryAt: number().int(),
|
|
12366
|
+
/** Last frame a detection confirmed the object is still there. */
|
|
12367
|
+
lastConfirmedAt: number().int(),
|
|
12368
|
+
/** Enrichment label carried from the source track (identity / plate). */
|
|
12369
|
+
label: string().optional(),
|
|
12370
|
+
/** Native-resolution key-frame media key for the parked object's best image. */
|
|
12371
|
+
keyFrameMediaKey: string().optional()
|
|
12372
|
+
});
|
|
12139
12373
|
var CameraOccupancySnapshotSchema = object({
|
|
12140
12374
|
/** Frame timestamp of the inference result that produced this snapshot. */
|
|
12141
12375
|
ts: number().int(),
|
|
@@ -12144,10 +12378,15 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
12144
12378
|
frameHeight: number().int().nonnegative(),
|
|
12145
12379
|
/** Per-zone breakdown — one entry per defined zone (user + onboard). */
|
|
12146
12380
|
zones: array(ZoneScopeBreakdownSchema).readonly(),
|
|
12147
|
-
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12381
|
+
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12382
|
+
* INCLUDES currently-confirmed stationary objects (they are still present). */
|
|
12148
12383
|
frame: PerScopeBreakdownSchema,
|
|
12149
12384
|
/** Detections that landed outside every zone. Empty when no zones defined. */
|
|
12150
|
-
unzoned: PerScopeBreakdownSchema
|
|
12385
|
+
unzoned: PerScopeBreakdownSchema,
|
|
12386
|
+
/** Parked objects on this camera (additive — absent on legacy snapshots).
|
|
12387
|
+
* Surfaced separately so the UI shows them in a dedicated section rather
|
|
12388
|
+
* than as repeated tracks/events. */
|
|
12389
|
+
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
12151
12390
|
});
|
|
12152
12391
|
/**
|
|
12153
12392
|
* Time-series resolution. The history methods return one bucket per
|
|
@@ -12750,7 +12989,12 @@ var NotificationRuleConditionsSchema = object({
|
|
|
12750
12989
|
clipDescription: object({
|
|
12751
12990
|
text: string().min(1),
|
|
12752
12991
|
minSimilarity: number().min(0).max(1)
|
|
12753
|
-
}).optional()
|
|
12992
|
+
}).optional(),
|
|
12993
|
+
/** Match events whose recognized-entity label (face identity name or plate
|
|
12994
|
+
* vehicle name, propagated onto `event.data.label`) is one of these values.
|
|
12995
|
+
* Empty/absent → unaffected (back-compat). Enables "notify me when <named
|
|
12996
|
+
* vehicle/person> is seen". */
|
|
12997
|
+
labels: array(string()).readonly().optional()
|
|
12754
12998
|
});
|
|
12755
12999
|
var NotificationRuleTemplateSchema = object({
|
|
12756
13000
|
title: string(),
|
|
@@ -13045,11 +13289,19 @@ method(object({
|
|
|
13045
13289
|
* login page needs NO change.
|
|
13046
13290
|
*
|
|
13047
13291
|
* - `widget` — a Module-Federation widget the login page mounts (via
|
|
13048
|
-
* `loadRemoteBundle`) for an in-page ceremony.
|
|
13049
|
-
*
|
|
13050
|
-
*
|
|
13051
|
-
*
|
|
13052
|
-
*
|
|
13292
|
+
* `loadRemoteBundle`) for an in-page ceremony. `auth.listLoginMethods`
|
|
13293
|
+
* stamps a public `bundleUrl` from `addonId` + `bundle`. Generic
|
|
13294
|
+
* mechanism kept for future use; no shipped addon uses it on the login
|
|
13295
|
+
* page (the passkey ceremony below runs natively in the shell instead).
|
|
13296
|
+
*
|
|
13297
|
+
* - `passkey` — a declarative WebAuthn ceremony the shell renders
|
|
13298
|
+
* natively (`@simplewebauthn/browser` lives in `addon-admin-ui`, not in
|
|
13299
|
+
* a remotely-loaded bundle). Carries the addon's effective `rpId` /
|
|
13300
|
+
* `origin` (from its `resolveRpID()` / `resolveOrigin()`) so the shell
|
|
13301
|
+
* can gate visibility (IP-literal origin, hostname/rpId mismatch) WITHOUT
|
|
13302
|
+
* fetching any remote code pre-auth. Contribution stays unconditional —
|
|
13303
|
+
* enrollment state is never leaked pre-auth; visibility is a shell
|
|
13304
|
+
* decision.
|
|
13053
13305
|
*
|
|
13054
13306
|
* Every contribution carries a `stage`:
|
|
13055
13307
|
* - `primary` — shown on the first credentials screen (OIDC /
|
|
@@ -13063,30 +13315,45 @@ method(object({
|
|
|
13063
13315
|
*/
|
|
13064
13316
|
/** When a login method renders in the two-phase login flow. */
|
|
13065
13317
|
var LoginStageEnum = _enum(["primary", "second-factor"]);
|
|
13066
|
-
/** One login-method contribution — redirect button
|
|
13067
|
-
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13078
|
-
|
|
13079
|
-
|
|
13080
|
-
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13318
|
+
/** One login-method contribution — redirect button, pre-auth widget, or native passkey ceremony. */
|
|
13319
|
+
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13320
|
+
object({
|
|
13321
|
+
kind: literal("redirect"),
|
|
13322
|
+
/** Stable id within the login-method set (e.g. `auth-oidc/google`). */
|
|
13323
|
+
id: string(),
|
|
13324
|
+
/** Operator-facing button label. */
|
|
13325
|
+
label: string(),
|
|
13326
|
+
/** lucide-react icon name. */
|
|
13327
|
+
icon: string().optional(),
|
|
13328
|
+
/** Addon-owned HTTP route the button navigates to (GET). */
|
|
13329
|
+
startUrl: string(),
|
|
13330
|
+
stage: LoginStageEnum
|
|
13331
|
+
}),
|
|
13332
|
+
object({
|
|
13333
|
+
kind: literal("widget"),
|
|
13334
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
|
|
13335
|
+
id: string(),
|
|
13336
|
+
/** Owning addon id — drives the public bundle URL + the MF namespace. */
|
|
13337
|
+
addonId: string(),
|
|
13338
|
+
/** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
|
|
13339
|
+
bundle: string(),
|
|
13340
|
+
/** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
|
|
13341
|
+
remote: WidgetRemoteSchema,
|
|
13342
|
+
stage: LoginStageEnum
|
|
13343
|
+
}),
|
|
13344
|
+
object({
|
|
13345
|
+
kind: literal("passkey"),
|
|
13346
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-direct-login`). */
|
|
13347
|
+
id: string(),
|
|
13348
|
+
/** Operator-facing button label. */
|
|
13349
|
+
label: string(),
|
|
13350
|
+
stage: LoginStageEnum,
|
|
13351
|
+
/** Effective WebAuthn RP ID (`resolveRpID()`) — the shell gates visibility on it. */
|
|
13352
|
+
rpId: string(),
|
|
13353
|
+
/** Effective expected origin (`resolveOrigin()`), null when unconfigured. */
|
|
13354
|
+
origin: string().nullable()
|
|
13355
|
+
})
|
|
13356
|
+
]);
|
|
13090
13357
|
method(_void(), array(LoginMethodContributionSchema).readonly());
|
|
13091
13358
|
/**
|
|
13092
13359
|
* Orchestrator-side destination metadata. The orchestrator computes
|
|
@@ -15007,14 +15274,14 @@ var TargetKindCapsSchema = object({
|
|
|
15007
15274
|
* the union is large and not meant for runtime validation here; the exported
|
|
15008
15275
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
15009
15276
|
*/
|
|
15010
|
-
var ConfigSchemaPassthrough = unknown();
|
|
15277
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
15011
15278
|
var TargetKindSchema = object({
|
|
15012
15279
|
kind: string(),
|
|
15013
15280
|
label: string(),
|
|
15014
15281
|
icon: string(),
|
|
15015
15282
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
15016
15283
|
addonId: string(),
|
|
15017
|
-
configSchema: ConfigSchemaPassthrough,
|
|
15284
|
+
configSchema: ConfigSchemaPassthrough$1,
|
|
15018
15285
|
supportsDiscovery: boolean(),
|
|
15019
15286
|
caps: TargetKindCapsSchema
|
|
15020
15287
|
});
|
|
@@ -15067,6 +15334,298 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
15067
15334
|
enabled: boolean()
|
|
15068
15335
|
}), _void(), { kind: "mutation" });
|
|
15069
15336
|
/**
|
|
15337
|
+
* Shared LLM generate contracts — imported by BOTH `llm.cap.ts` (consumer
|
|
15338
|
+
* surface) and `llm-runtime.cap.ts` (node-side managed executor) so the two
|
|
15339
|
+
* caps stay wire-compatible without a circular cap→cap import.
|
|
15340
|
+
*
|
|
15341
|
+
* Errors are a discriminated-union RESULT, never thrown: the shape survives
|
|
15342
|
+
* every transport tier structurally, and failed calls still write usage rows.
|
|
15343
|
+
* Token counts only in v1 — no costUsd (operator decision, 2026-07-15).
|
|
15344
|
+
*/
|
|
15345
|
+
var LlmUsageSchema = object({
|
|
15346
|
+
inputTokens: number(),
|
|
15347
|
+
outputTokens: number()
|
|
15348
|
+
});
|
|
15349
|
+
var LlmErrorCodeSchema = _enum([
|
|
15350
|
+
"timeout",
|
|
15351
|
+
"rate-limited",
|
|
15352
|
+
"auth",
|
|
15353
|
+
"refusal",
|
|
15354
|
+
"bad-request",
|
|
15355
|
+
"unavailable",
|
|
15356
|
+
"no-profile",
|
|
15357
|
+
"budget-exceeded",
|
|
15358
|
+
"adapter-error"
|
|
15359
|
+
]);
|
|
15360
|
+
var LlmGenerateResultSchema = discriminatedUnion("ok", [object({
|
|
15361
|
+
ok: literal(true),
|
|
15362
|
+
text: string(),
|
|
15363
|
+
model: string(),
|
|
15364
|
+
usage: LlmUsageSchema,
|
|
15365
|
+
truncated: boolean(),
|
|
15366
|
+
latencyMs: number()
|
|
15367
|
+
}), object({
|
|
15368
|
+
ok: literal(false),
|
|
15369
|
+
code: LlmErrorCodeSchema,
|
|
15370
|
+
message: string(),
|
|
15371
|
+
retryAfterMs: number().optional()
|
|
15372
|
+
})]);
|
|
15373
|
+
/**
|
|
15374
|
+
* `Uint8Array` is the sanctioned binary convention — superjson + the UDS
|
|
15375
|
+
* MsgPack channel round-trip typed arrays (embedding-encoder.cap.ts:29,
|
|
15376
|
+
* notification-output.cap.ts:27-31 precedents).
|
|
15377
|
+
*/
|
|
15378
|
+
var LlmImageSchema = object({
|
|
15379
|
+
bytes: _instanceof(Uint8Array),
|
|
15380
|
+
mimeType: string()
|
|
15381
|
+
});
|
|
15382
|
+
var LlmGenerateBaseInputSchema = object({
|
|
15383
|
+
/** Collection routing (the notification-output posture). */
|
|
15384
|
+
addonId: string().optional(),
|
|
15385
|
+
/** Explicit profile; else the resolution chain (spec §3). */
|
|
15386
|
+
profileId: string().optional(),
|
|
15387
|
+
/** MANDATORY usage tag: 'ai-summary', 'notifier-rules', 'adhoc-ui', … */
|
|
15388
|
+
consumer: string(),
|
|
15389
|
+
system: string().optional(),
|
|
15390
|
+
/** v1: single-turn. `messages[]` is a v2 additive field. */
|
|
15391
|
+
prompt: string(),
|
|
15392
|
+
/** Structured output — adapter-mapped (response_format / forced tool / responseSchema). */
|
|
15393
|
+
jsonSchema: record(string(), unknown()).optional(),
|
|
15394
|
+
/** Per-call override of the profile default. */
|
|
15395
|
+
maxTokens: number().int().positive().optional(),
|
|
15396
|
+
temperature: number().optional()
|
|
15397
|
+
});
|
|
15398
|
+
/**
|
|
15399
|
+
* `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
|
|
15400
|
+
* on EVERY node where `addon-ai` is installed; the hub `llm` provider reaches
|
|
15401
|
+
* a specific node's runtime with `nodePin(profile.runtime.nodeId)` — normal
|
|
15402
|
+
* cap routing, zero bespoke plumbing. `internal: true`: the operator reaches
|
|
15403
|
+
* this only through the `llm` cap's methods.
|
|
15404
|
+
*
|
|
15405
|
+
* One running llama-server child per node in v1 (models are RAM-heavy).
|
|
15406
|
+
* Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
|
|
15407
|
+
* watchdog — operator decision #3).
|
|
15408
|
+
*/
|
|
15409
|
+
var ManagedModelRefSchema = discriminatedUnion("kind", [
|
|
15410
|
+
object({
|
|
15411
|
+
kind: literal("catalog"),
|
|
15412
|
+
catalogId: string()
|
|
15413
|
+
}),
|
|
15414
|
+
object({
|
|
15415
|
+
kind: literal("url"),
|
|
15416
|
+
url: string(),
|
|
15417
|
+
sha256: string().optional()
|
|
15418
|
+
}),
|
|
15419
|
+
object({
|
|
15420
|
+
kind: literal("path"),
|
|
15421
|
+
path: string()
|
|
15422
|
+
})
|
|
15423
|
+
]);
|
|
15424
|
+
var ManagedRuntimeConfigSchema = object({
|
|
15425
|
+
/** WHERE the runtime lives — hub or any agent. */
|
|
15426
|
+
nodeId: string(),
|
|
15427
|
+
/** Closed for v1; 'ollama' is a v2 candidate. */
|
|
15428
|
+
engine: _enum(["llama-cpp"]),
|
|
15429
|
+
model: ManagedModelRefSchema,
|
|
15430
|
+
contextSize: number().int().default(4096),
|
|
15431
|
+
/** 0 = CPU-only. */
|
|
15432
|
+
gpuLayers: number().int().default(0),
|
|
15433
|
+
/** Default: cpus-2, clamped ≥1 (resolved node-side). */
|
|
15434
|
+
threads: number().int().optional(),
|
|
15435
|
+
/** Concurrent slots. */
|
|
15436
|
+
parallel: number().int().default(1),
|
|
15437
|
+
/** Else lazy: first generate boots it. */
|
|
15438
|
+
autoStart: boolean().default(false),
|
|
15439
|
+
/** 0 = never; frees RAM after quiet periods. */
|
|
15440
|
+
idleStopMinutes: number().int().default(30)
|
|
15441
|
+
});
|
|
15442
|
+
var LlmRuntimeStatusSchema = object({
|
|
15443
|
+
/** Status is ALWAYS node-qualified. */
|
|
15444
|
+
nodeId: string(),
|
|
15445
|
+
state: _enum([
|
|
15446
|
+
"stopped",
|
|
15447
|
+
"downloading",
|
|
15448
|
+
"starting",
|
|
15449
|
+
"ready",
|
|
15450
|
+
"crashed",
|
|
15451
|
+
"failed"
|
|
15452
|
+
]),
|
|
15453
|
+
pid: number().optional(),
|
|
15454
|
+
port: number().optional(),
|
|
15455
|
+
modelPath: string().optional(),
|
|
15456
|
+
modelId: string().optional(),
|
|
15457
|
+
downloadProgress: number().min(0).max(1).optional(),
|
|
15458
|
+
lastError: string().optional(),
|
|
15459
|
+
crashesInWindow: number(),
|
|
15460
|
+
/** Child RSS (sampled best-effort). */
|
|
15461
|
+
memoryBytes: number().optional(),
|
|
15462
|
+
vramBytes: number().optional()
|
|
15463
|
+
});
|
|
15464
|
+
var LlmNodeModelSchema = object({
|
|
15465
|
+
file: string(),
|
|
15466
|
+
sizeBytes: number(),
|
|
15467
|
+
catalogId: string().optional(),
|
|
15468
|
+
installedAt: number().optional()
|
|
15469
|
+
});
|
|
15470
|
+
var LlmRuntimeDiskUsageSchema = object({
|
|
15471
|
+
nodeId: string(),
|
|
15472
|
+
modelsBytes: number(),
|
|
15473
|
+
freeBytes: number().optional()
|
|
15474
|
+
});
|
|
15475
|
+
method(LlmGenerateBaseInputSchema.extend({
|
|
15476
|
+
images: array(LlmImageSchema).optional(),
|
|
15477
|
+
runtime: ManagedRuntimeConfigSchema,
|
|
15478
|
+
/** The managed profile's timeout, threaded by the hub provider. */
|
|
15479
|
+
timeoutMs: number().int().positive().optional()
|
|
15480
|
+
}), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
|
|
15481
|
+
kind: "mutation",
|
|
15482
|
+
auth: "admin"
|
|
15483
|
+
}), method(object({}), _void(), {
|
|
15484
|
+
kind: "mutation",
|
|
15485
|
+
auth: "admin"
|
|
15486
|
+
}), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
|
|
15487
|
+
kind: "mutation",
|
|
15488
|
+
auth: "admin"
|
|
15489
|
+
}), method(object({ file: string() }), _void(), {
|
|
15490
|
+
kind: "mutation",
|
|
15491
|
+
auth: "admin"
|
|
15492
|
+
}), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
|
|
15493
|
+
/**
|
|
15494
|
+
* `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
|
|
15495
|
+
* methods concat-fan across providers; single-row methods route to ONE
|
|
15496
|
+
* provider by the `addonId` in the call input (the notification-output
|
|
15497
|
+
* posture, notification-output.cap.ts:215-250). Provided by `addon-ai`
|
|
15498
|
+
* (hub-placed); the cap stays open for future providers.
|
|
15499
|
+
*
|
|
15500
|
+
* Profiles are ROWS (data), not addons: one row = one usable model endpoint.
|
|
15501
|
+
* `apiKey` is a password field — providers REDACT it on read and merge on
|
|
15502
|
+
* write; a stored key NEVER round-trips to a client.
|
|
15503
|
+
*/
|
|
15504
|
+
var LlmProfileKindSchema = _enum([
|
|
15505
|
+
"openai-compatible",
|
|
15506
|
+
"openai",
|
|
15507
|
+
"anthropic",
|
|
15508
|
+
"google",
|
|
15509
|
+
"managed-local"
|
|
15510
|
+
]);
|
|
15511
|
+
var LlmProfileSchema = object({
|
|
15512
|
+
id: string(),
|
|
15513
|
+
name: string(),
|
|
15514
|
+
kind: LlmProfileKindSchema,
|
|
15515
|
+
/** Stamped by the provider — keeps the fanned catalog routable. */
|
|
15516
|
+
addonId: string(),
|
|
15517
|
+
enabled: boolean(),
|
|
15518
|
+
/** Vendor model id, or the managed runtime's loaded model. */
|
|
15519
|
+
model: string(),
|
|
15520
|
+
/** Required for openai-compatible; override for cloud kinds. */
|
|
15521
|
+
baseUrl: string().optional(),
|
|
15522
|
+
/** ConfigUISchema type:'password' — never round-trips (spec §5). */
|
|
15523
|
+
apiKey: string().optional(),
|
|
15524
|
+
supportsVision: boolean(),
|
|
15525
|
+
temperature: number().min(0).max(2).optional(),
|
|
15526
|
+
maxTokens: number().int().positive().optional(),
|
|
15527
|
+
timeoutMs: number().int().positive().default(6e4),
|
|
15528
|
+
extraHeaders: record(string(), string()).optional(),
|
|
15529
|
+
/** kind === 'managed-local' only (spec §4). */
|
|
15530
|
+
runtime: ManagedRuntimeConfigSchema.optional()
|
|
15531
|
+
});
|
|
15532
|
+
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
15533
|
+
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
15534
|
+
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
15535
|
+
var ConfigSchemaPassthrough = unknown();
|
|
15536
|
+
var LlmProfileKindDescriptorSchema = object({
|
|
15537
|
+
kind: LlmProfileKindSchema,
|
|
15538
|
+
label: string(),
|
|
15539
|
+
icon: string(),
|
|
15540
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
15541
|
+
addonId: string(),
|
|
15542
|
+
configSchema: ConfigSchemaPassthrough
|
|
15543
|
+
});
|
|
15544
|
+
var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
|
|
15545
|
+
var LlmDefaultSchema = object({
|
|
15546
|
+
selector: LlmDefaultSelectorSchema,
|
|
15547
|
+
profileId: string()
|
|
15548
|
+
});
|
|
15549
|
+
/** Server-side rollup row — getUsage never dumps raw call rows (spec §6). */
|
|
15550
|
+
var LlmUsageRollupSchema = object({
|
|
15551
|
+
day: string(),
|
|
15552
|
+
consumer: string(),
|
|
15553
|
+
profileId: string(),
|
|
15554
|
+
calls: number(),
|
|
15555
|
+
okCalls: number(),
|
|
15556
|
+
errorCalls: number(),
|
|
15557
|
+
inputTokens: number(),
|
|
15558
|
+
outputTokens: number(),
|
|
15559
|
+
avgLatencyMs: number()
|
|
15560
|
+
});
|
|
15561
|
+
/** LLM-facing view over the reused ModelCatalogEntry mechanism (spec §4.2). */
|
|
15562
|
+
var ManagedModelCatalogEntrySchema = object({
|
|
15563
|
+
id: string(),
|
|
15564
|
+
label: string(),
|
|
15565
|
+
family: string(),
|
|
15566
|
+
purpose: _enum(["text", "vision"]),
|
|
15567
|
+
url: string(),
|
|
15568
|
+
sha256: string(),
|
|
15569
|
+
sizeBytes: number(),
|
|
15570
|
+
quantization: string(),
|
|
15571
|
+
/** Load-time guidance shown in the picker. */
|
|
15572
|
+
minRamBytes: number(),
|
|
15573
|
+
contextSizeDefault: number().int(),
|
|
15574
|
+
/** Vision models: companion projector file. */
|
|
15575
|
+
mmprojUrl: string().optional()
|
|
15576
|
+
});
|
|
15577
|
+
var LlmRuntimeNodeSchema = object({
|
|
15578
|
+
nodeId: string(),
|
|
15579
|
+
reachable: boolean(),
|
|
15580
|
+
status: LlmRuntimeStatusSchema.optional(),
|
|
15581
|
+
disk: LlmRuntimeDiskUsageSchema.optional(),
|
|
15582
|
+
error: string().optional()
|
|
15583
|
+
});
|
|
15584
|
+
var GenerateVisionInputSchema = LlmGenerateBaseInputSchema.extend({ images: array(LlmImageSchema).min(1) });
|
|
15585
|
+
var ProfileRefInputSchema = object({
|
|
15586
|
+
addonId: string(),
|
|
15587
|
+
profileId: string()
|
|
15588
|
+
});
|
|
15589
|
+
method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
|
|
15590
|
+
kind: "mutation",
|
|
15591
|
+
auth: "admin"
|
|
15592
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15593
|
+
kind: "mutation",
|
|
15594
|
+
auth: "admin"
|
|
15595
|
+
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
15596
|
+
kind: "mutation",
|
|
15597
|
+
auth: "admin"
|
|
15598
|
+
}), method(ProfileRefInputSchema, array(string())), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
15599
|
+
selector: LlmDefaultSelectorSchema,
|
|
15600
|
+
profileId: string().nullable()
|
|
15601
|
+
}), _void(), {
|
|
15602
|
+
kind: "mutation",
|
|
15603
|
+
auth: "admin"
|
|
15604
|
+
}), method(object({
|
|
15605
|
+
since: number().optional(),
|
|
15606
|
+
until: number().optional(),
|
|
15607
|
+
consumer: string().optional(),
|
|
15608
|
+
profileId: string().optional()
|
|
15609
|
+
}), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
|
|
15610
|
+
nodeId: string(),
|
|
15611
|
+
model: ManagedModelRefSchema
|
|
15612
|
+
}), _void(), {
|
|
15613
|
+
kind: "mutation",
|
|
15614
|
+
auth: "admin"
|
|
15615
|
+
}), method(object({
|
|
15616
|
+
nodeId: string(),
|
|
15617
|
+
file: string()
|
|
15618
|
+
}), _void(), {
|
|
15619
|
+
kind: "mutation",
|
|
15620
|
+
auth: "admin"
|
|
15621
|
+
}), method(ProfileRefInputSchema, LlmRuntimeStatusSchema), method(ProfileRefInputSchema, LlmRuntimeStatusSchema, {
|
|
15622
|
+
kind: "mutation",
|
|
15623
|
+
auth: "admin"
|
|
15624
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15625
|
+
kind: "mutation",
|
|
15626
|
+
auth: "admin"
|
|
15627
|
+
});
|
|
15628
|
+
/**
|
|
15070
15629
|
* Zod schemas for persisted record types.
|
|
15071
15630
|
*
|
|
15072
15631
|
* These schemas serve as the single source of truth for types that are
|
|
@@ -15268,6 +15827,22 @@ var TrackSnapshotSchema = object({
|
|
|
15268
15827
|
/** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
|
|
15269
15828
|
mediaKey: string()
|
|
15270
15829
|
});
|
|
15830
|
+
/**
|
|
15831
|
+
* One audio-classification label heard on the track's camera while the
|
|
15832
|
+
* track was alive, aggregated per label. An "episode" is one persisted
|
|
15833
|
+
* audio event (the confident-classification path: score ≥ the device's
|
|
15834
|
+
* `classificationMinScore`, class-change-or-heartbeat coalesced) — NOT
|
|
15835
|
+
* one 32 ms inference chunk, so counts stay human-scaled.
|
|
15836
|
+
*/
|
|
15837
|
+
var TrackAudioLabelSchema = object({
|
|
15838
|
+
label: string(),
|
|
15839
|
+
/** Highest classification score observed across the label's episodes. */
|
|
15840
|
+
peakScore: number(),
|
|
15841
|
+
/** Number of coalesced audio-event episodes carrying this label. */
|
|
15842
|
+
count: number(),
|
|
15843
|
+
firstAt: number(),
|
|
15844
|
+
lastAt: number()
|
|
15845
|
+
});
|
|
15271
15846
|
var TrackSchema = object({
|
|
15272
15847
|
trackId: string(),
|
|
15273
15848
|
deviceId: number(),
|
|
@@ -15282,6 +15857,10 @@ var TrackSchema = object({
|
|
|
15282
15857
|
snapshots: array(TrackSnapshotSchema).readonly(),
|
|
15283
15858
|
/** Deduplicated zones the track has entered at least once. */
|
|
15284
15859
|
zonesVisited: array(string()).readonly(),
|
|
15860
|
+
/** Deduplicated set of detector classes observed for this track over its
|
|
15861
|
+
* life (a track may be reclassified, e.g. person→vehicle). Absent on
|
|
15862
|
+
* legacy rows written before class accumulation shipped. */
|
|
15863
|
+
classes: array(string()).readonly().optional(),
|
|
15285
15864
|
/** Cumulative normalized distance travelled (0..1 units = full frame width). */
|
|
15286
15865
|
totalDistance: number(),
|
|
15287
15866
|
state: TrackStateSchema,
|
|
@@ -15295,7 +15874,11 @@ var TrackSchema = object({
|
|
|
15295
15874
|
bestEventId: string().optional(),
|
|
15296
15875
|
/** Tag of the importance sub-signal that dominated the score
|
|
15297
15876
|
* (identity|dwell|proximity|class|confidence|travel|zone). */
|
|
15298
|
-
importanceReason: string().optional()
|
|
15877
|
+
importanceReason: string().optional(),
|
|
15878
|
+
/** Audio-classification labels heard on the camera during the track's
|
|
15879
|
+
* life (score ≥ device `classificationMinScore`), aggregated per label.
|
|
15880
|
+
* Absent on legacy rows / tracks with no confident audio. */
|
|
15881
|
+
audioLabels: array(TrackAudioLabelSchema).readonly().optional()
|
|
15299
15882
|
});
|
|
15300
15883
|
var BaseEventFields = {
|
|
15301
15884
|
id: string(),
|
|
@@ -15443,7 +16026,7 @@ var KeyEventSchema = object({
|
|
|
15443
16026
|
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
15444
16027
|
windowMs: number().optional()
|
|
15445
16028
|
});
|
|
15446
|
-
|
|
16029
|
+
object({
|
|
15447
16030
|
trackId: string(),
|
|
15448
16031
|
className: string(),
|
|
15449
16032
|
confidence: number(),
|
|
@@ -15451,6 +16034,23 @@ var TrackedDetectionSchema = object({
|
|
|
15451
16034
|
zones: array(string()).readonly(),
|
|
15452
16035
|
state: TrackStateSchema
|
|
15453
16036
|
});
|
|
16037
|
+
var OverlayDetectionSchema = looseObject({
|
|
16038
|
+
id: string(),
|
|
16039
|
+
kind: _enum(["first-level", "detail"]),
|
|
16040
|
+
macroClass: string(),
|
|
16041
|
+
score: number(),
|
|
16042
|
+
bbox: object({
|
|
16043
|
+
x: number(),
|
|
16044
|
+
y: number(),
|
|
16045
|
+
width: number(),
|
|
16046
|
+
height: number()
|
|
16047
|
+
}),
|
|
16048
|
+
labels: array(looseObject({
|
|
16049
|
+
label: string(),
|
|
16050
|
+
score: number()
|
|
16051
|
+
})).readonly(),
|
|
16052
|
+
parentId: string().optional()
|
|
16053
|
+
});
|
|
15454
16054
|
var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: number() });
|
|
15455
16055
|
var SearchObjectEventsInput = object({
|
|
15456
16056
|
text: string(),
|
|
@@ -15461,6 +16061,20 @@ var SearchObjectEventsInput = object({
|
|
|
15461
16061
|
limit: number().default(50),
|
|
15462
16062
|
minScore: number().min(0).max(1).default(.2)
|
|
15463
16063
|
});
|
|
16064
|
+
var TrackCascadeCountsSchema = object({
|
|
16065
|
+
/** Persisted track roots deleted (authoritative). */
|
|
16066
|
+
tracks: number().int(),
|
|
16067
|
+
/** Object events removed with their tracks (best-effort; see note above). */
|
|
16068
|
+
events: number().int(),
|
|
16069
|
+
/** Track/face/plate-owned media removed (best-effort). Never identity media. */
|
|
16070
|
+
media: number().int(),
|
|
16071
|
+
/** Unassigned (non-enrolled) face reads removed (best-effort). */
|
|
16072
|
+
faces: number().int(),
|
|
16073
|
+
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
16074
|
+
plates: number().int(),
|
|
16075
|
+
/** Per-track CLIP search vectors removed (best-effort). */
|
|
16076
|
+
embeddings: number().int()
|
|
16077
|
+
});
|
|
15464
16078
|
DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
|
|
15465
16079
|
deviceId: number(),
|
|
15466
16080
|
trackId: string()
|
|
@@ -15492,6 +16106,24 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15492
16106
|
}), {
|
|
15493
16107
|
kind: "mutation",
|
|
15494
16108
|
auth: "admin"
|
|
16109
|
+
}), method(object({
|
|
16110
|
+
deviceId: number(),
|
|
16111
|
+
cutoffMs: number()
|
|
16112
|
+
}), TrackCascadeCountsSchema, {
|
|
16113
|
+
kind: "mutation",
|
|
16114
|
+
auth: "admin"
|
|
16115
|
+
}), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
|
|
16116
|
+
kind: "mutation",
|
|
16117
|
+
auth: "admin"
|
|
16118
|
+
}), method(object({
|
|
16119
|
+
deviceId: number(),
|
|
16120
|
+
trackIds: array(string()).min(1)
|
|
16121
|
+
}), object({
|
|
16122
|
+
deleted: number().int(),
|
|
16123
|
+
failed: array(string()).readonly()
|
|
16124
|
+
}), {
|
|
16125
|
+
kind: "mutation",
|
|
16126
|
+
auth: "admin"
|
|
15495
16127
|
}), method(object({
|
|
15496
16128
|
eventId: string(),
|
|
15497
16129
|
kind: MediaFileKindEnum.optional()
|
|
@@ -15500,7 +16132,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15500
16132
|
timestamp: number(),
|
|
15501
16133
|
frameWidth: number(),
|
|
15502
16134
|
frameHeight: number(),
|
|
15503
|
-
detections: array(
|
|
16135
|
+
detections: array(OverlayDetectionSchema).readonly()
|
|
15504
16136
|
}), object({
|
|
15505
16137
|
deviceId: number(),
|
|
15506
16138
|
trackId: string(),
|
|
@@ -18269,6 +18901,24 @@ DeviceType.Camera, method(object({
|
|
|
18269
18901
|
deviceId: number(),
|
|
18270
18902
|
status: OsdStatusSchema
|
|
18271
18903
|
});
|
|
18904
|
+
var VehicleSchema = object({
|
|
18905
|
+
id: string(),
|
|
18906
|
+
name: string(),
|
|
18907
|
+
sampleCount: number().int().nonnegative(),
|
|
18908
|
+
coverMediaKey: string().optional(),
|
|
18909
|
+
/** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
|
|
18910
|
+
coverBase64: string().optional()
|
|
18911
|
+
});
|
|
18912
|
+
var VehicleSampleInfoSchema = object({
|
|
18913
|
+
id: string(),
|
|
18914
|
+
/** The plate text this sample enrolled (self-labeling — no embedding). */
|
|
18915
|
+
text: string(),
|
|
18916
|
+
/** OCR confidence of the enrolled read (0..1). */
|
|
18917
|
+
score: number(),
|
|
18918
|
+
addedAt: number().int(),
|
|
18919
|
+
deviceId: number().int().optional(),
|
|
18920
|
+
base64: string().optional()
|
|
18921
|
+
});
|
|
18272
18922
|
var PlateInfoSchema = object({
|
|
18273
18923
|
plateId: string(),
|
|
18274
18924
|
deviceId: number().int(),
|
|
@@ -18280,6 +18930,16 @@ var PlateInfoSchema = object({
|
|
|
18280
18930
|
score: number(),
|
|
18281
18931
|
/** True when the text was manually corrected (exempt from retention). */
|
|
18282
18932
|
corrected: boolean(),
|
|
18933
|
+
/** Recognized vehicle id when a matcher lookup named this read (SQL NULL → absent). */
|
|
18934
|
+
recognizedVehicleId: string().optional(),
|
|
18935
|
+
/** Resolved vehicle name for `recognizedVehicleId` (UI convenience). */
|
|
18936
|
+
vehicleName: string().optional(),
|
|
18937
|
+
/** True once the read was assigned to a vehicle (enrolled as a sample). */
|
|
18938
|
+
assigned: boolean(),
|
|
18939
|
+
/** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
|
|
18940
|
+
plateBbox: BoundingBoxSchema.optional(),
|
|
18941
|
+
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
18942
|
+
keyFrameMediaKey: string().optional(),
|
|
18283
18943
|
base64: string().optional()
|
|
18284
18944
|
});
|
|
18285
18945
|
var MediaFileLiteSchema = object({
|
|
@@ -18319,6 +18979,48 @@ method(object({
|
|
|
18319
18979
|
}), method(object({ plateId: string() }), _void(), {
|
|
18320
18980
|
kind: "mutation",
|
|
18321
18981
|
auth: "admin"
|
|
18982
|
+
}), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
|
|
18983
|
+
kind: "mutation",
|
|
18984
|
+
auth: "admin"
|
|
18985
|
+
}), method(object({
|
|
18986
|
+
id: string(),
|
|
18987
|
+
name: string().min(1)
|
|
18988
|
+
}), _void(), {
|
|
18989
|
+
kind: "mutation",
|
|
18990
|
+
auth: "admin"
|
|
18991
|
+
}), method(object({ id: string() }), _void(), {
|
|
18992
|
+
kind: "mutation",
|
|
18993
|
+
auth: "admin"
|
|
18994
|
+
}), method(object({ vehicleId: string() }), array(VehicleSampleInfoSchema).readonly()), method(object({
|
|
18995
|
+
vehicleId: string(),
|
|
18996
|
+
sampleId: string()
|
|
18997
|
+
}), _void(), {
|
|
18998
|
+
kind: "mutation",
|
|
18999
|
+
auth: "admin"
|
|
19000
|
+
}), method(object({
|
|
19001
|
+
plateId: string(),
|
|
19002
|
+
vehicleId: string()
|
|
19003
|
+
}), _void(), {
|
|
19004
|
+
kind: "mutation",
|
|
19005
|
+
auth: "admin"
|
|
19006
|
+
}), method(object({ plateId: string() }), _void(), {
|
|
19007
|
+
kind: "mutation",
|
|
19008
|
+
auth: "admin"
|
|
19009
|
+
}), method(object({
|
|
19010
|
+
plateIds: array(string()).min(1),
|
|
19011
|
+
vehicleId: string()
|
|
19012
|
+
}), object({
|
|
19013
|
+
assigned: number().int(),
|
|
19014
|
+
failed: array(string()).readonly()
|
|
19015
|
+
}), {
|
|
19016
|
+
kind: "mutation",
|
|
19017
|
+
auth: "admin"
|
|
19018
|
+
}), method(object({ plateIds: array(string()).min(1) }), object({
|
|
19019
|
+
unassigned: number().int(),
|
|
19020
|
+
failed: array(string()).readonly()
|
|
19021
|
+
}), {
|
|
19022
|
+
kind: "mutation",
|
|
19023
|
+
auth: "admin"
|
|
18322
19024
|
});
|
|
18323
19025
|
var ModelFormatSchema = _enum(MODEL_FORMATS);
|
|
18324
19026
|
var HwAccelBackendInputSchema = _enum([
|
|
@@ -18397,6 +19099,10 @@ var GpuInfoSchema = object({
|
|
|
18397
19099
|
memoryMB: number().optional()
|
|
18398
19100
|
});
|
|
18399
19101
|
var NpuInfoSchema = object({ type: _enum(["apple-ane", "intel-npu"]) });
|
|
19102
|
+
var CoralInfoSchema = object({
|
|
19103
|
+
type: literal("coral-edgetpu"),
|
|
19104
|
+
bus: string().optional()
|
|
19105
|
+
});
|
|
18400
19106
|
var HardwareInfoSchema = object({
|
|
18401
19107
|
platform: HardwarePlatformSchema,
|
|
18402
19108
|
arch: HardwareArchSchema,
|
|
@@ -18405,7 +19111,8 @@ var HardwareInfoSchema = object({
|
|
|
18405
19111
|
totalRAM_MB: number(),
|
|
18406
19112
|
availableRAM_MB: number(),
|
|
18407
19113
|
gpu: GpuInfoSchema.nullable(),
|
|
18408
|
-
npu: NpuInfoSchema.nullable()
|
|
19114
|
+
npu: NpuInfoSchema.nullable(),
|
|
19115
|
+
coral: CoralInfoSchema.nullable().optional()
|
|
18409
19116
|
});
|
|
18410
19117
|
var PlatformScoreSchema = object({
|
|
18411
19118
|
runtime: _enum(["node", "python"]),
|
|
@@ -20864,6 +21571,168 @@ Object.freeze({
|
|
|
20864
21571
|
addonId: null,
|
|
20865
21572
|
access: "create"
|
|
20866
21573
|
},
|
|
21574
|
+
"llm.deleteModel": {
|
|
21575
|
+
capName: "llm",
|
|
21576
|
+
capScope: "system",
|
|
21577
|
+
addonId: null,
|
|
21578
|
+
access: "delete"
|
|
21579
|
+
},
|
|
21580
|
+
"llm.deleteProfile": {
|
|
21581
|
+
capName: "llm",
|
|
21582
|
+
capScope: "system",
|
|
21583
|
+
addonId: null,
|
|
21584
|
+
access: "delete"
|
|
21585
|
+
},
|
|
21586
|
+
"llm.generate": {
|
|
21587
|
+
capName: "llm",
|
|
21588
|
+
capScope: "system",
|
|
21589
|
+
addonId: null,
|
|
21590
|
+
access: "create"
|
|
21591
|
+
},
|
|
21592
|
+
"llm.generateVision": {
|
|
21593
|
+
capName: "llm",
|
|
21594
|
+
capScope: "system",
|
|
21595
|
+
addonId: null,
|
|
21596
|
+
access: "create"
|
|
21597
|
+
},
|
|
21598
|
+
"llm.getDefaults": {
|
|
21599
|
+
capName: "llm",
|
|
21600
|
+
capScope: "system",
|
|
21601
|
+
addonId: null,
|
|
21602
|
+
access: "view"
|
|
21603
|
+
},
|
|
21604
|
+
"llm.getRuntimeStatus": {
|
|
21605
|
+
capName: "llm",
|
|
21606
|
+
capScope: "system",
|
|
21607
|
+
addonId: null,
|
|
21608
|
+
access: "view"
|
|
21609
|
+
},
|
|
21610
|
+
"llm.getUsage": {
|
|
21611
|
+
capName: "llm",
|
|
21612
|
+
capScope: "system",
|
|
21613
|
+
addonId: null,
|
|
21614
|
+
access: "view"
|
|
21615
|
+
},
|
|
21616
|
+
"llm.installModel": {
|
|
21617
|
+
capName: "llm",
|
|
21618
|
+
capScope: "system",
|
|
21619
|
+
addonId: null,
|
|
21620
|
+
access: "create"
|
|
21621
|
+
},
|
|
21622
|
+
"llm.listModelCatalog": {
|
|
21623
|
+
capName: "llm",
|
|
21624
|
+
capScope: "system",
|
|
21625
|
+
addonId: null,
|
|
21626
|
+
access: "view"
|
|
21627
|
+
},
|
|
21628
|
+
"llm.listModels": {
|
|
21629
|
+
capName: "llm",
|
|
21630
|
+
capScope: "system",
|
|
21631
|
+
addonId: null,
|
|
21632
|
+
access: "view"
|
|
21633
|
+
},
|
|
21634
|
+
"llm.listNodeModels": {
|
|
21635
|
+
capName: "llm",
|
|
21636
|
+
capScope: "system",
|
|
21637
|
+
addonId: null,
|
|
21638
|
+
access: "view"
|
|
21639
|
+
},
|
|
21640
|
+
"llm.listProfileKinds": {
|
|
21641
|
+
capName: "llm",
|
|
21642
|
+
capScope: "system",
|
|
21643
|
+
addonId: null,
|
|
21644
|
+
access: "view"
|
|
21645
|
+
},
|
|
21646
|
+
"llm.listProfiles": {
|
|
21647
|
+
capName: "llm",
|
|
21648
|
+
capScope: "system",
|
|
21649
|
+
addonId: null,
|
|
21650
|
+
access: "view"
|
|
21651
|
+
},
|
|
21652
|
+
"llm.listRuntimeNodes": {
|
|
21653
|
+
capName: "llm",
|
|
21654
|
+
capScope: "system",
|
|
21655
|
+
addonId: null,
|
|
21656
|
+
access: "view"
|
|
21657
|
+
},
|
|
21658
|
+
"llm.setDefault": {
|
|
21659
|
+
capName: "llm",
|
|
21660
|
+
capScope: "system",
|
|
21661
|
+
addonId: null,
|
|
21662
|
+
access: "create"
|
|
21663
|
+
},
|
|
21664
|
+
"llm.startRuntime": {
|
|
21665
|
+
capName: "llm",
|
|
21666
|
+
capScope: "system",
|
|
21667
|
+
addonId: null,
|
|
21668
|
+
access: "create"
|
|
21669
|
+
},
|
|
21670
|
+
"llm.stopRuntime": {
|
|
21671
|
+
capName: "llm",
|
|
21672
|
+
capScope: "system",
|
|
21673
|
+
addonId: null,
|
|
21674
|
+
access: "create"
|
|
21675
|
+
},
|
|
21676
|
+
"llm.testProfile": {
|
|
21677
|
+
capName: "llm",
|
|
21678
|
+
capScope: "system",
|
|
21679
|
+
addonId: null,
|
|
21680
|
+
access: "create"
|
|
21681
|
+
},
|
|
21682
|
+
"llm.upsertProfile": {
|
|
21683
|
+
capName: "llm",
|
|
21684
|
+
capScope: "system",
|
|
21685
|
+
addonId: null,
|
|
21686
|
+
access: "create"
|
|
21687
|
+
},
|
|
21688
|
+
"llmRuntime.complete": {
|
|
21689
|
+
capName: "llm-runtime",
|
|
21690
|
+
capScope: "system",
|
|
21691
|
+
addonId: null,
|
|
21692
|
+
access: "create"
|
|
21693
|
+
},
|
|
21694
|
+
"llmRuntime.deleteModel": {
|
|
21695
|
+
capName: "llm-runtime",
|
|
21696
|
+
capScope: "system",
|
|
21697
|
+
addonId: null,
|
|
21698
|
+
access: "delete"
|
|
21699
|
+
},
|
|
21700
|
+
"llmRuntime.ensureStarted": {
|
|
21701
|
+
capName: "llm-runtime",
|
|
21702
|
+
capScope: "system",
|
|
21703
|
+
addonId: null,
|
|
21704
|
+
access: "create"
|
|
21705
|
+
},
|
|
21706
|
+
"llmRuntime.getDiskUsage": {
|
|
21707
|
+
capName: "llm-runtime",
|
|
21708
|
+
capScope: "system",
|
|
21709
|
+
addonId: null,
|
|
21710
|
+
access: "view"
|
|
21711
|
+
},
|
|
21712
|
+
"llmRuntime.installModel": {
|
|
21713
|
+
capName: "llm-runtime",
|
|
21714
|
+
capScope: "system",
|
|
21715
|
+
addonId: null,
|
|
21716
|
+
access: "create"
|
|
21717
|
+
},
|
|
21718
|
+
"llmRuntime.listLocalModels": {
|
|
21719
|
+
capName: "llm-runtime",
|
|
21720
|
+
capScope: "system",
|
|
21721
|
+
addonId: null,
|
|
21722
|
+
access: "view"
|
|
21723
|
+
},
|
|
21724
|
+
"llmRuntime.status": {
|
|
21725
|
+
capName: "llm-runtime",
|
|
21726
|
+
capScope: "system",
|
|
21727
|
+
addonId: null,
|
|
21728
|
+
access: "view"
|
|
21729
|
+
},
|
|
21730
|
+
"llmRuntime.stop": {
|
|
21731
|
+
capName: "llm-runtime",
|
|
21732
|
+
capScope: "system",
|
|
21733
|
+
addonId: null,
|
|
21734
|
+
access: "create"
|
|
21735
|
+
},
|
|
20867
21736
|
"localNetwork.getAllowedAddresses": {
|
|
20868
21737
|
capName: "local-network",
|
|
20869
21738
|
capScope: "system",
|
|
@@ -21494,6 +22363,12 @@ Object.freeze({
|
|
|
21494
22363
|
addonId: null,
|
|
21495
22364
|
access: "delete"
|
|
21496
22365
|
},
|
|
22366
|
+
"pipelineAnalytics.deleteTracks": {
|
|
22367
|
+
capName: "pipeline-analytics",
|
|
22368
|
+
capScope: "device",
|
|
22369
|
+
addonId: null,
|
|
22370
|
+
access: "delete"
|
|
22371
|
+
},
|
|
21497
22372
|
"pipelineAnalytics.getActiveTracks": {
|
|
21498
22373
|
capName: "pipeline-analytics",
|
|
21499
22374
|
capScope: "device",
|
|
@@ -21560,12 +22435,24 @@ Object.freeze({
|
|
|
21560
22435
|
addonId: null,
|
|
21561
22436
|
access: "create"
|
|
21562
22437
|
},
|
|
22438
|
+
"pipelineAnalytics.pruneTracksBefore": {
|
|
22439
|
+
capName: "pipeline-analytics",
|
|
22440
|
+
capScope: "device",
|
|
22441
|
+
addonId: null,
|
|
22442
|
+
access: "create"
|
|
22443
|
+
},
|
|
21563
22444
|
"pipelineAnalytics.searchObjectEvents": {
|
|
21564
22445
|
capName: "pipeline-analytics",
|
|
21565
22446
|
capScope: "device",
|
|
21566
22447
|
addonId: null,
|
|
21567
22448
|
access: "view"
|
|
21568
22449
|
},
|
|
22450
|
+
"pipelineAnalytics.wipeAllAnalytics": {
|
|
22451
|
+
capName: "pipeline-analytics",
|
|
22452
|
+
capScope: "device",
|
|
22453
|
+
addonId: null,
|
|
22454
|
+
access: "delete"
|
|
22455
|
+
},
|
|
21569
22456
|
"pipelineExecutor.cacheFrameInPool": {
|
|
21570
22457
|
capName: "pipeline-executor",
|
|
21571
22458
|
capScope: "system",
|
|
@@ -22070,18 +22957,42 @@ Object.freeze({
|
|
|
22070
22957
|
addonId: null,
|
|
22071
22958
|
access: "create"
|
|
22072
22959
|
},
|
|
22960
|
+
"plateGallery.assignPlate": {
|
|
22961
|
+
capName: "plate-gallery",
|
|
22962
|
+
capScope: "system",
|
|
22963
|
+
addonId: null,
|
|
22964
|
+
access: "create"
|
|
22965
|
+
},
|
|
22966
|
+
"plateGallery.assignPlates": {
|
|
22967
|
+
capName: "plate-gallery",
|
|
22968
|
+
capScope: "system",
|
|
22969
|
+
addonId: null,
|
|
22970
|
+
access: "create"
|
|
22971
|
+
},
|
|
22073
22972
|
"plateGallery.correctPlateText": {
|
|
22074
22973
|
capName: "plate-gallery",
|
|
22075
22974
|
capScope: "system",
|
|
22076
22975
|
addonId: null,
|
|
22077
22976
|
access: "create"
|
|
22078
22977
|
},
|
|
22978
|
+
"plateGallery.createVehicle": {
|
|
22979
|
+
capName: "plate-gallery",
|
|
22980
|
+
capScope: "system",
|
|
22981
|
+
addonId: null,
|
|
22982
|
+
access: "create"
|
|
22983
|
+
},
|
|
22079
22984
|
"plateGallery.deletePlate": {
|
|
22080
22985
|
capName: "plate-gallery",
|
|
22081
22986
|
capScope: "system",
|
|
22082
22987
|
addonId: null,
|
|
22083
22988
|
access: "delete"
|
|
22084
22989
|
},
|
|
22990
|
+
"plateGallery.deleteVehicle": {
|
|
22991
|
+
capName: "plate-gallery",
|
|
22992
|
+
capScope: "system",
|
|
22993
|
+
addonId: null,
|
|
22994
|
+
access: "delete"
|
|
22995
|
+
},
|
|
22085
22996
|
"plateGallery.getPlateByTrack": {
|
|
22086
22997
|
capName: "plate-gallery",
|
|
22087
22998
|
capScope: "system",
|
|
@@ -22100,6 +23011,30 @@ Object.freeze({
|
|
|
22100
23011
|
addonId: null,
|
|
22101
23012
|
access: "view"
|
|
22102
23013
|
},
|
|
23014
|
+
"plateGallery.listVehicles": {
|
|
23015
|
+
capName: "plate-gallery",
|
|
23016
|
+
capScope: "system",
|
|
23017
|
+
addonId: null,
|
|
23018
|
+
access: "view"
|
|
23019
|
+
},
|
|
23020
|
+
"plateGallery.listVehicleSamples": {
|
|
23021
|
+
capName: "plate-gallery",
|
|
23022
|
+
capScope: "system",
|
|
23023
|
+
addonId: null,
|
|
23024
|
+
access: "view"
|
|
23025
|
+
},
|
|
23026
|
+
"plateGallery.removeVehicleSample": {
|
|
23027
|
+
capName: "plate-gallery",
|
|
23028
|
+
capScope: "system",
|
|
23029
|
+
addonId: null,
|
|
23030
|
+
access: "delete"
|
|
23031
|
+
},
|
|
23032
|
+
"plateGallery.renameVehicle": {
|
|
23033
|
+
capName: "plate-gallery",
|
|
23034
|
+
capScope: "system",
|
|
23035
|
+
addonId: null,
|
|
23036
|
+
access: "create"
|
|
23037
|
+
},
|
|
22103
23038
|
"plateGallery.searchPlates": {
|
|
22104
23039
|
capName: "plate-gallery",
|
|
22105
23040
|
capScope: "system",
|
|
@@ -22112,6 +23047,18 @@ Object.freeze({
|
|
|
22112
23047
|
addonId: null,
|
|
22113
23048
|
access: "view"
|
|
22114
23049
|
},
|
|
23050
|
+
"plateGallery.unassignPlate": {
|
|
23051
|
+
capName: "plate-gallery",
|
|
23052
|
+
capScope: "system",
|
|
23053
|
+
addonId: null,
|
|
23054
|
+
access: "create"
|
|
23055
|
+
},
|
|
23056
|
+
"plateGallery.unassignPlates": {
|
|
23057
|
+
capName: "plate-gallery",
|
|
23058
|
+
capScope: "system",
|
|
23059
|
+
addonId: null,
|
|
23060
|
+
access: "create"
|
|
23061
|
+
},
|
|
22115
23062
|
"platformProbe.getCapabilities": {
|
|
22116
23063
|
capName: "platform-probe",
|
|
22117
23064
|
capScope: "system",
|
|
@@ -22304,6 +23251,48 @@ Object.freeze({
|
|
|
22304
23251
|
addonId: null,
|
|
22305
23252
|
access: "create"
|
|
22306
23253
|
},
|
|
23254
|
+
"sceneMonitor.captureReference": {
|
|
23255
|
+
capName: "scene-monitor",
|
|
23256
|
+
capScope: "device",
|
|
23257
|
+
addonId: null,
|
|
23258
|
+
access: "create"
|
|
23259
|
+
},
|
|
23260
|
+
"sceneMonitor.createScene": {
|
|
23261
|
+
capName: "scene-monitor",
|
|
23262
|
+
capScope: "device",
|
|
23263
|
+
addonId: null,
|
|
23264
|
+
access: "create"
|
|
23265
|
+
},
|
|
23266
|
+
"sceneMonitor.deleteReference": {
|
|
23267
|
+
capName: "scene-monitor",
|
|
23268
|
+
capScope: "device",
|
|
23269
|
+
addonId: null,
|
|
23270
|
+
access: "delete"
|
|
23271
|
+
},
|
|
23272
|
+
"sceneMonitor.deleteScene": {
|
|
23273
|
+
capName: "scene-monitor",
|
|
23274
|
+
capScope: "device",
|
|
23275
|
+
addonId: null,
|
|
23276
|
+
access: "delete"
|
|
23277
|
+
},
|
|
23278
|
+
"sceneMonitor.listScenes": {
|
|
23279
|
+
capName: "scene-monitor",
|
|
23280
|
+
capScope: "device",
|
|
23281
|
+
addonId: null,
|
|
23282
|
+
access: "view"
|
|
23283
|
+
},
|
|
23284
|
+
"sceneMonitor.recheckNow": {
|
|
23285
|
+
capName: "scene-monitor",
|
|
23286
|
+
capScope: "device",
|
|
23287
|
+
addonId: null,
|
|
23288
|
+
access: "create"
|
|
23289
|
+
},
|
|
23290
|
+
"sceneMonitor.updateScene": {
|
|
23291
|
+
capName: "scene-monitor",
|
|
23292
|
+
capScope: "device",
|
|
23293
|
+
addonId: null,
|
|
23294
|
+
access: "create"
|
|
23295
|
+
},
|
|
22307
23296
|
"scriptRunner.run": {
|
|
22308
23297
|
capName: "script-runner",
|
|
22309
23298
|
capScope: "device",
|
|
@@ -23388,6 +24377,7 @@ Object.freeze({
|
|
|
23388
24377
|
Object.freeze({
|
|
23389
24378
|
"broker": "broker",
|
|
23390
24379
|
"device-export": "device-export",
|
|
24380
|
+
"llm": "ai",
|
|
23391
24381
|
"mesh-network": "mesh",
|
|
23392
24382
|
"mqtt-broker": "broker",
|
|
23393
24383
|
"network-access": "ingress",
|
|
@@ -23522,77 +24512,12 @@ function rankDecodeHwAccels(preferred, supportedMethods) {
|
|
|
23522
24512
|
return candidates.sort((a, b) => decodeHwAccelRankIndex(a) - decodeHwAccelRankIndex(b));
|
|
23523
24513
|
}
|
|
23524
24514
|
//#endregion
|
|
23525
|
-
//#region src/shm-orphan-purge.ts
|
|
23526
|
-
/**
|
|
23527
|
-
* Startup reclamation of orphaned shared-memory segments.
|
|
23528
|
-
*
|
|
23529
|
-
* A decoder writes frames into named `/dev/shm` segments and unlinks each one
|
|
23530
|
-
* on graceful session teardown (`DecoderFrameRingSink.destroy`). When the
|
|
23531
|
-
* decoder process dies *ungracefully* — SIGBUS, OOM-kill, or a SIGKILL during
|
|
23532
|
-
* redeploy — that teardown never runs and the segment is orphaned: it stays in
|
|
23533
|
-
* the tmpfs forever, since nothing else knows its name. Across many
|
|
23534
|
-
* crashes/redeploys these accumulate until `/dev/shm` fills, at which point the
|
|
23535
|
-
* next `mmap` write faults with an uncatchable SIGBUS and the decoder
|
|
23536
|
-
* crash-loops into its circuit breaker (which is exactly the incident this
|
|
23537
|
-
* guards against).
|
|
23538
|
-
*
|
|
23539
|
-
* The reclamation is safe *at process startup*: a freshly-booting decoder owns
|
|
23540
|
-
* no live sessions, so every pre-existing segment with its prefix is by
|
|
23541
|
-
* definition an orphan from a dead instance. `shm_unlink` only removes the
|
|
23542
|
-
* name — any consumer still holding a mapping keeps reading valid memory until
|
|
23543
|
-
* it closes (POSIX deferred reclaim + the ring seqlock), so unlinking is safe
|
|
23544
|
-
* even if a stale reader is momentarily still attached.
|
|
23545
|
-
*
|
|
23546
|
-
* POSIX-only: segments surface as files under `/dev/shm` on Linux. On platforms
|
|
23547
|
-
* without that directory (Windows, macOS) the scan finds nothing — no-op.
|
|
23548
|
-
*
|
|
23549
|
-
* NOTE: this lives inside the decoder addon (not `@camstack/shm-ring`) so it
|
|
23550
|
-
* ships in the self-contained addon bundle via `camstack deploy`, reusing the
|
|
23551
|
-
* already-deployed `unlinkSegment`; no host base-image rebuild required.
|
|
23552
|
-
*/
|
|
23553
|
-
/** Default tmpfs directory where POSIX shared-memory segments appear on Linux. */
|
|
23554
|
-
var DEFAULT_SHM_DIR = "/dev/shm";
|
|
23555
|
-
/**
|
|
23556
|
-
* Unlink every shared-memory segment whose name starts with `prefix`.
|
|
23557
|
-
*
|
|
23558
|
-
* Intended to run ONCE at decoder startup, before any session is created, to
|
|
23559
|
-
* reclaim segments orphaned by a previously-crashed instance. A per-file unlink
|
|
23560
|
-
* failure is swallowed so one stuck segment cannot block reclaiming the rest.
|
|
23561
|
-
*/
|
|
23562
|
-
function purgeOrphanSegments(prefix, options = {}) {
|
|
23563
|
-
const dir = options.dir ?? DEFAULT_SHM_DIR;
|
|
23564
|
-
const unlink = options.unlink ?? unlinkSegment;
|
|
23565
|
-
let entries;
|
|
23566
|
-
try {
|
|
23567
|
-
entries = readdirSync(dir);
|
|
23568
|
-
} catch {
|
|
23569
|
-
return {
|
|
23570
|
-
scanned: 0,
|
|
23571
|
-
removed: 0,
|
|
23572
|
-
names: []
|
|
23573
|
-
};
|
|
23574
|
-
}
|
|
23575
|
-
const names = [];
|
|
23576
|
-
for (const name of entries) {
|
|
23577
|
-
if (!name.startsWith(prefix)) continue;
|
|
23578
|
-
try {
|
|
23579
|
-
unlink(name);
|
|
23580
|
-
names.push(name);
|
|
23581
|
-
} catch {}
|
|
23582
|
-
}
|
|
23583
|
-
return {
|
|
23584
|
-
scanned: entries.length,
|
|
23585
|
-
removed: names.length,
|
|
23586
|
-
names
|
|
23587
|
-
};
|
|
23588
|
-
}
|
|
23589
|
-
//#endregion
|
|
23590
24515
|
//#region src/shared/notifying-ring-buffer.ts
|
|
23591
24516
|
/**
|
|
23592
24517
|
* A {@link RingBuffer} that can notify a single blocked consumer the moment an
|
|
23593
|
-
* item commits — the server side of the `pullFrames
|
|
24518
|
+
* item commits — the server side of the `pullFrames` long-poll.
|
|
23594
24519
|
*
|
|
23595
|
-
* The decoder's `onFrame
|
|
24520
|
+
* The decoder's `onFrame` callback drives `push()`; a consumer
|
|
23596
24521
|
* that finds the ring momentarily empty calls {@link waitForItem} and is woken
|
|
23597
24522
|
* on the very next `push()` (a real commit signal) rather than spinning at
|
|
23598
24523
|
* ~1kHz on `setTimeout(…,1)`. If nothing commits it resolves after `waitMs`,
|
|
@@ -23930,19 +24855,7 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
23930
24855
|
config;
|
|
23931
24856
|
logger;
|
|
23932
24857
|
frameCallbacks = /* @__PURE__ */ new Set();
|
|
23933
|
-
handleCallbacks = /* @__PURE__ */ new Set();
|
|
23934
24858
|
destroyed = false;
|
|
23935
|
-
/**
|
|
23936
|
-
* Frame delivery mode (see {@link DecoderFrameSink}). `'shm'` lazily
|
|
23937
|
-
* constructs `frameRingSink` and routes every decoded frame through it.
|
|
23938
|
-
*/
|
|
23939
|
-
frameSink;
|
|
23940
|
-
/**
|
|
23941
|
-
* The shared-memory ring writer for this stream. Created lazily on the
|
|
23942
|
-
* first decoded frame in `'shm'` mode (the segment cannot be sized until
|
|
23943
|
-
* the output geometry is known) and torn down in `destroy`.
|
|
23944
|
-
*/
|
|
23945
|
-
frameRingSink = null;
|
|
23946
24859
|
parser = null;
|
|
23947
24860
|
codecCtx = null;
|
|
23948
24861
|
scaler = null;
|
|
@@ -24063,8 +24976,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24063
24976
|
startTime = Date.now();
|
|
24064
24977
|
hwaccelPref;
|
|
24065
24978
|
hwaccelResolver;
|
|
24066
|
-
/** Cluster node id stamped into every `FrameHandle` the `'shm'` sink emits. */
|
|
24067
|
-
nodeId;
|
|
24068
24979
|
/** The backend that actually initialised successfully — `'none'` = software fallback. */
|
|
24069
24980
|
activeHwAccel = "none";
|
|
24070
24981
|
hwDevice = null;
|
|
@@ -24096,35 +25007,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24096
25007
|
this.outputMode = NodeAvDecoderSession.resolveOutputMode(config.outputFormat);
|
|
24097
25008
|
this.hwaccelPref = options?.hwaccel ?? "auto";
|
|
24098
25009
|
this.hwaccelResolver = options?.hwaccelResolver ?? null;
|
|
24099
|
-
this.frameSink = options?.frameSink ?? "callback";
|
|
24100
|
-
this.nodeId = options?.nodeId ?? "local";
|
|
24101
|
-
}
|
|
24102
|
-
/**
|
|
24103
|
-
* The shared-memory ring sink for this stream, or `null` before the first
|
|
24104
|
-
* `'shm'`-mode frame lazily creates it. Exposed so the owning addon can
|
|
24105
|
-
* surface ring stats via `decoder.getShmStats`.
|
|
24106
|
-
*/
|
|
24107
|
-
get frameRingSinkOrNull() {
|
|
24108
|
-
return this.frameRingSink;
|
|
24109
|
-
}
|
|
24110
|
-
/**
|
|
24111
|
-
* Lazily build the shared-memory ring sink for this stream. The segment
|
|
24112
|
-
* itself is still created lazily by the sink on its first `writeFrame`,
|
|
24113
|
-
* once the decoded geometry is known.
|
|
24114
|
-
*/
|
|
24115
|
-
ensureFrameRingSink() {
|
|
24116
|
-
if (this.frameRingSink === null) {
|
|
24117
|
-
const seedParts = [];
|
|
24118
|
-
if (typeof this.config.deviceId === "number") seedParts.push(String(this.config.deviceId));
|
|
24119
|
-
if (typeof this.config.tag === "string" && this.config.tag.length > 0) seedParts.push(this.config.tag);
|
|
24120
|
-
const seed = seedParts.length > 0 ? seedParts.join(":") : "anon";
|
|
24121
|
-
this.frameRingSink = new DecoderFrameRingSink({
|
|
24122
|
-
seed,
|
|
24123
|
-
logger: this.logger,
|
|
24124
|
-
nodeId: this.nodeId
|
|
24125
|
-
});
|
|
24126
|
-
}
|
|
24127
|
-
return this.frameRingSink;
|
|
24128
25010
|
}
|
|
24129
25011
|
/**
|
|
24130
25012
|
* Resolve the backend preference list and try each one against
|
|
@@ -24748,10 +25630,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24748
25630
|
}
|
|
24749
25631
|
if (!this.dstFrame || !this.scaler) return;
|
|
24750
25632
|
const decodeStart = performance.now();
|
|
24751
|
-
if (this.frameSink === "shm" && this.outputMode !== "jpeg") {
|
|
24752
|
-
this.scaleIntoRingSlot(frame, decodeStart);
|
|
24753
|
-
return;
|
|
24754
|
-
}
|
|
24755
25633
|
try {
|
|
24756
25634
|
this.dstFrame.makeWritable();
|
|
24757
25635
|
this.scaler.scaleFrameSync(this.dstFrame, frame);
|
|
@@ -24796,100 +25674,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24796
25674
|
else this.emitRawFrame(rawBuf, "gray", decodeStart);
|
|
24797
25675
|
}
|
|
24798
25676
|
/**
|
|
24799
|
-
* Scale a decoded frame **directly into a shared-memory ring slot** — the
|
|
24800
|
-
* zero write-side copy path (Phase 5 / D9 Task 7c).
|
|
24801
|
-
*
|
|
24802
|
-
* `beginFrame` reserves the slot (its seqlock open / odd — readers skip it);
|
|
24803
|
-
* `SoftwareScaleContext.scaleSync` (the low-level `sws_scale` mapping) writes
|
|
24804
|
-
* the packed pixels straight into the slot buffer with `linesize = width ×
|
|
24805
|
-
* bpp` — no linesize padding, so there is nothing to strip and nothing to
|
|
24806
|
-
* memcpy; `commitFrame` writes the metadata, closes the seqlock and publishes
|
|
24807
|
-
* the slot. The decoded source planes feed `scaleSync` as `srcSlice` +
|
|
24808
|
-
* `srcStride` (`frame.data` / `frame.linesize`).
|
|
24809
|
-
*
|
|
24810
|
-
* Used only for raw output formats (rgb/bgr/gray). The jpeg path keeps the
|
|
24811
|
-
* `dstFrame` + sharp encode route because sharp must consume an RGB buffer
|
|
24812
|
-
* before the final (variable-length) jpeg bytes exist.
|
|
24813
|
-
*/
|
|
24814
|
-
scaleIntoRingSlot(frame, decodeStart) {
|
|
24815
|
-
if (!this.scaler) return;
|
|
24816
|
-
const format = this.outputMode === "gray" ? "gray" : "rgb";
|
|
24817
|
-
const channels = this.outputMode === "gray" ? 1 : 3;
|
|
24818
|
-
const sink = this.ensureFrameRingSink();
|
|
24819
|
-
const reserved = sink.beginFrame(this.outWidth, this.outHeight, format);
|
|
24820
|
-
if (reserved === null) {
|
|
24821
|
-
this.droppedFrames++;
|
|
24822
|
-
return;
|
|
24823
|
-
}
|
|
24824
|
-
const srcPlanes = frame.data;
|
|
24825
|
-
if (!srcPlanes || srcPlanes.length === 0) {
|
|
24826
|
-
sink.abortFrame(reserved.slot);
|
|
24827
|
-
this.droppedFrames++;
|
|
24828
|
-
return;
|
|
24829
|
-
}
|
|
24830
|
-
const dstStride = this.outWidth * channels;
|
|
24831
|
-
const needed = dstStride * this.outHeight;
|
|
24832
|
-
if (reserved.buffer.length < needed) {
|
|
24833
|
-
this.logger.warn("node-av: ring slot too small for scaled frame — dropping", { meta: {
|
|
24834
|
-
needed,
|
|
24835
|
-
slotBytes: reserved.buffer.length,
|
|
24836
|
-
width: this.outWidth,
|
|
24837
|
-
height: this.outHeight
|
|
24838
|
-
} });
|
|
24839
|
-
sink.abortFrame(reserved.slot);
|
|
24840
|
-
this.droppedFrames++;
|
|
24841
|
-
return;
|
|
24842
|
-
}
|
|
24843
|
-
try {
|
|
24844
|
-
const srcStrides = Array.from(frame.linesize).slice(0, srcPlanes.length);
|
|
24845
|
-
this.scaler.scaleSync(Array.from(srcPlanes), srcStrides, 0, frame.height, [reserved.buffer], [dstStride]);
|
|
24846
|
-
} catch (err) {
|
|
24847
|
-
this.logger.warn("node-av scale-into-slot error", { meta: { error: errMsg(err) } });
|
|
24848
|
-
sink.abortFrame(reserved.slot);
|
|
24849
|
-
this.droppedFrames++;
|
|
24850
|
-
return;
|
|
24851
|
-
}
|
|
24852
|
-
const decodeMs = performance.now() - decodeStart;
|
|
24853
|
-
this.totalDecodeTimeMs += decodeMs;
|
|
24854
|
-
this.outputFrames++;
|
|
24855
|
-
this.lastEmitTime = performance.now();
|
|
24856
|
-
if (!this.firstFrameLogged) {
|
|
24857
|
-
this.firstFrameLogged = true;
|
|
24858
|
-
this.logger.info("node-av: scaled directly into shm ring slot", { meta: {
|
|
24859
|
-
phase: "frame-debug",
|
|
24860
|
-
width: this.outWidth,
|
|
24861
|
-
height: this.outHeight,
|
|
24862
|
-
dstStride,
|
|
24863
|
-
format
|
|
24864
|
-
} });
|
|
24865
|
-
}
|
|
24866
|
-
if (this.outputFrames === 1 || this.outputFrames % 500 === 0) this.logger.info("node-av frame emitted", { meta: {
|
|
24867
|
-
frameNumber: this.outputFrames,
|
|
24868
|
-
width: this.outWidth,
|
|
24869
|
-
height: this.outHeight,
|
|
24870
|
-
format,
|
|
24871
|
-
decodeMs,
|
|
24872
|
-
sink: "shm",
|
|
24873
|
-
subs: this.handleCallbacks.size
|
|
24874
|
-
} });
|
|
24875
|
-
const handle = sink.commitFrame(reserved.slot, {
|
|
24876
|
-
width: this.outWidth,
|
|
24877
|
-
height: this.outHeight,
|
|
24878
|
-
format,
|
|
24879
|
-
pts: performance.now(),
|
|
24880
|
-
byteLength: dstStride * this.outHeight
|
|
24881
|
-
});
|
|
24882
|
-
if (handle === null) {
|
|
24883
|
-
this.droppedFrames++;
|
|
24884
|
-
return;
|
|
24885
|
-
}
|
|
24886
|
-
const delivered = {
|
|
24887
|
-
handle,
|
|
24888
|
-
timestamp: Date.now()
|
|
24889
|
-
};
|
|
24890
|
-
for (const cb of this.handleCallbacks) cb(delivered);
|
|
24891
|
-
}
|
|
24892
|
-
/**
|
|
24893
25677
|
* Extract packed pixel buffer from a decoded frame.
|
|
24894
25678
|
* FFmpeg's av_frame_get_buffer() may pad each row to alignment (32/64 bytes).
|
|
24895
25679
|
* Sharp and WASM consumers expect tightly-packed rows (stride = width * channels).
|
|
@@ -24948,13 +25732,8 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24948
25732
|
height: this.outHeight,
|
|
24949
25733
|
format,
|
|
24950
25734
|
decodeMs,
|
|
24951
|
-
|
|
24952
|
-
subs: this.frameSink === "shm" ? this.handleCallbacks.size : this.frameCallbacks.size
|
|
25735
|
+
subs: this.frameCallbacks.size
|
|
24953
25736
|
} });
|
|
24954
|
-
if (this.frameSink === "shm") {
|
|
24955
|
-
this.emitShmFrame(data, format);
|
|
24956
|
-
return;
|
|
24957
|
-
}
|
|
24958
25737
|
const decodedFrame = {
|
|
24959
25738
|
data,
|
|
24960
25739
|
width: this.outWidth,
|
|
@@ -24964,47 +25743,12 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
24964
25743
|
};
|
|
24965
25744
|
for (const cb of this.frameCallbacks) cb(decodedFrame);
|
|
24966
25745
|
}
|
|
24967
|
-
/**
|
|
24968
|
-
* Write a decoded frame into this stream's shared-memory ring and deliver
|
|
24969
|
-
* the resulting `FrameHandle` to `onFrameHandle` subscribers. No pixel
|
|
24970
|
-
* `Buffer` is handed to callbacks — consumers open the segment and read the
|
|
24971
|
-
* pixels zero-copy via a `FrameRingReader`.
|
|
24972
|
-
*/
|
|
24973
|
-
emitShmFrame(data, format) {
|
|
24974
|
-
const handle = this.ensureFrameRingSink().writeFrame(data, {
|
|
24975
|
-
width: this.outWidth,
|
|
24976
|
-
height: this.outHeight,
|
|
24977
|
-
format,
|
|
24978
|
-
pts: performance.now(),
|
|
24979
|
-
byteLength: data.byteLength
|
|
24980
|
-
});
|
|
24981
|
-
if (handle === null) {
|
|
24982
|
-
this.droppedFrames++;
|
|
24983
|
-
return;
|
|
24984
|
-
}
|
|
24985
|
-
const frame = {
|
|
24986
|
-
handle,
|
|
24987
|
-
timestamp: Date.now()
|
|
24988
|
-
};
|
|
24989
|
-
for (const cb of this.handleCallbacks) cb(frame);
|
|
24990
|
-
}
|
|
24991
25746
|
onFrame(callback) {
|
|
24992
25747
|
this.frameCallbacks.add(callback);
|
|
24993
25748
|
return () => {
|
|
24994
25749
|
this.frameCallbacks.delete(callback);
|
|
24995
25750
|
};
|
|
24996
25751
|
}
|
|
24997
|
-
/**
|
|
24998
|
-
* Subscribe to shared-memory frame handles. Fires only when the session was
|
|
24999
|
-
* created with `frameSink: 'shm'`; in the legacy `'callback'` mode no
|
|
25000
|
-
* handles are produced and this subscription never fires.
|
|
25001
|
-
*/
|
|
25002
|
-
onFrameHandle(callback) {
|
|
25003
|
-
this.handleCallbacks.add(callback);
|
|
25004
|
-
return () => {
|
|
25005
|
-
this.handleCallbacks.delete(callback);
|
|
25006
|
-
};
|
|
25007
|
-
}
|
|
25008
25752
|
updateConfig(update) {
|
|
25009
25753
|
const prevFormat = this.config.outputFormat;
|
|
25010
25754
|
this.config = {
|
|
@@ -25042,7 +25786,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
25042
25786
|
if (this.destroyed) return;
|
|
25043
25787
|
this.destroyed = true;
|
|
25044
25788
|
this.frameCallbacks.clear();
|
|
25045
|
-
this.handleCallbacks.clear();
|
|
25046
25789
|
this.pullActive = false;
|
|
25047
25790
|
if (this.pullRedialTimer) {
|
|
25048
25791
|
clearTimeout(this.pullRedialTimer);
|
|
@@ -25058,8 +25801,6 @@ var NodeAvDecoderSession = class NodeAvDecoderSession {
|
|
|
25058
25801
|
if (!this.pullHwContext.isDisposed) this.pullHwContext.dispose();
|
|
25059
25802
|
this.pullHwContext = null;
|
|
25060
25803
|
}
|
|
25061
|
-
this.frameRingSink?.destroy();
|
|
25062
|
-
this.frameRingSink = null;
|
|
25063
25804
|
this.dstFrame?.[Symbol.dispose]?.();
|
|
25064
25805
|
this.avFrame?.[Symbol.dispose]?.();
|
|
25065
25806
|
this.avPacket?.[Symbol.dispose]?.();
|
|
@@ -25887,27 +26628,14 @@ var FRAME_BUFFER_CAPACITY = 32;
|
|
|
25887
26628
|
var DecoderNodeAvAddon = class extends BaseAddon {
|
|
25888
26629
|
/**
|
|
25889
26630
|
* Sessions are stored as the concrete `NodeAvDecoderSession` (the only type
|
|
25890
|
-
* this addon ever creates)
|
|
25891
|
-
* ring sink — `IDecoderSession` does not expose it.
|
|
26631
|
+
* this addon ever creates).
|
|
25892
26632
|
*/
|
|
25893
26633
|
sessions = /* @__PURE__ */ new Map();
|
|
25894
|
-
/** Pixel-frame buffers
|
|
26634
|
+
/** Pixel-frame buffers drained by `pullFrames`. */
|
|
25895
26635
|
frameBuffers = /* @__PURE__ */ new Map();
|
|
25896
|
-
/** `FrameHandle` buffers — populated only for `frameSink: 'shm'` sessions. */
|
|
25897
|
-
handleBuffers = /* @__PURE__ */ new Map();
|
|
25898
26636
|
unsubscribers = /* @__PURE__ */ new Map();
|
|
25899
26637
|
sessionMeta = /* @__PURE__ */ new Map();
|
|
25900
26638
|
/**
|
|
25901
|
-
* Per-shm-segment `FrameRingReader` cache backing `getFrame`. Opens each
|
|
25902
|
-
* named segment once and reuses the reader for every later handle on it;
|
|
25903
|
-
* built in `onInitialize` (it needs the scoped logger) and closed in
|
|
25904
|
-
* `onShutdown`.
|
|
25905
|
-
*/
|
|
25906
|
-
frameReaders = null;
|
|
25907
|
-
/** Running `getFrame` hit/miss counters surfaced via `getShmStats`. */
|
|
25908
|
-
getFrameHits = 0;
|
|
25909
|
-
getFrameMisses = 0;
|
|
25910
|
-
/**
|
|
25911
26639
|
* node-av audio-codec provider — registered independently of the video
|
|
25912
26640
|
* decoder backend gate whenever node-av loads on this node, so this single
|
|
25913
26641
|
* addon provides both the `decoder` and `audio-codec` caps.
|
|
@@ -25972,12 +26700,6 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
25972
26700
|
return registrations;
|
|
25973
26701
|
}
|
|
25974
26702
|
this.ctx.logger.info("node-av decoder addon initialized", { meta: { selectedBackend: backend } });
|
|
25975
|
-
const purged = purgeOrphanSegments(SEGMENT_NAME_PREFIX);
|
|
25976
|
-
if (purged.removed > 0) this.ctx.logger.warn("node-av decoder: reclaimed orphaned shm segments at startup", { meta: {
|
|
25977
|
-
removed: purged.removed,
|
|
25978
|
-
scanned: purged.scanned
|
|
25979
|
-
} });
|
|
25980
|
-
this.frameReaders = new FrameRingReaderCache(this.ctx.logger);
|
|
25981
26703
|
if (!this.config.probedBestHwaccel) this.reprobeHwaccel().catch((err) => {
|
|
25982
26704
|
this.ctx.logger.warn("nodeav: auto-reprobe hwaccel failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
25983
26705
|
});
|
|
@@ -26044,11 +26766,11 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26044
26766
|
}
|
|
26045
26767
|
/**
|
|
26046
26768
|
* The cluster node id of this decoder — the Moleculer nodeID (`hub`,
|
|
26047
|
-
* `dev-agent-0`, …), not the addon id.
|
|
26048
|
-
*
|
|
26049
|
-
*
|
|
26050
|
-
*
|
|
26051
|
-
*
|
|
26769
|
+
* `dev-agent-0`, …), not the addon id. Returned from `createSession` so the
|
|
26770
|
+
* caller can route session-scoped calls to the node that owns the session.
|
|
26771
|
+
* A hierarchical broker nodeID (`hub/...`) is collapsed to the
|
|
26772
|
+
* cluster-visible parent; in-process boot is left as-is. Mirrors the
|
|
26773
|
+
* resolution `PipelineRunnerAddon.onInitialize` performs.
|
|
26052
26774
|
*/
|
|
26053
26775
|
resolveLocalNodeId() {
|
|
26054
26776
|
const raw = this.ctx.kernel.localNodeId ?? this.ctx.id;
|
|
@@ -26057,15 +26779,12 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26057
26779
|
async createSession(config) {
|
|
26058
26780
|
const sessionId = randomUUID();
|
|
26059
26781
|
const hwaccel = await this.resolveHwAccelPref();
|
|
26060
|
-
const { frameSink } = config;
|
|
26061
26782
|
const nodeId = this.resolveLocalNodeId();
|
|
26062
26783
|
const session = new NodeAvDecoderSession(config, this.ctx.logger, {
|
|
26063
26784
|
hwaccel,
|
|
26064
|
-
hwaccelResolver: this.ctx.kernel.hwaccel
|
|
26065
|
-
frameSink,
|
|
26066
|
-
nodeId
|
|
26785
|
+
hwaccelResolver: this.ctx.kernel.hwaccel
|
|
26067
26786
|
});
|
|
26068
|
-
const unsub =
|
|
26787
|
+
const unsub = this.wireCallbackSink(sessionId, session);
|
|
26069
26788
|
this.sessions.set(sessionId, session);
|
|
26070
26789
|
this.unsubscribers.set(sessionId, unsub);
|
|
26071
26790
|
this.sessionMeta.set(sessionId, {
|
|
@@ -26080,7 +26799,6 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26080
26799
|
sessionId,
|
|
26081
26800
|
codec: config.codec,
|
|
26082
26801
|
hwaccelPref: hwaccel,
|
|
26083
|
-
frameSink,
|
|
26084
26802
|
nodeId
|
|
26085
26803
|
}
|
|
26086
26804
|
});
|
|
@@ -26111,18 +26829,6 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26111
26829
|
ringBuffer.push(capFrame);
|
|
26112
26830
|
});
|
|
26113
26831
|
}
|
|
26114
|
-
/**
|
|
26115
|
-
* Subscribe a `'shm'` session's `FrameHandle`s into a per-session ring
|
|
26116
|
-
* buffer drained by `pullHandles`. No pixel bytes cross the cap boundary
|
|
26117
|
-
* — the broker opens the named segment and reads the pixels zero-copy.
|
|
26118
|
-
*/
|
|
26119
|
-
wireShmSink(sessionId, session) {
|
|
26120
|
-
const handleRing = new NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
26121
|
-
this.handleBuffers.set(sessionId, handleRing);
|
|
26122
|
-
return session.onFrameHandle((frame) => {
|
|
26123
|
-
handleRing.push(frame.handle);
|
|
26124
|
-
});
|
|
26125
|
-
}
|
|
26126
26832
|
async destroySession(input) {
|
|
26127
26833
|
const { sessionId } = input;
|
|
26128
26834
|
const session = this.sessions.get(sessionId);
|
|
@@ -26130,12 +26836,10 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26130
26836
|
const unsub = this.unsubscribers.get(sessionId);
|
|
26131
26837
|
if (unsub) unsub();
|
|
26132
26838
|
this.frameBuffers.get(sessionId)?.cancel();
|
|
26133
|
-
this.handleBuffers.get(sessionId)?.cancel();
|
|
26134
26839
|
const deviceId = this.sessionMeta.get(sessionId)?.deviceId;
|
|
26135
26840
|
await session.destroy();
|
|
26136
26841
|
this.sessions.delete(sessionId);
|
|
26137
26842
|
this.frameBuffers.delete(sessionId);
|
|
26138
|
-
this.handleBuffers.delete(sessionId);
|
|
26139
26843
|
this.unsubscribers.delete(sessionId);
|
|
26140
26844
|
this.sessionMeta.delete(sessionId);
|
|
26141
26845
|
this.ctx.logger.info("node-av: destroyed session", {
|
|
@@ -26175,12 +26879,14 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26175
26879
|
if (ringBuffer.size === 0 && input.waitMs) await ringBuffer.waitForItem(input.waitMs);
|
|
26176
26880
|
return ringBuffer.drain(input.maxCount);
|
|
26177
26881
|
}
|
|
26178
|
-
|
|
26179
|
-
|
|
26180
|
-
|
|
26181
|
-
|
|
26182
|
-
|
|
26183
|
-
|
|
26882
|
+
/**
|
|
26883
|
+
* DORMANT — the shm `FrameHandle` frame plane is retired. Every session now
|
|
26884
|
+
* delivers pixels over the callback path drained by `pullFrames`, so no
|
|
26885
|
+
* handles are ever produced. Kept to satisfy the (still-defined) cap method;
|
|
26886
|
+
* always returns an empty list. Removal rides a later framework train.
|
|
26887
|
+
*/
|
|
26888
|
+
async pullHandles() {
|
|
26889
|
+
return [];
|
|
26184
26890
|
}
|
|
26185
26891
|
async updateConfig(input) {
|
|
26186
26892
|
const session = this.sessions.get(input.sessionId);
|
|
@@ -26193,50 +26899,22 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26193
26899
|
return session.getStats();
|
|
26194
26900
|
}
|
|
26195
26901
|
/**
|
|
26196
|
-
*
|
|
26197
|
-
*
|
|
26198
|
-
*
|
|
26199
|
-
*
|
|
26200
|
-
*
|
|
26201
|
-
* The reader hands back a `Buffer` view over its **reusable scratch
|
|
26202
|
-
* buffer**, only valid until the next `read` on the same reader; the
|
|
26203
|
-
* cap caller may keep the frame across reads, so the pixels are copied
|
|
26204
|
-
* into a fresh detached `ArrayBuffer` here. This matches the existing
|
|
26205
|
-
* callback-path copy in `wireCallbackSink`.
|
|
26902
|
+
* DORMANT — shm handle read-back is retired (the decoder no longer owns a
|
|
26903
|
+
* `/dev/shm` ring). Kept to satisfy the (still-defined) cap method; always
|
|
26904
|
+
* returns `null`. Full-frame retrieval by handle now flows through the
|
|
26905
|
+
* runner's RAM-backed `pipelineRunner.getNativeCrop`. Removal rides a later
|
|
26906
|
+
* framework train.
|
|
26206
26907
|
*/
|
|
26207
|
-
async getFrame(
|
|
26208
|
-
|
|
26209
|
-
if (!frame) {
|
|
26210
|
-
this.getFrameMisses += 1;
|
|
26211
|
-
return null;
|
|
26212
|
-
}
|
|
26213
|
-
this.getFrameHits += 1;
|
|
26214
|
-
const arrayBuf = new ArrayBuffer(frame.data.byteLength);
|
|
26215
|
-
new Uint8Array(arrayBuf).set(frame.data);
|
|
26216
|
-
return {
|
|
26217
|
-
data: new Uint8Array(arrayBuf),
|
|
26218
|
-
width: frame.width,
|
|
26219
|
-
height: frame.height,
|
|
26220
|
-
format: frame.format,
|
|
26221
|
-
timestamp: frame.timestamp
|
|
26222
|
-
};
|
|
26908
|
+
async getFrame() {
|
|
26909
|
+
return null;
|
|
26223
26910
|
}
|
|
26224
26911
|
/**
|
|
26225
|
-
* shm ring
|
|
26226
|
-
*
|
|
26227
|
-
*
|
|
26228
|
-
* shm ring has not yet been armed by a first decoded frame.
|
|
26912
|
+
* DORMANT — no shm ring exists to report stats for. Kept to satisfy the
|
|
26913
|
+
* (still-defined) cap method; always returns `null`. Removal rides a later
|
|
26914
|
+
* framework train.
|
|
26229
26915
|
*/
|
|
26230
|
-
async getShmStats(
|
|
26231
|
-
|
|
26232
|
-
if (!stats) return null;
|
|
26233
|
-
return {
|
|
26234
|
-
sessionId: input.sessionId,
|
|
26235
|
-
...stats,
|
|
26236
|
-
budgetMb: RING_BUDGET_MB,
|
|
26237
|
-
getFrameHits: this.getFrameHits,
|
|
26238
|
-
getFrameMisses: this.getFrameMisses
|
|
26239
|
-
};
|
|
26916
|
+
async getShmStats() {
|
|
26917
|
+
return null;
|
|
26240
26918
|
}
|
|
26241
26919
|
async onShutdown() {
|
|
26242
26920
|
this.audioProvider?.stop();
|
|
@@ -26251,12 +26929,9 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
26251
26929
|
await Promise.all(destroyPromises);
|
|
26252
26930
|
this.sessions.clear();
|
|
26253
26931
|
this.frameBuffers.clear();
|
|
26254
|
-
this.handleBuffers.clear();
|
|
26255
26932
|
this.unsubscribers.clear();
|
|
26256
26933
|
this.sessionMeta.clear();
|
|
26257
|
-
this.frameReaders?.close();
|
|
26258
|
-
this.frameReaders = null;
|
|
26259
26934
|
}
|
|
26260
26935
|
};
|
|
26261
26936
|
//#endregion
|
|
26262
|
-
export {
|
|
26937
|
+
export { DecoderNodeAvAddon, DecoderNodeAvAddon as default, NodeAvAudioCodecProvider, NodeAvAudioDecodeSession, NodeAvAudioEncodeSession, NodeAvDecoderSession, loadNodeAvRuntime, peekNodeAvRuntime };
|