@camstack/addon-pipeline 1.2.118 → 1.2.119

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 (32) hide show
  1. package/dist/{addon-utils-DJSN5h8c.js → addon-utils-RAHF2hmW.js} +1 -1
  2. package/dist/audio-analyzer/index.js +3 -3
  3. package/dist/audio-analyzer/index.mjs +2 -2
  4. package/dist/detection-pipeline/index.js +171 -167
  5. package/dist/detection-pipeline/index.mjs +169 -165
  6. package/dist/{dist-BkO76jAp.js → dist-DIsM42Ex.js} +99 -2
  7. package/dist/{dist-B763E61Y.mjs → dist-Db664g2O.mjs} +94 -3
  8. package/dist/{event-loop-stall-monitor-28f4R0JN.js → event-loop-stall-monitor-CSpdFZb6.js} +1 -1
  9. package/dist/{event-loop-stall-monitor-CYEvvp2Z.mjs → event-loop-stall-monitor-DY6339bX.mjs} +1 -1
  10. package/dist/{lazy-sharp-BaPl5mRa.js → lazy-sharp-LSlEXQe_.js} +1 -1
  11. package/dist/motion-wasm/index.js +2 -2
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/pipeline-runner/index.js +12 -14
  14. package/dist/pipeline-runner/index.mjs +11 -13
  15. package/dist/{process-memory-CK3FnKyZ.mjs → process-memory-CPm5TMBl.mjs} +1 -1
  16. package/dist/{process-memory-BCQJFZZg.js → process-memory-DpSJQF-p.js} +1 -1
  17. package/dist/recorder/index.js +2 -2
  18. package/dist/recorder/index.mjs +1 -1
  19. package/dist/session-decode/decode-worker-child.js +2 -2
  20. package/dist/session-decode/decode-worker-child.mjs +1 -1
  21. package/dist/stream-broker/_stub.js +2 -2
  22. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CxgvfkxP.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CstvrzmK.mjs} +2 -2
  23. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BiNja8Hw.mjs +26 -0
  24. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BAMyzCWw.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-D-NFPV_g.mjs} +1 -1
  25. package/dist/stream-broker/{hostInit-CTkpxp3w.mjs → hostInit-kHeLqEod.mjs} +2 -2
  26. package/dist/stream-broker/index.js +8 -6
  27. package/dist/stream-broker/index.mjs +8 -6
  28. package/dist/stream-broker/remoteEntry.js +1 -1
  29. package/dist/{worker-protocol-owR7WMRP.js → worker-protocol-C1tr8WP3.js} +1 -1
  30. package/dist/{worker-protocol-D_2QaXYr.mjs → worker-protocol-_zMPqSas.mjs} +1 -1
  31. package/package.json +1 -1
  32. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CL6Lgzdj.mjs +0 -26
