@camstack/addon-pipeline 1.2.12 → 1.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/audio-analyzer/index.js +1 -1
  2. package/dist/audio-analyzer/index.mjs +1 -1
  3. package/dist/detection-pipeline/index.js +2 -2
  4. package/dist/detection-pipeline/index.mjs +2 -2
  5. package/dist/{dist-B89zyHyl.js → dist-BUd2xSjP.js} +79 -0
  6. package/dist/{dist-DMiEnBkk.mjs → dist-DuI0Ypuu.mjs} +79 -0
  7. package/dist/{fmp4-keyframes-BzTPoPVn.js → fmp4-keyframes-5s8T9Mng.js} +42 -7
  8. package/dist/{fmp4-keyframes-qOgJBjMO.mjs → fmp4-keyframes-CV0X1mGy.mjs} +37 -12
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/pipeline-runner/index.js +2 -2
  12. package/dist/pipeline-runner/index.mjs +2 -2
  13. package/dist/recorder/index.js +36 -8
  14. package/dist/recorder/index.mjs +36 -8
  15. package/dist/{step-definitions-DLgHGq8f.js → step-definitions-Ck4_6GKK.js} +1 -1
  16. package/dist/{step-definitions-NeFJ6mFi.mjs → step-definitions-DqNWVeYB.mjs} +1 -1
  17. package/dist/stream-broker/_stub.js +2 -2
  18. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CAvWbzaf.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-5EJfCCjk.mjs} +2 -2
  19. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DbC3MMjB.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BDeEvhV5.mjs} +1 -1
  20. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js--zEZtV2N.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BGtpZkgz.mjs} +1 -1
  21. package/dist/stream-broker/{hostInit-Do62nL9c.mjs → hostInit-QaJMh7Ec.mjs} +2 -2
  22. package/dist/stream-broker/index.js +1186 -337
  23. package/dist/stream-broker/index.mjs +1187 -338
  24. package/dist/stream-broker/remoteEntry.js +1 -1
  25. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-CNyCTsIC.js → MaskShapeCanvas-DI4BY7W2-Di-nvxaR.js} +1 -1
  26. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-DOdX9Yp1.js → MotionZonesSettings-NcxxQN8r-C6Nj76Lm.js} +1 -1
  27. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-9huXsgpT.js → PrivacyMaskSettings-APgPLF7p-DwVoJunF.js} +1 -1
  28. package/embed-dist/assets/{index-BxOCtm8E.js → index-CtDVz0vI.js} +15 -15
  29. package/embed-dist/index.html +1 -1
  30. package/package.json +1 -1
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-B89zyHyl.js");
6
+ const require_dist = require("../dist-BUd2xSjP.js");
7
7
  const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
8
8
  const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-fsdDExML.js");
9
9
  let node_fs = require("node:fs");
@@ -1,5 +1,5 @@
1
1
  import { i as downloadFile, l as __require } from "../model-download-service-Cp9f4dk6-WO-foD7W.mjs";
2
- import { D as mapAudioLabelToMacro, H as BaseAddon, V as errMsg, _ as audioAnalysisCapability, d as HF_BASE_URL, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, q as hydrateSchema, v as audioAnalyzerCapability } from "../dist-DMiEnBkk.mjs";
2
+ import { D as mapAudioLabelToMacro, H as BaseAddon, V as errMsg, _ as audioAnalysisCapability, d as HF_BASE_URL, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, q as hydrateSchema, v as audioAnalyzerCapability } from "../dist-DuI0Ypuu.mjs";
3
3
  import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
4
4
  import * as fs from "node:fs";
5
5
  import * as path$1 from "node:path";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-B89zyHyl.js");
7
- const require_step_definitions = require("../step-definitions-DLgHGq8f.js");
6
+ const require_dist = require("../dist-BUd2xSjP.js");
7
+ const require_step_definitions = require("../step-definitions-Ck4_6GKK.js");
8
8
  const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
9
9
  const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-fsdDExML.js");
10
10
  let node_child_process = require("node:child_process");
@@ -1,6 +1,6 @@
1
1
  import { a as ensureModel, l as __require, o as isModelDownloaded, r as deleteModelFromDisk } from "../model-download-service-Cp9f4dk6-WO-foD7W.mjs";
