@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.
Files changed (3) hide show
  1. package/dist/index.js +1067 -256
  2. package/dist/index.mjs +1067 -256
  3. package/package.json +1 -2
package/dist/index.js CHANGED
@@ -3,9 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  let node_crypto = require("node:crypto");
6
- let _camstack_shm_ring = require("@camstack/shm-ring");
7
6
  let node_child_process = require("node:child_process");
8
- let node_fs = require("node:fs");
9
7
  //#region ../../node_modules/zod/v4/core/core.js
10
8
  var _a$1;
11
9
  function $constructor(name, initializer, params) {
@@ -4294,6 +4292,14 @@ function object(shape, params) {
4294
4292
  ...normalizeParams(params)
4295
4293
  });
4296
4294
  }
4295
+ function looseObject(shape, params) {
4296
+ return new ZodObject({
4297
+ type: "object",
4298
+ shape,
4299
+ catchall: unknown(),
4300
+ ...normalizeParams(params)
4301
+ });
4302
+ }
4297
4303
  var ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
4298
4304
  $ZodUnion.init(inst, def);
4299
4305
  ZodType.init(inst, def);
@@ -4635,7 +4641,7 @@ function _instanceof(cls, params = {}) {
4635
4641
  return inst;
4636
4642
  }
4637
4643
  //#endregion
4638
- //#region ../types/dist/sleep-Baang_XW.mjs
4644
+ //#region ../types/dist/sleep-_sv7WKkq.mjs
4639
4645
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4640
4646
  EventCategory["SystemBoot"] = "system.boot";
4641
4647
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -5045,10 +5051,51 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
5045
5051
  EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
5046
5052
  EventCategory["EnrichmentSceneStateChanged"] = "enrichment.scene.state-changed";
5047
5053
  EventCategory["EnrichmentActivitySummary"] = "enrichment.activity.summary";
5054
+ /**
5055
+ * Unified track-lifecycle event: ONE category carrying `phase:
5056
+ * 'start' | 'update' | 'end'` with a rich, typed
5057
+ * `PipelineAnalyticsTrackLifecyclePayload`. Supersedes the thin
5058
+ * `PipelineAnalyticsTrackStarted` / `PipelineAnalyticsTrackEnded`
5059
+ * pair — a consumer subscribes once and branches on `phase`.
5060
+ */
5061
+ EventCategory["PipelineAnalyticsTrackLifecycle"] = "pipeline-analytics.track-lifecycle";
5062
+ /**
5063
+ * @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
5064
+ * (`phase:'start'`). Kept as a soft alias — no known subscribers.
5065
+ */
5048
5066
  EventCategory["PipelineAnalyticsTrackStarted"] = "pipeline-analytics.track-started";
5067
+ /**
5068
+ * @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
5069
+ * (`phase:'end'`). Kept as a soft alias — no known subscribers.
5070
+ */
5049
5071
  EventCategory["PipelineAnalyticsTrackEnded"] = "pipeline-analytics.track-ended";
5050
5072
  EventCategory["PipelineAnalyticsDetectionEvent"] = "pipeline-analytics.detection-event";
5051
5073
  EventCategory["PipelineAnalyticsFrameTracked"] = "pipeline-analytics.frame-tracked";
5074
+ /**
5075
+ * Fired by `addon-post-analysis` when a parked (stationary) object appears
5076
+ * (a track was promoted to a stationary-registry entry) or departs (the
5077
+ * object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
5078
+ * the dedicated "Stationary" section — never the live event feed. Payload:
5079
+ * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
5080
+ */
5081
+ EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
5082
+ /**
5083
+ * Fired by `addon-post-analysis` whenever a gallery face row changes:
5084
+ * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
5085
+ * `'unassigned'` its identity link changed, `'deleted'` the row was
5086
+ * removed. Telemetry semantics (D8): a lost event only delays a refresh,
5087
+ * never a correctness issue. Payload `PipelineAnalyticsFaceGalleryChangedPayload`.
5088
+ */
5089
+ EventCategory["PipelineAnalyticsFaceGalleryChanged"] = "pipeline-analytics.face-gallery-changed";
5090
+ /**
5091
+ * Fired by `addon-post-analysis` whenever a gallery plate row changes:
5092
+ * `kind:'buffered'` a new read was persisted (`PlateRecognizer.onTrackEnd`),
5093
+ * `'assigned'` / `'unassigned'` its vehicle link changed
5094
+ * (`PlateGalleryProvider`), `'deleted'` the row was removed. Telemetry
5095
+ * semantics (D8): a lost event only delays a refresh, never a correctness
5096
+ * issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
5097
+ */
5098
+ EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
5052
5099
  EventCategory["FrigateLiveEvent"] = "frigate.live-event";
5053
5100
  EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
5054
5101
  /**
@@ -6889,7 +6936,8 @@ var MODEL_FORMATS = [
6889
6936
  "coreml",
6890
6937
  "openvino",
6891
6938
  "tflite",
6892
- "pt"
6939
+ "pt",
6940
+ "gguf"
6893
6941
  ];
6894
6942
  /**
6895
6943
  * Multi-file format payload.
@@ -6933,7 +6981,8 @@ var ModelFormatsSchema = object({
6933
6981
  coreml: ModelFormatEntrySchema.optional(),
6934
6982
  openvino: ModelFormatEntrySchema.optional(),
6935
6983
  tflite: ModelFormatEntrySchema.optional(),
6936
- pt: ModelFormatEntrySchema.optional()
6984
+ pt: ModelFormatEntrySchema.optional(),
6985
+ gguf: ModelFormatEntrySchema.optional()
6937
6986
  });
6938
6987
  /**
6939
6988
  * Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
@@ -6974,6 +7023,15 @@ var ModelCatalogEntrySchema = object({
6974
7023
  width: number(),
6975
7024
  height: number()
6976
7025
  }),
7026
+ /**
7027
+ * Channel count of the model input tensor. Omit ⇒ 3 (RGB), the default for
7028
+ * every detector / classifier / embedder. Set to 1 for a grayscale CTC text
7029
+ * recognizer (EasyOCR VGG plate-OCR: input `[N,1,H,W]`) so the preprocess
7030
+ * feeds a single-channel, EasyOCR-normalized tensor instead of the default
7031
+ * 3-channel RGB one. Threaded through `PoolModelConfig.inputChannels` to the
7032
+ * Python inference pool.
7033
+ */
7034
+ inputChannels: number().int().positive().optional(),
6977
7035
  labels: array(LabelDefinitionSchema).readonly(),
6978
7036
  inputLayout: _enum(["nchw", "nhwc"]).optional(),
6979
7037
  inputNormalization: _enum([
@@ -6983,6 +7041,16 @@ var ModelCatalogEntrySchema = object({
6983
7041
  ]).optional(),
6984
7042
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
6985
7043
  /**
7044
+ * Per-MODEL postprocessor override. Absent ⇒ the step's own
7045
+ * `StepDefinition.postprocessor` applies (the normal case — every model in a
7046
+ * step shares its decode). Set it when a step hosts models with DIFFERENT raw
7047
+ * output layouts under one slot: e.g. object-detection is `'yolo'` by default,
7048
+ * but a Coral SSD MobileNet build emits the `TFLite_Detection_PostProcess`
7049
+ * 4-tensor layout and needs `'ssd'`. Threaded into `PoolModelConfig.postprocessor`
7050
+ * by the engine factory (`modelEntry.postprocessor ?? def.postprocessor`).
7051
+ */
7052
+ postprocessor: custom().optional(),
7053
+ /**
6986
7054
  * When true, the executor produces a landmark-aligned crop (similarity warp
6987
7055
  * onto the canonical template) before this step runs, instead of a plain
6988
7056
  * axis-aligned bbox crop. Required for face-recognition embedders (ArcFace):
@@ -10428,7 +10496,8 @@ var ModelFormatSchema$1 = _enum([
10428
10496
  "coreml",
10429
10497
  "openvino",
10430
10498
  "tflite",
10431
- "pt"
10499
+ "pt",
10500
+ "gguf"
10432
10501
  ]);
10433
10502
  var PipelineSlotSchema = _enum([
10434
10503
  "detector",
@@ -10522,7 +10591,8 @@ var EngineProvisioningSchema = object({
10522
10591
  runtimeId: _enum([
10523
10592
  "onnx",
10524
10593
  "openvino",
10525
- "coreml"
10594
+ "coreml",
10595
+ "edgetpu"
10526
10596
  ]).nullable(),
10527
10597
  device: string().nullable(),
10528
10598
  state: _enum([
@@ -11734,6 +11804,140 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
11734
11804
  deviceId: number(),
11735
11805
  status: PtzAutotrackStatusSchema
11736
11806
  });
11807
+ /**
11808
+ * scene-monitor — device-scoped reference-region state cap. An operator marks
11809
+ * a rect ROI on a camera frame and names one or more states; the engine
11810
+ * (pipeline-analytics, designated post-processing node) reports which state is
11811
+ * active on an ongoing basis. Two operator-selectable check modes share every-
11812
+ * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
11813
+ * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
11814
+ *
11815
+ * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) — the framework
11816
+ * derives the device-detail contribution; the provider carries NO hand-written
11817
+ * settings-contribution methods. `status.kind:'push'` — the engine pushes on
11818
+ * every hysteresis flip / availability change; consumers never poll.
11819
+ */
11820
+ /** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
11821
+ * be added without a wire break (matching falls back to any-condition refs). */
11822
+ var SceneConditionSchema = string();
11823
+ /** One captured reference — condition-tagged, model-version-gated. `embedding`
11824
+ * is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
11825
+ var SceneReferenceSchema = object({
11826
+ embedding: array(number()),
11827
+ modelId: string(),
11828
+ condition: SceneConditionSchema,
11829
+ capturedAt: number(),
11830
+ thumbnailMediaId: string().optional()
11831
+ });
11832
+ var SceneMonitorStateSchema = object({
11833
+ id: string(),
11834
+ label: string(),
11835
+ references: array(SceneReferenceSchema),
11836
+ textPrompts: array(string()).optional(),
11837
+ referenceCount: number(),
11838
+ currentlyMatched: boolean()
11839
+ });
11840
+ /** Per-mode comparator params in a discriminated union so an `llm` scene never
11841
+ * carries similarity knobs and vice-versa. */
11842
+ var SceneCheckSchema = discriminatedUnion("mode", [object({
11843
+ mode: literal("similarity"),
11844
+ threshold: number().min(0).max(1),
11845
+ hysteresisCount: number().int().positive()
11846
+ }), object({
11847
+ mode: literal("llm"),
11848
+ prompt: string(),
11849
+ profileId: string().optional(),
11850
+ hysteresisCount: number().int().positive()
11851
+ })]);
11852
+ var SceneMonitorSchema = object({
11853
+ id: string(),
11854
+ label: string(),
11855
+ roi: MaskRectShapeSchema,
11856
+ enabled: boolean(),
11857
+ triggerMode: _enum([
11858
+ "periodic",
11859
+ "on-motion",
11860
+ "both"
11861
+ ]),
11862
+ checkIntervalSec: number().optional(),
11863
+ check: SceneCheckSchema,
11864
+ states: array(SceneMonitorStateSchema),
11865
+ currentStateId: string().nullable(),
11866
+ lastCheckedAt: number().nullable(),
11867
+ lastConfidence: number().nullable(),
11868
+ currentCondition: SceneConditionSchema.nullable(),
11869
+ availability: _enum(["ok", "unavailable"]),
11870
+ unavailableReason: string().nullable()
11871
+ });
11872
+ var SceneMonitorStatusSchema = object({
11873
+ monitors: array(SceneMonitorSchema),
11874
+ lastFetchedAt: number()
11875
+ });
11876
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
11877
+ deviceId: number(),
11878
+ label: string(),
11879
+ roi: MaskRectShapeSchema,
11880
+ check: SceneCheckSchema,
11881
+ triggerMode: _enum([
11882
+ "periodic",
11883
+ "on-motion",
11884
+ "both"
11885
+ ]).optional(),
11886
+ checkIntervalSec: number().optional()
11887
+ }), SceneMonitorSchema, {
11888
+ kind: "mutation",
11889
+ auth: "admin"
11890
+ }), method(object({
11891
+ deviceId: number(),
11892
+ monitorId: string(),
11893
+ patch: object({
11894
+ label: string().optional(),
11895
+ roi: MaskRectShapeSchema.optional(),
11896
+ enabled: boolean().optional(),
11897
+ triggerMode: _enum([
11898
+ "periodic",
11899
+ "on-motion",
11900
+ "both"
11901
+ ]).optional(),
11902
+ checkIntervalSec: number().optional(),
11903
+ check: SceneCheckSchema.optional()
11904
+ })
11905
+ }), _void(), {
11906
+ kind: "mutation",
11907
+ auth: "admin"
11908
+ }), method(object({
11909
+ deviceId: number(),
11910
+ monitorId: string()
11911
+ }), _void(), {
11912
+ kind: "mutation",
11913
+ auth: "admin"
11914
+ }), method(object({
11915
+ deviceId: number(),
11916
+ monitorId: string(),
11917
+ stateId: string().optional(),
11918
+ label: string().optional(),
11919
+ condition: SceneConditionSchema.optional()
11920
+ }), object({
11921
+ stateId: string(),
11922
+ referenceCount: number()
11923
+ }), {
11924
+ kind: "mutation",
11925
+ auth: "admin"
11926
+ }), method(object({
11927
+ deviceId: number(),
11928
+ monitorId: string(),
11929
+ stateId: string(),
11930
+ index: number()
11931
+ }), _void(), {
11932
+ kind: "mutation",
11933
+ auth: "admin"
11934
+ }), method(object({
11935
+ deviceId: number(),
11936
+ monitorId: string()
11937
+ }), _void(), {
11938
+ kind: "mutation",
11939
+ auth: "admin"
11940
+ });
11737
11941
  object({
11738
11942
  /** Whether the script is currently executing. */
11739
11943
  isRunning: boolean(),
@@ -12141,6 +12345,36 @@ var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
12141
12345
  * with the per-track detail panel and live overlay. */
12142
12346
  trackIds: array(string()).readonly()
12143
12347
  });