@@ -1,8 +1,8 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $ as pickClusterStepSettings, At as nodePin, D as YAMNET_TO_MACRO, Et as hydrateSchema, G as evaluateZoneRules, Gt as union, Ht as object, I as defaultDeviceFor$1, It as array, Kt as EventCategory, Pt as sleep, Q as pickClusterStepModels, Tt as createEvent, V as detectionPipelineCapability, W as enumerateInferenceDevices, Wt as string, X as overlayClusterStepSettings, c as DEFAULT_CLUSTER_STEP_MODELS, ct as resolvePoolMemoryPolicy, ht as errMsg, jt as parseJsonUnknown, l as DEFAULT_CLUSTER_STEP_SETTINGS, nt as pipelineExecutorCapability, ot as resolveClusterStepModelId, p as DEVICE_BACKEND_TO_FORMAT, pt as supportedRuntimes$1, t as APPLE_SA_TO_MACRO, ut as runtimeDevices$1, x as PoolMemoryWatchdog, xt as BaseAddon } from "../dist-B763E61Y.mjs";
3
- import { a as ALL_PIPELINE_STEPS, c as getDefaultModelForFormatFromDef, d as resolveModelForFormat, f as landmarkPrecisionVerdict, l as getStep, n as startEventLoopStallMonitor, o as ALL_STEPS, r as localFrameRegistry, s as getDefaultModelForFormat, u as getStepDefinition } from "../event-loop-stall-monitor-CYEvvp2Z.mjs";
2
+ import { $ as pickClusterStepModels, D as YAMNET_TO_MACRO, Dt as hydrateSchema, Et as createEvent, Ft as sleep, G as evaluateZoneRules, Gt as string, I as defaultDeviceFor$1, Kt as union, Lt as array, Mt as parseJsonUnknown, St as BaseAddon, Ut as object, V as detectionPipelineCapability, W as enumerateInferenceDevices, Z as overlayClusterStepSettings, c as DEFAULT_CLUSTER_STEP_MODELS, dt as runtimeDevices$1, et as pickClusterStepSettings, gt as errMsg, jt as nodePin, l as DEFAULT_CLUSTER_STEP_SETTINGS, lt as resolvePoolMemoryPolicy, mt as supportedRuntimes$1, p as DEVICE_BACKEND_TO_FORMAT, q as inferModelProvider, qt as EventCategory, rt as pipelineExecutorCapability, st as resolveClusterStepModelId, t as APPLE_SA_TO_MACRO, x as PoolMemoryWatchdog } from "../dist-Db664g2O.mjs";
3
+ import { a as ALL_PIPELINE_STEPS, c as getDefaultModelForFormatFromDef, d as resolveModelForFormat, f as landmarkPrecisionVerdict, l as getStep, n as startEventLoopStallMonitor, o as ALL_STEPS, r as localFrameRegistry, s as getDefaultModelForFormat, u as getStepDefinition } from "../event-loop-stall-monitor-DY6339bX.mjs";
4
4
  import { t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
5
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-CK3FnKyZ.mjs";
5
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-CPm5TMBl.mjs";
6
6
  import { a as ensureModel, o as isModelDownloaded, r as deleteModelFromDisk } from "../addon-utils-CgBCF-mE.mjs";
7
7
  import sharp from "sharp";
8
8
  import { spawn } from "node:child_process";
@@ -249,6 +249,113 @@ function normalizeEngineNodeId(rawNodeId) {
249
249
  return raw.includes("/") ? raw.split("/")[0] ?? "hub" : raw;
250
250
  }
251
251
  //#endregion
252
+ //#region src/shared/cluster-model-source.ts
253
+ /**
254
+ * Where the runner gets the ONE cluster model choice per cluster-scoped step.
255
+ *
256
+ * The sibling of `crop-convention-source.ts`, and deliberately built the same
257
+ * way, because it answers the neighbouring half of the same question: the crop
258
+ * convention decides WHAT pixels the encoder sees, this decides WHICH encoder
259
+ * sees them. Both are cluster-wide, both are read by the runner from the
260
+ * `pipeline-orchestrator` global settings over the hub-routed `addon-settings`
261
+ * cap, both pass NO `nodeId`.
262
+ *
263
+ * ## Deliberately NOT node-scoped, and not on the attach config
264
+ *
265
+ * With a `nodeId` the settings cap would project that node's scoped value and
266
+ * two nodes could embed into one index with two encoders — the original defect,
267
+ * relocated to the transport. And the attach config is the wrong carrier for
268
+ * the same reason it was wrong for the crop: eleven builders construct a
269
+ * `RunnerCameraConfig`, a cluster-wide constant threaded through eleven
270
+ * builders eventually reaches ten of them, and the cameras attached by the
271
+ * forgotten one quietly write a foreign feature space into the shared index.
272
+ *
273
+ * ## Why the failure mode keeps the last value
274
+ *
275
+ * A failed read must never revert to the registry default MID-PASS: that would
276
+ * silently change what every remaining vector of that dispatch means, which is
277
+ * the exact class of harm the scope exists to prevent. So a failure keeps the
278
+ * last known row and says so, loudly, once per failure.
279
+ */
280
+ /**
281
+ * The addon whose GLOBAL settings own the cluster row. Same owner as the crop
282
+ * convention and the native-frame lease: hub-resident and neutral between the
283
+ * addons that read it (post-analysis pins it for a re-embed pass, the runner
284
+ * enforces it live — neither should own a value the other depends on).
285
+ */
286
+ var CLUSTER_MODEL_OWNER_ADDON_ID = "pipeline-orchestrator";
287
+ /** TTL-cached read of the cluster model row. */
288
+ var ClusterModelSource = class {
289
+ models = DEFAULT_CLUSTER_STEP_MODELS;
290
+ settings = DEFAULT_CLUSTER_STEP_SETTINGS;
291
+ lastReadAtMs = Number.NEGATIVE_INFINITY;
292
+ inFlight = null;
293
+ logger;
294
+ now;
295
+ ttlMs;
296
+ constructor(options) {
297
+ this.logger = options.logger;
298
+ this.now = options.now ?? (() => Date.now());
299
+ this.ttlMs = options.ttlMs ?? 6e4;
300
+ }
301
+ /** The row in force right now — the registry defaults until a read lands. */
302
+ current() {
303
+ return this.models;
304
+ }
305
+ /** Cluster-scoped step knobs in force right now (admission floors, …). */
306
+ currentSettings() {
307
+ return this.settings;
308
+ }
309
+ /**
310
+ * Re-read if the cached row has expired. Concurrent callers share one
311
+ * in-flight read; a failure keeps the LAST KNOWN row.
312
+ */
313
+ async refresh(api) {
314
+ if (api === void 0) return this.models;
315
+ if (this.now() - this.lastReadAtMs < this.ttlMs) return this.models;
316
+ this.inFlight ??= this.read(api).finally(() => {
317
+ this.inFlight = null;
318
+ });
319
+ await this.inFlight;
320
+ return this.models;
321
+ }
322
+ async read(api) {
323
+ try {
324
+ const view = await api.addonSettings.getGlobalSettings.query({ addonId: CLUSTER_MODEL_OWNER_ADDON_ID });
325
+ const next = pickClusterStepModels(view);
326
+ const nextSettings = pickClusterStepSettings(view);
327
+ this.lastReadAtMs = this.now();
328
+ const changed = Object.keys(next).filter((stepId) => next[stepId] !== this.models[stepId]);
329
+ const settingsChanged = Object.keys(nextSettings).some((stepId) => {
330
+ const from = this.settings[stepId] ?? {};
331
+ const to = nextSettings[stepId] ?? {};
332
+ return [...new Set([...Object.keys(from), ...Object.keys(to)])].some((key) => from[key] !== to[key]);
333
+ });
334
+ if (changed.length > 0) {
335
+ this.logger.info("cluster step model changed — vectors already stored by the previous model need a re-embed pass", { meta: { changed: changed.map((stepId) => ({
336
+ stepId,
337
+ from: this.models[stepId],
338
+ to: next[stepId]
339
+ })) } });
340
+ this.models = next;
341
+ }
342
+ if (settingsChanged) {
343
+ this.logger.info("cluster step settings changed", { meta: {
344
+ from: { ...this.settings },
345
+ to: { ...nextSettings }
346
+ } });
347
+ this.settings = nextSettings;
348
+ }
349
+ } catch (err) {
350
+ this.logger.warn("cluster step model read failed — keeping the last known row", { meta: {
351
+ owner: CLUSTER_MODEL_OWNER_ADDON_ID,
352
+ inUse: { ...this.models },
353
+ error: err instanceof Error ? err.message : String(err)
354
+ } });
355
+ }
356
+ }
357
+ };
358
+ //#endregion
252
359
  //#region src/detection-pipeline/default-detection-model.ts
253
360
  /** The object-detection step id — the only slot this resolver applies to. */
254
361
  var OBJECT_DETECTION_STEP_ID = "object-detection";
@@ -2594,6 +2701,59 @@ var EngineProvisioner = class {
2594
2701
  }
2595
2702
  };