2
- import { C as detectionPipelineCapability, H as BaseAddon, I as runtimeDevices$1, K as createEvent, M as pipelineExecutorCapability, T as evaluateZoneRules, V as errMsg, Z as parseJsonUnknown, ct as object, dt as union, et as sleep, ft as EventCategory, h as YAMNET_TO_MACRO, j as nodePin, nt as array, q as hydrateSchema, s as DEVICE_BACKEND_TO_FORMAT, t as APPLE_SA_TO_MACRO, ut as string, w as enumerateInferenceDevices, x as defaultDeviceFor$1, z as supportedRuntimes$1 } from "../dist-DMiEnBkk.mjs";
3
- import { a as getStepDefinition, i as getStep, n as ALL_STEPS, o as resolveModelForFormat, r as getDefaultModelForFormat, t as ALL_PIPELINE_STEPS } from "../step-definitions-NeFJ6mFi.mjs";
2
+ import { C as detectionPipelineCapability, H as BaseAddon, I as runtimeDevices$1, K as createEvent, M as pipelineExecutorCapability, T as evaluateZoneRules, V as errMsg, Z as parseJsonUnknown, ct as object, dt as union, et as sleep, ft as EventCategory, h as YAMNET_TO_MACRO, j as nodePin, nt as array, q as hydrateSchema, s as DEVICE_BACKEND_TO_FORMAT, t as APPLE_SA_TO_MACRO, ut as string, w as enumerateInferenceDevices, x as defaultDeviceFor$1, z as supportedRuntimes$1 } from "../dist-DuI0Ypuu.mjs";
3
+ import { a as getStepDefinition, i as getStep, n as ALL_STEPS, o as resolveModelForFormat, r as getDefaultModelForFormat, t as ALL_PIPELINE_STEPS } from "../step-definitions-DqNWVeYB.mjs";
4
4
  import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
5
5
  import { spawn } from "node:child_process";
6
6
  import sharp from "sharp";
@@ -15542,6 +15542,55 @@ method(object({
15542
15542
  password: string()
15543
15543
  }), AuthResultSchema.nullable(), { kind: "mutation" }), method(object({ state: string() }), string()), method(record(string(), string()), AuthResultSchema, { kind: "mutation" }), method(object({ token: string() }), AuthResultSchema.nullable());
