@camstack/addon-decoder-ffmpeg 1.1.12 → 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 +1067 -256
- package/dist/index.mjs +1067 -256
- package/package.json +1 -2
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { DecoderFrameRingSink, FrameRingReaderCache, RING_BUDGET_MB, SEGMENT_NAME_PREFIX, unlinkSegment } from "@camstack/shm-ring";
|
|
3
2
|
import { spawn } from "node:child_process";
|
|
4
|
-
import { readdirSync } from "node:fs";
|
|
5
3
|
//#region ../../node_modules/zod/v4/core/core.js
|
|
6
4
|
var _a$1;
|
|
7
5
|
function $constructor(name, initializer, params) {
|
|
@@ -4290,6 +4288,14 @@ function object(shape, params) {
|
|
|
4290
4288
|
...normalizeParams(params)
|
|
4291
4289
|
});
|
|
4292
4290
|
}
|
|
4291
|
+
function looseObject(shape, params) {
|
|
4292
|
+
return new ZodObject({
|
|
4293
|
+
type: "object",
|
|
4294
|
+
shape,
|
|
4295
|
+
catchall: unknown(),
|
|
4296
|
+
...normalizeParams(params)
|
|
4297
|
+
});
|
|
4298
|
+
}
|
|
4293
4299
|
var ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
|
|
4294
4300
|
$ZodUnion.init(inst, def);
|
|
4295
4301
|
ZodType.init(inst, def);
|
|
@@ -4631,7 +4637,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4631
4637
|
return inst;
|
|
4632
4638
|
}
|
|
4633
4639
|
//#endregion
|
|
4634
|
-
//#region ../types/dist/sleep-
|
|
4640
|
+
//#region ../types/dist/sleep-_sv7WKkq.mjs
|
|
4635
4641
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4636
4642
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4637
4643
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5041,10 +5047,51 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
5041
5047
|
EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
|
|
5042
5048
|
EventCategory["EnrichmentSceneStateChanged"] = "enrichment.scene.state-changed";
|
|
5043
5049
|
EventCategory["EnrichmentActivitySummary"] = "enrichment.activity.summary";
|
|
5050
|
+
/**
|
|
5051
|
+
* Unified track-lifecycle event: ONE category carrying `phase:
|
|
5052
|
+
* 'start' | 'update' | 'end'` with a rich, typed
|
|
5053
|
+
* `PipelineAnalyticsTrackLifecyclePayload`. Supersedes the thin
|
|
5054
|
+
* `PipelineAnalyticsTrackStarted` / `PipelineAnalyticsTrackEnded`
|
|
5055
|
+
* pair — a consumer subscribes once and branches on `phase`.
|
|
5056
|
+
*/
|
|
5057
|
+
EventCategory["PipelineAnalyticsTrackLifecycle"] = "pipeline-analytics.track-lifecycle";
|
|
5058
|
+
/**
|
|
5059
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5060
|
+
* (`phase:'start'`). Kept as a soft alias — no known subscribers.
|
|
5061
|
+
*/
|
|
5044
5062
|
EventCategory["PipelineAnalyticsTrackStarted"] = "pipeline-analytics.track-started";
|
|
5063
|
+
/**
|
|
5064
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5065
|
+
* (`phase:'end'`). Kept as a soft alias — no known subscribers.
|
|
5066
|
+
*/
|
|
5045
5067
|
EventCategory["PipelineAnalyticsTrackEnded"] = "pipeline-analytics.track-ended";
|
|
5046
5068
|
EventCategory["PipelineAnalyticsDetectionEvent"] = "pipeline-analytics.detection-event";
|
|
5047
5069
|
EventCategory["PipelineAnalyticsFrameTracked"] = "pipeline-analytics.frame-tracked";
|
|
5070
|
+
/**
|
|
5071
|
+
* Fired by `addon-post-analysis` when a parked (stationary) object appears
|
|
5072
|
+
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
5073
|
+
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
5074
|
+
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
5075
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
5076
|
+
*/
|
|
5077
|
+
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
5078
|
+
/**
|
|
5079
|
+
* Fired by `addon-post-analysis` whenever a gallery face row changes:
|
|
5080
|
+
* `kind:'buffered'` a new detected face was persisted, `'assigned'` /
|
|
5081
|
+
* `'unassigned'` its identity link changed, `'deleted'` the row was
|
|
5082
|
+
* removed. Telemetry semantics (D8): a lost event only delays a refresh,
|
|
5083
|
+
* never a correctness issue. Payload `PipelineAnalyticsFaceGalleryChangedPayload`.
|
|
5084
|
+
*/
|
|
5085
|
+
EventCategory["PipelineAnalyticsFaceGalleryChanged"] = "pipeline-analytics.face-gallery-changed";
|
|
5086
|
+
/**
|
|
5087
|
+
* Fired by `addon-post-analysis` whenever a gallery plate row changes:
|
|
5088
|
+
* `kind:'buffered'` a new read was persisted (`PlateRecognizer.onTrackEnd`),
|
|
5089
|
+
* `'assigned'` / `'unassigned'` its vehicle link changed
|
|
5090
|
+
* (`PlateGalleryProvider`), `'deleted'` the row was removed. Telemetry
|
|
5091
|
+
* semantics (D8): a lost event only delays a refresh, never a correctness
|
|
5092
|
+
* issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
|
|
5093
|
+
*/
|
|
5094
|
+
EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
|
|
5048
5095
|
EventCategory["FrigateLiveEvent"] = "frigate.live-event";
|
|
5049
5096
|
EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
|
|
5050
5097
|
/**
|
|
@@ -6885,7 +6932,8 @@ var MODEL_FORMATS = [
|
|
|
6885
6932
|
"coreml",
|
|
6886
6933
|
"openvino",
|
|
6887
6934
|
"tflite",
|
|
6888
|
-
"pt"
|
|
6935
|
+
"pt",
|
|
6936
|
+
"gguf"
|
|
6889
6937
|
];
|
|
6890
6938
|
/**
|
|
6891
6939
|
* Multi-file format payload.
|
|
@@ -6929,7 +6977,8 @@ var ModelFormatsSchema = object({
|
|
|
6929
6977
|
coreml: ModelFormatEntrySchema.optional(),
|
|
6930
6978
|
openvino: ModelFormatEntrySchema.optional(),
|
|
6931
6979
|
tflite: ModelFormatEntrySchema.optional(),
|
|
6932
|
-
pt: ModelFormatEntrySchema.optional()
|
|
6980
|
+
pt: ModelFormatEntrySchema.optional(),
|
|
6981
|
+
gguf: ModelFormatEntrySchema.optional()
|
|
6933
6982
|
});
|
|
6934
6983
|
/**
|
|
6935
6984
|
* Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
|
|
@@ -6970,6 +7019,15 @@ var ModelCatalogEntrySchema = object({
|
|
|
6970
7019
|
width: number(),
|
|
6971
7020
|
height: number()
|
|
6972
7021
|
}),
|
|
7022
|
+
/**
|
|
7023
|
+
* Channel count of the model input tensor. Omit ⇒ 3 (RGB), the default for
|
|
7024
|
+
* every detector / classifier / embedder. Set to 1 for a grayscale CTC text
|
|
7025
|
+
* recognizer (EasyOCR VGG plate-OCR: input `[N,1,H,W]`) so the preprocess
|
|
7026
|
+
* feeds a single-channel, EasyOCR-normalized tensor instead of the default
|
|
7027
|
+
* 3-channel RGB one. Threaded through `PoolModelConfig.inputChannels` to the
|
|
7028
|
+
* Python inference pool.
|
|
7029
|
+
*/
|
|
7030
|
+
inputChannels: number().int().positive().optional(),
|
|
6973
7031
|
labels: array(LabelDefinitionSchema).readonly(),
|
|
6974
7032
|
inputLayout: _enum(["nchw", "nhwc"]).optional(),
|
|
6975
7033
|
inputNormalization: _enum([
|
|
@@ -6979,6 +7037,16 @@ var ModelCatalogEntrySchema = object({
|
|
|
6979
7037
|
]).optional(),
|
|
6980
7038
|
preprocessMode: _enum(["letterbox", "resize"]).optional(),
|
|
6981
7039
|
/**
|
|
7040
|
+
* Per-MODEL postprocessor override. Absent ⇒ the step's own
|
|
7041
|
+
* `StepDefinition.postprocessor` applies (the normal case — every model in a
|
|
7042
|
+
* step shares its decode). Set it when a step hosts models with DIFFERENT raw
|
|
7043
|
+
* output layouts under one slot: e.g. object-detection is `'yolo'` by default,
|
|
7044
|
+
* but a Coral SSD MobileNet build emits the `TFLite_Detection_PostProcess`
|
|
7045
|
+
* 4-tensor layout and needs `'ssd'`. Threaded into `PoolModelConfig.postprocessor`
|
|
7046
|
+
* by the engine factory (`modelEntry.postprocessor ?? def.postprocessor`).
|
|
7047
|
+
*/
|
|
7048
|
+
postprocessor: custom().optional(),
|
|
7049
|
+
/**
|
|
6982
7050
|
* When true, the executor produces a landmark-aligned crop (similarity warp
|
|
6983
7051
|
* onto the canonical template) before this step runs, instead of a plain
|
|
6984
7052
|
* axis-aligned bbox crop. Required for face-recognition embedders (ArcFace):
|
|
@@ -10424,7 +10492,8 @@ var ModelFormatSchema$1 = _enum([
|
|
|
10424
10492
|
"coreml",
|
|
10425
10493
|
"openvino",
|
|
10426
10494
|
"tflite",
|
|
10427
|
-
"pt"
|
|
10495
|
+
"pt",
|
|
10496
|
+
"gguf"
|
|
10428
10497
|
]);
|
|
10429
10498
|
var PipelineSlotSchema = _enum([
|
|
10430
10499
|
"detector",
|
|
@@ -10518,7 +10587,8 @@ var EngineProvisioningSchema = object({
|
|
|
10518
10587
|
runtimeId: _enum([
|
|
10519
10588
|
"onnx",
|
|
10520
10589
|
"openvino",
|
|
10521
|
-
"coreml"
|
|
10590
|
+
"coreml",
|
|
10591
|
+
"edgetpu"
|
|
10522
10592
|
]).nullable(),
|
|
10523
10593
|
device: string().nullable(),
|
|
10524
10594
|
state: _enum([
|
|
@@ -11730,6 +11800,140 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
|
|
|
11730
11800
|
deviceId: number(),
|
|
11731
11801
|
status: PtzAutotrackStatusSchema
|
|
11732
11802
|
});
|
|
11803
|
+
/**
|
|
11804
|
+
* scene-monitor — device-scoped reference-region state cap. An operator marks
|
|
11805
|
+
* a rect ROI on a camera frame and names one or more states; the engine
|
|
11806
|
+
* (pipeline-analytics, designated post-processing node) reports which state is
|
|
11807
|
+
* active on an ongoing basis. Two operator-selectable check modes share every-
|
|
11808
|
+
* thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
|
|
11809
|
+
* vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
|
|
11810
|
+
*
|
|
11811
|
+
* D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) — the framework
|
|
11812
|
+
* derives the device-detail contribution; the provider carries NO hand-written
|
|
11813
|
+
* settings-contribution methods. `status.kind:'push'` — the engine pushes on
|
|
11814
|
+
* every hysteresis flip / availability change; consumers never poll.
|
|
11815
|
+
*/
|
|
11816
|
+
/** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
|
|
11817
|
+
* be added without a wire break (matching falls back to any-condition refs). */
|
|
11818
|
+
var SceneConditionSchema = string();
|
|
11819
|
+
/** One captured reference — condition-tagged, model-version-gated. `embedding`
|
|
11820
|
+
* is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
|
|
11821
|
+
var SceneReferenceSchema = object({
|
|
11822
|
+
embedding: array(number()),
|
|
11823
|
+
modelId: string(),
|
|
11824
|
+
condition: SceneConditionSchema,
|
|
11825
|
+
capturedAt: number(),
|
|
11826
|
+
thumbnailMediaId: string().optional()
|
|
11827
|
+
});
|
|
11828
|
+
var SceneMonitorStateSchema = object({
|
|
11829
|
+
id: string(),
|
|
11830
|
+
label: string(),
|
|
11831
|
+
references: array(SceneReferenceSchema),
|
|
11832
|
+
textPrompts: array(string()).optional(),
|
|
11833
|
+
referenceCount: number(),
|
|
11834
|
+
currentlyMatched: boolean()
|
|
11835
|
+
});
|
|
11836
|
+
/** Per-mode comparator params in a discriminated union so an `llm` scene never
|
|
11837
|
+
* carries similarity knobs and vice-versa. */
|
|
11838
|
+
var SceneCheckSchema = discriminatedUnion("mode", [object({
|
|
11839
|
+
mode: literal("similarity"),
|
|
11840
|
+
threshold: number().min(0).max(1),
|
|
11841
|
+
hysteresisCount: number().int().positive()
|
|
11842
|
+
}), object({
|
|
11843
|
+
mode: literal("llm"),
|
|
11844
|
+
prompt: string(),
|
|
11845
|
+
profileId: string().optional(),
|
|
11846
|
+
hysteresisCount: number().int().positive()
|
|
11847
|
+
})]);
|
|
11848
|
+
var SceneMonitorSchema = object({
|
|
11849
|
+
id: string(),
|
|
11850
|
+
label: string(),
|
|
11851
|
+
roi: MaskRectShapeSchema,
|
|
11852
|
+
enabled: boolean(),
|
|
11853
|
+
triggerMode: _enum([
|
|
11854
|
+
"periodic",
|
|
11855
|
+
"on-motion",
|
|
11856
|
+
"both"
|
|
11857
|
+
]),
|
|
11858
|
+
checkIntervalSec: number().optional(),
|
|
11859
|
+
check: SceneCheckSchema,
|
|
11860
|
+
states: array(SceneMonitorStateSchema),
|
|
11861
|
+
currentStateId: string().nullable(),
|
|
11862
|
+
lastCheckedAt: number().nullable(),
|
|
11863
|
+
lastConfidence: number().nullable(),
|
|
11864
|
+
currentCondition: SceneConditionSchema.nullable(),
|
|
11865
|
+
availability: _enum(["ok", "unavailable"]),
|
|
11866
|
+
unavailableReason: string().nullable()
|
|
11867
|
+
});
|
|
11868
|
+
var SceneMonitorStatusSchema = object({
|
|
11869
|
+
monitors: array(SceneMonitorSchema),
|
|
11870
|
+
lastFetchedAt: number()
|
|
11871
|
+
});
|
|
11872
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
|
|
11873
|
+
deviceId: number(),
|
|
11874
|
+
label: string(),
|
|
11875
|
+
roi: MaskRectShapeSchema,
|
|
11876
|
+
check: SceneCheckSchema,
|
|
11877
|
+
triggerMode: _enum([
|
|
11878
|
+
"periodic",
|
|
11879
|
+
"on-motion",
|
|
11880
|
+
"both"
|
|
11881
|
+
]).optional(),
|
|
11882
|
+
checkIntervalSec: number().optional()
|
|
11883
|
+
}), SceneMonitorSchema, {
|
|
11884
|
+
kind: "mutation",
|
|
11885
|
+
auth: "admin"
|
|
11886
|
+
}), method(object({
|
|
11887
|
+
deviceId: number(),
|
|
11888
|
+
monitorId: string(),
|
|
11889
|
+
patch: object({
|
|
11890
|
+
label: string().optional(),
|
|
11891
|
+
roi: MaskRectShapeSchema.optional(),
|
|
11892
|
+
enabled: boolean().optional(),
|
|
11893
|
+
triggerMode: _enum([
|
|
11894
|
+
"periodic",
|
|
11895
|
+
"on-motion",
|
|
11896
|
+
"both"
|
|
11897
|
+
]).optional(),
|
|
11898
|
+
checkIntervalSec: number().optional(),
|
|
11899
|
+
check: SceneCheckSchema.optional()
|
|
11900
|
+
})
|
|
11901
|
+
}), _void(), {
|
|
11902
|
+
kind: "mutation",
|
|
11903
|
+
auth: "admin"
|
|
11904
|
+
}), method(object({
|
|
11905
|
+
deviceId: number(),
|
|
11906
|
+
monitorId: string()
|
|
11907
|
+
}), _void(), {
|
|
11908
|
+
kind: "mutation",
|
|
11909
|
+
auth: "admin"
|
|
11910
|
+
}), method(object({
|
|
11911
|
+
deviceId: number(),
|
|
11912
|
+
monitorId: string(),
|
|
11913
|
+
stateId: string().optional(),
|
|
11914
|
+
label: string().optional(),
|
|
11915
|
+
condition: SceneConditionSchema.optional()
|
|
11916
|
+
}), object({
|
|
11917
|
+
stateId: string(),
|
|
11918
|
+
referenceCount: number()
|
|
11919
|
+
}), {
|
|
11920
|
+
kind: "mutation",
|
|
11921
|
+
auth: "admin"
|
|
11922
|
+
}), method(object({
|
|
11923
|
+
deviceId: number(),
|
|
11924
|
+
monitorId: string(),
|
|
11925
|
+
stateId: string(),
|
|
11926
|
+
index: number()
|
|
11927
|
+
}), _void(), {
|
|
11928
|
+
kind: "mutation",
|
|
11929
|
+
auth: "admin"
|
|
11930
|
+
}), method(object({
|
|
11931
|
+
deviceId: number(),
|
|
11932
|
+
monitorId: string()
|
|
11933
|
+
}), _void(), {
|
|
11934
|
+
kind: "mutation",
|
|
11935
|
+
auth: "admin"
|
|
11936
|
+
});
|
|
11733
11937
|
object({
|
|
11734
11938
|
/** Whether the script is currently executing. */
|
|
11735
11939
|
isRunning: boolean(),
|
|
@@ -12137,6 +12341,36 @@ var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
|
|
|
12137
12341
|
* with the per-track detail panel and live overlay. */
|
|
12138
12342
|
trackIds: array(string()).readonly()
|
|
12139
12343
|
});
|
|
12344
|
+
/**
|
|
12345
|
+
* A parked ("stationary") object surfaced alongside occupancy — an object that
|
|
12346
|
+
* settled and stopped moving. It is NO LONGER a tracked object (the tracker was
|
|
12347
|
+
* told to forget it so it stops re-spawning tracks/events), but it IS still
|
|
12348
|
+
* physically present, so it keeps counting toward `frame` occupancy and is
|
|
12349
|
+
* listed here so the UI can show it in a dedicated "Stationary" section instead
|
|
12350
|
+
* of flooding the live event feed.
|
|
12351
|
+
*/
|
|
12352
|
+
var StationaryObjectSchema = object({
|
|
12353
|
+
id: string(),
|
|
12354
|
+
className: string(),
|
|
12355
|
+
bbox: object({
|
|
12356
|
+
x: number(),
|
|
12357
|
+
y: number(),
|
|
12358
|
+
w: number(),
|
|
12359
|
+
h: number()
|
|
12360
|
+
}),
|
|
12361
|
+
frameWidth: number().int().nonnegative(),
|
|
12362
|
+
frameHeight: number().int().nonnegative(),
|
|
12363
|
+
/** When the source track was first seen. */
|
|
12364
|
+
firstSeenAt: number().int(),
|
|
12365
|
+
/** When the object was recognised as parked (promotion time). */
|
|
12366
|
+
becameStationaryAt: number().int(),
|
|
12367
|
+
/** Last frame a detection confirmed the object is still there. */
|
|
12368
|
+
lastConfirmedAt: number().int(),
|
|
12369
|
+
/** Enrichment label carried from the source track (identity / plate). */
|
|
12370
|
+
label: string().optional(),
|
|
12371
|
+
/** Native-resolution key-frame media key for the parked object's best image. */
|
|
12372
|
+
keyFrameMediaKey: string().optional()
|
|
12373
|
+
});
|
|
12140
12374
|
var CameraOccupancySnapshotSchema = object({
|
|
12141
12375
|
/** Frame timestamp of the inference result that produced this snapshot. */
|
|
12142
12376
|
ts: number().int(),
|
|
@@ -12145,10 +12379,15 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
12145
12379
|
frameHeight: number().int().nonnegative(),
|
|
12146
12380
|
/** Per-zone breakdown — one entry per defined zone (user + onboard). */
|
|
12147
12381
|
zones: array(ZoneScopeBreakdownSchema).readonly(),
|
|
12148
|
-
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12382
|
+
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12383
|
+
* INCLUDES currently-confirmed stationary objects (they are still present). */
|
|
12149
12384
|
frame: PerScopeBreakdownSchema,
|
|
12150
12385
|
/** Detections that landed outside every zone. Empty when no zones defined. */
|
|
12151
|
-
unzoned: PerScopeBreakdownSchema
|
|
12386
|
+
unzoned: PerScopeBreakdownSchema,
|
|
12387
|
+
/** Parked objects on this camera (additive — absent on legacy snapshots).
|
|
12388
|
+
* Surfaced separately so the UI shows them in a dedicated section rather
|
|
12389
|
+
* than as repeated tracks/events. */
|
|
12390
|
+
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
12152
12391
|
});
|
|
12153
12392
|
/**
|
|
12154
12393
|
* Time-series resolution. The history methods return one bucket per
|
|
@@ -12751,7 +12990,12 @@ var NotificationRuleConditionsSchema = object({
|
|
|
12751
12990
|
clipDescription: object({
|
|
12752
12991
|
text: string().min(1),
|
|
12753
12992
|
minSimilarity: number().min(0).max(1)
|
|
12754
|
-
}).optional()
|
|
12993
|
+
}).optional(),
|
|
12994
|
+
/** Match events whose recognized-entity label (face identity name or plate
|
|
12995
|
+
* vehicle name, propagated onto `event.data.label`) is one of these values.
|
|
12996
|
+
* Empty/absent → unaffected (back-compat). Enables "notify me when <named
|
|
12997
|
+
* vehicle/person> is seen". */
|
|
12998
|
+
labels: array(string()).readonly().optional()
|
|
12755
12999
|
});
|
|
12756
13000
|
var NotificationRuleTemplateSchema = object({
|
|
12757
13001
|
title: string(),
|
|
@@ -13046,11 +13290,19 @@ method(object({
|
|
|
13046
13290
|
* login page needs NO change.
|
|
13047
13291
|
*
|
|
13048
13292
|
* - `widget` — a Module-Federation widget the login page mounts (via
|
|
13049
|
-
* `loadRemoteBundle`) for an in-page ceremony.
|
|
13050
|
-
*
|
|
13051
|
-
*
|
|
13052
|
-
*
|
|
13053
|
-
*
|
|
13293
|
+
* `loadRemoteBundle`) for an in-page ceremony. `auth.listLoginMethods`
|
|
13294
|
+
* stamps a public `bundleUrl` from `addonId` + `bundle`. Generic
|
|
13295
|
+
* mechanism kept for future use; no shipped addon uses it on the login
|
|
13296
|
+
* page (the passkey ceremony below runs natively in the shell instead).
|
|
13297
|
+
*
|
|
13298
|
+
* - `passkey` — a declarative WebAuthn ceremony the shell renders
|
|
13299
|
+
* natively (`@simplewebauthn/browser` lives in `addon-admin-ui`, not in
|
|
13300
|
+
* a remotely-loaded bundle). Carries the addon's effective `rpId` /
|
|
13301
|
+
* `origin` (from its `resolveRpID()` / `resolveOrigin()`) so the shell
|
|
13302
|
+
* can gate visibility (IP-literal origin, hostname/rpId mismatch) WITHOUT
|
|
13303
|
+
* fetching any remote code pre-auth. Contribution stays unconditional —
|
|
13304
|
+
* enrollment state is never leaked pre-auth; visibility is a shell
|
|
13305
|
+
* decision.
|
|
13054
13306
|
*
|
|
13055
13307
|
* Every contribution carries a `stage`:
|
|
13056
13308
|
* - `primary` — shown on the first credentials screen (OIDC /
|
|
@@ -13064,30 +13316,45 @@ method(object({
|
|
|
13064
13316
|
*/
|
|
13065
13317
|
/** When a login method renders in the two-phase login flow. */
|
|
13066
13318
|
var LoginStageEnum = _enum(["primary", "second-factor"]);
|
|
13067
|
-
/** One login-method contribution — redirect button
|
|
13068
|
-
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13078
|
-
|
|
13079
|
-
|
|
13080
|
-
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13319
|
+
/** One login-method contribution — redirect button, pre-auth widget, or native passkey ceremony. */
|
|
13320
|
+
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13321
|
+
object({
|
|
13322
|
+
kind: literal("redirect"),
|
|
13323
|
+
/** Stable id within the login-method set (e.g. `auth-oidc/google`). */
|
|
13324
|
+
id: string(),
|
|
13325
|
+
/** Operator-facing button label. */
|
|
13326
|
+
label: string(),
|
|
13327
|
+
/** lucide-react icon name. */
|
|
13328
|
+
icon: string().optional(),
|
|
13329
|
+
/** Addon-owned HTTP route the button navigates to (GET). */
|
|
13330
|
+
startUrl: string(),
|
|
13331
|
+
stage: LoginStageEnum
|
|
13332
|
+
}),
|
|
13333
|
+
object({
|
|
13334
|
+
kind: literal("widget"),
|
|
13335
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
|
|
13336
|
+
id: string(),
|
|
13337
|
+
/** Owning addon id — drives the public bundle URL + the MF namespace. */
|
|
13338
|
+
addonId: string(),
|
|
13339
|
+
/** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
|
|
13340
|
+
bundle: string(),
|
|
13341
|
+
/** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
|
|
13342
|
+
remote: WidgetRemoteSchema,
|
|
13343
|
+
stage: LoginStageEnum
|
|
13344
|
+
}),
|
|
13345
|
+
object({
|
|
13346
|
+
kind: literal("passkey"),
|
|
13347
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-direct-login`). */
|
|
13348
|
+
id: string(),
|
|
13349
|
+
/** Operator-facing button label. */
|
|
13350
|
+
label: string(),
|
|
13351
|
+
stage: LoginStageEnum,
|
|
13352
|
+
/** Effective WebAuthn RP ID (`resolveRpID()`) — the shell gates visibility on it. */
|
|
13353
|
+
rpId: string(),
|
|
13354
|
+
/** Effective expected origin (`resolveOrigin()`), null when unconfigured. */
|
|
13355
|
+
origin: string().nullable()
|
|
13356
|
+
})
|
|
13357
|
+
]);
|
|
13091
13358
|
method(_void(), array(LoginMethodContributionSchema).readonly());
|
|
13092
13359
|
/**
|
|
13093
13360
|
* Orchestrator-side destination metadata. The orchestrator computes
|
|
@@ -15008,14 +15275,14 @@ var TargetKindCapsSchema = object({
|
|
|
15008
15275
|
* the union is large and not meant for runtime validation here; the exported
|
|
15009
15276
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
15010
15277
|
*/
|
|
15011
|
-
var ConfigSchemaPassthrough = unknown();
|
|
15278
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
15012
15279
|
var TargetKindSchema = object({
|
|
15013
15280
|
kind: string(),
|
|
15014
15281
|
label: string(),
|
|
15015
15282
|
icon: string(),
|
|
15016
15283
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
15017
15284
|
addonId: string(),
|
|
15018
|
-
configSchema: ConfigSchemaPassthrough,
|
|
15285
|
+
configSchema: ConfigSchemaPassthrough$1,
|
|
15019
15286
|
supportsDiscovery: boolean(),
|
|
15020
15287
|
caps: TargetKindCapsSchema
|
|
15021
15288
|
});
|
|
@@ -15068,6 +15335,298 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
15068
15335
|
enabled: boolean()
|
|
15069
15336
|
}), _void(), { kind: "mutation" });
|
|
15070
15337
|
/**
|
|
15338
|
+
* Shared LLM generate contracts — imported by BOTH `llm.cap.ts` (consumer
|
|
15339
|
+
* surface) and `llm-runtime.cap.ts` (node-side managed executor) so the two
|
|
15340
|
+
* caps stay wire-compatible without a circular cap→cap import.
|
|
15341
|
+
*
|
|
15342
|
+
* Errors are a discriminated-union RESULT, never thrown: the shape survives
|
|
15343
|
+
* every transport tier structurally, and failed calls still write usage rows.
|
|
15344
|
+
* Token counts only in v1 — no costUsd (operator decision, 2026-07-15).
|
|
15345
|
+
*/
|
|
15346
|
+
var LlmUsageSchema = object({
|
|
15347
|
+
inputTokens: number(),
|
|
15348
|
+
outputTokens: number()
|
|
15349
|
+
});
|
|
15350
|
+
var LlmErrorCodeSchema = _enum([
|
|
15351
|
+
"timeout",
|
|
15352
|
+
"rate-limited",
|
|
15353
|
+
"auth",
|
|
15354
|
+
"refusal",
|
|
15355
|
+
"bad-request",
|
|
15356
|
+
"unavailable",
|
|
15357
|
+
"no-profile",
|
|
15358
|
+
"budget-exceeded",
|
|
15359
|
+
"adapter-error"
|
|
15360
|
+
]);
|
|
15361
|
+
var LlmGenerateResultSchema = discriminatedUnion("ok", [object({
|
|
15362
|
+
ok: literal(true),
|
|
15363
|
+
text: string(),
|
|
15364
|
+
model: string(),
|
|
15365
|
+
usage: LlmUsageSchema,
|
|
15366
|
+
truncated: boolean(),
|
|
15367
|
+
latencyMs: number()
|
|
15368
|
+
}), object({
|
|
15369
|
+
ok: literal(false),
|
|
15370
|
+
code: LlmErrorCodeSchema,
|
|
15371
|
+
message: string(),
|
|
15372
|
+
retryAfterMs: number().optional()
|
|
15373
|
+
})]);
|
|
15374
|
+
/**
|
|
15375
|
+
* `Uint8Array` is the sanctioned binary convention — superjson + the UDS
|
|
15376
|
+
* MsgPack channel round-trip typed arrays (embedding-encoder.cap.ts:29,
|
|
15377
|
+
* notification-output.cap.ts:27-31 precedents).
|
|
15378
|
+
*/
|
|
15379
|
+
var LlmImageSchema = object({
|
|
15380
|
+
bytes: _instanceof(Uint8Array),
|
|
15381
|
+
mimeType: string()
|
|
15382
|
+
});
|
|
15383
|
+
var LlmGenerateBaseInputSchema = object({
|
|
15384
|
+
/** Collection routing (the notification-output posture). */
|
|
15385
|
+
addonId: string().optional(),
|
|
15386
|
+
/** Explicit profile; else the resolution chain (spec §3). */
|
|
15387
|
+
profileId: string().optional(),
|
|
15388
|
+
/** MANDATORY usage tag: 'ai-summary', 'notifier-rules', 'adhoc-ui', … */
|
|
15389
|
+
consumer: string(),
|
|
15390
|
+
system: string().optional(),
|
|
15391
|
+
/** v1: single-turn. `messages[]` is a v2 additive field. */
|
|
15392
|
+
prompt: string(),
|
|
15393
|
+
/** Structured output — adapter-mapped (response_format / forced tool / responseSchema). */
|
|
15394
|
+
jsonSchema: record(string(), unknown()).optional(),
|
|
15395
|
+
/** Per-call override of the profile default. */
|
|
15396
|
+
maxTokens: number().int().positive().optional(),
|
|
15397
|
+
temperature: number().optional()
|
|
15398
|
+
});
|
|
15399
|
+
/**
|
|
15400
|
+
* `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
|
|
15401
|
+
* on EVERY node where `addon-ai` is installed; the hub `llm` provider reaches
|
|
15402
|
+
* a specific node's runtime with `nodePin(profile.runtime.nodeId)` — normal
|
|
15403
|
+
* cap routing, zero bespoke plumbing. `internal: true`: the operator reaches
|
|
15404
|
+
* this only through the `llm` cap's methods.
|
|
15405
|
+
*
|
|
15406
|
+
* One running llama-server child per node in v1 (models are RAM-heavy).
|
|
15407
|
+
* Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
|
|
15408
|
+
* watchdog — operator decision #3).
|
|
15409
|
+
*/
|
|
15410
|
+
var ManagedModelRefSchema = discriminatedUnion("kind", [
|
|
15411
|
+
object({
|
|
15412
|
+
kind: literal("catalog"),
|
|
15413
|
+
catalogId: string()
|
|
15414
|
+
}),
|
|
15415
|
+
object({
|
|
15416
|
+
kind: literal("url"),
|
|
15417
|
+
url: string(),
|
|
15418
|
+
sha256: string().optional()
|
|
15419
|
+
}),
|
|
15420
|
+
object({
|
|
15421
|
+
kind: literal("path"),
|
|
15422
|
+
path: string()
|
|
15423
|
+
})
|
|
15424
|
+
]);
|
|
15425
|
+
var ManagedRuntimeConfigSchema = object({
|
|
15426
|
+
/** WHERE the runtime lives — hub or any agent. */
|
|
15427
|
+
nodeId: string(),
|
|
15428
|
+
/** Closed for v1; 'ollama' is a v2 candidate. */
|
|
15429
|
+
engine: _enum(["llama-cpp"]),
|
|
15430
|
+
model: ManagedModelRefSchema,
|
|
15431
|
+
contextSize: number().int().default(4096),
|
|
15432
|
+
/** 0 = CPU-only. */
|
|
15433
|
+
gpuLayers: number().int().default(0),
|
|
15434
|
+
/** Default: cpus-2, clamped ≥1 (resolved node-side). */
|
|
15435
|
+
threads: number().int().optional(),
|
|
15436
|
+
/** Concurrent slots. */
|
|
15437
|
+
parallel: number().int().default(1),
|
|
15438
|
+
/** Else lazy: first generate boots it. */
|
|
15439
|
+
autoStart: boolean().default(false),
|
|
15440
|
+
/** 0 = never; frees RAM after quiet periods. */
|
|
15441
|
+
idleStopMinutes: number().int().default(30)
|
|
15442
|
+
});
|
|
15443
|
+
var LlmRuntimeStatusSchema = object({
|
|
15444
|
+
/** Status is ALWAYS node-qualified. */
|
|
15445
|
+
nodeId: string(),
|
|
15446
|
+
state: _enum([
|
|
15447
|
+
"stopped",
|
|
15448
|
+
"downloading",
|
|
15449
|
+
"starting",
|
|
15450
|
+
"ready",
|
|
15451
|
+
"crashed",
|
|
15452
|
+
"failed"
|
|
15453
|
+
]),
|
|
15454
|
+
pid: number().optional(),
|
|
15455
|
+
port: number().optional(),
|
|
15456
|
+
modelPath: string().optional(),
|
|
15457
|
+
modelId: string().optional(),
|
|
15458
|
+
downloadProgress: number().min(0).max(1).optional(),
|
|
15459
|
+
lastError: string().optional(),
|
|
15460
|
+
crashesInWindow: number(),
|
|
15461
|
+
/** Child RSS (sampled best-effort). */
|
|
15462
|
+
memoryBytes: number().optional(),
|
|
15463
|
+
vramBytes: number().optional()
|
|
15464
|
+
});
|
|
15465
|
+
var LlmNodeModelSchema = object({
|
|
15466
|
+
file: string(),
|
|
15467
|
+
sizeBytes: number(),
|
|
15468
|
+
catalogId: string().optional(),
|
|
15469
|
+
installedAt: number().optional()
|
|
15470
|
+
});
|
|
15471
|
+
var LlmRuntimeDiskUsageSchema = object({
|
|
15472
|
+
nodeId: string(),
|
|
15473
|
+
modelsBytes: number(),
|
|
15474
|
+
freeBytes: number().optional()
|
|
15475
|
+
});
|
|
15476
|
+
method(LlmGenerateBaseInputSchema.extend({
|
|
15477
|
+
images: array(LlmImageSchema).optional(),
|
|
15478
|
+
runtime: ManagedRuntimeConfigSchema,
|
|
15479
|
+
/** The managed profile's timeout, threaded by the hub provider. */
|
|
15480
|
+
timeoutMs: number().int().positive().optional()
|
|
15481
|
+
}), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
|
|
15482
|
+
kind: "mutation",
|
|
15483
|
+
auth: "admin"
|
|
15484
|
+
}), method(object({}), _void(), {
|
|
15485
|
+
kind: "mutation",
|
|
15486
|
+
auth: "admin"
|
|
15487
|
+
}), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
|
|
15488
|
+
kind: "mutation",
|
|
15489
|
+
auth: "admin"
|
|
15490
|
+
}), method(object({ file: string() }), _void(), {
|
|
15491
|
+
kind: "mutation",
|
|
15492
|
+
auth: "admin"
|
|
15493
|
+
}), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
|
|
15494
|
+
/**
|
|
15495
|
+
* `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
|
|
15496
|
+
* methods concat-fan across providers; single-row methods route to ONE
|
|
15497
|
+
* provider by the `addonId` in the call input (the notification-output
|
|
15498
|
+
* posture, notification-output.cap.ts:215-250). Provided by `addon-ai`
|
|
15499
|
+
* (hub-placed); the cap stays open for future providers.
|
|
15500
|
+
*
|
|
15501
|
+
* Profiles are ROWS (data), not addons: one row = one usable model endpoint.
|
|
15502
|
+
* `apiKey` is a password field — providers REDACT it on read and merge on
|
|
15503
|
+
* write; a stored key NEVER round-trips to a client.
|
|
15504
|
+
*/
|
|
15505
|
+
var LlmProfileKindSchema = _enum([
|
|
15506
|
+
"openai-compatible",
|
|
15507
|
+
"openai",
|
|
15508
|
+
"anthropic",
|
|
15509
|
+
"google",
|
|
15510
|
+
"managed-local"
|
|
15511
|
+
]);
|
|
15512
|
+
var LlmProfileSchema = object({
|
|
15513
|
+
id: string(),
|
|
15514
|
+
name: string(),
|
|
15515
|
+
kind: LlmProfileKindSchema,
|
|
15516
|
+
/** Stamped by the provider — keeps the fanned catalog routable. */
|
|
15517
|
+
addonId: string(),
|
|
15518
|
+
enabled: boolean(),
|
|
15519
|
+
/** Vendor model id, or the managed runtime's loaded model. */
|
|
15520
|
+
model: string(),
|
|
15521
|
+
/** Required for openai-compatible; override for cloud kinds. */
|
|
15522
|
+
baseUrl: string().optional(),
|
|
15523
|
+
/** ConfigUISchema type:'password' — never round-trips (spec §5). */
|
|
15524
|
+
apiKey: string().optional(),
|
|
15525
|
+
supportsVision: boolean(),
|
|
15526
|
+
temperature: number().min(0).max(2).optional(),
|
|
15527
|
+
maxTokens: number().int().positive().optional(),
|
|
15528
|
+
timeoutMs: number().int().positive().default(6e4),
|
|
15529
|
+
extraHeaders: record(string(), string()).optional(),
|
|
15530
|
+
/** kind === 'managed-local' only (spec §4). */
|
|
15531
|
+
runtime: ManagedRuntimeConfigSchema.optional()
|
|
15532
|
+
});
|
|
15533
|
+
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
15534
|
+
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
15535
|
+
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
15536
|
+
var ConfigSchemaPassthrough = unknown();
|
|
15537
|
+
var LlmProfileKindDescriptorSchema = object({
|
|
15538
|
+
kind: LlmProfileKindSchema,
|
|
15539
|
+
label: string(),
|
|
15540
|
+
icon: string(),
|
|
15541
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
15542
|
+
addonId: string(),
|
|
15543
|
+
configSchema: ConfigSchemaPassthrough
|
|
15544
|
+
});
|
|
15545
|
+
var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
|
|
15546
|
+
var LlmDefaultSchema = object({
|
|
15547
|
+
selector: LlmDefaultSelectorSchema,
|
|
15548
|
+
profileId: string()
|
|
15549
|
+
});
|
|
15550
|
+
/** Server-side rollup row — getUsage never dumps raw call rows (spec §6). */
|
|
15551
|
+
var LlmUsageRollupSchema = object({
|
|
15552
|
+
day: string(),
|
|
15553
|
+
consumer: string(),
|
|
15554
|
+
profileId: string(),
|
|
15555
|
+
calls: number(),
|
|
15556
|
+
okCalls: number(),
|
|
15557
|
+
errorCalls: number(),
|
|
15558
|
+
inputTokens: number(),
|
|
15559
|
+
outputTokens: number(),
|
|
15560
|
+
avgLatencyMs: number()
|
|
15561
|
+
});
|
|
15562
|
+
/** LLM-facing view over the reused ModelCatalogEntry mechanism (spec §4.2). */
|
|
15563
|
+
var ManagedModelCatalogEntrySchema = object({
|
|
15564
|
+
id: string(),
|
|
15565
|
+
label: string(),
|
|
15566
|
+
family: string(),
|
|
15567
|
+
purpose: _enum(["text", "vision"]),
|
|
15568
|
+
url: string(),
|
|
15569
|
+
sha256: string(),
|
|
15570
|
+
sizeBytes: number(),
|
|
15571
|
+
quantization: string(),
|
|
15572
|
+
/** Load-time guidance shown in the picker. */
|
|
15573
|
+
minRamBytes: number(),
|
|
15574
|
+
contextSizeDefault: number().int(),
|
|
15575
|
+
/** Vision models: companion projector file. */
|
|
15576
|
+
mmprojUrl: string().optional()
|
|
15577
|
+
});
|
|
15578
|
+
var LlmRuntimeNodeSchema = object({
|
|
15579
|
+
nodeId: string(),
|
|
15580
|
+
reachable: boolean(),
|
|
15581
|
+
status: LlmRuntimeStatusSchema.optional(),
|
|
15582
|
+
disk: LlmRuntimeDiskUsageSchema.optional(),
|
|
15583
|
+
error: string().optional()
|
|
15584
|
+
});
|
|
15585
|
+
var GenerateVisionInputSchema = LlmGenerateBaseInputSchema.extend({ images: array(LlmImageSchema).min(1) });
|
|
15586
|
+
var ProfileRefInputSchema = object({
|
|
15587
|
+
addonId: string(),
|
|
15588
|
+
profileId: string()
|
|
15589
|
+
});
|
|
15590
|
+
method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
|
|
15591
|
+
kind: "mutation",
|
|
15592
|
+
auth: "admin"
|
|
15593
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15594
|
+
kind: "mutation",
|
|
15595
|
+
auth: "admin"
|
|
15596
|
+
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
15597
|
+
kind: "mutation",
|
|
15598
|
+
auth: "admin"
|
|
15599
|
+
}), method(ProfileRefInputSchema, array(string())), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
15600
|
+
selector: LlmDefaultSelectorSchema,
|
|
15601
|
+
profileId: string().nullable()
|
|
15602
|
+
}), _void(), {
|
|
15603
|
+
kind: "mutation",
|
|
15604
|
+
auth: "admin"
|
|
15605
|
+
}), method(object({
|
|
15606
|
+
since: number().optional(),
|
|
15607
|
+
until: number().optional(),
|
|
15608
|
+
consumer: string().optional(),
|
|
15609
|
+
profileId: string().optional()
|
|
15610
|
+
}), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
|
|
15611
|
+
nodeId: string(),
|
|
15612
|
+
model: ManagedModelRefSchema
|
|
15613
|
+
}), _void(), {
|
|
15614
|
+
kind: "mutation",
|
|
15615
|
+
auth: "admin"
|
|
15616
|
+
}), method(object({
|
|
15617
|
+
nodeId: string(),
|
|
15618
|
+
file: string()
|
|
15619
|
+
}), _void(), {
|
|
15620
|
+
kind: "mutation",
|
|
15621
|
+
auth: "admin"
|
|
15622
|
+
}), method(ProfileRefInputSchema, LlmRuntimeStatusSchema), method(ProfileRefInputSchema, LlmRuntimeStatusSchema, {
|
|
15623
|
+
kind: "mutation",
|
|
15624
|
+
auth: "admin"
|
|
15625
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15626
|
+
kind: "mutation",
|
|
15627
|
+
auth: "admin"
|
|
15628
|
+
});
|
|
15629
|
+
/**
|
|
15071
15630
|
* Zod schemas for persisted record types.
|
|
15072
15631
|
*
|
|
15073
15632
|
* These schemas serve as the single source of truth for types that are
|
|
@@ -15269,6 +15828,22 @@ var TrackSnapshotSchema = object({
|
|
|
15269
15828
|
/** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
|
|
15270
15829
|
mediaKey: string()
|
|
15271
15830
|
});
|
|
15831
|
+
/**
|
|
15832
|
+
* One audio-classification label heard on the track's camera while the
|
|
15833
|
+
* track was alive, aggregated per label. An "episode" is one persisted
|
|
15834
|
+
* audio event (the confident-classification path: score ≥ the device's
|
|
15835
|
+
* `classificationMinScore`, class-change-or-heartbeat coalesced) — NOT
|
|
15836
|
+
* one 32 ms inference chunk, so counts stay human-scaled.
|
|
15837
|
+
*/
|
|
15838
|
+
var TrackAudioLabelSchema = object({
|
|
15839
|
+
label: string(),
|
|
15840
|
+
/** Highest classification score observed across the label's episodes. */
|
|
15841
|
+
peakScore: number(),
|
|
15842
|
+
/** Number of coalesced audio-event episodes carrying this label. */
|
|
15843
|
+
count: number(),
|
|
15844
|
+
firstAt: number(),
|
|
15845
|
+
lastAt: number()
|
|
15846
|
+
});
|
|
15272
15847
|
var TrackSchema = object({
|
|
15273
15848
|
trackId: string(),
|
|
15274
15849
|
deviceId: number(),
|
|
@@ -15283,6 +15858,10 @@ var TrackSchema = object({
|
|
|
15283
15858
|
snapshots: array(TrackSnapshotSchema).readonly(),
|
|
15284
15859
|
/** Deduplicated zones the track has entered at least once. */
|
|
15285
15860
|
zonesVisited: array(string()).readonly(),
|
|
15861
|
+
/** Deduplicated set of detector classes observed for this track over its
|
|
15862
|
+
* life (a track may be reclassified, e.g. person→vehicle). Absent on
|
|
15863
|
+
* legacy rows written before class accumulation shipped. */
|
|
15864
|
+
classes: array(string()).readonly().optional(),
|
|
15286
15865
|
/** Cumulative normalized distance travelled (0..1 units = full frame width). */
|
|
15287
15866
|
totalDistance: number(),
|
|
15288
15867
|
state: TrackStateSchema,
|
|
@@ -15296,7 +15875,11 @@ var TrackSchema = object({
|
|
|
15296
15875
|
bestEventId: string().optional(),
|
|
15297
15876
|
/** Tag of the importance sub-signal that dominated the score
|
|
15298
15877
|
* (identity|dwell|proximity|class|confidence|travel|zone). */
|
|
15299
|
-
importanceReason: string().optional()
|
|
15878
|
+
importanceReason: string().optional(),
|
|
15879
|
+
/** Audio-classification labels heard on the camera during the track's
|
|
15880
|
+
* life (score ≥ device `classificationMinScore`), aggregated per label.
|
|
15881
|
+
* Absent on legacy rows / tracks with no confident audio. */
|
|
15882
|
+
audioLabels: array(TrackAudioLabelSchema).readonly().optional()
|
|
15300
15883
|
});
|
|
15301
15884
|
var BaseEventFields = {
|
|
15302
15885
|
id: string(),
|
|
@@ -15444,7 +16027,7 @@ var KeyEventSchema = object({
|
|
|
15444
16027
|
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
15445
16028
|
windowMs: number().optional()
|
|
15446
16029
|
});
|
|
15447
|
-
|
|
16030
|
+
object({
|
|
15448
16031
|
trackId: string(),
|
|
15449
16032
|
className: string(),
|
|
15450
16033
|
confidence: number(),
|
|
@@ -15452,6 +16035,23 @@ var TrackedDetectionSchema = object({
|
|
|
15452
16035
|
zones: array(string()).readonly(),
|
|
15453
16036
|
state: TrackStateSchema
|
|
15454
16037
|
});
|
|
16038
|
+
var OverlayDetectionSchema = looseObject({
|
|
16039
|
+
id: string(),
|
|
16040
|
+
kind: _enum(["first-level", "detail"]),
|
|
16041
|
+
macroClass: string(),
|
|
16042
|
+
score: number(),
|
|
16043
|
+
bbox: object({
|
|
16044
|
+
x: number(),
|
|
16045
|
+
y: number(),
|
|
16046
|
+
width: number(),
|
|
16047
|
+
height: number()
|
|
16048
|
+
}),
|
|
16049
|
+
labels: array(looseObject({
|
|
16050
|
+
label: string(),
|
|
16051
|
+
score: number()
|
|
16052
|
+
})).readonly(),
|
|
16053
|
+
parentId: string().optional()
|
|
16054
|
+
});
|
|
15455
16055
|
var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: number() });
|
|
15456
16056
|
var SearchObjectEventsInput = object({
|
|
15457
16057
|
text: string(),
|
|
@@ -15462,6 +16062,20 @@ var SearchObjectEventsInput = object({
|
|
|
15462
16062
|
limit: number().default(50),
|
|
15463
16063
|
minScore: number().min(0).max(1).default(.2)
|
|
15464
16064
|
});
|
|
16065
|
+
var TrackCascadeCountsSchema = object({
|
|
16066
|
+
/** Persisted track roots deleted (authoritative). */
|
|
16067
|
+
tracks: number().int(),
|
|
16068
|
+
/** Object events removed with their tracks (best-effort; see note above). */
|
|
16069
|
+
events: number().int(),
|
|
16070
|
+
/** Track/face/plate-owned media removed (best-effort). Never identity media. */
|
|
16071
|
+
media: number().int(),
|
|
16072
|
+
/** Unassigned (non-enrolled) face reads removed (best-effort). */
|
|
16073
|
+
faces: number().int(),
|
|
16074
|
+
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
16075
|
+
plates: number().int(),
|
|
16076
|
+
/** Per-track CLIP search vectors removed (best-effort). */
|
|
16077
|
+
embeddings: number().int()
|
|
16078
|
+
});
|
|
15465
16079
|
DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
|
|
15466
16080
|
deviceId: number(),
|
|
15467
16081
|
trackId: string()
|
|
@@ -15493,6 +16107,24 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15493
16107
|
}), {
|
|
15494
16108
|
kind: "mutation",
|
|
15495
16109
|
auth: "admin"
|
|
16110
|
+
}), method(object({
|
|
16111
|
+
deviceId: number(),
|
|
16112
|
+
cutoffMs: number()
|
|
16113
|
+
}), TrackCascadeCountsSchema, {
|
|
16114
|
+
kind: "mutation",
|
|
16115
|
+
auth: "admin"
|
|
16116
|
+
}), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
|
|
16117
|
+
kind: "mutation",
|
|
16118
|
+
auth: "admin"
|
|
16119
|
+
}), method(object({
|
|
16120
|
+
deviceId: number(),
|
|
16121
|
+
trackIds: array(string()).min(1)
|
|
16122
|
+
}), object({
|
|
16123
|
+
deleted: number().int(),
|
|
16124
|
+
failed: array(string()).readonly()
|
|
16125
|
+
}), {
|
|
16126
|
+
kind: "mutation",
|
|
16127
|
+
auth: "admin"
|
|
15496
16128
|
}), method(object({
|
|
15497
16129
|
eventId: string(),
|
|
15498
16130
|
kind: MediaFileKindEnum.optional()
|
|
@@ -15501,7 +16133,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15501
16133
|
timestamp: number(),
|
|
15502
16134
|
frameWidth: number(),
|
|
15503
16135
|
frameHeight: number(),
|
|
15504
|
-
detections: array(
|
|
16136
|
+
detections: array(OverlayDetectionSchema).readonly()
|
|
15505
16137
|
}), object({
|
|
15506
16138
|
deviceId: number(),
|
|
15507
16139
|
trackId: string(),
|
|
@@ -18270,6 +18902,24 @@ DeviceType.Camera, method(object({
|
|
|
18270
18902
|
deviceId: number(),
|
|
18271
18903
|
status: OsdStatusSchema
|
|
18272
18904
|
});
|
|
18905
|
+
var VehicleSchema = object({
|
|
18906
|
+
id: string(),
|
|
18907
|
+
name: string(),
|
|
18908
|
+
sampleCount: number().int().nonnegative(),
|
|
18909
|
+
coverMediaKey: string().optional(),
|
|
18910
|
+
/** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
|
|
18911
|
+
coverBase64: string().optional()
|
|
18912
|
+
});
|
|
18913
|
+
var VehicleSampleInfoSchema = object({
|
|
18914
|
+
id: string(),
|
|
18915
|
+
/** The plate text this sample enrolled (self-labeling — no embedding). */
|
|
18916
|
+
text: string(),
|
|
18917
|
+
/** OCR confidence of the enrolled read (0..1). */
|
|
18918
|
+
score: number(),
|
|
18919
|
+
addedAt: number().int(),
|
|
18920
|
+
deviceId: number().int().optional(),
|
|
18921
|
+
base64: string().optional()
|
|
18922
|
+
});
|
|
18273
18923
|
var PlateInfoSchema = object({
|
|
18274
18924
|
plateId: string(),
|
|
18275
18925
|
deviceId: number().int(),
|
|
@@ -18281,6 +18931,16 @@ var PlateInfoSchema = object({
|
|
|
18281
18931
|
score: number(),
|
|
18282
18932
|
/** True when the text was manually corrected (exempt from retention). */
|
|
18283
18933
|
corrected: boolean(),
|
|
18934
|
+
/** Recognized vehicle id when a matcher lookup named this read (SQL NULL → absent). */
|
|
18935
|
+
recognizedVehicleId: string().optional(),
|
|
18936
|
+
/** Resolved vehicle name for `recognizedVehicleId` (UI convenience). */
|
|
18937
|
+
vehicleName: string().optional(),
|
|
18938
|
+
/** True once the read was assigned to a vehicle (enrolled as a sample). */
|
|
18939
|
+
assigned: boolean(),
|
|
18940
|
+
/** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
|
|
18941
|
+
plateBbox: BoundingBoxSchema.optional(),
|
|
18942
|
+
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
18943
|
+
keyFrameMediaKey: string().optional(),
|
|
18284
18944
|
base64: string().optional()
|
|
18285
18945
|
});
|
|
18286
18946
|
var MediaFileLiteSchema = object({
|
|
@@ -18320,6 +18980,48 @@ method(object({
|
|
|
18320
18980
|
}), method(object({ plateId: string() }), _void(), {
|
|
18321
18981
|
kind: "mutation",
|
|
18322
18982
|
auth: "admin"
|
|
18983
|
+
}), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
|
|
18984
|
+
kind: "mutation",
|
|
18985
|
+
auth: "admin"
|
|
18986
|
+
}), method(object({
|
|
18987
|
+
id: string(),
|
|
18988
|
+
name: string().min(1)
|
|
18989
|
+
}), _void(), {
|
|
18990
|
+
kind: "mutation",
|
|
18991
|
+
auth: "admin"
|
|
18992
|
+
}), method(object({ id: string() }), _void(), {
|
|
18993
|
+
kind: "mutation",
|
|
18994
|
+
auth: "admin"
|
|
18995
|
+
}), method(object({ vehicleId: string() }), array(VehicleSampleInfoSchema).readonly()), method(object({
|
|
18996
|
+
vehicleId: string(),
|
|
18997
|
+
sampleId: string()
|
|
18998
|
+
}), _void(), {
|
|
18999
|
+
kind: "mutation",
|
|
19000
|
+
auth: "admin"
|
|
19001
|
+
}), method(object({
|
|
19002
|
+
plateId: string(),
|
|
19003
|
+
vehicleId: string()
|
|
19004
|
+
}), _void(), {
|
|
19005
|
+
kind: "mutation",
|
|
19006
|
+
auth: "admin"
|
|
19007
|
+
}), method(object({ plateId: string() }), _void(), {
|
|
19008
|
+
kind: "mutation",
|
|
19009
|
+
auth: "admin"
|
|
19010
|
+
}), method(object({
|
|
19011
|
+
plateIds: array(string()).min(1),
|
|
19012
|
+
vehicleId: string()
|
|
19013
|
+
}), object({
|
|
19014
|
+
assigned: number().int(),
|
|
19015
|
+
failed: array(string()).readonly()
|
|
19016
|
+
}), {
|
|
19017
|
+
kind: "mutation",
|
|
19018
|
+
auth: "admin"
|
|
19019
|
+
}), method(object({ plateIds: array(string()).min(1) }), object({
|
|
19020
|
+
unassigned: number().int(),
|
|
19021
|
+
failed: array(string()).readonly()
|
|
19022
|
+
}), {
|
|
19023
|
+
kind: "mutation",
|
|
19024
|
+
auth: "admin"
|
|
18323
19025
|
});
|
|
18324
19026
|
var ModelFormatSchema = _enum(MODEL_FORMATS);
|
|
18325
19027
|
var HwAccelBackendInputSchema = _enum([
|
|
@@ -18398,6 +19100,10 @@ var GpuInfoSchema = object({
|
|
|
18398
19100
|
memoryMB: number().optional()
|
|
18399
19101
|
});
|
|
18400
19102
|
var NpuInfoSchema = object({ type: _enum(["apple-ane", "intel-npu"]) });
|
|
19103
|
+
var CoralInfoSchema = object({
|
|
19104
|
+
type: literal("coral-edgetpu"),
|
|
19105
|
+
bus: string().optional()
|
|
19106
|
+
});
|
|
18401
19107
|
var HardwareInfoSchema = object({
|
|
18402
19108
|
platform: HardwarePlatformSchema,
|
|
18403
19109
|
arch: HardwareArchSchema,
|
|
@@ -18406,7 +19112,8 @@ var HardwareInfoSchema = object({
|
|
|
18406
19112
|
totalRAM_MB: number(),
|
|
18407
19113
|
availableRAM_MB: number(),
|
|
18408
19114
|
gpu: GpuInfoSchema.nullable(),
|
|
18409
|
-
npu: NpuInfoSchema.nullable()
|
|
19115
|
+
npu: NpuInfoSchema.nullable(),
|
|
19116
|
+
coral: CoralInfoSchema.nullable().optional()
|
|
18410
19117
|
});
|
|
18411
19118
|
var PlatformScoreSchema = object({
|
|
18412
19119
|
runtime: _enum(["node", "python"]),
|
|
@@ -20865,6 +21572,168 @@ Object.freeze({
|
|
|
20865
21572
|
addonId: null,
|
|
20866
21573
|
access: "create"
|
|
20867
21574
|
},
|
|
21575
|
+
"llm.deleteModel": {
|
|
21576
|
+
capName: "llm",
|
|
21577
|
+
capScope: "system",
|
|
21578
|
+
addonId: null,
|
|
21579
|
+
access: "delete"
|
|
21580
|
+
},
|
|
21581
|
+
"llm.deleteProfile": {
|
|
21582
|
+
capName: "llm",
|
|
21583
|
+
capScope: "system",
|
|
21584
|
+
addonId: null,
|
|
21585
|
+
access: "delete"
|
|
21586
|
+
},
|
|
21587
|
+
"llm.generate": {
|
|
21588
|
+
capName: "llm",
|
|
21589
|
+
capScope: "system",
|
|
21590
|
+
addonId: null,
|
|
21591
|
+
access: "create"
|
|
21592
|
+
},
|
|
21593
|
+
"llm.generateVision": {
|
|
21594
|
+
capName: "llm",
|
|
21595
|
+
capScope: "system",
|
|
21596
|
+
addonId: null,
|
|
21597
|
+
access: "create"
|
|
21598
|
+
},
|
|
21599
|
+
"llm.getDefaults": {
|
|
21600
|
+
capName: "llm",
|
|
21601
|
+
capScope: "system",
|
|
21602
|
+
addonId: null,
|
|
21603
|
+
access: "view"
|
|
21604
|
+
},
|
|
21605
|
+
"llm.getRuntimeStatus": {
|
|
21606
|
+
capName: "llm",
|
|
21607
|
+
capScope: "system",
|
|
21608
|
+
addonId: null,
|
|
21609
|
+
access: "view"
|
|
21610
|
+
},
|
|
21611
|
+
"llm.getUsage": {
|
|
21612
|
+
capName: "llm",
|
|
21613
|
+
capScope: "system",
|
|
21614
|
+
addonId: null,
|
|
21615
|
+
access: "view"
|
|
21616
|
+
},
|
|
21617
|
+
"llm.installModel": {
|
|
21618
|
+
capName: "llm",
|
|
21619
|
+
capScope: "system",
|
|
21620
|
+
addonId: null,
|
|
21621
|
+
access: "create"
|
|
21622
|
+
},
|
|
21623
|
+
"llm.listModelCatalog": {
|
|
21624
|
+
capName: "llm",
|
|
21625
|
+
capScope: "system",
|
|
21626
|
+
addonId: null,
|
|
21627
|
+
access: "view"
|
|
21628
|
+
},
|
|
21629
|
+
"llm.listModels": {
|
|
21630
|
+
capName: "llm",
|
|
21631
|
+
capScope: "system",
|
|
21632
|
+
addonId: null,
|
|
21633
|
+
access: "view"
|
|
21634
|
+
},
|
|
21635
|
+
"llm.listNodeModels": {
|
|
21636
|
+
capName: "llm",
|
|
21637
|
+
capScope: "system",
|
|
21638
|
+
addonId: null,
|
|
21639
|
+
access: "view"
|
|
21640
|
+
},
|
|
21641
|
+
"llm.listProfileKinds": {
|
|
21642
|
+
capName: "llm",
|
|
21643
|
+
capScope: "system",
|
|
21644
|
+
addonId: null,
|
|
21645
|
+
access: "view"
|
|
21646
|
+
},
|
|
21647
|
+
"llm.listProfiles": {
|
|
21648
|
+
capName: "llm",
|
|
21649
|
+
capScope: "system",
|
|
21650
|
+
addonId: null,
|
|
21651
|
+
access: "view"
|
|
21652
|
+
},
|
|
21653
|
+
"llm.listRuntimeNodes": {
|
|
21654
|
+
capName: "llm",
|
|
21655
|
+
capScope: "system",
|
|
21656
|
+
addonId: null,
|
|
21657
|
+
access: "view"
|
|
21658
|
+
},
|
|
21659
|
+
"llm.setDefault": {
|
|
21660
|
+
capName: "llm",
|
|
21661
|
+
capScope: "system",
|
|
21662
|
+
addonId: null,
|
|
21663
|
+
access: "create"
|
|
21664
|
+
},
|
|
21665
|
+
"llm.startRuntime": {
|
|
21666
|
+
capName: "llm",
|
|
21667
|
+
capScope: "system",
|
|
21668
|
+
addonId: null,
|
|
21669
|
+
access: "create"
|
|
21670
|
+
},
|
|
21671
|
+
"llm.stopRuntime": {
|
|
21672
|
+
capName: "llm",
|
|
21673
|
+
capScope: "system",
|
|
21674
|
+
addonId: null,
|
|
21675
|
+
access: "create"
|
|
21676
|
+
},
|
|
21677
|
+
"llm.testProfile": {
|
|
21678
|
+
capName: "llm",
|
|
21679
|
+
capScope: "system",
|
|
21680
|
+
addonId: null,
|
|
21681
|
+
access: "create"
|
|
21682
|
+
},
|
|
21683
|
+
"llm.upsertProfile": {
|
|
21684
|
+
capName: "llm",
|
|
21685
|
+
capScope: "system",
|
|
21686
|
+
addonId: null,
|
|
21687
|
+
access: "create"
|
|
21688
|
+
},
|
|
21689
|
+
"llmRuntime.complete": {
|
|
21690
|
+
capName: "llm-runtime",
|
|
21691
|
+
capScope: "system",
|
|
21692
|
+
addonId: null,
|
|
21693
|
+
access: "create"
|
|
21694
|
+
},
|
|
21695
|
+
"llmRuntime.deleteModel": {
|
|
21696
|
+
capName: "llm-runtime",
|
|
21697
|
+
capScope: "system",
|
|
21698
|
+
addonId: null,
|
|
21699
|
+
access: "delete"
|
|
21700
|
+
},
|
|
21701
|
+
"llmRuntime.ensureStarted": {
|
|
21702
|
+
capName: "llm-runtime",
|
|
21703
|
+
capScope: "system",
|
|
21704
|
+
addonId: null,
|
|
21705
|
+
access: "create"
|
|
21706
|
+
},
|
|
21707
|
+
"llmRuntime.getDiskUsage": {
|
|
21708
|
+
capName: "llm-runtime",
|
|
21709
|
+
capScope: "system",
|
|
21710
|
+
addonId: null,
|
|
21711
|
+
access: "view"
|
|
21712
|
+
},
|
|
21713
|
+
"llmRuntime.installModel": {
|
|
21714
|
+
capName: "llm-runtime",
|
|
21715
|
+
capScope: "system",
|
|
21716
|
+
addonId: null,
|
|
21717
|
+
access: "create"
|
|
21718
|
+
},
|
|
21719
|
+
"llmRuntime.listLocalModels": {
|
|
21720
|
+
capName: "llm-runtime",
|
|
21721
|
+
capScope: "system",
|
|
21722
|
+
addonId: null,
|
|
21723
|
+
access: "view"
|
|
21724
|
+
},
|
|
21725
|
+
"llmRuntime.status": {
|
|
21726
|
+
capName: "llm-runtime",
|
|
21727
|
+
capScope: "system",
|
|
21728
|
+
addonId: null,
|
|
21729
|
+
access: "view"
|
|
21730
|
+
},
|
|
21731
|
+
"llmRuntime.stop": {
|
|
21732
|
+
capName: "llm-runtime",
|
|
21733
|
+
capScope: "system",
|
|
21734
|
+
addonId: null,
|
|
21735
|
+
access: "create"
|
|
21736
|
+
},
|
|
20868
21737
|
"localNetwork.getAllowedAddresses": {
|
|
20869
21738
|
capName: "local-network",
|
|
20870
21739
|
capScope: "system",
|
|
@@ -21495,6 +22364,12 @@ Object.freeze({
|
|
|
21495
22364
|
addonId: null,
|
|
21496
22365
|
access: "delete"
|
|
21497
22366
|
},
|
|
22367
|
+
"pipelineAnalytics.deleteTracks": {
|
|
22368
|
+
capName: "pipeline-analytics",
|
|
22369
|
+
capScope: "device",
|
|
22370
|
+
addonId: null,
|
|
22371
|
+
access: "delete"
|
|
22372
|
+
},
|
|
21498
22373
|
"pipelineAnalytics.getActiveTracks": {
|
|
21499
22374
|
capName: "pipeline-analytics",
|
|
21500
22375
|
capScope: "device",
|
|
@@ -21561,12 +22436,24 @@ Object.freeze({
|
|
|
21561
22436
|
addonId: null,
|
|
21562
22437
|
access: "create"
|
|
21563
22438
|
},
|
|
22439
|
+
"pipelineAnalytics.pruneTracksBefore": {
|
|
22440
|
+
capName: "pipeline-analytics",
|
|
22441
|
+
capScope: "device",
|
|
22442
|
+
addonId: null,
|
|
22443
|
+
access: "create"
|
|
22444
|
+
},
|
|
21564
22445
|
"pipelineAnalytics.searchObjectEvents": {
|
|
21565
22446
|
capName: "pipeline-analytics",
|
|
21566
22447
|
capScope: "device",
|
|
21567
22448
|
addonId: null,
|
|
21568
22449
|
access: "view"
|
|
21569
22450
|
},
|
|
22451
|
+
"pipelineAnalytics.wipeAllAnalytics": {
|
|
22452
|
+
capName: "pipeline-analytics",
|
|
22453
|
+
capScope: "device",
|
|
22454
|
+
addonId: null,
|
|
22455
|
+
access: "delete"
|
|
22456
|
+
},
|
|
21570
22457
|
"pipelineExecutor.cacheFrameInPool": {
|
|
21571
22458
|
capName: "pipeline-executor",
|
|
21572
22459
|
capScope: "system",
|
|
@@ -22071,18 +22958,42 @@ Object.freeze({
|
|
|
22071
22958
|
addonId: null,
|
|
22072
22959
|
access: "create"
|
|
22073
22960
|
},
|
|
22961
|
+
"plateGallery.assignPlate": {
|
|
22962
|
+
capName: "plate-gallery",
|
|
22963
|
+
capScope: "system",
|
|
22964
|
+
addonId: null,
|
|
22965
|
+
access: "create"
|
|
22966
|
+
},
|
|
22967
|
+
"plateGallery.assignPlates": {
|
|
22968
|
+
capName: "plate-gallery",
|
|
22969
|
+
capScope: "system",
|
|
22970
|
+
addonId: null,
|
|
22971
|
+
access: "create"
|
|
22972
|
+
},
|
|
22074
22973
|
"plateGallery.correctPlateText": {
|
|
22075
22974
|
capName: "plate-gallery",
|
|
22076
22975
|
capScope: "system",
|
|
22077
22976
|
addonId: null,
|
|
22078
22977
|
access: "create"
|
|
22079
22978
|
},
|
|
22979
|
+
"plateGallery.createVehicle": {
|
|
22980
|
+
capName: "plate-gallery",
|
|
22981
|
+
capScope: "system",
|
|
22982
|
+
addonId: null,
|
|
22983
|
+
access: "create"
|
|
22984
|
+
},
|
|
22080
22985
|
"plateGallery.deletePlate": {
|
|
22081
22986
|
capName: "plate-gallery",
|
|
22082
22987
|
capScope: "system",
|
|
22083
22988
|
addonId: null,
|
|
22084
22989
|
access: "delete"
|
|
22085
22990
|
},
|
|
22991
|
+
"plateGallery.deleteVehicle": {
|
|
22992
|
+
capName: "plate-gallery",
|
|
22993
|
+
capScope: "system",
|
|
22994
|
+
addonId: null,
|
|
22995
|
+
access: "delete"
|
|
22996
|
+
},
|
|
22086
22997
|
"plateGallery.getPlateByTrack": {
|
|
22087
22998
|
capName: "plate-gallery",
|
|
22088
22999
|
capScope: "system",
|
|
@@ -22101,6 +23012,30 @@ Object.freeze({
|
|
|
22101
23012
|
addonId: null,
|
|
22102
23013
|
access: "view"
|
|
22103
23014
|
},
|
|
23015
|
+
"plateGallery.listVehicles": {
|
|
23016
|
+
capName: "plate-gallery",
|
|
23017
|
+
capScope: "system",
|
|
23018
|
+
addonId: null,
|
|
23019
|
+
access: "view"
|
|
23020
|
+
},
|
|
23021
|
+
"plateGallery.listVehicleSamples": {
|
|
23022
|
+
capName: "plate-gallery",
|
|
23023
|
+
capScope: "system",
|
|
23024
|
+
addonId: null,
|
|
23025
|
+
access: "view"
|
|
23026
|
+
},
|
|
23027
|
+
"plateGallery.removeVehicleSample": {
|
|
23028
|
+
capName: "plate-gallery",
|
|
23029
|
+
capScope: "system",
|
|
23030
|
+
addonId: null,
|
|
23031
|
+
access: "delete"
|
|
23032
|
+
},
|
|
23033
|
+
"plateGallery.renameVehicle": {
|
|
23034
|
+
capName: "plate-gallery",
|
|
23035
|
+
capScope: "system",
|
|
23036
|
+
addonId: null,
|
|
23037
|
+
access: "create"
|
|
23038
|
+
},
|
|
22104
23039
|
"plateGallery.searchPlates": {
|
|
22105
23040
|
capName: "plate-gallery",
|
|
22106
23041
|
capScope: "system",
|
|
@@ -22113,6 +23048,18 @@ Object.freeze({
|
|
|
22113
23048
|
addonId: null,
|
|
22114
23049
|
access: "view"
|
|
22115
23050
|
},
|
|
23051
|
+
"plateGallery.unassignPlate": {
|
|
23052
|
+
capName: "plate-gallery",
|
|
23053
|
+
capScope: "system",
|
|
23054
|
+
addonId: null,
|
|
23055
|
+
access: "create"
|
|
23056
|
+
},
|
|
23057
|
+
"plateGallery.unassignPlates": {
|
|
23058
|
+
capName: "plate-gallery",
|
|
23059
|
+
capScope: "system",
|
|
23060
|
+
addonId: null,
|
|
23061
|
+
access: "create"
|
|
23062
|
+
},
|
|
22116
23063
|
"platformProbe.getCapabilities": {
|
|
22117
23064
|
capName: "platform-probe",
|
|
22118
23065
|
capScope: "system",
|
|
@@ -22305,6 +23252,48 @@ Object.freeze({
|
|
|
22305
23252
|
addonId: null,
|
|
22306
23253
|
access: "create"
|
|
22307
23254
|
},
|
|
23255
|
+
"sceneMonitor.captureReference": {
|
|
23256
|
+
capName: "scene-monitor",
|
|
23257
|
+
capScope: "device",
|
|
23258
|
+
addonId: null,
|
|
23259
|
+
access: "create"
|
|
23260
|
+
},
|
|
23261
|
+
"sceneMonitor.createScene": {
|
|
23262
|
+
capName: "scene-monitor",
|
|
23263
|
+
capScope: "device",
|
|
23264
|
+
addonId: null,
|
|
23265
|
+
access: "create"
|
|
23266
|
+
},
|
|
23267
|
+
"sceneMonitor.deleteReference": {
|
|
23268
|
+
capName: "scene-monitor",
|
|
23269
|
+
capScope: "device",
|
|
23270
|
+
addonId: null,
|
|
23271
|
+
access: "delete"
|
|
23272
|
+
},
|
|
23273
|
+
"sceneMonitor.deleteScene": {
|
|
23274
|
+
capName: "scene-monitor",
|
|
23275
|
+
capScope: "device",
|
|
23276
|
+
addonId: null,
|
|
23277
|
+
access: "delete"
|
|
23278
|
+
},
|
|
23279
|
+
"sceneMonitor.listScenes": {
|
|
23280
|
+
capName: "scene-monitor",
|
|
23281
|
+
capScope: "device",
|
|
23282
|
+
addonId: null,
|
|
23283
|
+
access: "view"
|
|
23284
|
+
},
|
|
23285
|
+
"sceneMonitor.recheckNow": {
|
|
23286
|
+
capName: "scene-monitor",
|
|
23287
|
+
capScope: "device",
|
|
23288
|
+
addonId: null,
|
|
23289
|
+
access: "create"
|
|
23290
|
+
},
|
|
23291
|
+
"sceneMonitor.updateScene": {
|
|
23292
|
+
capName: "scene-monitor",
|
|
23293
|
+
capScope: "device",
|
|
23294
|
+
addonId: null,
|
|
23295
|
+
access: "create"
|
|
23296
|
+
},
|
|
22308
23297
|
"scriptRunner.run": {
|
|
22309
23298
|
capName: "script-runner",
|
|
22310
23299
|
capScope: "device",
|
|
@@ -23389,6 +24378,7 @@ Object.freeze({
|
|
|
23389
24378
|
Object.freeze({
|
|
23390
24379
|
"broker": "broker",
|
|
23391
24380
|
"device-export": "device-export",
|
|
24381
|
+
"llm": "ai",
|
|
23392
24382
|
"mesh-network": "mesh",
|
|
23393
24383
|
"mqtt-broker": "broker",
|
|
23394
24384
|
"network-access": "ingress",
|
|
@@ -23575,9 +24565,9 @@ async function resolveDecoderBackend(api, nodeId, logger) {
|
|
|
23575
24565
|
//#region src/shared/notifying-ring-buffer.ts
|
|
23576
24566
|
/**
|
|
23577
24567
|
* A {@link RingBuffer} that can notify a single blocked consumer the moment an
|
|
23578
|
-
* item commits — the server side of the `pullFrames
|
|
24568
|
+
* item commits — the server side of the `pullFrames` long-poll.
|
|
23579
24569
|
*
|
|
23580
|
-
* The decoder's `onFrame
|
|
24570
|
+
* The decoder's `onFrame` callback drives `push()`; a consumer
|
|
23581
24571
|
* that finds the ring momentarily empty calls {@link waitForItem} and is woken
|
|
23582
24572
|
* on the very next `push()` (a real commit signal) rather than spinning at
|
|
23583
24573
|
* ~1kHz on `setTimeout(…,1)`. If nothing commits it resolves after `waitMs`,
|
|
@@ -24021,8 +25011,7 @@ var FrameStreamSplitter = class {
|
|
|
24021
25011
|
//#region src/ffmpeg-decoder-session.ts
|
|
24022
25012
|
/**
|
|
24023
25013
|
* FfmpegDecoderSession — an `IDecoderSession` that decodes video in an **ffmpeg
|
|
24024
|
-
* subprocess** and
|
|
24025
|
-
* ring `decoder-nodeav` writes into.
|
|
25014
|
+
* subprocess** and delivers each decoded frame via the `onFrame` callback.
|
|
24026
25015
|
*
|
|
24027
25016
|
* Why a subprocess: on the Intel hub, node-av's in-process VA-API decode path
|
|
24028
25017
|
* SIGBUSes (uncatchable) on 8MP h264 and kills the whole decoder runner. ffmpeg
|
|
@@ -24035,15 +25024,13 @@ var FrameStreamSplitter = class {
|
|
|
24035
25024
|
* ffmpeg → `-vf <scale_vaapi=…,hwdownload,… | hwdownload,…,scale=…>` (GPU or
|
|
24036
25025
|
* CPU scale, per {@link buildFfmpegDecodeArgs})
|
|
24037
25026
|
* raw video → ffmpeg stdout (pipe:1) → a {@link FrameStreamSplitter} that fills
|
|
24038
|
-
* each frame's bytes
|
|
24039
|
-
* `
|
|
24040
|
-
*
|
|
24041
|
-
* No `Buffer.concat` accumulator, one copy per byte, none for dropped
|
|
24042
|
-
* frames.
|
|
25027
|
+
* each frame's bytes into a per-frame buffer → `DecodedFrame` via
|
|
25028
|
+
* `onFrame`. No `Buffer.concat` accumulator, one copy per byte, none
|
|
25029
|
+
* for dropped frames.
|
|
24043
25030
|
*
|
|
24044
|
-
* Output geometry: the broker does not pass source width/height
|
|
24045
|
-
*
|
|
24046
|
-
*
|
|
25031
|
+
* Output geometry: the broker does not pass source width/height, so ffmpeg picks
|
|
25032
|
+
* the height (`scale=min(iw\,maxW):-2`) and the session learns the exact output
|
|
25033
|
+
* W×H by parsing the muxer's `Output #0` stderr block
|
|
24047
25034
|
* ({@link parseFfmpegOutputDims}). Until dims are known, stdout is buffered.
|
|
24048
25035
|
*/
|
|
24049
25036
|
var noopLogger = {
|
|
@@ -24083,8 +25070,6 @@ var MAX_GOP_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
|
24083
25070
|
var FfmpegDecoderSession = class {
|
|
24084
25071
|
config;
|
|
24085
25072
|
logger;
|
|
24086
|
-
frameSink;
|
|
24087
|
-
nodeId;
|
|
24088
25073
|
renderDevice;
|
|
24089
25074
|
ffmpegPath;
|
|
24090
25075
|
/**
|
|
@@ -24134,9 +25119,6 @@ var FfmpegDecoderSession = class {
|
|
|
24134
25119
|
/** Stdout chunks received BEFORE geometry is known — replayed into the
|
|
24135
25120
|
* splitter once `frameSize` is learned. A short-lived list (no concat). */
|
|
24136
25121
|
pendingStdout = [];
|
|
24137
|
-
/** The shm ring slot the splitter is currently filling, if any — aborted on
|
|
24138
|
-
* respawn / teardown so a half-filled slot never publishes. */
|
|
24139
|
-
openSlot = null;
|
|
24140
25122
|
stderrAccum = "";
|
|
24141
25123
|
destroyed = false;
|
|
24142
25124
|
pixel;
|
|
@@ -24146,9 +25128,7 @@ var FfmpegDecoderSession = class {
|
|
|
24146
25128
|
outWidth = 0;
|
|
24147
25129
|
outHeight = 0;
|
|
24148
25130
|
frameSize = 0;
|
|
24149
|
-
frameRingSink = null;
|
|
24150
25131
|
frameCallbacks = /* @__PURE__ */ new Set();
|
|
24151
|
-
handleCallbacks = /* @__PURE__ */ new Set();
|
|
24152
25132
|
frameDropper;
|
|
24153
25133
|
inputPackets = 0;
|
|
24154
25134
|
/**
|
|
@@ -24172,8 +25152,6 @@ var FfmpegDecoderSession = class {
|
|
|
24172
25152
|
if (typeof config.deviceId === "number") sessionTags["deviceId"] = config.deviceId;
|
|
24173
25153
|
if (typeof config.tag === "string" && config.tag.length > 0) sessionTags["tag"] = config.tag;
|
|
24174
25154
|
this.logger = Object.keys(sessionTags).length > 0 ? logger.withTags(sessionTags) : logger;
|
|
24175
|
-
this.frameSink = options?.frameSink ?? "callback";
|
|
24176
|
-
this.nodeId = options?.nodeId ?? "local";
|
|
24177
25155
|
this.renderDevice = options?.renderDevice;
|
|
24178
25156
|
this.ffmpegPath = options?.ffmpegPath ?? "ffmpeg";
|
|
24179
25157
|
const seededChain = options?.hwaccelChain ?? (options?.hwaccel != null && !isSoftwareHwAccel(options.hwaccel) ? [options.hwaccel] : []);
|
|
@@ -24186,10 +25164,6 @@ var FfmpegDecoderSession = class {
|
|
|
24186
25164
|
this.frameFormat = this.pixel === "gray" ? "gray" : "rgb";
|
|
24187
25165
|
this.frameDropper = new FrameDropper(config.maxFps);
|
|
24188
25166
|
}
|
|
24189
|
-
/** Exposed so the owning addon can surface ring stats via `getShmStats`. */
|
|
24190
|
-
get frameRingSinkOrNull() {
|
|
24191
|
-
return this.frameRingSink;
|
|
24192
|
-
}
|
|
24193
25167
|
get isPullMode() {
|
|
24194
25168
|
return this.pullUrl !== null;
|
|
24195
25169
|
}
|
|
@@ -24211,26 +25185,11 @@ var FfmpegDecoderSession = class {
|
|
|
24211
25185
|
this.logger.info("ffmpeg decoder: pull-mode openStream — dialing input URL", { meta: { url } });
|
|
24212
25186
|
this.spawnFfmpeg();
|
|
24213
25187
|
}
|
|
24214
|
-
ensureFrameRingSink() {
|
|
24215
|
-
if (this.frameRingSink === null) {
|
|
24216
|
-
const seedParts = [];
|
|
24217
|
-
if (typeof this.config.deviceId === "number") seedParts.push(String(this.config.deviceId));
|
|
24218
|
-
if (typeof this.config.tag === "string" && this.config.tag.length > 0) seedParts.push(this.config.tag);
|
|
24219
|
-
const seed = seedParts.length > 0 ? seedParts.join(":") : "anon";
|
|
24220
|
-
this.frameRingSink = new DecoderFrameRingSink({
|
|
24221
|
-
seed,
|
|
24222
|
-
logger: this.logger,
|
|
24223
|
-
nodeId: this.nodeId
|
|
24224
|
-
});
|
|
24225
|
-
}
|
|
24226
|
-
return this.frameRingSink;
|
|
24227
|
-
}
|
|
24228
25188
|
spawnFfmpeg() {
|
|
24229
25189
|
if (this.destroyed) return;
|
|
24230
25190
|
this.spawnAttempted = true;
|
|
24231
25191
|
this.clearPullRedial();
|
|
24232
25192
|
this.killFfmpeg();
|
|
24233
|
-
this.abortOpenSlot();
|
|
24234
25193
|
this.splitter = null;
|
|
24235
25194
|
this.pendingStdout = [];
|
|
24236
25195
|
this.stderrAccum = "";
|
|
@@ -24276,8 +25235,7 @@ var FfmpegDecoderSession = class {
|
|
|
24276
25235
|
hwaccel: this.hwaccel,
|
|
24277
25236
|
gpuScale: this.activeGpuScaleFilter ?? "cpu",
|
|
24278
25237
|
codec: this.config.codec,
|
|
24279
|
-
format: this.frameFormat
|
|
24280
|
-
sink: this.frameSink
|
|
25238
|
+
format: this.frameFormat
|
|
24281
25239
|
} });
|
|
24282
25240
|
}
|
|
24283
25241
|
killFfmpeg() {
|
|
@@ -24415,36 +25373,10 @@ var FfmpegDecoderSession = class {
|
|
|
24415
25373
|
return {
|
|
24416
25374
|
startFrame: () => {
|
|
24417
25375
|
if (!this.frameDropper.shouldKeep()) return null;
|
|
24418
|
-
if (this.frameSink === "shm") {
|
|
24419
|
-
const reserved = this.ensureFrameRingSink().beginFrame(this.outWidth, this.outHeight, this.frameFormat);
|
|
24420
|
-
if (reserved === null) return null;
|
|
24421
|
-
this.openSlot = reserved.slot;
|
|
24422
|
-
return reserved.buffer;
|
|
24423
|
-
}
|
|
24424
25376
|
return Buffer.allocUnsafe(this.frameSize);
|
|
24425
25377
|
},
|
|
24426
25378
|
finishFrame: (target) => {
|
|
24427
25379
|
this.outputFrames++;
|
|
24428
|
-
if (this.frameSink === "shm") {
|
|
24429
|
-
const slot = this.openSlot;
|
|
24430
|
-
this.openSlot = null;
|
|
24431
|
-
const sink = this.frameRingSink;
|
|
24432
|
-
if (slot === null || sink === null) return;
|
|
24433
|
-
const handle = sink.commitFrame(slot, {
|
|
24434
|
-
width: this.outWidth,
|
|
24435
|
-
height: this.outHeight,
|
|
24436
|
-
format: this.frameFormat,
|
|
24437
|
-
pts: performance.now(),
|
|
24438
|
-
byteLength: this.frameSize
|
|
24439
|
-
});
|
|
24440
|
-
if (handle === null) return;
|
|
24441
|
-
const delivered = {
|
|
24442
|
-
handle,
|
|
24443
|
-
timestamp: Date.now()
|
|
24444
|
-
};
|
|
24445
|
-
for (const cb of this.handleCallbacks) cb(delivered);
|
|
24446
|
-
return;
|
|
24447
|
-
}
|
|
24448
25380
|
const frame = {
|
|
24449
25381
|
data: target,
|
|
24450
25382
|
width: this.outWidth,
|
|
@@ -24459,14 +25391,6 @@ var FfmpegDecoderSession = class {
|
|
|
24459
25391
|
}
|
|
24460
25392
|
};
|
|
24461
25393
|
}
|
|
24462
|
-
/** Abandon a shm slot the splitter left open (respawn / teardown mid-frame),
|
|
24463
|
-
* so a half-filled slot is never published. */
|
|
24464
|
-
abortOpenSlot() {
|
|
24465
|
-
const slot = this.openSlot;
|
|
24466
|
-
if (slot === null) return;
|
|
24467
|
-
this.openSlot = null;
|
|
24468
|
-
this.frameRingSink?.abortFrame(slot);
|
|
24469
|
-
}
|
|
24470
25394
|
pushPacket(packet) {
|
|
24471
25395
|
if (this.destroyed) return;
|
|
24472
25396
|
if (this.pullUrl !== null) return;
|
|
@@ -24520,13 +25444,6 @@ var FfmpegDecoderSession = class {
|
|
|
24520
25444
|
this.frameCallbacks.delete(callback);
|
|
24521
25445
|
};
|
|
24522
25446
|
}
|
|
24523
|
-
/** Subscribe to shared-memory frame handles — fires only in `'shm'` mode. */
|
|
24524
|
-
onFrameHandle(callback) {
|
|
24525
|
-
this.handleCallbacks.add(callback);
|
|
24526
|
-
return () => {
|
|
24527
|
-
this.handleCallbacks.delete(callback);
|
|
24528
|
-
};
|
|
24529
|
-
}
|
|
24530
25447
|
updateConfig(update) {
|
|
24531
25448
|
const needsRestart = update.scale !== void 0 && update.scale !== this.config.scale || update.outputFormat !== void 0 && update.outputFormat !== this.config.outputFormat || update.codec !== void 0 && update.codec !== this.config.codec;
|
|
24532
25449
|
this.config = {
|
|
@@ -24541,12 +25458,8 @@ var FfmpegDecoderSession = class {
|
|
|
24541
25458
|
this.destroyed = true;
|
|
24542
25459
|
this.clearPullRedial();
|
|
24543
25460
|
this.killFfmpeg();
|
|
24544
|
-
this.abortOpenSlot();
|
|
24545
25461
|
this.splitter = null;
|
|
24546
|
-
this.frameRingSink?.destroy();
|
|
24547
|
-
this.frameRingSink = null;
|
|
24548
25462
|
this.frameCallbacks.clear();
|
|
24549
|
-
this.handleCallbacks.clear();
|
|
24550
25463
|
}
|
|
24551
25464
|
getStats() {
|
|
24552
25465
|
const uptimeSec = Math.max((Date.now() - this.startTime) / 1e3, 1);
|
|
@@ -24617,71 +25530,6 @@ function probeGpuScaleFilters(ffmpegPath, logger) {
|
|
|
24617
25530
|
});
|
|
24618
25531
|
}
|
|
24619
25532
|
//#endregion
|
|
24620
|
-
//#region src/shm-orphan-purge.ts
|
|
24621
|
-
/**
|
|
24622
|
-
* Startup reclamation of orphaned shared-memory segments.
|
|
24623
|
-
*
|
|
24624
|
-
* A decoder writes frames into named `/dev/shm` segments and unlinks each one
|
|
24625
|
-
* on graceful session teardown (`DecoderFrameRingSink.destroy`). When the
|
|
24626
|
-
* decoder process dies *ungracefully* — SIGBUS, OOM-kill, or a SIGKILL during
|
|
24627
|
-
* redeploy — that teardown never runs and the segment is orphaned: it stays in
|
|
24628
|
-
* the tmpfs forever, since nothing else knows its name. Across many
|
|
24629
|
-
* crashes/redeploys these accumulate until `/dev/shm` fills, at which point the
|
|
24630
|
-
* next `mmap` write faults with an uncatchable SIGBUS and the decoder
|
|
24631
|
-
* crash-loops into its circuit breaker (which is exactly the incident this
|
|
24632
|
-
* guards against).
|
|
24633
|
-
*
|
|
24634
|
-
* The reclamation is safe *at process startup*: a freshly-booting decoder owns
|
|
24635
|
-
* no live sessions, so every pre-existing segment with its prefix is by
|
|
24636
|
-
* definition an orphan from a dead instance. `shm_unlink` only removes the
|
|
24637
|
-
* name — any consumer still holding a mapping keeps reading valid memory until
|
|
24638
|
-
* it closes (POSIX deferred reclaim + the ring seqlock), so unlinking is safe
|
|
24639
|
-
* even if a stale reader is momentarily still attached.
|
|
24640
|
-
*
|
|
24641
|
-
* POSIX-only: segments surface as files under `/dev/shm` on Linux. On platforms
|
|
24642
|
-
* without that directory (Windows, macOS) the scan finds nothing — no-op.
|
|
24643
|
-
*
|
|
24644
|
-
* NOTE: this lives inside the decoder addon (not `@camstack/shm-ring`) so it
|
|
24645
|
-
* ships in the self-contained addon bundle via `camstack deploy`, reusing the
|
|
24646
|
-
* already-deployed `unlinkSegment`; no host base-image rebuild required.
|
|
24647
|
-
*/
|
|
24648
|
-
/** Default tmpfs directory where POSIX shared-memory segments appear on Linux. */
|
|
24649
|
-
var DEFAULT_SHM_DIR = "/dev/shm";
|
|
24650
|
-
/**
|
|
24651
|
-
* Unlink every shared-memory segment whose name starts with `prefix`.
|
|
24652
|
-
*
|
|
24653
|
-
* Intended to run ONCE at decoder startup, before any session is created, to
|
|
24654
|
-
* reclaim segments orphaned by a previously-crashed instance. A per-file unlink
|
|
24655
|
-
* failure is swallowed so one stuck segment cannot block reclaiming the rest.
|
|
24656
|
-
*/
|
|
24657
|
-
function purgeOrphanSegments(prefix, options = {}) {
|
|
24658
|
-
const dir = options.dir ?? DEFAULT_SHM_DIR;
|
|
24659
|
-
const unlink = options.unlink ?? unlinkSegment;
|
|
24660
|
-
let entries;
|
|
24661
|
-
try {
|
|
24662
|
-
entries = readdirSync(dir);
|
|
24663
|
-
} catch {
|
|
24664
|
-
return {
|
|
24665
|
-
scanned: 0,
|
|
24666
|
-
removed: 0,
|
|
24667
|
-
names: []
|
|
24668
|
-
};
|
|
24669
|
-
}
|
|
24670
|
-
const names = [];
|
|
24671
|
-
for (const name of entries) {
|
|
24672
|
-
if (!name.startsWith(prefix)) continue;
|
|
24673
|
-
try {
|
|
24674
|
-
unlink(name);
|
|
24675
|
-
names.push(name);
|
|
24676
|
-
} catch {}
|
|
24677
|
-
}
|
|
24678
|
-
return {
|
|
24679
|
-
scanned: entries.length,
|
|
24680
|
-
removed: names.length,
|
|
24681
|
-
names
|
|
24682
|
-
};
|
|
24683
|
-
}
|
|
24684
|
-
//#endregion
|
|
24685
25533
|
//#region src/audio-codec/ffmpeg-audio-process.ts
|
|
24686
25534
|
/**
|
|
24687
25535
|
* Minimal ffmpeg subprocess wrapper shared by the audio decode + encode
|
|
@@ -25850,12 +26698,8 @@ var DEFAULT_DECODER_FFMPEG_ADDON_CONFIG = { ...DEFAULT_DECODER_HWACCEL_CONFIG };
|
|
|
25850
26698
|
var DecoderFfmpegAddon = class extends BaseAddon {
|
|
25851
26699
|
sessions = /* @__PURE__ */ new Map();
|
|
25852
26700
|
frameBuffers = /* @__PURE__ */ new Map();
|
|
25853
|
-
handleBuffers = /* @__PURE__ */ new Map();
|
|
25854
26701
|
unsubscribers = /* @__PURE__ */ new Map();
|
|
25855
26702
|
sessionMeta = /* @__PURE__ */ new Map();
|
|
25856
|
-
frameReaders = null;
|
|
25857
|
-
getFrameHits = 0;
|
|
25858
|
-
getFrameMisses = 0;
|
|
25859
26703
|
/**
|
|
25860
26704
|
* The ffmpeg binary every session spawns — resolved once at init via the
|
|
25861
26705
|
* node-level `ctx.deps.ensureFfmpeg()`. The SAME binary the broker / recorder
|
|
@@ -25938,12 +26782,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
25938
26782
|
return registrations;
|
|
25939
26783
|
}
|
|
25940
26784
|
this.ctx.logger.info("ffmpeg decoder addon initialized", { meta: { selectedBackend: backend } });
|
|
25941
|
-
const purged = purgeOrphanSegments(SEGMENT_NAME_PREFIX);
|
|
25942
|
-
if (purged.removed > 0) this.ctx.logger.warn("ffmpeg decoder: reclaimed orphaned shm segments at startup", { meta: {
|
|
25943
|
-
removed: purged.removed,
|
|
25944
|
-
scanned: purged.scanned
|
|
25945
|
-
} });
|
|
25946
|
-
this.frameReaders = new FrameRingReaderCache(this.ctx.logger);
|
|
25947
26785
|
this.probedGpuScaleFilters = await probeGpuScaleFilters(this.ffmpegPath, this.ctx.logger);
|
|
25948
26786
|
this.ctx.logger.info("decoder-ffmpeg: probed GPU scale filters", { meta: {
|
|
25949
26787
|
filters: [...this.probedGpuScaleFilters],
|
|
@@ -26085,15 +26923,12 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26085
26923
|
}
|
|
26086
26924
|
async createSession(config) {
|
|
26087
26925
|
const sessionId = randomUUID();
|
|
26088
|
-
const { frameSink } = config;
|
|
26089
26926
|
const nodeId = this.resolveLocalNodeId();
|
|
26090
26927
|
const hwaccelChain = await this.resolveDecodeHwaccelChain();
|
|
26091
26928
|
const availableGpuScaleFilters = new Set([...this.probedGpuScaleFilters].filter((f) => !this.unsupportedGpuScaleFilters.has(f)));
|
|
26092
26929
|
const session = new FfmpegDecoderSession(config, this.ctx.logger, {
|
|
26093
26930
|
hwaccelChain,
|
|
26094
26931
|
ffmpegPath: this.ffmpegPath,
|
|
26095
|
-
frameSink,
|
|
26096
|
-
nodeId,
|
|
26097
26932
|
availableGpuScaleFilters,
|
|
26098
26933
|
onGpuScaleUnsupported: (filter) => {
|
|
26099
26934
|
if (this.unsupportedGpuScaleFilters.has(filter)) return;
|
|
@@ -26101,7 +26936,7 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26101
26936
|
this.ctx.logger.warn("decoder-ffmpeg: GPU scaler non-functional on this build — future sessions use CPU scale", { meta: { filter } });
|
|
26102
26937
|
}
|
|
26103
26938
|
});
|
|
26104
|
-
const unsub =
|
|
26939
|
+
const unsub = this.wireCallbackSink(sessionId, session);
|
|
26105
26940
|
this.sessions.set(sessionId, session);
|
|
26106
26941
|
this.unsubscribers.set(sessionId, unsub);
|
|
26107
26942
|
this.sessionMeta.set(sessionId, {
|
|
@@ -26116,7 +26951,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26116
26951
|
sessionId,
|
|
26117
26952
|
codec: config.codec,
|
|
26118
26953
|
hwaccelChain,
|
|
26119
|
-
frameSink,
|
|
26120
26954
|
nodeId
|
|
26121
26955
|
}
|
|
26122
26956
|
});
|
|
@@ -26143,13 +26977,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26143
26977
|
ringBuffer.push(capFrame);
|
|
26144
26978
|
});
|
|
26145
26979
|
}
|
|
26146
|
-
wireShmSink(sessionId, session) {
|
|
26147
|
-
const handleRing = new NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
26148
|
-
this.handleBuffers.set(sessionId, handleRing);
|
|
26149
|
-
return session.onFrameHandle((frame) => {
|
|
26150
|
-
handleRing.push(frame.handle);
|
|
26151
|
-
});
|
|
26152
|
-
}
|
|
26153
26980
|
async destroySession(input) {
|
|
26154
26981
|
const { sessionId } = input;
|
|
26155
26982
|
const session = this.sessions.get(sessionId);
|
|
@@ -26157,12 +26984,10 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26157
26984
|
const unsub = this.unsubscribers.get(sessionId);
|
|
26158
26985
|
if (unsub) unsub();
|
|
26159
26986
|
this.frameBuffers.get(sessionId)?.cancel();
|
|
26160
|
-
this.handleBuffers.get(sessionId)?.cancel();
|
|
26161
26987
|
const deviceId = this.sessionMeta.get(sessionId)?.deviceId;
|
|
26162
26988
|
await session.destroy();
|
|
26163
26989
|
this.sessions.delete(sessionId);
|
|
26164
26990
|
this.frameBuffers.delete(sessionId);
|
|
26165
|
-
this.handleBuffers.delete(sessionId);
|
|
26166
26991
|
this.unsubscribers.delete(sessionId);
|
|
26167
26992
|
this.sessionMeta.delete(sessionId);
|
|
26168
26993
|
this.ctx.logger.info("ffmpeg: destroyed session", {
|
|
@@ -26202,12 +27027,14 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26202
27027
|
if (ringBuffer.size === 0 && input.waitMs) await ringBuffer.waitForItem(input.waitMs);
|
|
26203
27028
|
return ringBuffer.drain(input.maxCount);
|
|
26204
27029
|
}
|
|
26205
|
-
|
|
26206
|
-
|
|
26207
|
-
|
|
26208
|
-
|
|
26209
|
-
|
|
26210
|
-
|
|
27030
|
+
/**
|
|
27031
|
+
* DORMANT STUB. The shm `FrameHandle` frame ring was removed (the runner ships
|
|
27032
|
+
* inline pixels via `pullFrames` now). The cap method stays defined for wire
|
|
27033
|
+
* compat until the framework train drops it, but no session ever produces
|
|
27034
|
+
* handles — always empty.
|
|
27035
|
+
*/
|
|
27036
|
+
async pullHandles(_input) {
|
|
27037
|
+
return [];
|
|
26211
27038
|
}
|
|
26212
27039
|
async updateConfig(input) {
|
|
26213
27040
|
const session = this.sessions.get(input.sessionId);
|
|
@@ -26219,33 +27046,20 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26219
27046
|
if (!session) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|
|
26220
27047
|
return session.getStats();
|
|
26221
27048
|
}
|
|
26222
|
-
|
|
26223
|
-
|
|
26224
|
-
|
|
26225
|
-
|
|
26226
|
-
|
|
26227
|
-
|
|
26228
|
-
|
|
26229
|
-
const arrayBuf = new ArrayBuffer(frame.data.byteLength);
|
|
26230
|
-
new Uint8Array(arrayBuf).set(frame.data);
|
|
26231
|
-
return {
|
|
26232
|
-
data: new Uint8Array(arrayBuf),
|
|
26233
|
-
width: frame.width,
|
|
26234
|
-
height: frame.height,
|
|
26235
|
-
format: frame.format,
|
|
26236
|
-
timestamp: frame.timestamp
|
|
26237
|
-
};
|
|
27049
|
+
/**
|
|
27050
|
+
* DORMANT STUB. Frames are no longer retained in a shm ring this addon can
|
|
27051
|
+
* read back by handle — the decode-owning node serves pixels over its own cap
|
|
27052
|
+
* surface. Kept defined for wire compat; always resolves `null`.
|
|
27053
|
+
*/
|
|
27054
|
+
async getFrame(_input) {
|
|
27055
|
+
return null;
|
|
26238
27056
|
}
|
|
26239
|
-
|
|
26240
|
-
|
|
26241
|
-
|
|
26242
|
-
|
|
26243
|
-
|
|
26244
|
-
|
|
26245
|
-
budgetMb: RING_BUDGET_MB,
|
|
26246
|
-
getFrameHits: this.getFrameHits,
|
|
26247
|
-
getFrameMisses: this.getFrameMisses
|
|
26248
|
-
};
|
|
27057
|
+
/**
|
|
27058
|
+
* DORMANT STUB. No session writes a shm frame ring anymore, so there are no
|
|
27059
|
+
* ring stats to report. Kept defined for wire compat; always `null`.
|
|
27060
|
+
*/
|
|
27061
|
+
async getShmStats(_input) {
|
|
27062
|
+
return null;
|
|
26249
27063
|
}
|
|
26250
27064
|
async onShutdown() {
|
|
26251
27065
|
this.ctx.logger.info("ffmpeg decoder addon shutdown — destroying all sessions");
|
|
@@ -26260,11 +27074,8 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
26260
27074
|
await Promise.all(destroyPromises);
|
|
26261
27075
|
this.sessions.clear();
|
|
26262
27076
|
this.frameBuffers.clear();
|
|
26263
|
-
this.handleBuffers.clear();
|
|
26264
27077
|
this.unsubscribers.clear();
|
|
26265
27078
|
this.sessionMeta.clear();
|
|
26266
|
-
this.frameReaders?.close();
|
|
26267
|
-
this.frameReaders = null;
|
|
26268
27079
|
}
|
|
26269
27080
|
};
|
|
26270
27081
|
//#endregion
|