2596
2703
  //#endregion
2704
+ //#region src/session-decode/frame-view-payload.ts
2705
+ var FRAME_VIEW_PAYLOAD_POLICY = "jpeg";
2706
+ //#endregion
2707
+ //#region src/detection-pipeline/root-frame-view.ts
2708
+ /** Resolve the pre-padding content requested from the decode worker for one root. */
2709
+ function resolveRootFrameViewSpec(model, source, crop, format = FRAME_VIEW_PAYLOAD_POLICY) {
2710
+ const sourceWidth = crop?.width ?? source.width;
2711
+ const sourceHeight = crop?.height ?? source.height;
2712
+ if (model.preprocessMode !== "letterbox") return {
2713
+ ...crop ? { crop } : {},
2714
+ content: {
2715
+ width: model.inputSize.width,
2716
+ height: model.inputSize.height
2717
+ },
2718
+ fit: "stretch",
2719
+ format
2720
+ };
2721
+ const scale = Math.min(model.inputSize.width / sourceWidth, model.inputSize.height / sourceHeight);
2722
+ return {
2723
+ ...crop ? { crop } : {},
2724
+ content: {
2725
+ width: Math.max(1, Math.round(sourceWidth * scale)),
2726
+ height: Math.max(1, Math.round(sourceHeight * scale))
2727
+ },
2728
+ fit: "contain",
2729
+ format
2730
+ };
2731
+ }
2732
+ //#endregion
2733
+ //#region src/detection-pipeline/frame-ref-view-provider.ts
2734
+ var LocalFrameRefMissError = class extends Error {
2735
+ reason;
2736
+ constructor(reason) {
2737
+ super(`local frameRef miss: ${reason}`);
2738
+ this.reason = reason;
2739
+ this.name = "LocalFrameRefMissError";
2740
+ }
2741
+ };
2742
+ /** Bind one registry lease for all heterogeneous roots of a pipeline invocation. */
2743
+ function createRootFrameViewResolver(registry, ref) {
2744
+ const lease = registry.acquire(ref);
2745
+ if (!lease) throw new LocalFrameRefMissError(ref.registryId === registry.registryId ? "stale-ref" : "foreign-registry");
2746
+ const resolve = async (root, crop) => {
2747
+ const model = root.definition.models.find((candidate) => candidate.id === root.modelId);
2748
+ if (!model) throw new Error(`frame view model not found: ${root.modelId}`);
2749
+ const spec = resolveRootFrameViewSpec(model, ref, crop);
2750
+ const result = await lease.resolve(spec);
2751
+ if (result.kind === "miss") throw new LocalFrameRefMissError(result.reason);
2752
+ return result;
2753
+ };
2754
+ return Object.assign(resolve, { release: (reason) => lease.release(reason) });
2755
+ }
2756
+ //#endregion
2597
2757
  //#region src/detection-pipeline/pipeline/crop-utils.ts