15544
15544
  /**
15545
+ * A live terminal session hosted by the provider addon. Output and input do
15546
+ * NOT flow through the capability — they use the addon data plane
15547
+ * (`GET /addon/terminal/<id>/out` SSE, `POST /addon/terminal/<id>/in`) because
15548
+ * terminal output must be ordered and lossless. The event bus is telemetry and
15549
+ * may drop chunks ([D8]), and a dropped chunk desynchronises the vt parser
15550
+ * permanently until a full repaint. The capability owns only lifecycle.
15551
+ */
15552
+ var TerminalSessionInfoSchema = object({
15553
+ /** Opaque session id minted by the provider on `openSession`. */
15554
+ sessionId: string(),
15555
+ /** The pre-declared profile this session runs (never a free-form command). */
15556
+ profileId: string(),
15557
+ /** Human-readable profile label for the UI session list. */
15558
+ label: string(),
15559
+ cols: number().int().positive(),
15560
+ rows: number().int().positive(),
15561
+ /** ms-epoch the session's pty was spawned. */
15562
+ startedAt: number()
15563
+ });
15564
+ /**
15565
+ * A profile the operator may open — a pre-declared, allowlisted program
15566
+ * (`monitor` → `btm`). The capability accepts only these ids; a free-form
15567
+ * command string would be remote code execution as the server's user, so it is
15568
+ * deliberately not part of the contract.
15569
+ */
15570
+ var TerminalProfileInfoSchema = object({
15571
+ profileId: string(),
15572
+ label: string(),
15573
+ description: string().optional()
15574
+ });
15575
+ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
15576
+ profileId: string(),
15577
+ cols: number().int().positive(),
15578
+ rows: number().int().positive()
15579
+ }), TerminalSessionInfoSchema, {
15580
+ kind: "mutation",
15581
+ auth: "admin"
15582
+ }), method(object({
15583
+ sessionId: string(),
15584
+ cols: number().int().positive(),
15585
+ rows: number().int().positive()
15586
+ }), _void(), {
15587
+ kind: "mutation",
15588
+ auth: "admin"
15589
+ }), method(object({ sessionId: string() }), _void(), {
15590
+ kind: "mutation",
15591
+ auth: "admin"
15592
+ });
15593
+ /**
15545
15594
  * Orchestrator-side destination metadata. The orchestrator computes
15546
15595
  * `id = <addonId>:<subId>` from its provider lookup so consumers
15547
15596
  * (admin UI, restore flow) see one canonical key.
@@ -26916,6 +26965,36 @@ Object.freeze({
26916
26965
  addonId: null,
26917
26966
  access: "create"
26918
26967
  },
26968
+ "terminalSession.close": {
26969
+ capName: "terminal-session",
26970
+ capScope: "system",
26971
+ addonId: null,
26972
+ access: "create"
26973
+ },
26974
+ "terminalSession.listProfiles": {
26975
+ capName: "terminal-session",
26976
+ capScope: "system",
26977
+ addonId: null,
26978
+ access: "view"
26979
+ },
26980
+ "terminalSession.listSessions": {
26981
+ capName: "terminal-session",
26982
+ capScope: "system",
26983
+ addonId: null,
26984
+ access: "view"
26985
+ },
26986
+ "terminalSession.openSession": {
26987
+ capName: "terminal-session",
26988
+ capScope: "system",
26989
+ addonId: null,
26990
+ access: "create"
26991
+ },
26992
+ "terminalSession.resize": {
26993
+ capName: "terminal-session",
26994
+ capScope: "system",
26995
+ addonId: null,
26996
+ access: "create"
26997
+ },
26919
26998
  "toast.onToast": {
26920
26999
  capName: "toast",
26921
27000
  capScope: "system",
@@ -15542,6 +15542,55 @@ method(object({
15542
15542
  password: string()
15543
15543
  }), AuthResultSchema.nullable(), { kind: "mutation" }), method(object({ state: string() }), string()), method(record(string(), string()), AuthResultSchema, { kind: "mutation" }), method(object({ token: string() }), AuthResultSchema.nullable());
15544
15544
  /**
15545
+ * A live terminal session hosted by the provider addon. Output and input do
15546
+ * NOT flow through the capability — they use the addon data plane
15547
+ * (`GET /addon/terminal/<id>/out` SSE, `POST /addon/terminal/<id>/in`) because
15548
+ * terminal output must be ordered and lossless. The event bus is telemetry and
15549
+ * may drop chunks ([D8]), and a dropped chunk desynchronises the vt parser
15550
+ * permanently until a full repaint. The capability owns only lifecycle.
15551
+ */
15552
+ var TerminalSessionInfoSchema = object({
15553
+ /** Opaque session id minted by the provider on `openSession`. */
15554
+ sessionId: string(),
15555
+ /** The pre-declared profile this session runs (never a free-form command). */
15556
+ profileId: string(),
15557
+ /** Human-readable profile label for the UI session list. */
15558
+ label: string(),
15559
+ cols: number().int().positive(),
15560
+ rows: number().int().positive(),
15561
+ /** ms-epoch the session's pty was spawned. */
15562
+ startedAt: number()
15563
+ });
15564
+ /**
15565
+ * A profile the operator may open — a pre-declared, allowlisted program
15566
+ * (`monitor` → `btm`). The capability accepts only these ids; a free-form
15567
+ * command string would be remote code execution as the server's user, so it is
15568
+ * deliberately not part of the contract.
15569
+ */
15570
+ var TerminalProfileInfoSchema = object({
15571
+ profileId: string(),
15572
+ label: string(),
15573
+ description: string().optional()
15574
+ });
15575
+ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
15576
+ profileId: string(),
15577
+ cols: number().int().positive(),
15578
+ rows: number().int().positive()
15579
+ }), TerminalSessionInfoSchema, {
15580
+ kind: "mutation",
15581
+ auth: "admin"
15582
+ }), method(object({
15583
+ sessionId: string(),
15584
+ cols: number().int().positive(),
15585
+ rows: number().int().positive()
15586
+ }), _void(), {
15587
+ kind: "mutation",
15588
+ auth: "admin"
15589
+ }), method(object({ sessionId: string() }), _void(), {
15590
+ kind: "mutation",
15591
+ auth: "admin"
15592
+ });
15593
+ /**
15545
15594
  * Orchestrator-side destination metadata. The orchestrator computes
15546
15595
  * `id = <addonId>:<subId>` from its provider lookup so consumers
15547
15596
  * (admin UI, restore flow) see one canonical key.
@@ -26916,6 +26965,36 @@ Object.freeze({
26916
26965
  addonId: null,
26917
26966
  access: "create"
26918
26967
  },
26968
+ "terminalSession.close": {
26969
+ capName: "terminal-session",
26970
+ capScope: "system",
26971
+ addonId: null,
26972
+ access: "create"
26973
+ },
26974
+ "terminalSession.listProfiles": {
26975
+ capName: "terminal-session",
26976
+ capScope: "system",
26977
+ addonId: null,
26978
+ access: "view"
26979
+ },
26980
+ "terminalSession.listSessions": {
26981
+ capName: "terminal-session",
26982
+ capScope: "system",
26983
+ addonId: null,
26984
+ access: "view"
26985
+ },
26986
+ "terminalSession.openSession": {
26987
+ capName: "terminal-session",
26988
+ capScope: "system",
26989
+ addonId: null,
26990
+ access: "create"
26991
+ },
26992
+ "terminalSession.resize": {
26993
+ capName: "terminal-session",
26994
+ capScope: "system",
26995
+ addonId: null,
26996
+ access: "create"
26997
+ },
26919
26998
  "toast.onToast": {
26920
26999
  capName: "toast",
26921
27000
  capScope: "system",
@@ -6631,12 +6631,16 @@ function readTrack(file, segment) {
6631
6631
  });
6632
6632
  }
6633
6633
  /**
6634
- * Demux a recorded LOW `.m4s` segment and return its sync samples plus the
6635
- * WebCodecs decoder config, or null if the segment carries no video / no
6636
- * decodable keyframe. `segmentStartMs` anchors keyframe timestamps to absolute
6637
- * epoch ms; the first sample's cts is the zero reference.
6634
+ * Demux a recorded `.m4s` fragment and return ALL its samples (sync + delta) in
6635
+ * decode order plus the WebCodecs decoder config, or null if the segment
6636
+ * carries no video / no decodable sample. `segmentStartMs` anchors each sample's
6637
+ * `ts` to absolute epoch ms; the first sample's cts is the zero reference.
6638
+ *
6639
+ * No decode, no ffmpeg — byte copies only. This is the general form used by the
6640
+ * recorded-playback demux; {@link extractKeyframes} filters its output to sync
6641
+ * samples for the scrub-pack service.
6638
6642
  */