12348
+ /**
12349
+ * A parked ("stationary") object surfaced alongside occupancy — an object that
12350
+ * settled and stopped moving. It is NO LONGER a tracked object (the tracker was
12351
+ * told to forget it so it stops re-spawning tracks/events), but it IS still
12352
+ * physically present, so it keeps counting toward `frame` occupancy and is
12353
+ * listed here so the UI can show it in a dedicated "Stationary" section instead
12354
+ * of flooding the live event feed.
12355
+ */
12356
+ var StationaryObjectSchema = object({
12357
+ id: string(),
12358
+ className: string(),
12359
+ bbox: object({
12360
+ x: number(),
12361
+ y: number(),
12362
+ w: number(),
12363
+ h: number()
12364
+ }),
12365
+ frameWidth: number().int().nonnegative(),
12366
+ frameHeight: number().int().nonnegative(),
12367
+ /** When the source track was first seen. */
12368
+ firstSeenAt: number().int(),
12369
+ /** When the object was recognised as parked (promotion time). */
12370
+ becameStationaryAt: number().int(),
12371
+ /** Last frame a detection confirmed the object is still there. */
12372
+ lastConfirmedAt: number().int(),
12373
+ /** Enrichment label carried from the source track (identity / plate). */
12374
+ label: string().optional(),
12375
+ /** Native-resolution key-frame media key for the parked object's best image. */
12376
+ keyFrameMediaKey: string().optional()
12377
+ });
12144
12378
  var CameraOccupancySnapshotSchema = object({
12145
12379
  /** Frame timestamp of the inference result that produced this snapshot. */
12146
12380
  ts: number().int(),
@@ -12149,10 +12383,15 @@ var CameraOccupancySnapshotSchema = object({
12149
12383
  frameHeight: number().int().nonnegative(),
12150
12384
  /** Per-zone breakdown — one entry per defined zone (user + onboard). */
12151
12385
  zones: array(ZoneScopeBreakdownSchema).readonly(),
12152
- /** Frame-wide aggregate (everywhere, regardless of zone membership). */
12386
+ /** Frame-wide aggregate (everywhere, regardless of zone membership).
12387
+ * INCLUDES currently-confirmed stationary objects (they are still present). */
12153
12388
  frame: PerScopeBreakdownSchema,
12154
12389
  /** Detections that landed outside every zone. Empty when no zones defined. */
12155
- unzoned: PerScopeBreakdownSchema
12390
+ unzoned: PerScopeBreakdownSchema,
12391
+ /** Parked objects on this camera (additive — absent on legacy snapshots).
12392
+ * Surfaced separately so the UI shows them in a dedicated section rather
12393
+ * than as repeated tracks/events. */
12394
+ stationaryObjects: array(StationaryObjectSchema).readonly().optional()
12156
12395
  });
12157
12396
  /**
12158
12397
  * Time-series resolution. The history methods return one bucket per
@@ -12755,7 +12994,12 @@ var NotificationRuleConditionsSchema = object({
12755
12994
  clipDescription: object({
12756
12995
  text: string().min(1),
12757
12996
  minSimilarity: number().min(0).max(1)
12758
- }).optional()
12997
+ }).optional(),
12998
+ /** Match events whose recognized-entity label (face identity name or plate
12999
+ * vehicle name, propagated onto `event.data.label`) is one of these values.
13000
+ * Empty/absent → unaffected (back-compat). Enables "notify me when <named
13001
+ * vehicle/person> is seen". */
13002
+ labels: array(string()).readonly().optional()
12759
13003
  });