2598
2758
  /**
2599
2759
  * Crop utilities — ROI extraction and bbox coordinate transforms.
@@ -5179,59 +5339,6 @@ function capitalize(s) {
5179
5339
  return s.charAt(0).toUpperCase() + s.slice(1);
5180
5340
  }
5181
5341
  //#endregion
5182
- //#region src/session-decode/frame-view-payload.ts
5183
- var FRAME_VIEW_PAYLOAD_POLICY = "jpeg";
5184
- //#endregion
5185
- //#region src/detection-pipeline/root-frame-view.ts
5186
- /** Resolve the pre-padding content requested from the decode worker for one root. */
5187
- function resolveRootFrameViewSpec(model, source, crop, format = FRAME_VIEW_PAYLOAD_POLICY) {
5188
- const sourceWidth = crop?.width ?? source.width;
5189
- const sourceHeight = crop?.height ?? source.height;
5190
- if (model.preprocessMode !== "letterbox") return {
5191
- ...crop ? { crop } : {},
5192
- content: {
5193
- width: model.inputSize.width,
5194
- height: model.inputSize.height
5195
- },
5196
- fit: "stretch",
5197
- format
5198
- };
5199
- const scale = Math.min(model.inputSize.width / sourceWidth, model.inputSize.height / sourceHeight);
5200
- return {
5201
- ...crop ? { crop } : {},
5202
- content: {
5203
- width: Math.max(1, Math.round(sourceWidth * scale)),
5204
- height: Math.max(1, Math.round(sourceHeight * scale))
5205
- },
5206
- fit: "contain",
5207
- format
5208
- };
5209
- }
5210
- //#endregion
5211
- //#region src/detection-pipeline/frame-ref-view-provider.ts
5212
- var LocalFrameRefMissError = class extends Error {
5213
- reason;
5214
- constructor(reason) {
5215
- super(`local frameRef miss: ${reason}`);
5216
- this.reason = reason;
5217
- this.name = "LocalFrameRefMissError";
5218
- }
5219
- };
5220
- /** Bind one registry lease for all heterogeneous roots of a pipeline invocation. */
5221
- function createRootFrameViewResolver(registry, ref) {
5222
- const lease = registry.acquire(ref);
5223
- if (!lease) throw new LocalFrameRefMissError(ref.registryId === registry.registryId ? "stale-ref" : "foreign-registry");
5224
- const resolve = async (root, crop) => {
5225
- const model = root.definition.models.find((candidate) => candidate.id === root.modelId);
5226
- if (!model) throw new Error(`frame view model not found: ${root.modelId}`);
5227
- const spec = resolveRootFrameViewSpec(model, ref, crop);
5228
- const result = await lease.resolve(spec);
5229
- if (result.kind === "miss") throw new LocalFrameRefMissError(result.reason);
5230
- return result;
5231
- };
5232
- return Object.assign(resolve, { release: (reason) => lease.release(reason) });
5233
- }
5234
- //#endregion
5235
5342
  //#region src/detection-pipeline/pipeline/native-crop-compose.ts
