@camstack/addon-decoder-nodeav 1.1.11 → 1.1.12

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