6639
- async function extractKeyframes(segment, segmentStartMs) {
6643
+ async function extractSamples(segment, segmentStartMs) {
6640
6644
  const file = import_mp4box_all.createFile();
6641
6645
  const ready = await readTrack(file, segment);
6642
6646
  if (!ready) return null;
@@ -6647,10 +6651,10 @@ async function extractKeyframes(segment, segmentStartMs) {
6647
6651
  file.onSamples = (_id, _user, samples) => {
6648
6652
  for (const s of samples) {
6649
6653
  if (firstCts === null) firstCts = s.cts;
6650
- if (!s.is_sync) continue;
6651
6654
  const ts = segmentStartMs + Math.round((s.cts - firstCts) / s.timescale * 1e3);
6652
6655
  collected.push({
6653
6656
  ts,
6657
+ isSync: s.is_sync,
6654
6658
  data: new Uint8Array(s.data)
6655
6659
  });
6656
6660
  }
@@ -6664,7 +6668,26 @@ async function extractKeyframes(segment, segmentStartMs) {
6664
6668
  return collected.length > 0 ? {
6665
6669
  codec,
6666
6670
  description,
6667
- keyframes: collected
6671
+ samples: collected
6672
+ } : null;
6673
+ }
6674
+ /**
6675
+ * Demux a recorded LOW `.m4s` segment and return its sync samples plus the
6676
+ * WebCodecs decoder config, or null if the segment carries no video / no
6677
+ * decodable keyframe. `segmentStartMs` anchors keyframe timestamps to absolute
6678
+ * epoch ms; the first sample's cts is the zero reference.
6679
+ */
6680
+ async function extractKeyframes(segment, segmentStartMs) {
6681
+ const extracted = await extractSamples(segment, segmentStartMs);
6682
+ if (!extracted) return null;
6683
+ const keyframes = extracted.samples.filter((s) => s.isSync).map((s) => ({
6684
+ ts: s.ts,
6685
+ data: s.data
6686
+ }));
6687
+ return keyframes.length > 0 ? {
6688
+ codec: extracted.codec,
6689
+ description: extracted.description,
6690
+ keyframes
6668
6691
  } : null;
6669
6692
  }
6670
6693
  //#endregion
@@ -6674,3 +6697,15 @@ Object.defineProperty(exports, "extractKeyframes", {
6674
6697
  return extractKeyframes;
6675
6698
  }
6676
6699
  });
6700
+ Object.defineProperty(exports, "extractSamples", {
6701
+ enumerable: true,
6702
+ get: function() {
6703
+ return extractSamples;
6704
+ }
6705
+ });
6706
+ Object.defineProperty(exports, "require_mp4box_all", {
6707
+ enumerable: true,
6708
+ get: function() {
6709
+ return require_mp4box_all;
6710
+ }
6711
+ });
@@ -1,7 +1,6 @@
1
1
  import { c as __commonJSMin, u as __toESM } from "./model-download-service-Cp9f4dk6-WO-foD7W.mjs";
2
- //#endregion
3
- //#region src/recorder/scrub/fmp4-keyframes.ts
4
- var import_mp4box_all = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports) => {
2
+ //#region ../../node_modules/mp4box/dist/mp4box.all.js
3
+ var require_mp4box_all = /* @__PURE__ */ __commonJSMin(((exports) => {
5
4
  var Log = (function() {
6
5
  var start = /* @__PURE__ */ new Date();
7
6
  var LOG_LEVEL_ERROR = 4;
@@ -6566,7 +6565,10 @@ var import_mp4box_all = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin((
6566
6565
  return file;
6567
6566
  };
6568
6567
  if (typeof exports !== "undefined") exports.createFile = MP4Box.createFile;
6569
- })))());
6568
+ }));
6569
+ //#endregion
6570
+ //#region src/recorder/scrub/fmp4-keyframes.ts
6571
+ var import_mp4box_all = /* @__PURE__ */ __toESM(require_mp4box_all());
6570
6572
  /**
6571
6573
  * Normalize an RFC 6381 codec string for WebCodecs HEVC. Apple/WebKit
6572
6574
  * WebCodecs only accepts the `hvc1.*` fourcc — a `hev1.*` string (what mp4box
@@ -6629,12 +6631,16 @@ function readTrack(file, segment) {
6629
6631
  });
6630
6632
  }
6631
6633
  /**
6632
- * Demux a recorded LOW `.m4s` segment and return its sync samples plus the
6633
- * WebCodecs decoder config, or null if the segment carries no video / no
6634
- * decodable keyframe. `segmentStartMs` anchors keyframe timestamps to absolute
6635
- * epoch ms; the first sample's cts is the zero reference.
6634
+ * Demux a recorded `.m4s` fragment and return ALL its samples (sync + delta) in
6635
+ * decode order plus the WebCodecs decoder config, or null if the segment
6636
+ * carries no video / no decodable sample. `segmentStartMs` anchors each sample's
6637
+ * `ts` to absolute epoch ms; the first sample's cts is the zero reference.
6638
+ *
6639
+ * No decode, no ffmpeg — byte copies only. This is the general form used by the
6640
+ * recorded-playback demux; {@link extractKeyframes} filters its output to sync
6641
+ * samples for the scrub-pack service.
6636
6642
  */
6637
- async function extractKeyframes(segment, segmentStartMs) {
6643
+ async function extractSamples(segment, segmentStartMs) {
6638
6644
  const file = import_mp4box_all.createFile();
6639
6645
  const ready = await readTrack(file, segment);
6640
6646
  if (!ready) return null;
@@ -6645,10 +6651,10 @@ async function extractKeyframes(segment, segmentStartMs) {
6645
6651
  file.onSamples = (_id, _user, samples) => {
6646
6652
  for (const s of samples) {
6647
6653
  if (firstCts === null) firstCts = s.cts;
6648
- if (!s.is_sync) continue;
6649
6654
  const ts = segmentStartMs + Math.round((s.cts - firstCts) / s.timescale * 1e3);
6650
6655
  collected.push({
6651
6656
  ts,
6657
+ isSync: s.is_sync,
6652
6658
  data: new Uint8Array(s.data)
6653
6659
  });
6654
6660
  }
@@ -6662,8 +6668,27 @@ async function extractKeyframes(segment, segmentStartMs) {
6662
6668
  return collected.length > 0 ? {
6663
6669
  codec,
6664
6670
  description,
6665
- keyframes: collected
6671
+ samples: collected
6672
+ } : null;
6673
+ }
6674
+ /**
6675
+ * Demux a recorded LOW `.m4s` segment and return its sync samples plus the
6676
+ * WebCodecs decoder config, or null if the segment carries no video / no
6677
+ * decodable keyframe. `segmentStartMs` anchors keyframe timestamps to absolute
6678
+ * epoch ms; the first sample's cts is the zero reference.
6679
+ */
6680
+ async function extractKeyframes(segment, segmentStartMs) {
6681
+ const extracted = await extractSamples(segment, segmentStartMs);
6682
+ if (!extracted) return null;
6683
+ const keyframes = extracted.samples.filter((s) => s.isSync).map((s) => ({
6684
+ ts: s.ts,
6685
+ data: s.data
6686
+ }));
6687
+ return keyframes.length > 0 ? {
6688
+ codec: extracted.codec,
6689
+ description: extracted.description,
6690
+ keyframes
6666
6691
  } : null;
6667
6692
  }
6668
6693
  //#endregion
6669
- export { extractKeyframes as t };
6694
+ export { extractSamples as n, require_mp4box_all as r, extractKeyframes as t };
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-B89zyHyl.js");
5
+ const require_dist = require("../dist-BUd2xSjP.js");
6
6
  let node_fs = require("node:fs");
7
7
  let node_path = require("node:path");
8
8
  //#region src/motion-wasm/wasm-motion-detector.ts
@@ -1,4 +1,4 @@
1
- import { A as motionDetectionCapability, G as DeviceType, H as BaseAddon, T as evaluateZoneRules, q as hydrateSchema } from "../dist-DMiEnBkk.mjs";
1
+ import { A as motionDetectionCapability, G as DeviceType, H as BaseAddon, T as evaluateZoneRules, q as hydrateSchema } from "../dist-DuI0Ypuu.mjs";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  //#region src/motion-wasm/wasm-motion-detector.ts
@@ -3,11 +3,11 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-B89zyHyl.js");
6
+ const require_dist = require("../dist-BUd2xSjP.js");
7
7
  const require_remote_restream = require("../remote-restream-CO36Sr30.js");
8
8
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
9
9
  const require_worker_protocol = require("../worker-protocol-DextwlTX.js");
10
- const require_step_definitions = require("../step-definitions-DLgHGq8f.js");
10
+ const require_step_definitions = require("../step-definitions-Ck4_6GKK.js");
11
11
  let node_child_process = require("node:child_process");
12
12
  let node_url = require("node:url");
13
13
  let sharp = require("sharp");
@@ -1,8 +1,8 @@
1
- import { H as BaseAddon, K as createEvent, N as pipelineRunnerCapability, S as defineCustomActions, V as errMsg, at as lazy, b as customAction, ct as object, ft as EventCategory, j as nodePin, nt as array, rt as boolean, s as DEVICE_BACKEND_TO_FORMAT, st as number, tt as _enum, ut as string } from "../dist-DMiEnBkk.mjs";
1
+ import { H as BaseAddon, K as createEvent, N as pipelineRunnerCapability, S as defineCustomActions, V as errMsg, at as lazy, b as customAction, ct as object, ft as EventCategory, j as nodePin, nt as array, rt as boolean, s as DEVICE_BACKEND_TO_FORMAT, st as number, tt as _enum, ut as string } from "../dist-DuI0Ypuu.mjs";
2
2
  import { n as profileForStreamId, t as isRemoteRestream } from "../remote-restream-BeHi78PZ.mjs";
3
3
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
4
4
  import { t as isWorkerReply } from "../worker-protocol-D7RzZIla.mjs";
5
- import { a as getStepDefinition } from "../step-definitions-NeFJ6mFi.mjs";
5
+ import { a as getStepDefinition } from "../step-definitions-DqNWVeYB.mjs";
6
6
  import { fork } from "node:child_process";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import sharp from "sharp";
@@ -1,8 +1,8 @@
1
1
  const require_chunk = require("../chunk-emK7D4bc.js");
2
- const require_dist = require("../dist-B89zyHyl.js");
2
+ const require_dist = require("../dist-BUd2xSjP.js");
3
3
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
4
4
  const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-fsdDExML.js");
5
- const require_fmp4_keyframes = require("../fmp4-keyframes-BzTPoPVn.js");
5
+ const require_fmp4_keyframes = require("../fmp4-keyframes-5s8T9Mng.js");
6
6
  let node_child_process = require("node:child_process");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -2310,7 +2310,7 @@ function startSegmentWatcher(deps) {
2310
2310
  }
2311
2311
  //#endregion
2312
2312
  //#region src/recorder/addon/ffmpeg-args.ts
2313
- /** ffmpeg argv: pull RTSP (TCP), copy video / transcode audio to AAC, write
2313
+ /** ffmpeg argv: pull RTSP (TCP), copy video / transcode audio to Opus, write
2314
2314
  * fragmented-mp4 segments named by epoch seconds FLAT under `outDir`, plus a
2315
2315
  * rolling `live.m3u8` playlist. The recorder segment-watcher tails the
2316
2316
  * playlist and hands each finalized flat file to `SegmentStore.onFinalized`,
@@ -2329,7 +2329,9 @@ function buildPassthroughArgs(a) {
2329
2329
  "copy",
2330
2330
  ...a.videoCodec === "H265" ? ["-tag:v", "hvc1"] : [],
2331
2331
  "-c:a",
2332
- "aac",
2332
+ "libopus",
2333
+ "-b:a",
2334
+ "48k",
2333
2335
  "-f",
2334
2336
  "segment",
2335
2337
  "-segment_time",
@@ -3017,8 +3019,12 @@ var ScrubPackService = class {
3017
3019
  this.inflight.set(key, run);
3018
3020
  return run;
3019
3021
  }
3022
+ resolveCacheDir() {
3023
+ const c = this.deps.cacheDir;
3024
+ return typeof c === "function" ? c() : c;
3025
+ }
3020
3026
  cachePath(deviceId, windowStartMs) {
3021
- return node_path.default.join(this.deps.cacheDir, String(deviceId), utcDay(windowStartMs), `${windowStartMs}.pack`);
3027
+ return node_path.default.join(this.resolveCacheDir(), String(deviceId), utcDay(windowStartMs), `${windowStartMs}.pack`);
3022
3028
  }
3023
3029
  async getOrBuildInner(deviceId, windowStartMs) {
3024
3030
  const now = (this.deps.now ?? Date.now)();
@@ -3095,14 +3101,15 @@ var ScrubPackService = class {
3095
3101
  /** Remove per-device day directories older than the retention horizon. */
3096
3102
  async prune(retentionDays) {
3097
3103
  const cutoff = (this.deps.now ?? Date.now)() - retentionDays * 864e5;
3104
+ const cacheDir = this.resolveCacheDir();
3098
3105
  let deviceDirs = [];
3099
3106
  try {
3100
- deviceDirs = await node_fs.promises.readdir(this.deps.cacheDir);
3107
+ deviceDirs = await node_fs.promises.readdir(cacheDir);
3101
3108
  } catch {
3102
3109
  return;
3103
3110
  }
3104
3111
  for (const dev of deviceDirs) {
3105
- const devPath = node_path.default.join(this.deps.cacheDir, dev);
3112
+ const devPath = node_path.default.join(cacheDir, dev);
3106
3113
  let days = [];
3107
3114
  try {
3108
3115
  days = await node_fs.promises.readdir(devPath);
@@ -3597,7 +3604,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
3597
3604
  const abs = this.resolveSegmentAbsPath(row);
3598
3605
  return new Uint8Array(await node_fs.promises.readFile(abs));
3599
3606
  },
3600
- cacheDir: node_path.default.join(this.ctx.dataDir, "scrub-packs"),
3607
+ cacheDir: () => this.resolveScrubCacheRoot(),
3601
3608
  logger: this.ctx.logger.child("ScrubPacks")
3602
3609
  });
3603
3610
  this.scrubPackService = scrubService;
@@ -3731,6 +3738,10 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
3731
3738
  this.ctx.logger.warn("recorder: no recordings storage locations resolved — index not hydrated");
3732
3739
  return;
3733
3740
  }
3741
+ node_fs.promises.rm(node_path.default.join(this.ctx.dataDir, "scrub-packs"), {
3742
+ recursive: true,
3743
+ force: true
3744
+ }).catch(() => {});
3734
3745
  try {
3735
3746
  await this.controller?.start();
3736
3747
  } catch (err) {
@@ -4020,6 +4031,23 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
4020
4031
  return node_path.default.join(loc.root, row.path);
4021
4032
  }
4022
4033
  /**
4034
+ * Cache root for the scrub packs — a hidden `.scrub-packs` dir UNDER a
4035
+ * recordings storage root (the `recordingsLow` location the packs derive from,
4036
+ * else the first resolved location). Packs are a regenerable keyframe cache of
4037
+ * the LOW segments; keeping them beside the recordings (a top-level mount, NOT
4038
+ * backed up) — instead of in `addons-data` — keeps the backup perimeter pure
4039
+ * operator state so it can be tarred whole with no exclusion list. See ADR-0027.
4040
+ *
4041
+ * Resolved lazily: `resolvedLocations` is only populated in `onHubReachable`,
4042
+ * after the ScrubPackService is constructed in `onInitialize`. getOrBuild/prune
4043
+ * run only at request time / on a delayed timer, by when locations are ready.
4044
+ */
4045
+ resolveScrubCacheRoot() {
4046
+ const loc = this.resolvedLocations.find((l) => l.type === "recordingsLow") ?? this.resolvedLocations[0];
4047
+ if (loc === void 0) return node_path.default.join(this.ctx.dataDir, "scrub-packs");
4048
+ return node_path.default.join(loc.root, ".scrub-packs");
4049
+ }
4050
+ /**
4023
4051
  * Scrub-pack request handler: `GET /<deviceId>/<windowStartSec>` →
4024
4052
  * `application/octet-stream` pack | 204 (no footage) | 400 (bad path / window
4025
4053
  * not aligned to `SCRUB_WINDOW_MS`) | 500. Immutable (fully past) windows carry
@@ -1,7 +1,7 @@
1
1
  import { n as createFileDataPlaneHandler, s as parseRangeHeader, t as contentTypeFor } from "../model-download-service-Cp9f4dk6-WO-foD7W.mjs";
2
- import { $ as selectAssignedProfileSlots, F as recordingExportCapability, G as DeviceType, H as BaseAddon, L as storageEvictableCapability, P as recordingCapability, V as errMsg, c as EVENT_PAD_MS, f as OpsLogEntrySchema, ft as EventCategory, j as nodePin, k as migrateConfigToBands, lt as record, nt as array, p as RecordingConfigSchema, q as hydrateSchema, u as ExportRecordSchema, ut as string } from "../dist-DMiEnBkk.mjs";
2
+ import { $ as selectAssignedProfileSlots, F as recordingExportCapability, G as DeviceType, H as BaseAddon, L as storageEvictableCapability, P as recordingCapability, V as errMsg, c as EVENT_PAD_MS, f as OpsLogEntrySchema, ft as EventCategory, j as nodePin, k as migrateConfigToBands, lt as record, nt as array, p as RecordingConfigSchema, q as hydrateSchema, u as ExportRecordSchema, ut as string } from "../dist-DuI0Ypuu.mjs";
3
3
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
4
- import { t as extractKeyframes } from "../fmp4-keyframes-qOgJBjMO.mjs";
4
+ import { t as extractKeyframes } from "../fmp4-keyframes-CV0X1mGy.mjs";
5
5
  import { spawn } from "node:child_process";
6
6
  import { createReadStream, promises, realpathSync } from "node:fs";
7
7
  import path from "node:path";
@@ -2308,7 +2308,7 @@ function startSegmentWatcher(deps) {
2308
2308
  }
2309
2309
  //#endregion
2310
2310
  //#region src/recorder/addon/ffmpeg-args.ts
2311
- /** ffmpeg argv: pull RTSP (TCP), copy video / transcode audio to AAC, write
2311
+ /** ffmpeg argv: pull RTSP (TCP), copy video / transcode audio to Opus, write
2312
2312
  * fragmented-mp4 segments named by epoch seconds FLAT under `outDir`, plus a
2313
2313
  * rolling `live.m3u8` playlist. The recorder segment-watcher tails the
2314
2314
  * playlist and hands each finalized flat file to `SegmentStore.onFinalized`,
@@ -2327,7 +2327,9 @@ function buildPassthroughArgs(a) {
2327
2327
  "copy",
2328
2328
  ...a.videoCodec === "H265" ? ["-tag:v", "hvc1"] : [],
2329
2329
  "-c:a",
2330
- "aac",
2330
+ "libopus",
2331
+ "-b:a",
2332
+ "48k",
2331
2333
  "-f",
2332
2334
  "segment",
2333
2335
  "-segment_time",
@@ -3015,8 +3017,12 @@ var ScrubPackService = class {
3015
3017
  this.inflight.set(key, run);
3016
3018
  return run;
3017
3019
  }
3020
+ resolveCacheDir() {
3021
+ const c = this.deps.cacheDir;
3022
+ return typeof c === "function" ? c() : c;
3023
+ }
3018
3024
  cachePath(deviceId, windowStartMs) {
3019
- return path.join(this.deps.cacheDir, String(deviceId), utcDay(windowStartMs), `${windowStartMs}.pack`);
3025
+ return path.join(this.resolveCacheDir(), String(deviceId), utcDay(windowStartMs), `${windowStartMs}.pack`);
3020
3026
  }
3021
3027
  async getOrBuildInner(deviceId, windowStartMs) {
3022
3028
  const now = (this.deps.now ?? Date.now)();
@@ -3093,14 +3099,15 @@ var ScrubPackService = class {
3093
3099
  /** Remove per-device day directories older than the retention horizon. */
3094
3100
  async prune(retentionDays) {
3095
3101
  const cutoff = (this.deps.now ?? Date.now)() - retentionDays * 864e5;
3102
+ const cacheDir = this.resolveCacheDir();
3096
3103
  let deviceDirs = [];
3097
3104
  try {
3098
- deviceDirs = await promises.readdir(this.deps.cacheDir);
3105
+ deviceDirs = await promises.readdir(cacheDir);
3099
3106
  } catch {
3100
3107
  return;
3101
3108
  }
3102
3109
  for (const dev of deviceDirs) {
3103
- const devPath = path.join(this.deps.cacheDir, dev);
3110
+ const devPath = path.join(cacheDir, dev);
3104
3111
  let days = [];
3105
3112
  try {
3106
3113
  days = await promises.readdir(devPath);
@@ -3595,7 +3602,7 @@ var RecorderV2Addon = class extends BaseAddon {
3595
3602
  const abs = this.resolveSegmentAbsPath(row);
3596
3603
  return new Uint8Array(await promises.readFile(abs));
3597
3604
  },
3598
- cacheDir: path.join(this.ctx.dataDir, "scrub-packs"),
3605
+ cacheDir: () => this.resolveScrubCacheRoot(),
3599
3606
  logger: this.ctx.logger.child("ScrubPacks")
3600
3607
  });
3601
3608
  this.scrubPackService = scrubService;
@@ -3729,6 +3736,10 @@ var RecorderV2Addon = class extends BaseAddon {
3729
3736
  this.ctx.logger.warn("recorder: no recordings storage locations resolved — index not hydrated");
3730
3737
  return;
3731
3738
  }
3739
+ promises.rm(path.join(this.ctx.dataDir, "scrub-packs"), {
3740
+ recursive: true,
3741
+ force: true
3742
+ }).catch(() => {});
3732
3743
  try {
3733
3744
  await this.controller?.start();
3734
3745
  } catch (err) {
@@ -4018,6 +4029,23 @@ var RecorderV2Addon = class extends BaseAddon {
4018
4029
  return path.join(loc.root, row.path);
4019
4030
  }
4020
4031
  /**
4032
+ * Cache root for the scrub packs — a hidden `.scrub-packs` dir UNDER a
4033
+ * recordings storage root (the `recordingsLow` location the packs derive from,
4034
+ * else the first resolved location). Packs are a regenerable keyframe cache of
4035
+ * the LOW segments; keeping them beside the recordings (a top-level mount, NOT
4036
+ * backed up) — instead of in `addons-data` — keeps the backup perimeter pure
4037
+ * operator state so it can be tarred whole with no exclusion list. See ADR-0027.
4038
+ *
4039
+ * Resolved lazily: `resolvedLocations` is only populated in `onHubReachable`,
4040
+ * after the ScrubPackService is constructed in `onInitialize`. getOrBuild/prune
4041
+ * run only at request time / on a delayed timer, by when locations are ready.
4042
+ */
4043
+ resolveScrubCacheRoot() {
4044
+ const loc = this.resolvedLocations.find((l) => l.type === "recordingsLow") ?? this.resolvedLocations[0];
4045
+ if (loc === void 0) return path.join(this.ctx.dataDir, "scrub-packs");
4046
+ return path.join(loc.root, ".scrub-packs");
4047
+ }
4048
+ /**
4021
4049
  * Scrub-pack request handler: `GET /<deviceId>/<windowStartSec>` →
4022
4050
  * `application/octet-stream` pack | 204 (no footage) | 400 (bad path / window
4023
4051
  * not aligned to `SCRUB_WINDOW_MS`) | 500. Immutable (fully past) windows carry
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-B89zyHyl.js");
1
+ const require_dist = require("./dist-BUd2xSjP.js");
2
2
  //#region src/detection-pipeline/registry/model-catalogs.ts
3
3
  var HF_REPO = "camstack/camstack-models";
4
4
  var HF_SCRYPTED = "scrypted/plugin-models";
@@ -1,4 +1,4 @@
1
- import { E as hfModelUrl, a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS } from "./dist-DMiEnBkk.mjs";
1
+ import { E as hfModelUrl, a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS } from "./dist-DuI0Ypuu.mjs";
2
2
  //#region src/detection-pipeline/registry/model-catalogs.ts
3
3
  var HF_REPO = "camstack/camstack-models";
4
4
  var HF_SCRYPTED = "scrypted/plugin-models";