5236
5343
  /** Clamp `v` into `[lo, hi]`. */
5237
5344
  function clamp(v, lo, hi) {
@@ -5360,7 +5467,10 @@ function mergeCustomModels(staticModels, customModels) {
5360
5467
  for (const m of customModels) {
5361
5468
  if (seen.has(m.id)) continue;
5362
5469
  seen.add(m.id);
5363
- merged.push(m);
5470
+ merged.push({
5471
+ ...m,
5472
+ provider: inferModelProvider(m)
5473
+ });
5364
5474
  }
5365
5475
  return merged;
5366
5476
  }
@@ -5659,113 +5769,6 @@ function applyZoneRuleGate(result, zones, rules) {
5659
5769
  };
5660
5770
  }
5661
5771
  //#endregion
5662
- //#region src/shared/cluster-model-source.ts
5663
- /**
5664
- * Where the runner gets the ONE cluster model choice per cluster-scoped step.
5665
- *
5666
- * The sibling of `crop-convention-source.ts`, and deliberately built the same
5667
- * way, because it answers the neighbouring half of the same question: the crop
5668
- * convention decides WHAT pixels the encoder sees, this decides WHICH encoder
5669
- * sees them. Both are cluster-wide, both are read by the runner from the
5670
- * `pipeline-orchestrator` global settings over the hub-routed `addon-settings`
5671
- * cap, both pass NO `nodeId`.
5672
- *
5673
- * ## Deliberately NOT node-scoped, and not on the attach config
5674
- *
5675
- * With a `nodeId` the settings cap would project that node's scoped value and
5676
- * two nodes could embed into one index with two encoders — the original defect,
5677
- * relocated to the transport. And the attach config is the wrong carrier for
5678
- * the same reason it was wrong for the crop: eleven builders construct a
5679
- * `RunnerCameraConfig`, a cluster-wide constant threaded through eleven
5680
- * builders eventually reaches ten of them, and the cameras attached by the
5681
- * forgotten one quietly write a foreign feature space into the shared index.
5682
- *
5683
- * ## Why the failure mode keeps the last value
5684
- *
5685
- * A failed read must never revert to the registry default MID-PASS: that would
5686
- * silently change what every remaining vector of that dispatch means, which is
5687
- * the exact class of harm the scope exists to prevent. So a failure keeps the
5688
- * last known row and says so, loudly, once per failure.
5689
- */
5690
- /**
5691
- * The addon whose GLOBAL settings own the cluster row. Same owner as the crop
5692
- * convention and the native-frame lease: hub-resident and neutral between the
5693
- * addons that read it (post-analysis pins it for a re-embed pass, the runner
5694
- * enforces it live — neither should own a value the other depends on).
5695
- */
5696
- var CLUSTER_MODEL_OWNER_ADDON_ID = "pipeline-orchestrator";
5697
- /** TTL-cached read of the cluster model row. */
5698
- var ClusterModelSource = class {
5699
- models = DEFAULT_CLUSTER_STEP_MODELS;
5700
- settings = DEFAULT_CLUSTER_STEP_SETTINGS;
5701
- lastReadAtMs = Number.NEGATIVE_INFINITY;
5702
- inFlight = null;
5703
- logger;
5704
- now;
5705
- ttlMs;
5706
- constructor(options) {
5707
- this.logger = options.logger;
5708
- this.now = options.now ?? (() => Date.now());
5709
- this.ttlMs = options.ttlMs ?? 6e4;
5710
- }
5711
- /** The row in force right now — the registry defaults until a read lands. */
5712
- current() {
5713
- return this.models;
5714
- }
5715
- /** Cluster-scoped step knobs in force right now (admission floors, …). */
5716
- currentSettings() {
5717
- return this.settings;
5718
- }
5719
- /**
5720
- * Re-read if the cached row has expired. Concurrent callers share one
5721
- * in-flight read; a failure keeps the LAST KNOWN row.
5722
- */
5723
- async refresh(api) {
5724
- if (api === void 0) return this.models;
5725
- if (this.now() - this.lastReadAtMs < this.ttlMs) return this.models;
5726
- this.inFlight ??= this.read(api).finally(() => {
5727
- this.inFlight = null;
5728
- });
5729
- await this.inFlight;
5730
- return this.models;
5731
- }
5732
- async read(api) {
5733
- try {
5734
- const view = await api.addonSettings.getGlobalSettings.query({ addonId: CLUSTER_MODEL_OWNER_ADDON_ID });
5735
- const next = pickClusterStepModels(view);
5736
- const nextSettings = pickClusterStepSettings(view);
5737
- this.lastReadAtMs = this.now();
5738
- const changed = Object.keys(next).filter((stepId) => next[stepId] !== this.models[stepId]);
5739
- const settingsChanged = Object.keys(nextSettings).some((stepId) => {
5740
- const from = this.settings[stepId] ?? {};
5741
- const to = nextSettings[stepId] ?? {};
5742
- return [...new Set([...Object.keys(from), ...Object.keys(to)])].some((key) => from[key] !== to[key]);
5743
- });
5744
- if (changed.length > 0) {
5745
- this.logger.info("cluster step model changed — vectors already stored by the previous model need a re-embed pass", { meta: { changed: changed.map((stepId) => ({
5746
- stepId,
5747
- from: this.models[stepId],
5748
- to: next[stepId]
5749
- })) } });
5750
- this.models = next;
5751
- }
5752
- if (settingsChanged) {
5753
- this.logger.info("cluster step settings changed", { meta: {
5754
- from: { ...this.settings },
5755
- to: { ...nextSettings }
5756
- } });
5757
- this.settings = nextSettings;
5758
- }
5759
- } catch (err) {
5760
- this.logger.warn("cluster step model read failed — keeping the last known row", { meta: {
5761
- owner: CLUSTER_MODEL_OWNER_ADDON_ID,
5762
- inUse: { ...this.models },
5763
- error: err instanceof Error ? err.message : String(err)
5764
- } });
5765
- }
5766
- }
5767
- };
5768
- //#endregion
5769
5772
  //#region src/detection-pipeline/provider.ts
