@camstack/addon-pipeline 1.2.79 → 1.2.80

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/{addon-utils-BZQxAJ1G.js → addon-utils-C4Cge0S0.js} +1 -1
  2. package/dist/audio-analyzer/index.js +2 -2
  3. package/dist/audio-analyzer/index.mjs +1 -1
  4. package/dist/detection-pipeline/index.js +4 -4
  5. package/dist/detection-pipeline/index.mjs +2 -2
  6. package/dist/{dist-BfTf6doa.mjs → dist-Bf1SE1Ew.mjs} +133 -3
  7. package/dist/{dist-BUIpqu3E.js → dist-ZSiUDGzf.js} +133 -3
  8. package/dist/{event-loop-stall-monitor-CmrHj0i9.mjs → event-loop-stall-monitor-CjWfos2b.mjs} +1 -1
  9. package/dist/{event-loop-stall-monitor-CGLQP0by.js → event-loop-stall-monitor-VcDGoua3.js} +1 -1
  10. package/dist/{lazy-sharp-BZVhL9Lw.js → lazy-sharp-CC4WNiKY.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 +4 -4
  14. package/dist/pipeline-runner/index.mjs +3 -3
  15. package/dist/recorder/index.js +62 -5
  16. package/dist/recorder/index.mjs +61 -4
  17. package/dist/session-decode/decode-worker-child.js +2 -2
  18. package/dist/session-decode/decode-worker-child.mjs +1 -1
  19. package/dist/stream-broker/_stub.js +2 -2
  20. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BohtrVou.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B1oVjQLS.mjs} +2 -2
  21. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BjDv7QMq.mjs +26 -0
  22. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dxr14lIu.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DEps-9qh.mjs} +1 -1
  23. package/dist/stream-broker/{hostInit-pDANS0x0.mjs → hostInit-D_m-NYyr.mjs} +2 -2
  24. package/dist/stream-broker/index.js +1 -1
  25. package/dist/stream-broker/index.mjs +1 -1
  26. package/dist/stream-broker/remoteEntry.js +1 -1
  27. package/dist/{worker-protocol-BaLcYcOo.js → worker-protocol-KDfFzKgT.js} +1 -1
  28. package/dist/{worker-protocol-BdOv9Ujl.mjs → worker-protocol-p66oO1xL.mjs} +1 -1
  29. package/package.json +1 -1
  30. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-y8ehJPoY.mjs +0 -26
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-BUIpqu3E.js");
1
+ const require_dist = require("./dist-ZSiUDGzf.js");
2
2
  require("node:crypto");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_dist.__toESM(node_path, 1);
@@ -2,9 +2,9 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BUIpqu3E.js");
5
+ const require_dist = require("../dist-ZSiUDGzf.js");
6
6
  const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