12760
13004
  var NotificationRuleTemplateSchema = object({
12761
13005
  title: string(),
@@ -13050,11 +13294,19 @@ method(object({
13050
13294
  * login page needs NO change.
13051
13295
  *
13052
13296
  * - `widget` — a Module-Federation widget the login page mounts (via
13053
- * `loadRemoteBundle`) for an in-page ceremony. Covers the passkey
13054
- * login ceremony, which must run `@simplewebauthn/browser` INSIDE the
13055
- * addon bundle. The referenced widget also declares `preAuth: true` in
13056
- * its `addon-widgets-source` catalog entry. `auth.listLoginMethods`
13057
- * stamps a public `bundleUrl` from `addonId` + `bundle`.
13297
+ * `loadRemoteBundle`) for an in-page ceremony. `auth.listLoginMethods`
13298
+ * stamps a public `bundleUrl` from `addonId` + `bundle`. Generic
13299
+ * mechanism kept for future use; no shipped addon uses it on the login
13300
+ * page (the passkey ceremony below runs natively in the shell instead).
13301
+ *
13302
+ * - `passkey` — a declarative WebAuthn ceremony the shell renders
13303
+ * natively (`@simplewebauthn/browser` lives in `addon-admin-ui`, not in
13304
+ * a remotely-loaded bundle). Carries the addon's effective `rpId` /
13305
+ * `origin` (from its `resolveRpID()` / `resolveOrigin()`) so the shell
13306
+ * can gate visibility (IP-literal origin, hostname/rpId mismatch) WITHOUT
13307
+ * fetching any remote code pre-auth. Contribution stays unconditional —
13308
+ * enrollment state is never leaked pre-auth; visibility is a shell
13309
+ * decision.
13058
13310
  *
13059
13311
  * Every contribution carries a `stage`:
13060
13312
  * - `primary` — shown on the first credentials screen (OIDC /
@@ -13068,30 +13320,45 @@ method(object({
13068
13320
  */
13069
13321
  /** When a login method renders in the two-phase login flow. */
13070
13322
  var LoginStageEnum = _enum(["primary", "second-factor"]);
13071
- /** One login-method contribution — redirect button OR pre-auth widget. */
13072
- var LoginMethodContributionSchema = discriminatedUnion("kind", [object({
13073
- kind: literal("redirect"),
13074
- /** Stable id within the login-method set (e.g. `auth-oidc/google`). */
13075
- id: string(),
13076
- /** Operator-facing button label. */
13077
- label: string(),
13078
- /** lucide-react icon name. */
13079
- icon: string().optional(),
13080
- /** Addon-owned HTTP route the button navigates to (GET). */
13081
- startUrl: string(),
13082
- stage: LoginStageEnum
13083
- }), object({
13084
- kind: literal("widget"),
13085
- /** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
13086
- id: string(),
13087
- /** Owning addon id drives the public bundle URL + the MF namespace. */
13088
- addonId: string(),
13089
- /** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
13090
- bundle: string(),
13091
- /** MF remote descriptor `{ remoteName, exposedModule, componentKey }`. */
13092
- remote: WidgetRemoteSchema,
13093
- stage: LoginStageEnum
13094
- })]);
13323
+ /** One login-method contribution — redirect button, pre-auth widget, or native passkey ceremony. */
13324
+ var LoginMethodContributionSchema = discriminatedUnion("kind", [
13325
+ object({
13326
+ kind: literal("redirect"),
13327
+ /** Stable id within the login-method set (e.g. `auth-oidc/google`). */
13328
+ id: string(),
13329
+ /** Operator-facing button label. */
13330
+ label: string(),
13331
+ /** lucide-react icon name. */
13332
+ icon: string().optional(),
13333
+ /** Addon-owned HTTP route the button navigates to (GET). */
13334
+ startUrl: string(),
13335
+ stage: LoginStageEnum
13336
+ }),
13337
+ object({
13338
+ kind: literal("widget"),
13339
+ /** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
13340
+ id: string(),
13341
+ /** Owning addon id — drives the public bundle URL + the MF namespace. */
13342
+ addonId: string(),
13343
+ /** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
13344
+ bundle: string(),
13345
+ /** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
13346
+ remote: WidgetRemoteSchema,
13347
+ stage: LoginStageEnum
13348
+ }),
13349
+ object({
13350
+ kind: literal("passkey"),
13351
+ /** Stable id within the login-method set (e.g. `auth-webauthn/passkey-direct-login`). */
13352
+ id: string(),
13353
+ /** Operator-facing button label. */
13354
+ label: string(),
13355
+ stage: LoginStageEnum,
13356
+ /** Effective WebAuthn RP ID (`resolveRpID()`) — the shell gates visibility on it. */
13357
+ rpId: string(),
13358
+ /** Effective expected origin (`resolveOrigin()`), null when unconfigured. */
13359
+ origin: string().nullable()
13360
+ })
13361
+ ]);
13095
13362
  method(_void(), array(LoginMethodContributionSchema).readonly());
13096
13363
  /**
13097
13364
  * Orchestrator-side destination metadata. The orchestrator computes
@@ -15012,14 +15279,14 @@ var TargetKindCapsSchema = object({
15012
15279
  * the union is large and not meant for runtime validation here; the exported
15013
15280
  * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
15014
15281
  */
15015
- var ConfigSchemaPassthrough = unknown();
15282
+ var ConfigSchemaPassthrough$1 = unknown();
15016
15283
  var TargetKindSchema = object({
15017
15284
  kind: string(),
15018
15285
  label: string(),
15019
15286
  icon: string(),
15020
15287
  /** Stamped by each provider so the concat-fanned catalog stays routable. */
15021
15288
  addonId: string(),
15022
- configSchema: ConfigSchemaPassthrough,
15289
+ configSchema: ConfigSchemaPassthrough$1,
15023
15290
  supportsDiscovery: boolean(),
15024
15291
  caps: TargetKindCapsSchema
15025
15292
  });
@@ -15072,6 +15339,298 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
15072
15339
  enabled: boolean()
15073
15340
  }), _void(), { kind: "mutation" });
15074
15341
  /**
15342
+ * Shared LLM generate contracts — imported by BOTH `llm.cap.ts` (consumer
15343
+ * surface) and `llm-runtime.cap.ts` (node-side managed executor) so the two
15344
+ * caps stay wire-compatible without a circular cap→cap import.
15345
+ *
15346
+ * Errors are a discriminated-union RESULT, never thrown: the shape survives
15347
+ * every transport tier structurally, and failed calls still write usage rows.
15348
+ * Token counts only in v1 — no costUsd (operator decision, 2026-07-15).
15349
+ */
15350
+ var LlmUsageSchema = object({
15351
+ inputTokens: number(),
15352
+ outputTokens: number()
15353
+ });
15354
+ var LlmErrorCodeSchema = _enum([
15355
+ "timeout",
15356
+ "rate-limited",
15357
+ "auth",
15358
+ "refusal",
15359
+ "bad-request",
15360
+ "unavailable",
15361
+ "no-profile",
15362
+ "budget-exceeded",
15363
+ "adapter-error"
15364
+ ]);
15365
+ var LlmGenerateResultSchema = discriminatedUnion("ok", [object({
15366
+ ok: literal(true),
15367
+ text: string(),
15368
+ model: string(),
15369
+ usage: LlmUsageSchema,
15370
+ truncated: boolean(),
15371
+ latencyMs: number()
15372
+ }), object({
15373
+ ok: literal(false),
15374
+ code: LlmErrorCodeSchema,
15375
+ message: string(),
15376
+ retryAfterMs: number().optional()
15377
+ })]);
15378
+ /**
15379
+ * `Uint8Array` is the sanctioned binary convention — superjson + the UDS
15380
+ * MsgPack channel round-trip typed arrays (embedding-encoder.cap.ts:29,
15381
+ * notification-output.cap.ts:27-31 precedents).
15382
+ */
15383
+ var LlmImageSchema = object({
15384
+ bytes: _instanceof(Uint8Array),
15385
+ mimeType: string()
15386
+ });
15387
+ var LlmGenerateBaseInputSchema = object({
15388
+ /** Collection routing (the notification-output posture). */
15389
+ addonId: string().optional(),
15390
+ /** Explicit profile; else the resolution chain (spec §3). */
15391
+ profileId: string().optional(),
15392
+ /** MANDATORY usage tag: 'ai-summary', 'notifier-rules', 'adhoc-ui', … */
15393
+ consumer: string(),
15394
+ system: string().optional(),
15395
+ /** v1: single-turn. `messages[]` is a v2 additive field. */
15396
+ prompt: string(),
15397
+ /** Structured output — adapter-mapped (response_format / forced tool / responseSchema). */
15398
+ jsonSchema: record(string(), unknown()).optional(),
15399
+ /** Per-call override of the profile default. */
15400
+ maxTokens: number().int().positive().optional(),
15401
+ temperature: number().optional()
15402
+ });
15403
+ /**
15404
+ * `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
15405
+ * on EVERY node where `addon-ai` is installed; the hub `llm` provider reaches
15406
+ * a specific node's runtime with `nodePin(profile.runtime.nodeId)` — normal
15407
+ * cap routing, zero bespoke plumbing. `internal: true`: the operator reaches
15408
+ * this only through the `llm` cap's methods.
15409
+ *
15410
+ * One running llama-server child per node in v1 (models are RAM-heavy).
15411
+ * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
15412
+ * watchdog — operator decision #3).
15413
+ */
15414
+ var ManagedModelRefSchema = discriminatedUnion("kind", [
15415
+ object({
15416
+ kind: literal("catalog"),
15417
+ catalogId: string()
15418
+ }),
15419
+ object({
15420
+ kind: literal("url"),
15421
+ url: string(),
15422
+ sha256: string().optional()
15423
+ }),
15424
+ object({
15425
+ kind: literal("path"),
15426
+ path: string()
15427
+ })
15428
+ ]);
15429
+ var ManagedRuntimeConfigSchema = object({
15430
+ /** WHERE the runtime lives — hub or any agent. */
15431
+ nodeId: string(),
15432
+ /** Closed for v1; 'ollama' is a v2 candidate. */
15433
+ engine: _enum(["llama-cpp"]),
15434
+ model: ManagedModelRefSchema,
15435
+ contextSize: number().int().default(4096),
15436
+ /** 0 = CPU-only. */
15437
+ gpuLayers: number().int().default(0),
15438
+ /** Default: cpus-2, clamped ≥1 (resolved node-side). */
15439
+ threads: number().int().optional(),
15440
+ /** Concurrent slots. */
15441
+ parallel: number().int().default(1),
15442
+ /** Else lazy: first generate boots it. */
15443
+ autoStart: boolean().default(false),
15444
+ /** 0 = never; frees RAM after quiet periods. */
15445
+ idleStopMinutes: number().int().default(30)
15446
+ });
15447
+ var LlmRuntimeStatusSchema = object({
15448
+ /** Status is ALWAYS node-qualified. */
15449
+ nodeId: string(),
15450
+ state: _enum([
15451
+ "stopped",
15452
+ "downloading",
15453
+ "starting",
15454
+ "ready",
15455
+ "crashed",
15456
+ "failed"
15457
+ ]),
15458
+ pid: number().optional(),
15459
+ port: number().optional(),
15460
+ modelPath: string().optional(),
15461
+ modelId: string().optional(),
15462
+ downloadProgress: number().min(0).max(1).optional(),
15463
+ lastError: string().optional(),
15464
+ crashesInWindow: number(),
15465
+ /** Child RSS (sampled best-effort). */
15466
+ memoryBytes: number().optional(),
15467
+ vramBytes: number().optional()
15468
+ });
15469
+ var LlmNodeModelSchema = object({
15470
+ file: string(),
15471
+ sizeBytes: number(),
15472
+ catalogId: string().optional(),
15473
+ installedAt: number().optional()
15474
+ });
15475
+ var LlmRuntimeDiskUsageSchema = object({
15476
+ nodeId: string(),
15477
+ modelsBytes: number(),
15478
+ freeBytes: number().optional()
15479
+ });
15480
+ method(LlmGenerateBaseInputSchema.extend({
15481
+ images: array(LlmImageSchema).optional(),
15482
+ runtime: ManagedRuntimeConfigSchema,
15483
+ /** The managed profile's timeout, threaded by the hub provider. */
15484
+ timeoutMs: number().int().positive().optional()
15485
+ }), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
15486
+ kind: "mutation",
15487
+ auth: "admin"
15488
+ }), method(object({}), _void(), {
15489
+ kind: "mutation",
15490
+ auth: "admin"
15491
+ }), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
15492
+ kind: "mutation",
15493
+ auth: "admin"
15494
+ }), method(object({ file: string() }), _void(), {
15495
+ kind: "mutation",
15496
+ auth: "admin"
15497
+ }), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
15498
+ /**
15499
+ * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
15500
+ * methods concat-fan across providers; single-row methods route to ONE
15501
+ * provider by the `addonId` in the call input (the notification-output
15502
+ * posture, notification-output.cap.ts:215-250). Provided by `addon-ai`
15503
+ * (hub-placed); the cap stays open for future providers.
15504
+ *
15505
+ * Profiles are ROWS (data), not addons: one row = one usable model endpoint.
15506
+ * `apiKey` is a password field — providers REDACT it on read and merge on
15507
+ * write; a stored key NEVER round-trips to a client.
15508
+ */
15509
+ var LlmProfileKindSchema = _enum([
15510
+ "openai-compatible",
15511
+ "openai",
15512
+ "anthropic",
15513
+ "google",
15514
+ "managed-local"
15515
+ ]);
15516
+ var LlmProfileSchema = object({
15517
+ id: string(),
15518
+ name: string(),
15519
+ kind: LlmProfileKindSchema,
15520
+ /** Stamped by the provider — keeps the fanned catalog routable. */
15521
+ addonId: string(),
15522
+ enabled: boolean(),
15523
+ /** Vendor model id, or the managed runtime's loaded model. */
15524
+ model: string(),
15525
+ /** Required for openai-compatible; override for cloud kinds. */
15526
+ baseUrl: string().optional(),
15527
+ /** ConfigUISchema type:'password' — never round-trips (spec §5). */
15528
+ apiKey: string().optional(),
15529
+ supportsVision: boolean(),
15530
+ temperature: number().min(0).max(2).optional(),
15531
+ maxTokens: number().int().positive().optional(),
15532
+ timeoutMs: number().int().positive().default(6e4),
15533
+ extraHeaders: record(string(), string()).optional(),
15534
+ /** kind === 'managed-local' only (spec §4). */
15535
+ runtime: ManagedRuntimeConfigSchema.optional()
15536
+ });
15537
+ /** ConfigUISchema tree passed through untyped on the wire (the
15538
+ * notification-output `ConfigSchemaPassthrough` precedent at
15539
+ * notification-output.cap.ts:151); the exported TS type re-tightens it. */
15540
+ var ConfigSchemaPassthrough = unknown();
15541
+ var LlmProfileKindDescriptorSchema = object({
15542
+ kind: LlmProfileKindSchema,
15543
+ label: string(),
15544
+ icon: string(),
15545
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
15546
+ addonId: string(),
15547
+ configSchema: ConfigSchemaPassthrough
15548
+ });
15549
+ var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
15550
+ var LlmDefaultSchema = object({
15551
+ selector: LlmDefaultSelectorSchema,
15552
+ profileId: string()
15553
+ });
15554
+ /** Server-side rollup row — getUsage never dumps raw call rows (spec §6). */
15555
+ var LlmUsageRollupSchema = object({
15556
+ day: string(),
15557
+ consumer: string(),
15558
+ profileId: string(),
15559
+ calls: number(),
15560
+ okCalls: number(),
15561
+ errorCalls: number(),
15562
+ inputTokens: number(),
15563
+ outputTokens: number(),
15564
+ avgLatencyMs: number()
15565
+ });
15566
+ /** LLM-facing view over the reused ModelCatalogEntry mechanism (spec §4.2). */
15567
+ var ManagedModelCatalogEntrySchema = object({
15568
+ id: string(),
15569
+ label: string(),
15570
+ family: string(),
15571
+ purpose: _enum(["text", "vision"]),
15572
+ url: string(),
15573
+ sha256: string(),
15574
+ sizeBytes: number(),
15575
+ quantization: string(),
15576
+ /** Load-time guidance shown in the picker. */
15577
+ minRamBytes: number(),
15578
+ contextSizeDefault: number().int(),
15579
+ /** Vision models: companion projector file. */
15580
+ mmprojUrl: string().optional()
15581
+ });
15582
+ var LlmRuntimeNodeSchema = object({
15583
+ nodeId: string(),
15584
+ reachable: boolean(),
15585
+ status: LlmRuntimeStatusSchema.optional(),
15586
+ disk: LlmRuntimeDiskUsageSchema.optional(),
15587
+ error: string().optional()
15588
+ });
15589
+ var GenerateVisionInputSchema = LlmGenerateBaseInputSchema.extend({ images: array(LlmImageSchema).min(1) });
15590
+ var ProfileRefInputSchema = object({
15591
+ addonId: string(),
15592
+ profileId: string()
15593
+ });
15594
+ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
15595
+ kind: "mutation",
15596
+ auth: "admin"
15597
+ }), method(ProfileRefInputSchema, _void(), {
15598
+ kind: "mutation",
15599
+ auth: "admin"
15600
+ }), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
15601
+ kind: "mutation",
15602
+ auth: "admin"
15603
+ }), method(ProfileRefInputSchema, array(string())), method(object({}), array(LlmDefaultSchema)), method(object({
15604
+ selector: LlmDefaultSelectorSchema,
15605
+ profileId: string().nullable()
15606
+ }), _void(), {
15607
+ kind: "mutation",
15608
+ auth: "admin"
15609
+ }), method(object({
15610
+ since: number().optional(),
15611
+ until: number().optional(),
15612
+ consumer: string().optional(),
15613
+ profileId: string().optional()
15614
+ }), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
15615
+ nodeId: string(),
15616
+ model: ManagedModelRefSchema
15617
+ }), _void(), {
15618
+ kind: "mutation",
15619
+ auth: "admin"
15620
+ }), method(object({
15621
+ nodeId: string(),
15622
+ file: string()
15623
+ }), _void(), {
15624
+ kind: "mutation",
15625
+ auth: "admin"
15626
+ }), method(ProfileRefInputSchema, LlmRuntimeStatusSchema), method(ProfileRefInputSchema, LlmRuntimeStatusSchema, {
15627
+ kind: "mutation",
15628
+ auth: "admin"
15629
+ }), method(ProfileRefInputSchema, _void(), {
15630
+ kind: "mutation",
15631
+ auth: "admin"
15632
+ });
15633
+ /**
15075
15634
  * Zod schemas for persisted record types.
15076
15635
  *
15077
15636
  * These schemas serve as the single source of truth for types that are
@@ -15273,6 +15832,22 @@ var TrackSnapshotSchema = object({
15273
15832
  /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
15274
15833
  mediaKey: string()
15275
15834
  });
15835
+ /**
15836
+ * One audio-classification label heard on the track's camera while the
15837
+ * track was alive, aggregated per label. An "episode" is one persisted
15838
+ * audio event (the confident-classification path: score ≥ the device's
15839
+ * `classificationMinScore`, class-change-or-heartbeat coalesced) — NOT
15840
+ * one 32 ms inference chunk, so counts stay human-scaled.
15841
+ */
15842
+ var TrackAudioLabelSchema = object({
15843
+ label: string(),
15844
+ /** Highest classification score observed across the label's episodes. */
15845
+ peakScore: number(),
15846
+ /** Number of coalesced audio-event episodes carrying this label. */
15847
+ count: number(),
15848
+ firstAt: number(),
15849
+ lastAt: number()
15850
+ });
15276
15851
  var TrackSchema = object({
15277
15852
  trackId: string(),
15278
15853
  deviceId: number(),
@@ -15287,6 +15862,10 @@ var TrackSchema = object({
15287
15862
  snapshots: array(TrackSnapshotSchema).readonly(),
15288
15863
  /** Deduplicated zones the track has entered at least once. */
15289
15864
  zonesVisited: array(string()).readonly(),
15865
+ /** Deduplicated set of detector classes observed for this track over its
15866
+ * life (a track may be reclassified, e.g. person→vehicle). Absent on
15867
+ * legacy rows written before class accumulation shipped. */
15868
+ classes: array(string()).readonly().optional(),
15290
15869
  /** Cumulative normalized distance travelled (0..1 units = full frame width). */
15291
15870
  totalDistance: number(),
15292
15871
  state: TrackStateSchema,
@@ -15300,7 +15879,11 @@ var TrackSchema = object({
15300
15879
  bestEventId: string().optional(),
15301
15880
  /** Tag of the importance sub-signal that dominated the score
15302
15881
  * (identity|dwell|proximity|class|confidence|travel|zone). */
15303
- importanceReason: string().optional()
15882
+ importanceReason: string().optional(),
15883
+ /** Audio-classification labels heard on the camera during the track's
15884
+ * life (score ≥ device `classificationMinScore`), aggregated per label.
15885
+ * Absent on legacy rows / tracks with no confident audio. */
15886
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional()
15304
15887
  });
15305
15888
  var BaseEventFields = {
15306
15889
  id: string(),
@@ -15448,7 +16031,7 @@ var KeyEventSchema = object({
15448
16031
  /** Track lifetime in ms (lastSeen - firstSeen). */
15449
16032
  windowMs: number().optional()
15450
16033
  });
15451
- var TrackedDetectionSchema = object({
16034
+ object({
15452
16035
  trackId: string(),
15453
16036
  className: string(),
15454
16037
  confidence: number(),
@@ -15456,6 +16039,23 @@ var TrackedDetectionSchema = object({
15456
16039
  zones: array(string()).readonly(),
15457
16040
  state: TrackStateSchema
15458
16041
  });
16042
+ var OverlayDetectionSchema = looseObject({
16043
+ id: string(),
16044
+ kind: _enum(["first-level", "detail"]),
16045
+ macroClass: string(),
16046
+ score: number(),
16047
+ bbox: object({
16048
+ x: number(),
16049
+ y: number(),
16050
+ width: number(),
16051
+ height: number()
16052
+ }),
16053
+ labels: array(looseObject({
16054
+ label: string(),
16055
+ score: number()
16056
+ })).readonly(),
16057
+ parentId: string().optional()
16058
+ });
15459
16059
  var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: number() });
15460
16060
  var SearchObjectEventsInput = object({
15461
16061
  text: string(),
@@ -15466,6 +16066,20 @@ var SearchObjectEventsInput = object({
15466
16066
  limit: number().default(50),
15467
16067
  minScore: number().min(0).max(1).default(.2)
15468
16068
  });
16069
+ var TrackCascadeCountsSchema = object({
16070
+ /** Persisted track roots deleted (authoritative). */
16071
+ tracks: number().int(),
16072
+ /** Object events removed with their tracks (best-effort; see note above). */
16073
+ events: number().int(),
16074
+ /** Track/face/plate-owned media removed (best-effort). Never identity media. */
16075
+ media: number().int(),
16076
+ /** Unassigned (non-enrolled) face reads removed (best-effort). */
16077
+ faces: number().int(),
16078
+ /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
16079
+ plates: number().int(),
16080
+ /** Per-track CLIP search vectors removed (best-effort). */
16081
+ embeddings: number().int()
16082
+ });
15469
16083
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
15470
16084
  deviceId: number(),
15471
16085
  trackId: string()
@@ -15497,6 +16111,24 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
15497
16111
  }), {
15498
16112
  kind: "mutation",
15499
16113
  auth: "admin"
16114
+ }), method(object({
16115
+ deviceId: number(),
16116
+ cutoffMs: number()
16117
+ }), TrackCascadeCountsSchema, {
16118
+ kind: "mutation",
16119
+ auth: "admin"
16120
+ }), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
16121
+ kind: "mutation",
16122
+ auth: "admin"
16123
+ }), method(object({
16124
+ deviceId: number(),
16125
+ trackIds: array(string()).min(1)
16126
+ }), object({
16127
+ deleted: number().int(),
16128
+ failed: array(string()).readonly()
16129
+ }), {
16130
+ kind: "mutation",
16131
+ auth: "admin"
15500
16132
  }), method(object({
15501
16133
  eventId: string(),
15502
16134
  kind: MediaFileKindEnum.optional()
@@ -15505,7 +16137,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
15505
16137
  timestamp: number(),
15506
16138
  frameWidth: number(),
15507
16139
  frameHeight: number(),
15508
- detections: array(TrackedDetectionSchema).readonly()
16140
+ detections: array(OverlayDetectionSchema).readonly()
15509
16141
  }), object({
15510
16142
  deviceId: number(),
15511
16143
  trackId: string(),
@@ -18274,6 +18906,24 @@ DeviceType.Camera, method(object({
18274
18906
  deviceId: number(),
18275
18907
  status: OsdStatusSchema
18276
18908
  });
18909
+ var VehicleSchema = object({
18910
+ id: string(),
18911
+ name: string(),
18912
+ sampleCount: number().int().nonnegative(),
18913
+ coverMediaKey: string().optional(),
18914
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
18915
+ coverBase64: string().optional()
18916
+ });
18917
+ var VehicleSampleInfoSchema = object({
18918
+ id: string(),
18919
+ /** The plate text this sample enrolled (self-labeling — no embedding). */
18920
+ text: string(),
18921
+ /** OCR confidence of the enrolled read (0..1). */
18922
+ score: number(),
18923
+ addedAt: number().int(),
18924
+ deviceId: number().int().optional(),
18925
+ base64: string().optional()
18926
+ });
18277
18927
  var PlateInfoSchema = object({
18278
18928
  plateId: string(),
18279
18929
  deviceId: number().int(),
@@ -18285,6 +18935,16 @@ var PlateInfoSchema = object({
18285
18935
  score: number(),
18286
18936
  /** True when the text was manually corrected (exempt from retention). */
18287
18937
  corrected: boolean(),
18938
+ /** Recognized vehicle id when a matcher lookup named this read (SQL NULL → absent). */
18939
+ recognizedVehicleId: string().optional(),
18940
+ /** Resolved vehicle name for `recognizedVehicleId` (UI convenience). */
18941
+ vehicleName: string().optional(),
18942
+ /** True once the read was assigned to a vehicle (enrolled as a sample). */
18943
+ assigned: boolean(),
18944
+ /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
18945
+ plateBbox: BoundingBoxSchema.optional(),
18946
+ /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
18947
+ keyFrameMediaKey: string().optional(),
18288
18948
  base64: string().optional()
18289
18949
  });
18290
18950
  var MediaFileLiteSchema = object({
@@ -18324,6 +18984,48 @@ method(object({
18324
18984
  }), method(object({ plateId: string() }), _void(), {
18325
18985
  kind: "mutation",
18326
18986
  auth: "admin"
18987
+ }), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
18988
+ kind: "mutation",
18989
+ auth: "admin"
18990
+ }), method(object({
18991
+ id: string(),
18992
+ name: string().min(1)
18993
+ }), _void(), {
18994
+ kind: "mutation",
18995
+ auth: "admin"
18996
+ }), method(object({ id: string() }), _void(), {
18997
+ kind: "mutation",
18998
+ auth: "admin"
18999
+ }), method(object({ vehicleId: string() }), array(VehicleSampleInfoSchema).readonly()), method(object({
19000
+ vehicleId: string(),
19001
+ sampleId: string()
19002
+ }), _void(), {
19003
+ kind: "mutation",
19004
+ auth: "admin"
19005
+ }), method(object({
19006
+ plateId: string(),
19007
+ vehicleId: string()
19008
+ }), _void(), {
19009
+ kind: "mutation",
19010
+ auth: "admin"
19011
+ }), method(object({ plateId: string() }), _void(), {
19012
+ kind: "mutation",
19013
+ auth: "admin"
19014
+ }), method(object({
19015
+ plateIds: array(string()).min(1),
19016
+ vehicleId: string()
19017
+ }), object({
19018
+ assigned: number().int(),
19019
+ failed: array(string()).readonly()
19020
+ }), {
19021
+ kind: "mutation",
19022
+ auth: "admin"
19023
+ }), method(object({ plateIds: array(string()).min(1) }), object({
19024
+ unassigned: number().int(),
19025
+ failed: array(string()).readonly()
19026
+ }), {
19027
+ kind: "mutation",
19028
+ auth: "admin"
18327
19029
  });
18328
19030
  var ModelFormatSchema = _enum(MODEL_FORMATS);
18329
19031
  var HwAccelBackendInputSchema = _enum([
@@ -18402,6 +19104,10 @@ var GpuInfoSchema = object({
18402
19104
  memoryMB: number().optional()
18403
19105
  });
18404
19106
  var NpuInfoSchema = object({ type: _enum(["apple-ane", "intel-npu"]) });
19107
+ var CoralInfoSchema = object({
19108
+ type: literal("coral-edgetpu"),
19109
+ bus: string().optional()
19110
+ });
18405
19111
  var HardwareInfoSchema = object({
18406
19112
  platform: HardwarePlatformSchema,
18407
19113
  arch: HardwareArchSchema,
@@ -18410,7 +19116,8 @@ var HardwareInfoSchema = object({
18410
19116
  totalRAM_MB: number(),
18411
19117
  availableRAM_MB: number(),
18412
19118
  gpu: GpuInfoSchema.nullable(),
18413
- npu: NpuInfoSchema.nullable()
19119
+ npu: NpuInfoSchema.nullable(),
19120
+ coral: CoralInfoSchema.nullable().optional()
18414
19121
  });
18415
19122
  var PlatformScoreSchema = object({
18416
19123
  runtime: _enum(["node", "python"]),
@@ -20869,6 +21576,168 @@ Object.freeze({
20869
21576
  addonId: null,
20870
21577
  access: "create"
20871
21578
  },
21579
+ "llm.deleteModel": {
21580
+ capName: "llm",
21581
+ capScope: "system",
21582
+ addonId: null,
21583
+ access: "delete"
21584
+ },
21585
+ "llm.deleteProfile": {
21586
+ capName: "llm",
21587
+ capScope: "system",
21588
+ addonId: null,
21589
+ access: "delete"
21590
+ },
21591
+ "llm.generate": {
21592
+ capName: "llm",
21593
+ capScope: "system",
21594
+ addonId: null,
21595
+ access: "create"
21596
+ },
21597
+ "llm.generateVision": {
21598
+ capName: "llm",
21599
+ capScope: "system",
21600
+ addonId: null,
21601
+ access: "create"
21602
+ },
21603
+ "llm.getDefaults": {
21604
+ capName: "llm",
21605
+ capScope: "system",
21606
+ addonId: null,
21607
+ access: "view"
21608
+ },
21609
+ "llm.getRuntimeStatus": {
21610
+ capName: "llm",
21611
+ capScope: "system",
21612
+ addonId: null,
21613
+ access: "view"
21614
+ },
21615
+ "llm.getUsage": {
21616
+ capName: "llm",
21617
+ capScope: "system",
21618
+ addonId: null,
21619
+ access: "view"
21620
+ },
21621
+ "llm.installModel": {
21622
+ capName: "llm",
21623
+ capScope: "system",
21624
+ addonId: null,
21625
+ access: "create"
21626
+ },
21627
+ "llm.listModelCatalog": {
21628
+ capName: "llm",
21629
+ capScope: "system",
21630
+ addonId: null,
21631
+ access: "view"
21632
+ },
21633
+ "llm.listModels": {
21634
+ capName: "llm",
21635
+ capScope: "system",
21636
+ addonId: null,
21637
+ access: "view"
21638
+ },
21639
+ "llm.listNodeModels": {
21640
+ capName: "llm",
21641
+ capScope: "system",
21642
+ addonId: null,
21643
+ access: "view"
21644
+ },
21645
+ "llm.listProfileKinds": {
21646
+ capName: "llm",
21647
+ capScope: "system",
21648
+ addonId: null,
21649
+ access: "view"
21650
+ },
21651
+ "llm.listProfiles": {
21652
+ capName: "llm",
21653
+ capScope: "system",
21654
+ addonId: null,
21655
+ access: "view"
21656
+ },
21657
+ "llm.listRuntimeNodes": {
21658
+ capName: "llm",
21659
+ capScope: "system",
21660
+ addonId: null,
21661
+ access: "view"
21662
+ },
21663
+ "llm.setDefault": {
21664
+ capName: "llm",
21665
+ capScope: "system",
21666
+ addonId: null,
21667
+ access: "create"
21668
+ },
21669
+ "llm.startRuntime": {
21670
+ capName: "llm",
21671
+ capScope: "system",
21672
+ addonId: null,
21673
+ access: "create"
21674
+ },
21675
+ "llm.stopRuntime": {
21676
+ capName: "llm",
21677
+ capScope: "system",
21678
+ addonId: null,
21679
+ access: "create"
21680
+ },
21681
+ "llm.testProfile": {
21682
+ capName: "llm",
21683
+ capScope: "system",
21684
+ addonId: null,
21685
+ access: "create"
21686
+ },
21687
+ "llm.upsertProfile": {
21688
+ capName: "llm",
21689
+ capScope: "system",
21690
+ addonId: null,
21691
+ access: "create"
21692
+ },
21693
+ "llmRuntime.complete": {
21694
+ capName: "llm-runtime",
21695
+ capScope: "system",
21696
+ addonId: null,
21697
+ access: "create"
21698
+ },
21699
+ "llmRuntime.deleteModel": {
21700
+ capName: "llm-runtime",
21701
+ capScope: "system",
21702
+ addonId: null,
21703
+ access: "delete"
21704
+ },
21705
+ "llmRuntime.ensureStarted": {
21706
+ capName: "llm-runtime",
21707
+ capScope: "system",
21708
+ addonId: null,
21709
+ access: "create"
21710
+ },
21711
+ "llmRuntime.getDiskUsage": {
21712
+ capName: "llm-runtime",
21713
+ capScope: "system",
21714
+ addonId: null,
21715
+ access: "view"
21716
+ },
21717
+ "llmRuntime.installModel": {
21718
+ capName: "llm-runtime",
21719
+ capScope: "system",
21720
+ addonId: null,
21721
+ access: "create"
21722
+ },
21723
+ "llmRuntime.listLocalModels": {
21724
+ capName: "llm-runtime",
21725
+ capScope: "system",
21726
+ addonId: null,
21727
+ access: "view"
21728
+ },
21729
+ "llmRuntime.status": {
21730
+ capName: "llm-runtime",
21731
+ capScope: "system",
21732
+ addonId: null,
21733
+ access: "view"
21734
+ },
21735
+ "llmRuntime.stop": {
21736
+ capName: "llm-runtime",
21737
+ capScope: "system",
21738
+ addonId: null,
21739
+ access: "create"
21740
+ },
20872
21741
  "localNetwork.getAllowedAddresses": {
20873
21742
  capName: "local-network",
20874
21743
  capScope: "system",
@@ -21499,6 +22368,12 @@ Object.freeze({
21499
22368
  addonId: null,
21500
22369
  access: "delete"
21501
22370
  },
22371
+ "pipelineAnalytics.deleteTracks": {
22372
+ capName: "pipeline-analytics",
22373
+ capScope: "device",
22374
+ addonId: null,
22375
+ access: "delete"
22376
+ },
21502
22377
  "pipelineAnalytics.getActiveTracks": {
21503
22378
  capName: "pipeline-analytics",
21504
22379
  capScope: "device",
@@ -21565,12 +22440,24 @@ Object.freeze({
21565
22440
  addonId: null,
21566
22441
  access: "create"
21567
22442
  },
22443
+ "pipelineAnalytics.pruneTracksBefore": {
22444
+ capName: "pipeline-analytics",
22445
+ capScope: "device",
22446
+ addonId: null,
22447
+ access: "create"
22448
+ },
21568
22449
  "pipelineAnalytics.searchObjectEvents": {
21569
22450
  capName: "pipeline-analytics",
21570
22451
  capScope: "device",
21571
22452
  addonId: null,
21572
22453
  access: "view"
21573
22454
  },
22455
+ "pipelineAnalytics.wipeAllAnalytics": {
22456
+ capName: "pipeline-analytics",
22457
+ capScope: "device",
22458
+ addonId: null,
22459
+ access: "delete"
22460
+ },
21574
22461
  "pipelineExecutor.cacheFrameInPool": {
21575
22462
  capName: "pipeline-executor",
21576
22463
  capScope: "system",
@@ -22075,18 +22962,42 @@ Object.freeze({
22075
22962
  addonId: null,
22076
22963
  access: "create"
22077
22964
  },
22965
+ "plateGallery.assignPlate": {
22966
+ capName: "plate-gallery",
22967
+ capScope: "system",
22968
+ addonId: null,
22969
+ access: "create"
22970
+ },
22971
+ "plateGallery.assignPlates": {
22972
+ capName: "plate-gallery",
22973
+ capScope: "system",
22974
+ addonId: null,
22975
+ access: "create"
22976
+ },
22078
22977
  "plateGallery.correctPlateText": {
22079
22978
  capName: "plate-gallery",
22080
22979
  capScope: "system",
22081
22980
  addonId: null,
22082
22981
  access: "create"
22083
22982
  },
22983
+ "plateGallery.createVehicle": {
22984
+ capName: "plate-gallery",
22985
+ capScope: "system",
22986
+ addonId: null,
22987
+ access: "create"
22988
+ },
22084
22989
  "plateGallery.deletePlate": {
22085
22990
  capName: "plate-gallery",
22086
22991
  capScope: "system",
22087
22992
  addonId: null,
22088
22993
  access: "delete"
22089
22994
  },
22995
+ "plateGallery.deleteVehicle": {
22996
+ capName: "plate-gallery",
22997
+ capScope: "system",
22998
+ addonId: null,
22999
+ access: "delete"
23000
+ },
22090
23001
  "plateGallery.getPlateByTrack": {
22091
23002
  capName: "plate-gallery",
22092
23003
  capScope: "system",
@@ -22105,6 +23016,30 @@ Object.freeze({
22105
23016
  addonId: null,
22106
23017
  access: "view"
22107
23018
  },
23019
+ "plateGallery.listVehicles": {
23020
+ capName: "plate-gallery",
23021
+ capScope: "system",
23022
+ addonId: null,
23023
+ access: "view"
23024
+ },
23025
+ "plateGallery.listVehicleSamples": {
23026
+ capName: "plate-gallery",
23027
+ capScope: "system",
23028
+ addonId: null,
23029
+ access: "view"
23030
+ },
23031
+ "plateGallery.removeVehicleSample": {
23032
+ capName: "plate-gallery",
23033
+ capScope: "system",
23034
+ addonId: null,
23035
+ access: "delete"
23036
+ },
23037
+ "plateGallery.renameVehicle": {
23038
+ capName: "plate-gallery",
23039
+ capScope: "system",
23040
+ addonId: null,
23041
+ access: "create"
23042
+ },
22108
23043
  "plateGallery.searchPlates": {
22109
23044
  capName: "plate-gallery",
22110
23045
  capScope: "system",
@@ -22117,6 +23052,18 @@ Object.freeze({
22117
23052
  addonId: null,
22118
23053
  access: "view"
22119
23054
  },
23055
+ "plateGallery.unassignPlate": {
23056
+ capName: "plate-gallery",
23057
+ capScope: "system",
23058
+ addonId: null,
23059
+ access: "create"
23060
+ },
23061
+ "plateGallery.unassignPlates": {
23062
+ capName: "plate-gallery",
23063
+ capScope: "system",
23064
+ addonId: null,
23065
+ access: "create"
23066
+ },
22120
23067
  "platformProbe.getCapabilities": {
22121
23068
  capName: "platform-probe",
22122
23069
  capScope: "system",
@@ -22309,6 +23256,48 @@ Object.freeze({
22309
23256
  addonId: null,
22310
23257
  access: "create"
22311
23258
  },
23259
+ "sceneMonitor.captureReference": {
23260
+ capName: "scene-monitor",
23261
+ capScope: "device",
23262
+ addonId: null,
23263
+ access: "create"
23264
+ },
23265
+ "sceneMonitor.createScene": {
23266
+ capName: "scene-monitor",
23267
+ capScope: "device",
23268
+ addonId: null,
23269
+ access: "create"
23270
+ },
23271
+ "sceneMonitor.deleteReference": {
23272
+ capName: "scene-monitor",
23273
+ capScope: "device",
23274
+ addonId: null,
23275
+ access: "delete"
23276
+ },
23277
+ "sceneMonitor.deleteScene": {
23278
+ capName: "scene-monitor",
23279
+ capScope: "device",
23280
+ addonId: null,
23281
+ access: "delete"
23282
+ },
23283
+ "sceneMonitor.listScenes": {
23284
+ capName: "scene-monitor",
23285
+ capScope: "device",
23286
+ addonId: null,
23287
+ access: "view"
23288
+ },
23289
+ "sceneMonitor.recheckNow": {
23290
+ capName: "scene-monitor",
23291
+ capScope: "device",
23292
+ addonId: null,
23293
+ access: "create"
23294
+ },
23295
+ "sceneMonitor.updateScene": {
23296
+ capName: "scene-monitor",
23297
+ capScope: "device",
23298
+ addonId: null,
23299
+ access: "create"
23300
+ },
22312
23301
  "scriptRunner.run": {
22313
23302
  capName: "script-runner",
22314
23303
  capScope: "device",
@@ -23393,6 +24382,7 @@ Object.freeze({
23393
24382
  Object.freeze({
23394
24383
  "broker": "broker",
23395
24384
  "device-export": "device-export",
24385
+ "llm": "ai",
23396
24386
  "mesh-network": "mesh",
23397
24387
  "mqtt-broker": "broker",
23398
24388
  "network-access": "ingress",
@@ -23579,9 +24569,9 @@ async function resolveDecoderBackend(api, nodeId, logger) {
23579
24569
  //#region src/shared/notifying-ring-buffer.ts
23580
24570
  /**
23581
24571
  * A {@link RingBuffer} that can notify a single blocked consumer the moment an
23582
- * item commits — the server side of the `pullFrames`/`pullHandles` long-poll.
24572
+ * item commits — the server side of the `pullFrames` long-poll.
23583
24573
  *
23584
- * The decoder's `onFrame`/`onFrameHandle` callback drives `push()`; a consumer
24574
+ * The decoder's `onFrame` callback drives `push()`; a consumer
23585
24575
  * that finds the ring momentarily empty calls {@link waitForItem} and is woken
23586
24576
  * on the very next `push()` (a real commit signal) rather than spinning at
23587
24577
  * ~1kHz on `setTimeout(…,1)`. If nothing commits it resolves after `waitMs`,
@@ -24025,8 +25015,7 @@ var FrameStreamSplitter = class {
24025
25015
  //#region src/ffmpeg-decoder-session.ts
24026
25016
  /**
24027
25017
  * FfmpegDecoderSession — an `IDecoderSession` that decodes video in an **ffmpeg
24028
- * subprocess** and feeds the shared-memory frame ring (Phase 5 / D9), the same
24029
- * ring `decoder-nodeav` writes into.
25018
+ * subprocess** and delivers each decoded frame via the `onFrame` callback.
24030
25019
  *
24031
25020
  * Why a subprocess: on the Intel hub, node-av's in-process VA-API decode path
24032
25021
  * SIGBUSes (uncatchable) on 8MP h264 and kills the whole decoder runner. ffmpeg
@@ -24039,15 +25028,13 @@ var FrameStreamSplitter = class {
24039
25028
  * ffmpeg → `-vf <scale_vaapi=…,hwdownload,… | hwdownload,…,scale=…>` (GPU or
24040
25029
  * CPU scale, per {@link buildFfmpegDecodeArgs})
24041
25030
  * raw video → ffmpeg stdout (pipe:1) → a {@link FrameStreamSplitter} that fills
24042
- * each frame's bytes DIRECTLY into its shm ring slot (zero-copy
24043
- * `beginFrame`/`commitFrame`) `FrameHandle` (shm mode), or into a
24044
- * per-frame buffer → `DecodedFrame` via `onFrame` (callback mode).
24045
- * No `Buffer.concat` accumulator, one copy per byte, none for dropped
24046
- * frames.
25031
+ * each frame's bytes into a per-frame buffer `DecodedFrame` via
25032
+ * `onFrame`. No `Buffer.concat` accumulator, one copy per byte, none
25033
+ * for dropped frames.
24047
25034
  *
24048
- * Output geometry: the broker does not pass source width/height for the shm
24049
- * path, so ffmpeg picks the height (`scale=min(iw\,maxW):-2`) and the session
24050
- * learns the exact output W×H by parsing the muxer's `Output #0` stderr block
25035
+ * Output geometry: the broker does not pass source width/height, so ffmpeg picks
25036
+ * the height (`scale=min(iw\,maxW):-2`) and the session learns the exact output
25037
+ * W×H by parsing the muxer's `Output #0` stderr block
24051
25038
  * ({@link parseFfmpegOutputDims}). Until dims are known, stdout is buffered.
24052
25039
  */
24053
25040
  var noopLogger = {
@@ -24087,8 +25074,6 @@ var MAX_GOP_BUFFER_BYTES = 16 * 1024 * 1024;
24087
25074
  var FfmpegDecoderSession = class {
24088
25075
  config;
24089
25076
  logger;
24090
- frameSink;
24091
- nodeId;
24092
25077
  renderDevice;
24093
25078
  ffmpegPath;
24094
25079
  /**
@@ -24138,9 +25123,6 @@ var FfmpegDecoderSession = class {
24138
25123
  /** Stdout chunks received BEFORE geometry is known — replayed into the
24139
25124
  * splitter once `frameSize` is learned. A short-lived list (no concat). */
24140
25125
  pendingStdout = [];
24141
- /** The shm ring slot the splitter is currently filling, if any — aborted on
24142
- * respawn / teardown so a half-filled slot never publishes. */
24143
- openSlot = null;
24144
25126
  stderrAccum = "";
24145
25127
  destroyed = false;
24146
25128
  pixel;
@@ -24150,9 +25132,7 @@ var FfmpegDecoderSession = class {
24150
25132
  outWidth = 0;
24151
25133
  outHeight = 0;
24152
25134
  frameSize = 0;
24153
- frameRingSink = null;
24154
25135
  frameCallbacks = /* @__PURE__ */ new Set();
24155
- handleCallbacks = /* @__PURE__ */ new Set();
24156
25136
  frameDropper;
24157
25137
  inputPackets = 0;
24158
25138
  /**
@@ -24176,8 +25156,6 @@ var FfmpegDecoderSession = class {
24176
25156
  if (typeof config.deviceId === "number") sessionTags["deviceId"] = config.deviceId;
24177
25157
  if (typeof config.tag === "string" && config.tag.length > 0) sessionTags["tag"] = config.tag;
24178
25158
  this.logger = Object.keys(sessionTags).length > 0 ? logger.withTags(sessionTags) : logger;
24179
- this.frameSink = options?.frameSink ?? "callback";
24180
- this.nodeId = options?.nodeId ?? "local";
24181
25159
  this.renderDevice = options?.renderDevice;
24182
25160
  this.ffmpegPath = options?.ffmpegPath ?? "ffmpeg";
24183
25161
  const seededChain = options?.hwaccelChain ?? (options?.hwaccel != null && !isSoftwareHwAccel(options.hwaccel) ? [options.hwaccel] : []);
@@ -24190,10 +25168,6 @@ var FfmpegDecoderSession = class {
24190
25168
  this.frameFormat = this.pixel === "gray" ? "gray" : "rgb";
24191
25169
  this.frameDropper = new FrameDropper(config.maxFps);
24192
25170
  }
24193
- /** Exposed so the owning addon can surface ring stats via `getShmStats`. */
24194
- get frameRingSinkOrNull() {
24195
- return this.frameRingSink;
24196
- }
24197
25171
  get isPullMode() {
24198
25172
  return this.pullUrl !== null;
24199
25173
  }
@@ -24215,26 +25189,11 @@ var FfmpegDecoderSession = class {
24215
25189
  this.logger.info("ffmpeg decoder: pull-mode openStream — dialing input URL", { meta: { url } });
24216
25190
  this.spawnFfmpeg();
24217
25191
  }
24218
- ensureFrameRingSink() {
24219
- if (this.frameRingSink === null) {
24220
- const seedParts = [];
24221
- if (typeof this.config.deviceId === "number") seedParts.push(String(this.config.deviceId));
24222
- if (typeof this.config.tag === "string" && this.config.tag.length > 0) seedParts.push(this.config.tag);
24223
- const seed = seedParts.length > 0 ? seedParts.join(":") : "anon";
24224
- this.frameRingSink = new _camstack_shm_ring.DecoderFrameRingSink({
24225
- seed,
24226
- logger: this.logger,
24227
- nodeId: this.nodeId
24228
- });
24229
- }
24230
- return this.frameRingSink;
24231
- }
24232
25192
  spawnFfmpeg() {
24233
25193
  if (this.destroyed) return;
24234
25194
  this.spawnAttempted = true;
24235
25195
  this.clearPullRedial();
24236
25196
  this.killFfmpeg();
24237
- this.abortOpenSlot();
24238
25197
  this.splitter = null;
24239
25198
  this.pendingStdout = [];
24240
25199
  this.stderrAccum = "";
@@ -24280,8 +25239,7 @@ var FfmpegDecoderSession = class {
24280
25239
  hwaccel: this.hwaccel,
24281
25240
  gpuScale: this.activeGpuScaleFilter ?? "cpu",
24282
25241
  codec: this.config.codec,
24283
- format: this.frameFormat,
24284
- sink: this.frameSink
25242
+ format: this.frameFormat
24285
25243
  } });
24286
25244
  }
24287
25245
  killFfmpeg() {
@@ -24419,36 +25377,10 @@ var FfmpegDecoderSession = class {
24419
25377
  return {
24420
25378
  startFrame: () => {
24421
25379
  if (!this.frameDropper.shouldKeep()) return null;
24422
- if (this.frameSink === "shm") {
24423
- const reserved = this.ensureFrameRingSink().beginFrame(this.outWidth, this.outHeight, this.frameFormat);
24424
- if (reserved === null) return null;
24425
- this.openSlot = reserved.slot;
24426
- return reserved.buffer;
24427
- }
24428
25380
  return Buffer.allocUnsafe(this.frameSize);
24429
25381
  },
24430
25382
  finishFrame: (target) => {
24431
25383
  this.outputFrames++;
24432
- if (this.frameSink === "shm") {
24433
- const slot = this.openSlot;
24434
- this.openSlot = null;
24435
- const sink = this.frameRingSink;
24436
- if (slot === null || sink === null) return;
24437
- const handle = sink.commitFrame(slot, {
24438
- width: this.outWidth,
24439
- height: this.outHeight,
24440
- format: this.frameFormat,
24441
- pts: performance.now(),
24442
- byteLength: this.frameSize
24443
- });
24444
- if (handle === null) return;
24445
- const delivered = {
24446
- handle,
24447
- timestamp: Date.now()
24448
- };
24449
- for (const cb of this.handleCallbacks) cb(delivered);
24450
- return;
24451
- }
24452
25384
  const frame = {
24453
25385
  data: target,
24454
25386
  width: this.outWidth,
@@ -24463,14 +25395,6 @@ var FfmpegDecoderSession = class {
24463
25395
  }
24464
25396
  };
24465
25397
  }
24466
- /** Abandon a shm slot the splitter left open (respawn / teardown mid-frame),
24467
- * so a half-filled slot is never published. */
24468
- abortOpenSlot() {
24469
- const slot = this.openSlot;
24470
- if (slot === null) return;
24471
- this.openSlot = null;
24472
- this.frameRingSink?.abortFrame(slot);
24473
- }
24474
25398
  pushPacket(packet) {
24475
25399
  if (this.destroyed) return;
24476
25400
  if (this.pullUrl !== null) return;
@@ -24524,13 +25448,6 @@ var FfmpegDecoderSession = class {
24524
25448
  this.frameCallbacks.delete(callback);
24525
25449
  };
24526
25450
  }
24527
- /** Subscribe to shared-memory frame handles — fires only in `'shm'` mode. */
24528
- onFrameHandle(callback) {
24529
- this.handleCallbacks.add(callback);
24530
- return () => {
24531
- this.handleCallbacks.delete(callback);
24532
- };
24533
- }
24534
25451
  updateConfig(update) {
24535
25452
  const needsRestart = update.scale !== void 0 && update.scale !== this.config.scale || update.outputFormat !== void 0 && update.outputFormat !== this.config.outputFormat || update.codec !== void 0 && update.codec !== this.config.codec;
24536
25453
  this.config = {
@@ -24545,12 +25462,8 @@ var FfmpegDecoderSession = class {
24545
25462
  this.destroyed = true;
24546
25463
  this.clearPullRedial();
24547
25464
  this.killFfmpeg();
24548
- this.abortOpenSlot();
24549
25465
  this.splitter = null;
24550
- this.frameRingSink?.destroy();
24551
- this.frameRingSink = null;
24552
25466
  this.frameCallbacks.clear();
24553
- this.handleCallbacks.clear();
24554
25467
  }
24555
25468
  getStats() {
24556
25469
  const uptimeSec = Math.max((Date.now() - this.startTime) / 1e3, 1);
@@ -24621,71 +25534,6 @@ function probeGpuScaleFilters(ffmpegPath, logger) {
24621
25534
  });
24622
25535
  }
24623
25536
  //#endregion
24624
- //#region src/shm-orphan-purge.ts
24625
- /**
24626
- * Startup reclamation of orphaned shared-memory segments.
24627
- *
24628
- * A decoder writes frames into named `/dev/shm` segments and unlinks each one
24629
- * on graceful session teardown (`DecoderFrameRingSink.destroy`). When the
24630
- * decoder process dies *ungracefully* — SIGBUS, OOM-kill, or a SIGKILL during
24631
- * redeploy — that teardown never runs and the segment is orphaned: it stays in
24632
- * the tmpfs forever, since nothing else knows its name. Across many
24633
- * crashes/redeploys these accumulate until `/dev/shm` fills, at which point the
24634
- * next `mmap` write faults with an uncatchable SIGBUS and the decoder
24635
- * crash-loops into its circuit breaker (which is exactly the incident this
24636
- * guards against).
24637
- *
24638
- * The reclamation is safe *at process startup*: a freshly-booting decoder owns
24639
- * no live sessions, so every pre-existing segment with its prefix is by
24640
- * definition an orphan from a dead instance. `shm_unlink` only removes the
24641
- * name — any consumer still holding a mapping keeps reading valid memory until
24642
- * it closes (POSIX deferred reclaim + the ring seqlock), so unlinking is safe
24643
- * even if a stale reader is momentarily still attached.
24644
- *
24645
- * POSIX-only: segments surface as files under `/dev/shm` on Linux. On platforms
24646
- * without that directory (Windows, macOS) the scan finds nothing — no-op.
24647
- *
24648
- * NOTE: this lives inside the decoder addon (not `@camstack/shm-ring`) so it
24649
- * ships in the self-contained addon bundle via `camstack deploy`, reusing the
24650
- * already-deployed `unlinkSegment`; no host base-image rebuild required.
24651
- */
24652
- /** Default tmpfs directory where POSIX shared-memory segments appear on Linux. */
24653
- var DEFAULT_SHM_DIR = "/dev/shm";
24654
- /**
24655
- * Unlink every shared-memory segment whose name starts with `prefix`.
24656
- *
24657
- * Intended to run ONCE at decoder startup, before any session is created, to
24658
- * reclaim segments orphaned by a previously-crashed instance. A per-file unlink
24659
- * failure is swallowed so one stuck segment cannot block reclaiming the rest.
24660
- */
24661
- function purgeOrphanSegments(prefix, options = {}) {
24662
- const dir = options.dir ?? DEFAULT_SHM_DIR;
24663
- const unlink = options.unlink ?? _camstack_shm_ring.unlinkSegment;
24664
- let entries;
24665
- try {
24666
- entries = (0, node_fs.readdirSync)(dir);
24667
- } catch {
24668
- return {
24669
- scanned: 0,
24670
- removed: 0,
24671
- names: []
24672
- };
24673
- }
24674
- const names = [];
24675
- for (const name of entries) {
24676
- if (!name.startsWith(prefix)) continue;
24677
- try {
24678
- unlink(name);
24679
- names.push(name);
24680
- } catch {}
24681
- }
24682
- return {
24683
- scanned: entries.length,
24684
- removed: names.length,
24685
- names
24686
- };
24687
- }
24688
- //#endregion
24689
25537
  //#region src/audio-codec/ffmpeg-audio-process.ts
24690
25538
  /**
24691
25539
  * Minimal ffmpeg subprocess wrapper shared by the audio decode + encode
@@ -25854,12 +26702,8 @@ var DEFAULT_DECODER_FFMPEG_ADDON_CONFIG = { ...DEFAULT_DECODER_HWACCEL_CONFIG };
25854
26702
  var DecoderFfmpegAddon = class extends BaseAddon {
25855
26703
  sessions = /* @__PURE__ */ new Map();
25856
26704
  frameBuffers = /* @__PURE__ */ new Map();
25857
- handleBuffers = /* @__PURE__ */ new Map();
25858
26705
  unsubscribers = /* @__PURE__ */ new Map();
25859
26706
  sessionMeta = /* @__PURE__ */ new Map();
25860
- frameReaders = null;
25861
- getFrameHits = 0;
25862
- getFrameMisses = 0;
25863
26707
  /**
25864
26708
  * The ffmpeg binary every session spawns — resolved once at init via the
25865
26709
  * node-level `ctx.deps.ensureFfmpeg()`. The SAME binary the broker / recorder
@@ -25942,12 +26786,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
25942
26786
  return registrations;
25943
26787
  }
25944
26788
  this.ctx.logger.info("ffmpeg decoder addon initialized", { meta: { selectedBackend: backend } });
25945
- const purged = purgeOrphanSegments(_camstack_shm_ring.SEGMENT_NAME_PREFIX);
25946
- if (purged.removed > 0) this.ctx.logger.warn("ffmpeg decoder: reclaimed orphaned shm segments at startup", { meta: {
25947
- removed: purged.removed,
25948
- scanned: purged.scanned
25949
- } });
25950
- this.frameReaders = new _camstack_shm_ring.FrameRingReaderCache(this.ctx.logger);
25951
26789
  this.probedGpuScaleFilters = await probeGpuScaleFilters(this.ffmpegPath, this.ctx.logger);
25952
26790
  this.ctx.logger.info("decoder-ffmpeg: probed GPU scale filters", { meta: {
25953
26791
  filters: [...this.probedGpuScaleFilters],
@@ -26089,15 +26927,12 @@ var DecoderFfmpegAddon = class extends BaseAddon {
26089
26927
  }
26090
26928
  async createSession(config) {
26091
26929
  const sessionId = (0, node_crypto.randomUUID)();
26092
- const { frameSink } = config;
26093
26930
  const nodeId = this.resolveLocalNodeId();
26094
26931
  const hwaccelChain = await this.resolveDecodeHwaccelChain();
26095
26932
  const availableGpuScaleFilters = new Set([...this.probedGpuScaleFilters].filter((f) => !this.unsupportedGpuScaleFilters.has(f)));
26096
26933
  const session = new FfmpegDecoderSession(config, this.ctx.logger, {
26097
26934
  hwaccelChain,
26098
26935
  ffmpegPath: this.ffmpegPath,
26099
- frameSink,
26100
- nodeId,
26101
26936
  availableGpuScaleFilters,
26102
26937
  onGpuScaleUnsupported: (filter) => {
26103
26938
  if (this.unsupportedGpuScaleFilters.has(filter)) return;
@@ -26105,7 +26940,7 @@ var DecoderFfmpegAddon = class extends BaseAddon {
26105
26940
  this.ctx.logger.warn("decoder-ffmpeg: GPU scaler non-functional on this build — future sessions use CPU scale", { meta: { filter } });
26106
26941
  }
26107
26942
  });
26108
- const unsub = frameSink === "shm" ? this.wireShmSink(sessionId, session) : this.wireCallbackSink(sessionId, session);
26943
+ const unsub = this.wireCallbackSink(sessionId, session);
26109
26944
  this.sessions.set(sessionId, session);
26110
26945
  this.unsubscribers.set(sessionId, unsub);
26111
26946
  this.sessionMeta.set(sessionId, {
@@ -26120,7 +26955,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
26120
26955
  sessionId,
26121
26956
  codec: config.codec,
26122
26957
  hwaccelChain,
26123
- frameSink,
26124
26958
  nodeId
26125
26959
  }
26126
26960
  });
@@ -26147,13 +26981,6 @@ var DecoderFfmpegAddon = class extends BaseAddon {
26147
26981
  ringBuffer.push(capFrame);
26148
26982
  });
26149
26983
  }
26150
- wireShmSink(sessionId, session) {
26151
- const handleRing = new NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
26152
- this.handleBuffers.set(sessionId, handleRing);
26153
- return session.onFrameHandle((frame) => {
26154
- handleRing.push(frame.handle);
26155
- });
26156
- }
26157
26984
  async destroySession(input) {
26158
26985
  const { sessionId } = input;
26159
26986
  const session = this.sessions.get(sessionId);
@@ -26161,12 +26988,10 @@ var DecoderFfmpegAddon = class extends BaseAddon {
26161
26988
  const unsub = this.unsubscribers.get(sessionId);
26162
26989
  if (unsub) unsub();
26163
26990
  this.frameBuffers.get(sessionId)?.cancel();
26164
- this.handleBuffers.get(sessionId)?.cancel();
26165
26991
  const deviceId = this.sessionMeta.get(sessionId)?.deviceId;
26166
26992
  await session.destroy();
26167
26993
  this.sessions.delete(sessionId);
26168
26994
  this.frameBuffers.delete(sessionId);
26169
- this.handleBuffers.delete(sessionId);
26170
26995
  this.unsubscribers.delete(sessionId);
26171
26996
  this.sessionMeta.delete(sessionId);
26172
26997
  this.ctx.logger.info("ffmpeg: destroyed session", {
@@ -26206,12 +27031,14 @@ var DecoderFfmpegAddon = class extends BaseAddon {
26206
27031
  if (ringBuffer.size === 0 && input.waitMs) await ringBuffer.waitForItem(input.waitMs);
26207
27032
  return ringBuffer.drain(input.maxCount);
26208
27033
  }
26209
- async pullHandles(input) {
26210
- if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
26211
- const handleRing = this.handleBuffers.get(input.sessionId);
26212
- if (!handleRing) return [];
26213
- if (handleRing.size === 0 && input.waitMs) await handleRing.waitForItem(input.waitMs);
26214
- return handleRing.drain(input.maxCount);
27034
+ /**
27035
+ * DORMANT STUB. The shm `FrameHandle` frame ring was removed (the runner ships
27036
+ * inline pixels via `pullFrames` now). The cap method stays defined for wire
27037
+ * compat until the framework train drops it, but no session ever produces
27038
+ * handles always empty.
27039
+ */
27040
+ async pullHandles(_input) {
27041
+ return [];
26215
27042
  }
26216
27043
  async updateConfig(input) {
26217
27044
  const session = this.sessions.get(input.sessionId);
@@ -26223,33 +27050,20 @@ var DecoderFfmpegAddon = class extends BaseAddon {
26223
27050
  if (!session) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
26224
27051
  return session.getStats();
26225
27052
  }
26226
- async getFrame(input) {
26227
- const frame = this.frameReaders?.read(input.handle) ?? null;
26228
- if (!frame) {
26229
- this.getFrameMisses += 1;
26230
- return null;
26231
- }
26232
- this.getFrameHits += 1;
26233
- const arrayBuf = new ArrayBuffer(frame.data.byteLength);
26234
- new Uint8Array(arrayBuf).set(frame.data);
26235
- return {
26236
- data: new Uint8Array(arrayBuf),
26237
- width: frame.width,
26238
- height: frame.height,
26239
- format: frame.format,
26240
- timestamp: frame.timestamp
26241
- };
27053
+ /**
27054
+ * DORMANT STUB. Frames are no longer retained in a shm ring this addon can
27055
+ * read back by handle — the decode-owning node serves pixels over its own cap
27056
+ * surface. Kept defined for wire compat; always resolves `null`.
27057
+ */
27058
+ async getFrame(_input) {
27059
+ return null;
26242
27060
  }
26243
- async getShmStats(input) {
26244
- const stats = this.sessions.get(input.sessionId)?.frameRingSinkOrNull?.getShmStats() ?? null;
26245
- if (!stats) return null;
26246
- return {
26247
- sessionId: input.sessionId,
26248
- ...stats,
26249
- budgetMb: _camstack_shm_ring.RING_BUDGET_MB,
26250
- getFrameHits: this.getFrameHits,
26251
- getFrameMisses: this.getFrameMisses
26252
- };
27061
+ /**
27062
+ * DORMANT STUB. No session writes a shm frame ring anymore, so there are no
27063
+ * ring stats to report. Kept defined for wire compat; always `null`.
27064
+ */
27065
+ async getShmStats(_input) {
27066
+ return null;
26253
27067
  }
26254
27068
  async onShutdown() {
26255
27069
  this.ctx.logger.info("ffmpeg decoder addon shutdown — destroying all sessions");
@@ -26264,11 +27078,8 @@ var DecoderFfmpegAddon = class extends BaseAddon {
26264
27078
  await Promise.all(destroyPromises);
26265
27079
  this.sessions.clear();
26266
27080
  this.frameBuffers.clear();
26267
- this.handleBuffers.clear();
26268
27081
  this.unsubscribers.clear();
26269
27082
  this.sessionMeta.clear();
26270
- this.frameReaders?.close();
26271
- this.frameReaders = null;
26272
27083
  }
26273
27084
  };
26274
27085
  //#endregion