5770
5773
  /**
5771
5774
  * DetectionPipelineProvider — implements IPipelineExecutorProvider.
@@ -8756,7 +8759,8 @@ function buildSchemaSlots(formats, modelsDir, customByStep) {
8756
8759
  sizeMB: entry.sizeMB
8757
8760
  }])),
8758
8761
  ...m.group ? { group: m.group } : {},
8759
- ...m.legacy !== void 0 ? { legacy: m.legacy } : {}
8762
+ ...m.legacy !== void 0 ? { legacy: m.legacy } : {},
8763
+ provider: m.provider ?? "camstack"
8760
8764
  })),
8761
8765
  defaultModelId: step.defaultModelId,
8762
8766
  ...step.defaultModelIdByFormat ? { defaultModelIdByFormat: { ...step.defaultModelIdByFormat } } : {},
@@ -9173,6 +9173,25 @@ var ModelVariantGroupSchema = object({
9173
9173
  */
9174
9174
  resolution: number().int().positive().optional()
9175
9175
  });
9176
+ var ModelProviderIdSchema = _enum([
9177
+ "camstack",
9178
+ "frigate",
9179
+ "scrypted",
9180
+ "custom"
9181
+ ]);
9182
+ /**
9183
+ * Resolve a catalog entry's picker provider. An explicit stamp always wins;
9184
+ * otherwise Frigate/Scrypted are recognised from id prefix or description.
9185
+ * Unstamped BYO registry rows fall through to `custom`. Family `yolov9` is
9186
+ * NOT a Scrypted signal — CamStack ships that family too.
9187
+ */
9188
+ function inferModelProvider(entry) {
9189
+ if (entry.provider !== void 0) return entry.provider;
9190
+ const haystack = `${entry.id} ${entry.description ?? ""}`;
9191
+ if (/scrypted/i.test(haystack)) return "scrypted";
9192
+ if (/frigate/i.test(haystack)) return "frigate";
9193
+ return "custom";
9194
+ }
9176
9195
  var ModelCatalogEntrySchema = object({
9177
9196
  id: string(),
9178
9197
  name: string(),
@@ -9270,6 +9289,12 @@ var ModelCatalogEntrySchema = object({
9270
9289
  */
9271
9290
  group: ModelVariantGroupSchema.optional(),
9272
9291
  /**
9292
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
9293
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
9294
+ * persisted before this field existed (`inferModelProvider` fills those).
9295
+ */
9296
+ provider: ModelProviderIdSchema.optional(),
9297
+ /**
9273
9298
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
9274
9299
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
9275
9300
  * labels already ARE the CamStack macros (Scrypted identity map).
@@ -18660,6 +18685,46 @@ var RecentTracksPageSchema = object({
18660
18685
  /** Cursor for the next page, or null when this page is the last. */
18661
18686
  nextCursor: string().nullable()
18662
18687
  });
18688
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
18689
+ var LIST_GROUPS_MAX_LIMIT = 100;
18690
+ var AnalyticsGroupRecordSchema = object({
18691
+ id: string(),
18692
+ deviceId: number().int(),
18693
+ openedAt: number().int(),
18694
+ closedAt: number().int(),
18695
+ timestamp: number().int(),
18696
+ memberCount: number().int(),
18697
+ memberTrackIds: array(string()).readonly(),
18698
+ className: string(),
18699
+ classes: array(string()).readonly(),
18700
+ /** Relative event-media path, or null when the group has no picture yet. */
18701
+ mediaUrl: string().nullable(),
18702
+ singleton: boolean()
18703
+ });
18704
+ var AnalyticsGroupMemberSchema = object({
18705
+ trackId: string(),
18706
+ deviceId: number().int(),
18707
+ className: string(),
18708
+ firstSeen: number().int(),
18709
+ lastSeen: number().int(),
18710
+ mediaUrl: string().nullable()
18711
+ });
18712
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
18713
+ var ListGroupsQueryInput = object({
18714
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
18715
+ deviceIds: array(number()),
18716
+ /** Window lower bound on `closedAt` (inclusive). */
18717
+ since: number().optional(),
18718
+ /** Window upper bound on `openedAt` (inclusive). */
18719
+ until: number().optional(),
18720
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
18721
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
18722
+ cursor: string().optional()
18723
+ });
18724
+ var ListGroupsPageSchema = object({
18725
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
18726
+ nextCursor: string().nullable()
18727
+ });
18663
18728
  var KeyEventQueryInput = object({
18664
18729
  deviceId: number(),
18665
18730
  /** Window lower bound (track firstSeen ≥ since). */
@@ -18883,7 +18948,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18883
18948
  * stationary registry). Default false: the timeline lists passages,
18884
18949
  * not parking records (operator decision, 2026-08-15). */
18885
18950
  includeStationary: boolean().optional()
18886
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18951
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
18952
+ deviceId: number(),
18953
+ groupId: string().min(1)
18954
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
18887
18955
  kind: "mutation",
18888
18956
  auth: "admin"
18889
18957
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -19193,7 +19261,8 @@ var PipelineModelOptionSchema = object({
19193
19261
  sizeMB: number()
19194
19262
  })),
19195
19263
  group: ModelVariantGroupSchema.optional(),
19196
- legacy: boolean().optional()
19264
+ legacy: boolean().optional(),
19265
+ provider: ModelProviderIdSchema.optional()
19197
19266
  });