7
- const require_addon_utils = require("../addon-utils-BZQxAJ1G.js");
7
+ const require_addon_utils = require("../addon-utils-C4Cge0S0.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_dist.__toESM(node_path);
10
10
  let node_fs = require("node:fs");
@@ -1,5 +1,5 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { B as mapAudioLabelToMacro, E as audioAnalyzerCapability, T as audioAnalysisCapability, ft as hydrateSchema, m as HF_BASE_URL, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, st as BaseAddon, tt as errMsg } from "../dist-BfTf6doa.mjs";
2
+ import { B as mapAudioLabelToMacro, E as audioAnalyzerCapability, T as audioAnalysisCapability, ft as hydrateSchema, m as HF_BASE_URL, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, st as BaseAddon, tt as errMsg } from "../dist-Bf1SE1Ew.mjs";
3
3
  import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
4
4
  import { i as downloadFile } from "../addon-utils-A2S9D7pu.mjs";
5
5
  import * as path$1 from "node:path";
@@ -2,11 +2,11 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BUIpqu3E.js");
6
- const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-CGLQP0by.js");
7
- const require_lazy_sharp = require("../lazy-sharp-BZVhL9Lw.js");
5
+ const require_dist = require("../dist-ZSiUDGzf.js");
6
+ const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-VcDGoua3.js");
7
+ const require_lazy_sharp = require("../lazy-sharp-CC4WNiKY.js");
8
8
  const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
9
- const require_addon_utils = require("../addon-utils-BZQxAJ1G.js");
9
+ const require_addon_utils = require("../addon-utils-C4Cge0S0.js");
10
10
  let sharp = require("sharp");
11
11
  sharp = require_dist.__toESM(sharp);
12
12
  let node_child_process = require("node:child_process");
@@ -1,6 +1,6 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $ as supportedRuntimes$1, A as defaultDeviceFor$1, At as string, C as YAMNET_TO_MACRO, G as pipelineExecutorCapability, L as enumerateInferenceDevices, Mt as EventCategory, Ot as object, P as detectionPipelineCapability, R as evaluateZoneRules, St as array, X as runtimeDevices$1, _t as parseJsonUnknown, bt as sleep, dt as createEvent, ft as hydrateSchema, gt as nodePin, jt as union, st as BaseAddon, t as APPLE_SA_TO_MACRO, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT } from "../dist-BfTf6doa.mjs";
3
- import { a as getDefaultModelForFormat, c as getStepDefinition, i as ALL_STEPS, l as resolveModelForFormat, n as startEventLoopStallMonitor, o as getDefaultModelForFormatFromDef, r as ALL_PIPELINE_STEPS, s as getStep } from "../event-loop-stall-monitor-CmrHj0i9.mjs";
2
+ import { $ as supportedRuntimes$1, A as defaultDeviceFor$1, At as string, C as YAMNET_TO_MACRO, G as pipelineExecutorCapability, L as enumerateInferenceDevices, Mt as EventCategory, Ot as object, P as detectionPipelineCapability, R as evaluateZoneRules, St as array, X as runtimeDevices$1, _t as parseJsonUnknown, bt as sleep, dt as createEvent, ft as hydrateSchema, gt as nodePin, jt as union, st as BaseAddon, t as APPLE_SA_TO_MACRO, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT } from "../dist-Bf1SE1Ew.mjs";
3
+ import { a as getDefaultModelForFormat, c as getStepDefinition, i as ALL_STEPS, l as resolveModelForFormat, n as startEventLoopStallMonitor, o as getDefaultModelForFormatFromDef, r as ALL_PIPELINE_STEPS, s as getStep } from "../event-loop-stall-monitor-CjWfos2b.mjs";
4
4
  import { t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
5
5
  import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
6
6
  import { a as ensureModel, o as isModelDownloaded, r as deleteModelFromDisk } from "../addon-utils-A2S9D7pu.mjs";
@@ -13704,6 +13704,18 @@ var LlmGenerateBaseInputSchema = object({
13704
13704
  * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
13705
13705
  * watchdog — operator decision #3).
13706
13706
  */
13707
+ /**
13708
+ * A companion artifact that MUST land beside the main GGUF: the `mmproj`
13709
+ * projector of a vision model, or shards 2..N of a split GGUF. Carried on the
13710
+ * REF rather than looked up at install time, so what the operator approved in
13711
+ * the preview is exactly what the node downloads.
13712
+ */
13713
+ var ManagedModelExtraFileSchema = object({
13714
+ url: string(),
13715
+ filename: string(),
13716
+ sizeBytes: number(),
13717
+ sha256: string().optional()
13718
+ });
13707
13719
  var ManagedModelRefSchema = discriminatedUnion("kind", [
13708
13720
  object({
13709
13721
  kind: literal("catalog"),
@@ -13712,7 +13724,11 @@ var ManagedModelRefSchema = discriminatedUnion("kind", [
13712
13724
  object({
13713
13725
  kind: literal("url"),
13714
13726
  url: string(),
13715
- sha256: string().optional()
13727
+ sha256: string().optional(),
13728
+ /** Picker/status label; the file basename when absent. */
13729
+ label: string().optional(),
13730
+ sizeBytes: number().optional(),
13731
+ extraFiles: array(ManagedModelExtraFileSchema).optional()
13716
13732
  }),
13717
13733
  object({
13718
13734
  kind: literal("path"),
@@ -13773,11 +13789,39 @@ var ManagedRuntimeConfigSchema = object({
13773
13789
  "q4_1",
13774
13790
  "q4_0"
13775
13791
  ]).optional(),
13792
+ /**
13793
+ * Escape hatch for llama-server flags this schema does NOT model — `--jinja`
13794
+ * (which most vision chat templates need and some language-only models
13795
+ * dislike), `--cont-batching`, `--rope-scaling`, …
13796
+ *
13797
+ * It is NOT a second place to set the flags above. A token that collides
13798
+ * with a typed field is REJECTED at start, naming the field that owns it
13799
+ * (`assertNoOwnedFlags`), because two knobs writing the same argv is exactly
13800
+ * the "two switches that disagree" failure this repo has already shipped
13801
+ * twice (D62).
13802
+ */
13803
+ extraArgs: array(string()).default([]),
13776
13804
  /** Else lazy: first generate boots it. */
13777
13805
  autoStart: boolean().default(false),
13778
13806
  /** 0 = never; frees RAM after quiet periods. */
13779
13807
  idleStopMinutes: number().int().default(30)
13780
13808
  });
13809
+ /**
13810
+ * Where a multi-GB install currently is. A single 0..1 fraction cannot answer
13811
+ * "is it stuck?" for an install that is three files (shards + mmproj) followed
13812
+ * by a sha256 pass over 22 GB — during which the fraction sat at 1.0 and the
13813
+ * node looked hung. Phase + file + bytes is the smallest shape that does.
13814
+ */
13815
+ var LlmDownloadProgressSchema = object({
13816
+ phase: _enum(["downloading", "verifying"]),
13817
+ /** The artifact currently moving, e.g. `mmproj-F16.gguf`. */
13818
+ file: string(),
13819
+ fileIndex: number().int(),
13820
+ fileCount: number().int(),
13821
+ /** Across the WHOLE install, not the current file. */
13822
+ downloadedBytes: number(),
13823
+ totalBytes: number().optional()
13824
+ });
13781
13825
  var LlmRuntimeStatusSchema = object({
13782
13826
  /** Status is ALWAYS node-qualified. */
13783
13827
  nodeId: string(),
@@ -13794,6 +13838,8 @@ var LlmRuntimeStatusSchema = object({
13794
13838
  modelPath: string().optional(),
13795
13839
  modelId: string().optional(),
13796
13840
  downloadProgress: number().min(0).max(1).optional(),
13841
+ /** Detail behind `downloadProgress`; present for the same lifetime. */
13842
+ download: LlmDownloadProgressSchema.optional(),
13797
13843
  lastError: string().optional(),
13798
13844
  crashesInWindow: number(),
13799
13845
  /** Child RSS (sampled best-effort). */
@@ -13804,7 +13850,14 @@ var LlmNodeModelSchema = object({
13804
13850
  file: string(),
13805
13851
  sizeBytes: number(),
13806
13852
  catalogId: string().optional(),
13807
- installedAt: number().optional()
13853
+ installedAt: number().optional(),
13854
+ /**
13855
+ * Absolute path on the node. Present so a file that is on disk but matches
13856
+ * no catalog entry — a custom Hugging Face install, or a GGUF the operator
13857
+ * copied in by hand — is still SELECTABLE, as a `{kind:'path'}` ref. Without
13858
+ * it the picker could list such a file and do nothing with it.
13859
+ */
13860
+ path: string().optional()
13808
13861
  });
13809
13862
  var LlmRuntimeDiskUsageSchema = object({
13810
13863
  nodeId: string(),
@@ -13950,6 +14003,36 @@ var ManagedModelCatalogEntrySchema = object({
13950
14003
  /** Vision models: companion projector file. */
13951
14004
  mmprojUrl: string().optional()
13952
14005
  });
14006
+ /**
14007
+ * The outcome of turning one operator-typed Hugging Face reference into a
14008
+ * download plan. A RESULT, never a throw: "this repo has 24 quantizations and
14009
+ * I will not pick for you" is a normal answer the UI has to render, not an
14010
+ * exception.
14011
+ *
14012
+ * `candidates` is the whole reason the refusal is usable — every string in it
14013
+ * is a tag that resolves when pasted back as `<org>/<repo>:<TAG>`.
14014
+ */
14015
+ var HfModelResolutionSchema = discriminatedUnion("ok", [object({
14016
+ ok: literal(true),
14017
+ /** Ready to hand to `installModel` unchanged. */
14018
+ model: ManagedModelRefSchema,
14019
+ label: string(),
14020
+ repo: string(),
14021
+ quantization: string(),
14022
+ purpose: _enum(["text", "vision"]),
14023
+ totalBytes: number(),
14024
+ /** mmproj + shards, for the preview: an operator approving 23 GB should
14025
+ * see that 0.9 GB of it is a projector they did not name. */
14026
+ extraFilenames: array(string())
14027
+ }), object({
14028
+ ok: literal(false),
14029
+ code: string(),
14030
+ message: string(),
14031
+ candidates: array(string()).optional(),
14032
+ /** Set when the refusal was only the ceiling: re-calling with
14033
+ * `maxBytes: requiredBytes` is the operator's explicit override. */
14034
+ requiredBytes: number().optional()
14035
+ })]);
13953
14036
  var LlmRuntimeNodeSchema = object({
13954
14037
  nodeId: string(),
13955
14038
  reachable: boolean(),
@@ -13986,6 +14069,15 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
13986
14069
  consumer: string().optional(),
13987
14070
  profileId: string().optional()
13988
14071
  }), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
14072
+ /** `https://huggingface.co/<org>/<repo>/resolve/main/<f>.gguf`,
14073
+ * `<org>/<repo>/<f>.gguf`, `<org>/<repo>` or `<org>/<repo>:<QUANT>`. */
14074
+ ref: string(),
14075
+ /** Explicit ceiling override, in bytes. Absent = the built-in ceiling. */
14076
+ maxBytes: number().positive().optional()
14077
+ }), HfModelResolutionSchema, {
14078
+ kind: "mutation",
14079
+ auth: "admin"
14080
+ }), method(object({
13989
14081
  nodeId: string(),
13990
14082
  model: ManagedModelRefSchema
13991
14083
  }), _void(), {
@@ -19613,6 +19705,17 @@ var maxSessionHoldMsField = {
19613
19705
  default: 12e4,
19614
19706
  step: 5e3
19615
19707
  };
19708
+ /**
19709
+ * Quiet period that closes an `audioMode: 'on-motion'` audio window. Floor of
19710
+ * 5s so a rearm can never degenerate into per-event stream churn; default 90s
19711
+ * comfortably outlives the gap between two PIR wakes on a battery camera.
19712
+ */
19713
+ var audioMotionWindowMsField = {
19714
+ min: 5e3,
19715
+ max: 6e5,
19716
+ default: 9e4,
19717
+ step: 5e3
19718
+ };
19616
19719
  var motionFpsField = {
19617
19720
  min: 1,
19618
19721
  max: 30,
@@ -19789,6 +19892,27 @@ var RunnerCameraConfigSchema = object({
19789
19892
  * resolved `CameraDetectionConfig`.
19790
19893
  */
19791
19894
  maxSessionHoldMs: number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
19895
+ /**
19896
+ * Orchestrator-side quiet period (ms) that closes an `audioMode:
19897
+ * 'on-motion'` audio window, measured from the LAST motion event.
19898
+ *
19899
+ * This exists because the falling edge cannot be relied on. Camera-native
19900
+ * providers emit motion as a RISING EDGE ONLY (Reolink's Baichuan push and
19901
+ * its email-push SMTP path both emit `detected: true` and never the
19902
+ * counterpart); only the frame-diff analyzer emits falls. So on an
19903
+ * onboard-only camera a window that closed only on `detected: false` never
19904
+ * closed at all, and `on-motion` silently behaved as `always-on` — on a
19905
+ * battery camera, the one failure mode the mode exists to prevent.
19906
+ *
19907
+ * Every motion event rearms this timer WITHOUT restarting the stream, so a
19908
+ * burst of re-fires costs nothing. A falling edge, when one does arrive,
19909
+ * still closes earlier via `motionCooldownMs` — whichever comes first wins.
19910
+ *
19911
+ * Not consumed by the runner: carried here so it shares the per-camera
19912
+ * device-settings surface with `motionCooldownMs`, exactly like
19913
+ * `maxSessionHoldMs`.
19914
+ */
19915
+ audioMotionWindowMs: number().min(audioMotionWindowMsField.min).max(audioMotionWindowMsField.max).optional(),
19792
19916
  motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
19793
19917
  detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
19794
19918
  motionStreamId: string(),
@@ -19884,7 +20008,7 @@ var RunnerCameraConfigSchema = object({
19884
20008
  */
19885
20009
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
19886
20010
  });
19887
- motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
20011
+ motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
19888
20012
  /**
19889
20013
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
19890
20014
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -30121,6 +30245,12 @@ Object.freeze({
30121
30245
  addonId: null,
30122
30246
  access: "view"
30123
30247
  },
30248
+ "llm.resolveModelRef": {
30249
+ capName: "llm",
30250
+ capScope: "system",
30251
+ addonId: null,
30252
+ access: "create"
30253
+ },
30124
30254
  "llm.setDefault": {
30125
30255
  capName: "llm",
30126
30256
  capScope: "system",
@@ -13727,6 +13727,18 @@ var LlmGenerateBaseInputSchema = object({
13727
13727
  * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
13728
13728
  * watchdog — operator decision #3).
13729
13729
  */
13730
+ /**
13731
+ * A companion artifact that MUST land beside the main GGUF: the `mmproj`
13732
+ * projector of a vision model, or shards 2..N of a split GGUF. Carried on the
13733
+ * REF rather than looked up at install time, so what the operator approved in
13734
+ * the preview is exactly what the node downloads.
13735
+ */
13736
+ var ManagedModelExtraFileSchema = object({
13737
+ url: string(),
13738
+ filename: string(),
13739
+ sizeBytes: number(),
13740
+ sha256: string().optional()
13741
+ });
13730
13742
  var ManagedModelRefSchema = discriminatedUnion("kind", [
13731
13743
  object({
13732
13744
  kind: literal("catalog"),
@@ -13735,7 +13747,11 @@ var ManagedModelRefSchema = discriminatedUnion("kind", [
13735
13747
  object({
13736
13748
  kind: literal("url"),
13737
13749
  url: string(),
13738
- sha256: string().optional()
13750
+ sha256: string().optional(),
13751
+ /** Picker/status label; the file basename when absent. */
13752
+ label: string().optional(),
13753
+ sizeBytes: number().optional(),
13754
+ extraFiles: array(ManagedModelExtraFileSchema).optional()
13739
13755
  }),
13740
13756
  object({
13741
13757
  kind: literal("path"),
@@ -13796,11 +13812,39 @@ var ManagedRuntimeConfigSchema = object({
13796
13812
  "q4_1",
13797
13813
  "q4_0"
13798
13814
  ]).optional(),
13815
+ /**
13816
+ * Escape hatch for llama-server flags this schema does NOT model — `--jinja`
13817
+ * (which most vision chat templates need and some language-only models
13818
+ * dislike), `--cont-batching`, `--rope-scaling`, …
13819
+ *
13820
+ * It is NOT a second place to set the flags above. A token that collides
13821
+ * with a typed field is REJECTED at start, naming the field that owns it
13822
+ * (`assertNoOwnedFlags`), because two knobs writing the same argv is exactly
13823
+ * the "two switches that disagree" failure this repo has already shipped
13824
+ * twice (D62).
13825
+ */
13826
+ extraArgs: array(string()).default([]),
13799
13827
  /** Else lazy: first generate boots it. */
13800
13828
  autoStart: boolean().default(false),
13801
13829
  /** 0 = never; frees RAM after quiet periods. */
13802
13830
  idleStopMinutes: number().int().default(30)
13803
13831
  });
13832
+ /**
13833
+ * Where a multi-GB install currently is. A single 0..1 fraction cannot answer
13834
+ * "is it stuck?" for an install that is three files (shards + mmproj) followed
13835
+ * by a sha256 pass over 22 GB — during which the fraction sat at 1.0 and the
13836
+ * node looked hung. Phase + file + bytes is the smallest shape that does.
13837
+ */
13838
+ var LlmDownloadProgressSchema = object({
13839
+ phase: _enum(["downloading", "verifying"]),
13840
+ /** The artifact currently moving, e.g. `mmproj-F16.gguf`. */
13841
+ file: string(),
13842
+ fileIndex: number().int(),
13843
+ fileCount: number().int(),
13844
+ /** Across the WHOLE install, not the current file. */
13845
+ downloadedBytes: number(),
13846
+ totalBytes: number().optional()
13847
+ });
13804
13848
  var LlmRuntimeStatusSchema = object({
13805
13849
  /** Status is ALWAYS node-qualified. */
13806
13850
  nodeId: string(),
@@ -13817,6 +13861,8 @@ var LlmRuntimeStatusSchema = object({
13817
13861
  modelPath: string().optional(),
13818
13862
  modelId: string().optional(),
13819
13863
  downloadProgress: number().min(0).max(1).optional(),
13864
+ /** Detail behind `downloadProgress`; present for the same lifetime. */
13865
+ download: LlmDownloadProgressSchema.optional(),
13820
13866
  lastError: string().optional(),
13821
13867
  crashesInWindow: number(),
13822
13868
  /** Child RSS (sampled best-effort). */
@@ -13827,7 +13873,14 @@ var LlmNodeModelSchema = object({
13827
13873
  file: string(),
13828
13874
  sizeBytes: number(),
13829
13875
  catalogId: string().optional(),
13830
- installedAt: number().optional()
13876
+ installedAt: number().optional(),
13877
+ /**
13878
+ * Absolute path on the node. Present so a file that is on disk but matches
13879
+ * no catalog entry — a custom Hugging Face install, or a GGUF the operator
13880
+ * copied in by hand — is still SELECTABLE, as a `{kind:'path'}` ref. Without
13881
+ * it the picker could list such a file and do nothing with it.
13882
+ */
13883
+ path: string().optional()
13831
13884
  });
13832
13885
  var LlmRuntimeDiskUsageSchema = object({
13833
13886
  nodeId: string(),
@@ -13973,6 +14026,36 @@ var ManagedModelCatalogEntrySchema = object({
13973
14026
  /** Vision models: companion projector file. */
13974
14027
  mmprojUrl: string().optional()
13975
14028
  });
14029
+ /**
14030
+ * The outcome of turning one operator-typed Hugging Face reference into a
14031
+ * download plan. A RESULT, never a throw: "this repo has 24 quantizations and
14032
+ * I will not pick for you" is a normal answer the UI has to render, not an
14033
+ * exception.
14034
+ *
14035
+ * `candidates` is the whole reason the refusal is usable — every string in it
14036
+ * is a tag that resolves when pasted back as `<org>/<repo>:<TAG>`.
14037
+ */
14038
+ var HfModelResolutionSchema = discriminatedUnion("ok", [object({
14039
+ ok: literal(true),
14040
+ /** Ready to hand to `installModel` unchanged. */
14041
+ model: ManagedModelRefSchema,
14042
+ label: string(),
14043
+ repo: string(),
14044
+ quantization: string(),
14045
+ purpose: _enum(["text", "vision"]),
14046
+ totalBytes: number(),
14047
+ /** mmproj + shards, for the preview: an operator approving 23 GB should
14048
+ * see that 0.9 GB of it is a projector they did not name. */
14049
+ extraFilenames: array(string())
14050
+ }), object({
14051
+ ok: literal(false),
14052
+ code: string(),
14053
+ message: string(),
14054
+ candidates: array(string()).optional(),
14055
+ /** Set when the refusal was only the ceiling: re-calling with
14056
+ * `maxBytes: requiredBytes` is the operator's explicit override. */
14057
+ requiredBytes: number().optional()
14058
+ })]);
13976
14059
  var LlmRuntimeNodeSchema = object({
13977
14060
  nodeId: string(),
13978
14061
  reachable: boolean(),
@@ -14009,6 +14092,15 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
14009
14092
  consumer: string().optional(),
14010
14093
  profileId: string().optional()
14011
14094
  }), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
14095
+ /** `https://huggingface.co/<org>/<repo>/resolve/main/<f>.gguf`,
14096
+ * `<org>/<repo>/<f>.gguf`, `<org>/<repo>` or `<org>/<repo>:<QUANT>`. */
14097
+ ref: string(),
14098
+ /** Explicit ceiling override, in bytes. Absent = the built-in ceiling. */
14099
+ maxBytes: number().positive().optional()
14100
+ }), HfModelResolutionSchema, {
14101
+ kind: "mutation",
14102
+ auth: "admin"
14103
+ }), method(object({
14012
14104
  nodeId: string(),
14013
14105
  model: ManagedModelRefSchema
14014
14106
  }), _void(), {
@@ -19636,6 +19728,17 @@ var maxSessionHoldMsField = {
19636
19728
  default: 12e4,
19637
19729
  step: 5e3
19638
19730
  };
19731
+ /**
19732
+ * Quiet period that closes an `audioMode: 'on-motion'` audio window. Floor of
19733
+ * 5s so a rearm can never degenerate into per-event stream churn; default 90s
19734
+ * comfortably outlives the gap between two PIR wakes on a battery camera.
19735
+ */
19736
+ var audioMotionWindowMsField = {
19737
+ min: 5e3,
19738
+ max: 6e5,
19739
+ default: 9e4,
19740
+ step: 5e3
19741
+ };
19639
19742
  var motionFpsField = {
19640
19743
  min: 1,
19641
19744
  max: 30,
@@ -19812,6 +19915,27 @@ var RunnerCameraConfigSchema = object({
19812
19915
  * resolved `CameraDetectionConfig`.
19813
19916
  */
19814
19917
  maxSessionHoldMs: number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
19918
+ /**
19919
+ * Orchestrator-side quiet period (ms) that closes an `audioMode:
19920
+ * 'on-motion'` audio window, measured from the LAST motion event.
19921
+ *
19922
+ * This exists because the falling edge cannot be relied on. Camera-native
19923
+ * providers emit motion as a RISING EDGE ONLY (Reolink's Baichuan push and
19924
+ * its email-push SMTP path both emit `detected: true` and never the
19925
+ * counterpart); only the frame-diff analyzer emits falls. So on an
19926
+ * onboard-only camera a window that closed only on `detected: false` never
19927
+ * closed at all, and `on-motion` silently behaved as `always-on` — on a
19928
+ * battery camera, the one failure mode the mode exists to prevent.
19929
+ *
19930
+ * Every motion event rearms this timer WITHOUT restarting the stream, so a
19931
+ * burst of re-fires costs nothing. A falling edge, when one does arrive,
19932
+ * still closes earlier via `motionCooldownMs` — whichever comes first wins.
19933
+ *
19934
+ * Not consumed by the runner: carried here so it shares the per-camera
19935
+ * device-settings surface with `motionCooldownMs`, exactly like
19936
+ * `maxSessionHoldMs`.
19937
+ */
19938
+ audioMotionWindowMs: number().min(audioMotionWindowMsField.min).max(audioMotionWindowMsField.max).optional(),
19815
19939
  motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
19816
19940
  detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
19817
19941
  motionStreamId: string(),
@@ -19907,7 +20031,7 @@ var RunnerCameraConfigSchema = object({
19907
20031
  */
19908
20032
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
19909
20033
  });
19910
- motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
20034
+ motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
19911
20035
  /**
19912
20036
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
19913
20037
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -30144,6 +30268,12 @@ Object.freeze({
30144
30268
  addonId: null,
30145
30269
  access: "view"
30146
30270
  },
30271
+ "llm.resolveModelRef": {
30272
+ capName: "llm",
30273
+ capScope: "system",
30274
+ addonId: null,
30275
+ access: "create"
30276
+ },
30147
30277
  "llm.setDefault": {
30148
30278
  capName: "llm",
30149
30279
  capScope: "system",
@@ -1,4 +1,4 @@
1
- import { a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS, z as hfModelUrl } from "./dist-BfTf6doa.mjs";
1
+ import { a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS, z as hfModelUrl } from "./dist-Bf1SE1Ew.mjs";
2
2
  import { PerformanceObserver } from "node:perf_hooks";
3
3
  //#region src/detection-pipeline/registry/model-catalogs.ts
4
4
  var HF_REPO = "camstack/camstack-models";
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-BUIpqu3E.js");
1
+ const require_dist = require("./dist-ZSiUDGzf.js");
2
2
  let node_perf_hooks = require("node:perf_hooks");
3
3
  //#region src/detection-pipeline/registry/model-catalogs.ts
4
4
  var HF_REPO = "camstack/camstack-models";
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-BUIpqu3E.js");
1
+ const require_dist = require("./dist-ZSiUDGzf.js");
2
2
  let node_url = require("node:url");
3
3
  let node_module = require("node:module");
4
4
  let node_path = require("node:path");
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BUIpqu3E.js");
6
- const require_lazy_sharp = require("../lazy-sharp-BZVhL9Lw.js");
5
+ const require_dist = require("../dist-ZSiUDGzf.js");
6
+ const require_lazy_sharp = require("../lazy-sharp-CC4WNiKY.js");
7
7
  let node_path = require("node:path");
8
8
  let node_fs = require("node:fs");
9
9
  //#region src/motion-wasm/wasm-motion-detector.ts
@@ -1,4 +1,4 @@
1
- import { H as motionDetectionCapability, R as evaluateZoneRules, ft as hydrateSchema, st as BaseAddon, ut as DeviceType } from "../dist-BfTf6doa.mjs";
1
+ import { H as motionDetectionCapability, R as evaluateZoneRules, ft as hydrateSchema, st as BaseAddon, ut as DeviceType } from "../dist-Bf1SE1Ew.mjs";
2
2
  import { t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
3
3
  import { join } from "node:path";
4
4
  import { readFileSync } from "node:fs";
@@ -2,11 +2,11 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BUIpqu3E.js");
6
- const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-CGLQP0by.js");
7
- const require_worker_protocol = require("../worker-protocol-BaLcYcOo.js");
5
+ const require_dist = require("../dist-ZSiUDGzf.js");
6
+ const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-VcDGoua3.js");
7
+ const require_worker_protocol = require("../worker-protocol-KDfFzKgT.js");
8
8
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
9
- const require_lazy_sharp = require("../lazy-sharp-BZVhL9Lw.js");
9
+ const require_lazy_sharp = require("../lazy-sharp-CC4WNiKY.js");
10
10
  const require_remote_restream = require("../remote-restream-BYbAsgUf.js");
11
11
  let sharp = require("sharp");
12
12
  sharp = require_dist.__toESM(sharp);
@@ -1,6 +1,6 @@
1
- import { At as string, Ct as boolean, Dt as number, Et as literal, K as pipelineRunnerCapability, M as deriveDetailCropRect, Mt as EventCategory, Ot as object, St as array, Tt as lazy, U as pickDetailCropConvention, W as pickNativeLeaseOverride, dt as createEvent, gt as nodePin, j as defineCustomActions, k as customAction, s as DEFAULT_DETAIL_CROP_CONVENTION, st as BaseAddon, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT, xt as _enum } from "../dist-BfTf6doa.mjs";
2
- import { c as getStepDefinition, t as attributeStall } from "../event-loop-stall-monitor-CmrHj0i9.mjs";
3
- import { a as nativeLeaseSettingEnv, t as isWorkerReply } from "../worker-protocol-BdOv9Ujl.mjs";
1
+ import { At as string, Ct as boolean, Dt as number, Et as literal, K as pipelineRunnerCapability, M as deriveDetailCropRect, Mt as EventCategory, Ot as object, St as array, Tt as lazy, U as pickDetailCropConvention, W as pickNativeLeaseOverride, dt as createEvent, gt as nodePin, j as defineCustomActions, k as customAction, s as DEFAULT_DETAIL_CROP_CONVENTION, st as BaseAddon, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT, xt as _enum } from "../dist-Bf1SE1Ew.mjs";
2
+ import { c as getStepDefinition, t as attributeStall } from "../event-loop-stall-monitor-CjWfos2b.mjs";
3
+ import { a as nativeLeaseSettingEnv, t as isWorkerReply } from "../worker-protocol-p66oO1xL.mjs";
4
4
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
6
6
  import { i as withDetectionIntent, n as profileForStreamId, t as isRemoteRestream } from "../remote-restream-Ci7RXNGb.mjs";
@@ -1,6 +1,6 @@
1
- const require_dist = require("../dist-BUIpqu3E.js");
1
+ const require_dist = require("../dist-ZSiUDGzf.js");
2
2
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
3
- const require_addon_utils = require("../addon-utils-BZQxAJ1G.js");
3
+ const require_addon_utils = require("../addon-utils-C4Cge0S0.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  let node_child_process = require("node:child_process");
6
6
  let node_path = require("node:path");
@@ -3764,6 +3764,7 @@ async function buildPlaybackManifest(deps, codecCache, deviceId, fromMs, toMs) {
3764
3764
  if (base === null) return noPlayback(deviceId);
3765
3765
  const readPrefix = deps.readFilePrefix ?? readPrefixFromDisk;
3766
3766
  const playbackDir = playbackDirFor(deps.dataDir, deviceId);
3767
+ if (deps.index.hydrationOf(deviceId, fromMs, toMs) !== "hydrated") await deps.hydrateWindow?.(deviceId, fromMs, toMs);
3767
3768
  const profiles = [...new Set(deps.index.segments(deviceId).map((s) => s.profile))];
3768
3769
  const variants = [];
3769
3770
  for (const profile of profiles) {
@@ -5016,6 +5017,61 @@ async function hydrateDeviceFromStorage(_api, index, deviceId, locations, logger
5016
5017
  }
5017
5018
  /** Entries classified between yields in {@link hydrateDeviceFromStorage}. */
5018
5019
  var HYDRATE_CHUNK = 2e4;
5020
+ /**
5021
+ * Device walks in flight at once in {@link hydrateDevicesFromStorage}.
5022
+ *
5023
+ * The walk is `readdir` latency on a network/FUSE share, not CPU: measured on
5024
+ * the live hub, 24 cold hour directories cost 31 s one at a time and 16.7 s
5025
+ * eight at a time. Four is the conservative point on that curve — enough to
5026
+ * hide most of the latency, few enough that the boot walk never becomes the
5027
+ * reason a live SegmentWriter waits. libuv's default threadpool is 4, so a
5028
+ * higher number here mostly queues.
5029
+ */
5030
+ var HYDRATE_DEVICE_CONCURRENCY = 4;
5031
+ /**
5032
+ * Walk the archive for a whole fleet: bounded-parallel, and each camera becomes
5033
+ * authoritative the moment ITS OWN walk lands (D167).
5034
+ *
5035
+ * Two things this fixes, both measured on the live hub on 2026-08-15:
5036
+ *
5037
+ * - The caller was `for (const id of ids) await hydrateDeviceFromStorage(...)`,
5038
+ * so seven cameras' 357 s of `readdir` WAS the 362 s wall clock. Nothing in
5039
+ * the walk contends for CPU — it is share latency — so overlapping is free.
5040
+ * - `markHydratedAll` ran in a SECOND loop after every device. A camera whose
5041
+ * walk finished at minute 1 still reported `hydrationOf → 'unknown'` until
5042
+ * minute 6, and every read of it in that window paid for an on-demand walk
5043
+ * over rows already in the index, competing with the walk still running for
5044
+ * the others.
5045
+ *
5046
+ * A device whose walk throws is NOT marked: `unknown` is the honest answer, and
5047
+ * it is what keeps the read path walking that window on demand.
5048
+ */
5049
+ async function hydrateDevicesFromStorage(api, index, deviceIds, locations, logger, options = {}) {
5050
+ const concurrency = Math.max(1, options.concurrency ?? HYDRATE_DEVICE_CONCURRENCY);
5051
+ const queue = [...deviceIds];
5052
+ const walkOptions = {
5053
+ ...options.yieldBetween ? { yieldBetween: options.yieldBetween } : {},
5054
+ ...options.chunkSize !== void 0 ? { chunkSize: options.chunkSize } : {}
5055
+ };
5056
+ const lane = async () => {
5057
+ for (;;) {
5058
+ const deviceId = queue.shift();
5059
+ if (deviceId === void 0) return;
5060
+ try {
5061
+ options.onDeviceStarted?.(deviceId);
5062
+ await hydrateDeviceFromStorage(api, index, deviceId, locations, logger, walkOptions);
5063
+ index.markHydratedAll(deviceId);
5064
+ options.onDeviceHydrated?.(deviceId);
5065
+ } catch (err) {
5066
+ logger.warn("recorder: device hydrate failed — window stays unknown, reads will walk it", {
5067
+ tags: { deviceId },
5068
+ meta: { error: require_dist.errMsg(err) }
5069
+ });
5070
+ }
5071
+ }
5072
+ };
5073
+ await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, lane));
5074
+ }
5019
5075
  var macrotask$1 = () => new Promise((resolve) => {
5020
5076
  setImmediate(resolve);
5021
5077
  });
@@ -8108,11 +8164,12 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
8108
8164
  return;
8109
8165
  }
8110
8166
  this.warmToday([...configs.keys()]);
8111
- for (const deviceId of configs.keys()) await hydrateDeviceFromStorage(this.ctx.api, this.index, deviceId, this.resolvedLocations, this.ctx.logger);
8112
- for (const deviceId of configs.keys()) this.index.markHydratedAll(deviceId);
8167
+ const hydrateStartedMs = Date.now();
8168
+ await hydrateDevicesFromStorage(this.ctx.api, this.index, [...configs.keys()], this.resolvedLocations, this.ctx.logger);
8113
8169
  this.ctx.logger.info("recorder: hydrated index from storage", { meta: {
8114
8170
  deviceCount: configs.size,
8115
- locationCount: this.resolvedLocations.length
8171
+ locationCount: this.resolvedLocations.length,
8172
+ ms: Date.now() - hydrateStartedMs
8116
8173
  } });
8117
8174
  const loggedDomains = /* @__PURE__ */ new Set();
8118
8175
  for (const loc of this.resolvedLocations) {