19198
19267
  var ConfigFieldBridge = custom();
19199
19268
  var PipelineAddonSchemaSchema = object({
@@ -31875,6 +31944,12 @@ Object.freeze({
31875
31944
  addonId: null,
31876
31945
  access: "view"
31877
31946
  },
31947
+ "pipelineAnalytics.getGroup": {
31948
+ capName: "pipeline-analytics",
31949
+ capScope: "device",
31950
+ addonId: null,
31951
+ access: "view"
31952
+ },
31878
31953
  "pipelineAnalytics.getKeyEvents": {
31879
31954
  capName: "pipeline-analytics",
31880
31955
  capScope: "device",
@@ -31959,6 +32034,12 @@ Object.freeze({
31959
32034
  addonId: null,
31960
32035
  access: "view"
31961
32036
  },
32037
+ "pipelineAnalytics.listGroups": {
32038
+ capName: "pipeline-analytics",
32039
+ capScope: "device",
32040
+ addonId: null,
32041
+ access: "view"
32042
+ },
31962
32043
  "pipelineAnalytics.listOpsLog": {
31963
32044
  capName: "pipeline-analytics",
31964
32045
  capScope: "device",
@@ -35378,6 +35459,11 @@ Object.freeze({
35378
35459
  form: "single",
35379
35460
  optional: false
35380
35461
  }],
35462
+ "pipelineAnalytics.getGroup": [{
35463
+ name: "deviceId",
35464
+ form: "single",
35465
+ optional: false
35466
+ }],
35381
35467
  "pipelineAnalytics.getKeyEvents": [{
35382
35468
  name: "deviceId",
35383
35469
  form: "single",
@@ -35433,6 +35519,11 @@ Object.freeze({
35433
35519
  form: "array",
35434
35520
  optional: false
35435
35521
  }],
35522
+ "pipelineAnalytics.listGroups": [{
35523
+ name: "deviceIds",
35524
+ form: "array",
35525
+ optional: false
35526
+ }],
35436
35527
  "pipelineAnalytics.listOpsLog": [{
35437
35528
  name: "deviceId",
35438
35529
  form: "single",
@@ -38103,6 +38194,12 @@ Object.defineProperty(exports, "hydrateSchema", {
38103
38194
  return hydrateSchema;
38104
38195
  }
38105
38196
  });
38197
+ Object.defineProperty(exports, "inferModelProvider", {
38198
+ enumerable: true,
38199
+ get: function() {
38200
+ return inferModelProvider;
38201
+ }
38202
+ });
38106
38203
  Object.defineProperty(exports, "invocationFromEncodeProfile", {
38107
38204
  enumerable: true,
38108
38205
  get: function() {