@camstack/addon-pipeline 1.2.359 → 1.2.360

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/audio-analyzer/index.js +3 -3
  2. package/dist/audio-analyzer/index.mjs +3 -3
  3. package/dist/{custom-model-catalog-BZFt7qC7.js → custom-model-catalog-K9Hx82GZ.js} +2 -2
  4. package/dist/{custom-model-catalog-Bdp__rWS.mjs → custom-model-catalog-ihWAlDr4.mjs} +2 -2
  5. package/dist/detection-pipeline/index.js +4 -4
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-TseSUtZJ.mjs → dist-BYCrV0VR.mjs} +75 -75
  8. package/dist/{dist-8okS4wxl.js → dist-CYakbWA8.js} +74 -74
  9. package/dist/{model-catalogs-C0aOsH6-.js → model-catalogs-C9cyLHdH.js} +1 -1
  10. package/dist/{model-catalogs-BSCj4g0O.mjs → model-catalogs-CX9SJ9in.mjs} +1 -1
  11. package/dist/motion-wasm/index.js +1 -1
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/{node-BStoDcFQ.js → node-CTCm8Vtw.js} +1 -1
  14. package/dist/{node-I5Y2Dzmr.mjs → node-C_qDNN9f.mjs} +1 -1
  15. package/dist/pipeline-runner/index.js +3 -3
  16. package/dist/pipeline-runner/index.mjs +3 -3
  17. package/dist/{process-memory-D4H6J3CG.mjs → process-memory-B3lkW9H-.mjs} +1 -1
  18. package/dist/{process-memory-7YJfeXUK.js → process-memory-Ds5S3L0F.js} +1 -1
  19. package/dist/recorder/index.js +2 -2
  20. package/dist/recorder/index.mjs +2 -2
  21. package/dist/{segment-demux-js-BwQRJ24B.js → segment-demux-js-BgDpp15L.js} +1 -1
  22. package/dist/{segment-demux-js-RgT0biID.mjs → segment-demux-js-W5FIgijf.mjs} +1 -1
  23. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DqdW2pDb.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-v95KZ2Rt.mjs} +1 -1
  24. package/dist/stream-broker/demux-worker-child.js +1 -1
  25. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  26. package/dist/stream-broker/{hostInit-xe-7xOfx.mjs → hostInit-CDaDWDva.mjs} +1 -1
  27. package/dist/stream-broker/index.js +3 -3
  28. package/dist/stream-broker/index.mjs +3 -3
  29. package/dist/stream-broker/remoteEntry.js +1 -1
  30. package/package.json +1 -1
  31. package/python/test_webgpu_isolated.py +132 -0
  32. package/python/webgpu_isolated.py +115 -17
@@ -3,9 +3,9 @@ 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-8okS4wxl.js");
7
- const require_model_catalogs = require("../model-catalogs-C0aOsH6-.js");
8
- const require_process_memory = require("../process-memory-7YJfeXUK.js");
6
+ const require_dist = require("../dist-CYakbWA8.js");
7
+ const require_model_catalogs = require("../model-catalogs-C9cyLHdH.js");
8
+ const require_process_memory = require("../process-memory-Ds5S3L0F.js");
9
9
  let node_fs = require("node:fs");
10
10
  node_fs = require_chunk.__toESM(node_fs);
11
11
  let node_path = require("node:path");
@@ -1,7 +1,7 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $ as audioAnalysisCapability, Hn as hydrateSchema, Kn as nodePin, L as PoolMemoryWatchdog, Ln as audioChunkBytesPerSample, Mn as errMsg, Pn as BaseAddon, Rt as mapAudioLabelToMacro, Vn as expandAudioChunkToF32le, cr as EventCategory, et as audioAnalyzerCapability, h as DEFAULT_AUDIO_ANALYZER_CONFIG, r as AUDIO_BACKEND_CHOICES, un as resolvePoolMemoryPolicy } from "../dist-TseSUtZJ.mjs";
3
- import { n as AUDIO_CLASSIFIER_MODELS } from "../model-catalogs-BSCj4g0O.mjs";
4
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-D4H6J3CG.mjs";
2
+ import { $ as audioAnalysisCapability, Bn as expandAudioChunkToF32le, In as audioChunkBytesPerSample, Kn as nodePin, L as PoolMemoryWatchdog, Nn as BaseAddon, Rt as mapAudioLabelToMacro, Vn as hydrateSchema, cr as EventCategory, et as audioAnalyzerCapability, h as DEFAULT_AUDIO_ANALYZER_CONFIG, jn as errMsg, ln as resolvePoolMemoryPolicy, r as AUDIO_BACKEND_CHOICES } from "../dist-BYCrV0VR.mjs";
3
+ import { n as AUDIO_CLASSIFIER_MODELS } from "../model-catalogs-CX9SJ9in.mjs";
4
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-B3lkW9H-.mjs";
5
5
  import * as fs from "node:fs";
6
6
  import * as path$1 from "node:path";
7
7
  import { ensureModel } from "@camstack/system/addon-utils";
@@ -1,5 +1,5 @@
1
- const require_dist = require("./dist-8okS4wxl.js");
2
- const require_model_catalogs = require("./model-catalogs-C0aOsH6-.js");
1
+ const require_dist = require("./dist-CYakbWA8.js");
2
+ const require_model_catalogs = require("./model-catalogs-C9cyLHdH.js");
3
3
  const require_worker_protocol = require("./worker-protocol-C-G8qmye.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  //#region src/detection-pipeline/registry/effective-confidence.ts
@@ -1,5 +1,5 @@
1
- import { A as LABEL_SUPPLY_VOCABULARIES, Mt as inferModelProvider, b as DEVICE_BACKEND_TO_FORMAT, f as COCO_80_LABELS, hn as stepModelRunsOnFormat, i as AUDIO_MACRO_LABELS, p as COCO_TO_MACRO, tt as audioKindId } from "./dist-TseSUtZJ.mjs";
2
- import { a as FACE_DETECTION_MODELS, c as OBJECT_DETECTION_MODELS, d as PLATE_OCR_MODELS, f as SEGMENTATION_REFINER_MODELS, i as CLIP_EMBEDDING_MODELS, l as PACKAGE_DETECTION_MODELS, n as AUDIO_CLASSIFIER_MODELS, o as FACE_EMBEDDING_MODELS, p as VEHICLE_CLASSIFIER_MODELS, r as BIRD_CLASSIFIER_MODELS, s as INSTANCE_SEGMENTATION_MODELS, t as ANIMAL_CLASSIFIER_MODELS, u as PLATE_DETECTION_MODELS } from "./model-catalogs-BSCj4g0O.mjs";
1
+ import { A as LABEL_SUPPLY_VOCABULARIES, Mt as inferModelProvider, b as DEVICE_BACKEND_TO_FORMAT, f as COCO_80_LABELS, i as AUDIO_MACRO_LABELS, mn as stepModelRunsOnFormat, p as COCO_TO_MACRO, tt as audioKindId } from "./dist-BYCrV0VR.mjs";
2
+ import { a as FACE_DETECTION_MODELS, c as OBJECT_DETECTION_MODELS, d as PLATE_OCR_MODELS, f as SEGMENTATION_REFINER_MODELS, i as CLIP_EMBEDDING_MODELS, l as PACKAGE_DETECTION_MODELS, n as AUDIO_CLASSIFIER_MODELS, o as FACE_EMBEDDING_MODELS, p as VEHICLE_CLASSIFIER_MODELS, r as BIRD_CLASSIFIER_MODELS, s as INSTANCE_SEGMENTATION_MODELS, t as ANIMAL_CLASSIFIER_MODELS, u as PLATE_DETECTION_MODELS } from "./model-catalogs-CX9SJ9in.mjs";
3
3
  import { t as isWorkerReply } from "./worker-protocol-D_NzPcnh.mjs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  //#region src/detection-pipeline/registry/effective-confidence.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-8okS4wxl.js");
7
- const require_node = require("../node-BStoDcFQ.js");
8
- const require_custom_model_catalog = require("../custom-model-catalog-BZFt7qC7.js");
6
+ const require_dist = require("../dist-CYakbWA8.js");
7
+ const require_node = require("../node-CTCm8Vtw.js");
8
+ const require_custom_model_catalog = require("../custom-model-catalog-K9Hx82GZ.js");
9
9
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
10
- const require_process_memory = require("../process-memory-7YJfeXUK.js");
10
+ const require_process_memory = require("../process-memory-Ds5S3L0F.js");
11
11
  let node_os = require("node:os");
12
12
  node_os = require_chunk.__toESM(node_os);
13
13
  let node_fs = require("node:fs");
@@ -1,9 +1,9 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { Gt as modelBindingKey, Hn as hydrateSchema, It as loadContributionCapability, Kn as nodePin, L as PoolMemoryWatchdog, Mn as errMsg$1, Pn as BaseAddon, Pt as linkedLabelStepId, Q as applyModelBoundSettings, Qt as pipelineExecutorCapability, Rn as createEvent, St as evaluateZoneRules, Y as YAMNET_TO_MACRO, Yn as sleep, Yt as pickClusterStepSettings, Zn as array, an as resolveClusterStepModelId, bn as supportedRuntimes$1, cr as EventCategory, ct as collectModelBoundKeys, d as CLUSTER_MODEL_SCOPED_STEPS, g as DEFAULT_CLUSTER_STEP_SETTINGS, hn as stepModelRunsOnFormat, jt as hfModelUrl, k as LABEL_SUPPLY_MACRO, lt as containingParentIndex, mn as runtimeDevices$1, n as APPLE_SA_TO_MACRO, or as string, pn as rootSuppliesOf, pt as defaultDeviceFor$1, qt as overlayClusterStepSettings, rr as object, sr as union, un as resolvePoolMemoryPolicy, vt as detectionPipelineCapability, xt as enumerateInferenceDevices } from "../dist-TseSUtZJ.mjs";
3
- import { s as readProcessCost } from "../node-I5Y2Dzmr.mjs";
4
- import { C as resolveModelDefaultConfidence, S as landmarkPrecisionVerdict, _ as ALL_STEPS, b as getStepDefinition, f as localFrameRegistry, g as ALL_PIPELINE_STEPS, l as resolveDefaultDetectionModel, n as mergeCustomModels, o as deviceKeyOf, r as buildSubtreeCanRun, s as resolveDeviceEngine, t as CustomModelCatalog, u as macroGateVerdict, v as getDefaultModelForFormat, x as resolveModelForFormat, y as getStep } from "../custom-model-catalog-Bdp__rWS.mjs";
2
+ import { It as loadContributionCapability, Jt as pickClusterStepSettings, Kn as nodePin, Kt as overlayClusterStepSettings, L as PoolMemoryWatchdog, Ln as createEvent, Nn as BaseAddon, Pt as linkedLabelStepId, Q as applyModelBoundSettings, St as evaluateZoneRules, Vn as hydrateSchema, Wt as modelBindingKey, Y as YAMNET_TO_MACRO, Yn as sleep, Zn as array, Zt as pipelineExecutorCapability, cr as EventCategory, ct as collectModelBoundKeys, d as CLUSTER_MODEL_SCOPED_STEPS, fn as rootSuppliesOf, g as DEFAULT_CLUSTER_STEP_SETTINGS, in as resolveClusterStepModelId, jn as errMsg$1, jt as hfModelUrl, k as LABEL_SUPPLY_MACRO, ln as resolvePoolMemoryPolicy, lt as containingParentIndex, mn as stepModelRunsOnFormat, n as APPLE_SA_TO_MACRO, or as string, pn as runtimeDevices$1, pt as defaultDeviceFor$1, rr as object, sr as union, vt as detectionPipelineCapability, xt as enumerateInferenceDevices, yn as supportedRuntimes$1 } from "../dist-BYCrV0VR.mjs";
3
+ import { s as readProcessCost } from "../node-C_qDNN9f.mjs";
4
+ import { C as resolveModelDefaultConfidence, S as landmarkPrecisionVerdict, _ as ALL_STEPS, b as getStepDefinition, f as localFrameRegistry, g as ALL_PIPELINE_STEPS, l as resolveDefaultDetectionModel, n as mergeCustomModels, o as deviceKeyOf, r as buildSubtreeCanRun, s as resolveDeviceEngine, t as CustomModelCatalog, u as macroGateVerdict, v as getDefaultModelForFormat, x as resolveModelForFormat, y as getStep } from "../custom-model-catalog-ihWAlDr4.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
6
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-D4H6J3CG.mjs";
6
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-B3lkW9H-.mjs";
7
7
  import * as os$1 from "node:os";
8
8
  import * as fs from "node:fs";
9
9
  import * as path$1 from "node:path";
@@ -5387,7 +5387,7 @@ var ZodIssueCode = {
5387
5387
  var ZodFirstPartyTypeKind;
5388
5388
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5389
5389
  //#endregion
5390
- //#region ../types/dist/sleep-Tkp9dHXv.mjs
5390
+ //#region ../types/dist/sleep-BjDbnx6-.mjs
5391
5391
  /**
5392
5392
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5393
5393
  * window to float samples (D455).
@@ -8372,6 +8372,79 @@ var BOOT_RECOVERY_BACKOFF_MS = [
8372
8372
  90 * 6e4
8373
8373
  ];
8374
8374
  /**
8375
+ * Names that, when used as URL query parameters, almost certainly carry
8376
+ * a secret. Matching is case-insensitive and anchored to the full param
8377
+ * name to avoid false positives like `user_id` (an entity reference, not
8378
+ * a credential) or `topic=password-reset` (a value happens to contain
8379
+ * the substring "password" but the param name is unrelated).
8380
+ *
8381
+ * Add new patterns here as we encounter cameras/APIs that leak through
8382
+ * the existing list.
8383
+ */
8384
+ var CREDENTIAL_PARAM_PATTERNS = [
8385
+ /^user$/i,
8386
+ /^usr$/i,
8387
+ /^username$/i,
8388
+ /^password$/i,
8389
+ /^pwd$/i,
8390
+ /^pass$/i,
8391
+ /^passwd$/i,
8392
+ /^secret$/i,
8393
+ /^token$/i,
8394
+ /^auth$/i,
8395
+ /^auth[_-]?token$/i,
8396
+ /^bearer$/i,
8397
+ /^api[_-]?key$/i,
8398
+ /^access[_-]?token$/i,
8399
+ /^refresh[_-]?token$/i,
8400
+ /^bridge$/i,
8401
+ /^code$/i,
8402
+ /^state$/i,
8403
+ /^challenge$/i,
8404
+ /^link$/i
8405
+ ];
8406
+ function isCredentialParam(name) {
8407
+ return CREDENTIAL_PARAM_PATTERNS.some((re) => re.test(name));
8408
+ }
8409
+ /**
8410
+ * Redact secrets we may surface in logs or telemetry:
8411
+ * 1. `userinfo` (`user:password@host`) — the classic RTSP shape.
8412
+ * 2. Query-string credential params (`?user=...&password=...`) — the
8413
+ * Reolink RTMP shape and several HTTP-pushed-stream APIs.
8414
+ *
8415
+ * Returns the original string verbatim when the input is not a parseable
8416
+ * URL — a log helper must never throw. Param values are replaced with
8417
+ * `***` so an operator reading logs still sees WHICH credential was
8418
+ * present (useful for debugging "wrong password" issues) without
8419
+ * exposing the value.
8420
+ *
8421
+ * The credential-name match is case-insensitive but anchored to the
8422
+ * whole param name; values like `user_id=42` or `topic=password-reset`
8423
+ * are NOT masked (those are entity references, not credentials).
8424
+ */
8425
+ function maskUrlCredentials(rawUrl) {
8426
+ if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
8427
+ const masked = maskUrlCredentials(`http://relative.invalid${rawUrl}`);
8428
+ return masked.startsWith("http://relative.invalid") ? masked.slice(23) : rawUrl;
8429
+ }
8430
+ try {
8431
+ const u = new URL(rawUrl);
8432
+ let touched = false;
8433
+ if (u.username || u.password) {
8434
+ u.username = "";
8435
+ u.password = "";
8436
+ touched = true;
8437
+ }
8438
+ for (const key of Array.from(u.searchParams.keys())) if (isCredentialParam(key)) {
8439
+ u.searchParams.set(key, "***");
8440
+ touched = true;
8441
+ }
8442
+ return touched ? u.toString() : rawUrl;
8443
+ } catch {
8444
+ return rawUrl;
8445
+ }
8446
+ }
8447
+ /**
8375
8448
  * Promise-based timer helpers — used everywhere the codebase needs to
8376
8449
  * wait, back off, or schedule a retry. Before these helpers landed, each
8377
8450
  * call site re-implemented `new Promise(r => setTimeout(r, ms))` inline,
@@ -58111,79 +58184,6 @@ var BackgroundRunRegistry = class {
58111
58184
  function hfModelUrl(repo, path) {
58112
58185
  return `https://huggingface.co/${repo}/resolve/main/${path}`;
58113
58186
  }
58114
- /**
58115
- * Names that, when used as URL query parameters, almost certainly carry
58116
- * a secret. Matching is case-insensitive and anchored to the full param
58117
- * name to avoid false positives like `user_id` (an entity reference, not
58118
- * a credential) or `topic=password-reset` (a value happens to contain
58119
- * the substring "password" but the param name is unrelated).
58120
- *
58121
- * Add new patterns here as we encounter cameras/APIs that leak through
58122
- * the existing list.
58123
- */
58124
- var CREDENTIAL_PARAM_PATTERNS = [
58125
- /^user$/i,
58126
- /^usr$/i,
58127
- /^username$/i,
58128
- /^password$/i,
58129
- /^pwd$/i,
58130
- /^pass$/i,
58131
- /^passwd$/i,
58132
- /^secret$/i,
58133
- /^token$/i,
58134
- /^auth$/i,
58135
- /^auth[_-]?token$/i,
58136
- /^bearer$/i,
58137
- /^api[_-]?key$/i,
58138
- /^access[_-]?token$/i,
58139
- /^refresh[_-]?token$/i,
58140
- /^bridge$/i,
58141
- /^code$/i,
58142
- /^state$/i,
58143
- /^challenge$/i,
58144
- /^link$/i
58145
- ];
58146
- function isCredentialParam(name) {
58147
- return CREDENTIAL_PARAM_PATTERNS.some((re) => re.test(name));
58148
- }
58149
- /**
58150
- * Redact secrets we may surface in logs or telemetry:
58151
- * 1. `userinfo` (`user:password@host`) — the classic RTSP shape.
58152
- * 2. Query-string credential params (`?user=...&password=...`) — the
58153
- * Reolink RTMP shape and several HTTP-pushed-stream APIs.
58154
- *
58155
- * Returns the original string verbatim when the input is not a parseable
58156
- * URL — a log helper must never throw. Param values are replaced with
58157
- * `***` so an operator reading logs still sees WHICH credential was
58158
- * present (useful for debugging "wrong password" issues) without
58159
- * exposing the value.
58160
- *
58161
- * The credential-name match is case-insensitive but anchored to the
58162
- * whole param name; values like `user_id=42` or `topic=password-reset`
58163
- * are NOT masked (those are entity references, not credentials).
58164
- */
58165
- function maskUrlCredentials(rawUrl) {
58166
- if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
58167
- const masked = maskUrlCredentials(`http://relative.invalid${rawUrl}`);
58168
- return masked.startsWith("http://relative.invalid") ? masked.slice(23) : rawUrl;
58169
- }
58170
- try {
58171
- const u = new URL(rawUrl);
58172
- let touched = false;
58173
- if (u.username || u.password) {
58174
- u.username = "";
58175
- u.password = "";
58176
- touched = true;
58177
- }
58178
- for (const key of Array.from(u.searchParams.keys())) if (isCredentialParam(key)) {
58179
- u.searchParams.set(key, "***");
58180
- touched = true;
58181
- }
58182
- return touched ? u.toString() : rawUrl;
58183
- } catch {
58184
- return rawUrl;
58185
- }
58186
- }
58187
58187
  var MB = 1024 * 1024;
58188
58188
  var DEFAULT_POOL_MEMORY_POLICY = {
58189
58189
  sampleIntervalMs: 6e4,
@@ -58846,4 +58846,4 @@ function enumerateInferenceDevices(hw) {
58846
58846
  return out;
58847
58847
  }
58848
58848
  //#endregion
58849
- export { audioAnalysisCapability as $, discriminatedUnion as $n, pipelineRunnerCapability as $t, LABEL_SUPPLY_VOCABULARIES as A, DeviceFeature as An, gifFromMp4Args as At, RECORDING_EXPORT_MAX_READ_BYTES as B, defineCustomActions as Bn, mayReadLocation as Bt, EVENT_PAD_MS as C, AUDIO_PRESETS as Cn, evictionPolicyForMode as Ct, G711_SCALE_CORRECTION_DB as D, evaluateSensorEdge as Dn, ffmpegRate as Dt, FailureCounters as E, buildFfmpegArgs as En, failureContributionCapability as Et, OpsLogEntrySchema as F, CAM_PROFILE_ORDER as Fn, linkedStepOf as Ft, RecordingSignalStatusSchema as G, makeSourceBrokerId as Gn, modelBindingKey as Gt, RECORDING_SOURCE_CAMSTACK_ADDON as H, hydrateSchema as Hn, memoizePinnedFfmpeg as Ht, PIPELINE_RAMP as I, CamProfileSchema as In, loadContributionCapability as It, VISIT_MERGE_GAP_MS as J, selectAssignedProfileSlots as Jn, parseProcStatus as Jt, RingBuffer as K, nodePin as Kn, motionDetectionCapability as Kt, PoolMemoryWatchdog as L, audioChunkBytesPerSample as Ln, logChannelsCapability as Lt, NativeLeaseAdmissionSchema as M, errMsg as Mn, inferModelProvider as Mt, NativeLeaseSettingsSchema as N, BOOT_RECOVERY_BACKOFF_MS as Nn, isFirstLevelMacroClass as Nt, HF_REPO as O, invocationFromEncodeProfile as On, gbToBytes as Ot, NodeBenchRunnerInputSchema as P, BaseAddon as Pn, linkedLabelStepId as Pt, applyModelBoundSettings as Q, boolean as Qn, pipelineExecutorCapability as Qt, RATE_CONTROL_RELAXED as R, createEvent as Rn, mapAudioLabelToMacro as Rt, EVENT_OWNER_TYPES as S, webrtcSessionCapability as Sn, evaluateZoneRules as St, ExportRecordSchema as T, Fmp4BoxSplitter as Tn, exportSubjectOf as Tt, ROOT_SUPPLIABLE_STEP_IDS as U, isEvent as Un, migrateLegacyDeviceSignalConfig as Ut, RECORDING_SOURCE_CAMSTACK as V, expandAudioChunkToF32le as Vn, mayWriteToLocation as Vt, RecordingConfigSchema as W, makeProfileBrokerId as Wn, modeMayWrite as Wt, addonWidgetsSourceCapability as X, _enum as Xn, pickDetailCropConvention as Xt, YAMNET_TO_MACRO as Y, sleep as Yn, pickClusterStepSettings as Yt, allowedSuppliedEffect as Z, array as Zn, pickNativeLeaseOverride as Zt, DEFAULT_DETAIL_CROP_CONVENTION as _, storageOccupancyCapability as _n, deriveRootFaceRefineRect as _t, BackgroundRunRegistry as a, resolveClusterStepModelId as an, record as ar, cameraStreamsCapability as at, DEVICE_BACKEND_TO_FORMAT as b, supportedRuntimes as bn, egressTransportFromRequest as bt, CLIP_STILL_REASON_CODE_HEADER as c, resolveLocationMode as cn, EventCategory as cr, collectModelBoundKeys as ct, CLUSTER_MODEL_SCOPED_STEPS as d, resolveRecordingProfiles as dn, createLogChannelsProvider as dt, rankStreams as en, lazy as er, audioAnalyzerCapability as et, COCO_80_LABELS as f, resolveRootSupply as fn, declareLogChannel as ft, DEFAULT_CLUSTER_STEP_SETTINGS as g, storageEvictableCapability as gn, deriveRecordingMode as gt, DEFAULT_AUDIO_ANALYZER_CONFIG as h, stepModelRunsOnFormat as hn, deriveDetailCropRect as ht, AUDIO_MACRO_LABELS as i, recordingSignalCapability as in, preprocess as ir, cameraOwnsMotionViaSensor as it, MODEL_FORMATS as j, DeviceType as jn, hfModelUrl as jt, LABEL_SUPPLY_MACRO as k, isSoftwareDecode as kn, gifDeriveSpeed as kt, CLIP_STILL_REASON_HEADER as l, resolveModelBoundSetting as ln, containingParentIndex as lt, CROP_SUPPLY_SOURCES as m, runtimeDevices as mn, deriveBatteryPresence as mt, APPLE_SA_TO_MACRO as n, recordingCapability as nn, number as nr, batteryCapability as nt, BatteryStatusSchema as o, resolveCreateExportSubject as on, string as or, clipExportFailure as ot, COCO_TO_MACRO as p, rootSuppliesOf as pn, defaultDeviceFor as pt, VIDEOCLIPS_MAX_OFFER_BYTES as q, parseProfileBrokerId as qn, overlayClusterStepSettings as qt, AUDIO_BACKEND_CHOICES as r, recordingExportCapability as rn, object as rr, buildBenchStepTree as rt, CLIP_BROKER_PACED_RATES as s, resolveEgressDecodeHwAccel as sn, union as sr, clipExportFailureOf as st, APPEARANCE_BLOCKS as t, recordingArchiveCapability as tn, literal as tr, audioKindId as tt, CLIP_STREAM_TRANSPORT_HEADER as u, resolvePoolMemoryPolicy as un, createHwAccelCache as ut, DEFAULT_EVENTS_BAND_BUFFER_SEC as v, streamBrokerCapability as vn, detectionPipelineCapability as vt, EncodeProfileSchema as w, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as wn, evictionPolicyOfLocation as wt, DEVICE_RAMP as x, videoclipsCapability as xn, enumerateInferenceDevices as xt, DEFAULT_NATIVE_LEASE_SETTINGS as y, stripRetiredBandPreBufferSec as yn, egressTranscodeSharingKey as yt, RATE_CONTROL_TIGHT as z, customAction as zn, maskUrlCredentials as zt };
58849
+ export { audioAnalysisCapability as $, discriminatedUnion as $n, rankStreams as $t, LABEL_SUPPLY_VOCABULARIES as A, DeviceType as An, gifFromMp4Args as At, RECORDING_EXPORT_MAX_READ_BYTES as B, expandAudioChunkToF32le as Bn, mayWriteToLocation as Bt, EVENT_PAD_MS as C, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as Cn, evictionPolicyForMode as Ct, G711_SCALE_CORRECTION_DB as D, invocationFromEncodeProfile as Dn, ffmpegRate as Dt, FailureCounters as E, evaluateSensorEdge as En, failureContributionCapability as Et, OpsLogEntrySchema as F, CamProfileSchema as Fn, linkedStepOf as Ft, RecordingSignalStatusSchema as G, maskUrlCredentials as Gn, motionDetectionCapability as Gt, RECORDING_SOURCE_CAMSTACK_ADDON as H, isEvent as Hn, migrateLegacyDeviceSignalConfig as Ht, PIPELINE_RAMP as I, audioChunkBytesPerSample as In, loadContributionCapability as It, VISIT_MERGE_GAP_MS as J, selectAssignedProfileSlots as Jn, pickClusterStepSettings as Jt, RingBuffer as K, nodePin as Kn, overlayClusterStepSettings as Kt, PoolMemoryWatchdog as L, createEvent as Ln, logChannelsCapability as Lt, NativeLeaseAdmissionSchema as M, BOOT_RECOVERY_BACKOFF_MS as Mn, inferModelProvider as Mt, NativeLeaseSettingsSchema as N, BaseAddon as Nn, isFirstLevelMacroClass as Nt, HF_REPO as O, isSoftwareDecode as On, gbToBytes as Ot, NodeBenchRunnerInputSchema as P, CAM_PROFILE_ORDER as Pn, linkedLabelStepId as Pt, applyModelBoundSettings as Q, boolean as Qn, pipelineRunnerCapability as Qt, RATE_CONTROL_RELAXED as R, customAction as Rn, mapAudioLabelToMacro as Rt, EVENT_OWNER_TYPES as S, AUDIO_PRESETS as Sn, evaluateZoneRules as St, ExportRecordSchema as T, buildFfmpegArgs as Tn, exportSubjectOf as Tt, ROOT_SUPPLIABLE_STEP_IDS as U, makeProfileBrokerId as Un, modeMayWrite as Ut, RECORDING_SOURCE_CAMSTACK as V, hydrateSchema as Vn, memoizePinnedFfmpeg as Vt, RecordingConfigSchema as W, makeSourceBrokerId as Wn, modelBindingKey as Wt, addonWidgetsSourceCapability as X, _enum as Xn, pickNativeLeaseOverride as Xt, YAMNET_TO_MACRO as Y, sleep as Yn, pickDetailCropConvention as Yt, allowedSuppliedEffect as Z, array as Zn, pipelineExecutorCapability as Zt, DEFAULT_DETAIL_CROP_CONVENTION as _, streamBrokerCapability as _n, deriveRootFaceRefineRect as _t, BackgroundRunRegistry as a, resolveCreateExportSubject as an, record as ar, cameraStreamsCapability as at, DEVICE_BACKEND_TO_FORMAT as b, videoclipsCapability as bn, egressTransportFromRequest as bt, CLIP_STILL_REASON_CODE_HEADER as c, resolveModelBoundSetting as cn, EventCategory as cr, collectModelBoundKeys as ct, CLUSTER_MODEL_SCOPED_STEPS as d, resolveRootSupply as dn, createLogChannelsProvider as dt, recordingArchiveCapability as en, lazy as er, audioAnalyzerCapability as et, COCO_80_LABELS as f, rootSuppliesOf as fn, declareLogChannel as ft, DEFAULT_CLUSTER_STEP_SETTINGS as g, storageOccupancyCapability as gn, deriveRecordingMode as gt, DEFAULT_AUDIO_ANALYZER_CONFIG as h, storageEvictableCapability as hn, deriveDetailCropRect as ht, AUDIO_MACRO_LABELS as i, resolveClusterStepModelId as in, preprocess as ir, cameraOwnsMotionViaSensor as it, MODEL_FORMATS as j, errMsg as jn, hfModelUrl as jt, LABEL_SUPPLY_MACRO as k, DeviceFeature as kn, gifDeriveSpeed as kt, CLIP_STILL_REASON_HEADER as l, resolvePoolMemoryPolicy as ln, containingParentIndex as lt, CROP_SUPPLY_SOURCES as m, stepModelRunsOnFormat as mn, deriveBatteryPresence as mt, APPLE_SA_TO_MACRO as n, recordingExportCapability as nn, number as nr, batteryCapability as nt, BatteryStatusSchema as o, resolveEgressDecodeHwAccel as on, string as or, clipExportFailure as ot, COCO_TO_MACRO as p, runtimeDevices as pn, defaultDeviceFor as pt, VIDEOCLIPS_MAX_OFFER_BYTES as q, parseProfileBrokerId as qn, parseProcStatus as qt, AUDIO_BACKEND_CHOICES as r, recordingSignalCapability as rn, object as rr, buildBenchStepTree as rt, CLIP_BROKER_PACED_RATES as s, resolveLocationMode as sn, union as sr, clipExportFailureOf as st, APPEARANCE_BLOCKS as t, recordingCapability as tn, literal as tr, audioKindId as tt, CLIP_STREAM_TRANSPORT_HEADER as u, resolveRecordingProfiles as un, createHwAccelCache as ut, DEFAULT_EVENTS_BAND_BUFFER_SEC as v, stripRetiredBandPreBufferSec as vn, detectionPipelineCapability as vt, EncodeProfileSchema as w, Fmp4BoxSplitter as wn, evictionPolicyOfLocation as wt, DEVICE_RAMP as x, webrtcSessionCapability as xn, enumerateInferenceDevices as xt, DEFAULT_NATIVE_LEASE_SETTINGS as y, supportedRuntimes as yn, egressTranscodeSharingKey as yt, RATE_CONTROL_TIGHT as z, defineCustomActions as zn, mayReadLocation as zt };
@@ -5387,7 +5387,7 @@ var ZodIssueCode = {
5387
5387
  var ZodFirstPartyTypeKind;
5388
5388
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5389
5389
  //#endregion
5390
- //#region ../types/dist/sleep-Tkp9dHXv.mjs
5390
+ //#region ../types/dist/sleep-BjDbnx6-.mjs
5391
5391
  /**
5392
5392
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5393
5393
  * window to float samples (D455).
@@ -8372,6 +8372,79 @@ var BOOT_RECOVERY_BACKOFF_MS = [
8372
8372
  90 * 6e4
8373
8373
  ];
8374
8374
  /**
8375
+ * Names that, when used as URL query parameters, almost certainly carry
8376
+ * a secret. Matching is case-insensitive and anchored to the full param
8377
+ * name to avoid false positives like `user_id` (an entity reference, not
8378
+ * a credential) or `topic=password-reset` (a value happens to contain
8379
+ * the substring "password" but the param name is unrelated).
8380
+ *
8381
+ * Add new patterns here as we encounter cameras/APIs that leak through
8382
+ * the existing list.
8383
+ */
8384
+ var CREDENTIAL_PARAM_PATTERNS = [
8385
+ /^user$/i,
8386
+ /^usr$/i,
8387
+ /^username$/i,
8388
+ /^password$/i,
8389
+ /^pwd$/i,
8390
+ /^pass$/i,
8391
+ /^passwd$/i,
8392
+ /^secret$/i,
8393
+ /^token$/i,
8394
+ /^auth$/i,
8395
+ /^auth[_-]?token$/i,
8396
+ /^bearer$/i,
8397
+ /^api[_-]?key$/i,
8398
+ /^access[_-]?token$/i,
8399
+ /^refresh[_-]?token$/i,
8400
+ /^bridge$/i,
8401
+ /^code$/i,
8402
+ /^state$/i,
8403
+ /^challenge$/i,
8404
+ /^link$/i
8405
+ ];
8406
+ function isCredentialParam(name) {
8407
+ return CREDENTIAL_PARAM_PATTERNS.some((re) => re.test(name));
8408
+ }
8409
+ /**
8410
+ * Redact secrets we may surface in logs or telemetry:
8411
+ * 1. `userinfo` (`user:password@host`) — the classic RTSP shape.
8412
+ * 2. Query-string credential params (`?user=...&password=...`) — the
8413
+ * Reolink RTMP shape and several HTTP-pushed-stream APIs.
8414
+ *
8415
+ * Returns the original string verbatim when the input is not a parseable
8416
+ * URL — a log helper must never throw. Param values are replaced with
8417
+ * `***` so an operator reading logs still sees WHICH credential was
8418
+ * present (useful for debugging "wrong password" issues) without
8419
+ * exposing the value.
8420
+ *
8421
+ * The credential-name match is case-insensitive but anchored to the
8422
+ * whole param name; values like `user_id=42` or `topic=password-reset`
8423
+ * are NOT masked (those are entity references, not credentials).
8424
+ */
8425
+ function maskUrlCredentials(rawUrl) {
8426
+ if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
8427
+ const masked = maskUrlCredentials(`http://relative.invalid${rawUrl}`);
8428
+ return masked.startsWith("http://relative.invalid") ? masked.slice(23) : rawUrl;
8429
+ }
8430
+ try {
8431
+ const u = new URL(rawUrl);
8432
+ let touched = false;
8433
+ if (u.username || u.password) {
8434
+ u.username = "";
8435
+ u.password = "";
8436
+ touched = true;
8437
+ }
8438
+ for (const key of Array.from(u.searchParams.keys())) if (isCredentialParam(key)) {
8439
+ u.searchParams.set(key, "***");
8440
+ touched = true;
8441
+ }
8442
+ return touched ? u.toString() : rawUrl;
8443
+ } catch {
8444
+ return rawUrl;
8445
+ }
8446
+ }
8447
+ /**
8375
8448
  * Promise-based timer helpers — used everywhere the codebase needs to
8376
8449
  * wait, back off, or schedule a retry. Before these helpers landed, each
8377
8450
  * call site re-implemented `new Promise(r => setTimeout(r, ms))` inline,
@@ -58111,79 +58184,6 @@ var BackgroundRunRegistry = class {
58111
58184
  function hfModelUrl(repo, path) {
58112
58185
  return `https://huggingface.co/${repo}/resolve/main/${path}`;
58113
58186
  }
58114
- /**
58115
- * Names that, when used as URL query parameters, almost certainly carry
58116
- * a secret. Matching is case-insensitive and anchored to the full param
58117
- * name to avoid false positives like `user_id` (an entity reference, not
58118
- * a credential) or `topic=password-reset` (a value happens to contain
58119
- * the substring "password" but the param name is unrelated).
58120
- *
58121
- * Add new patterns here as we encounter cameras/APIs that leak through
58122
- * the existing list.
58123
- */
58124
- var CREDENTIAL_PARAM_PATTERNS = [
58125
- /^user$/i,
58126
- /^usr$/i,
58127
- /^username$/i,
58128
- /^password$/i,
58129
- /^pwd$/i,
58130
- /^pass$/i,
58131
- /^passwd$/i,
58132
- /^secret$/i,
58133
- /^token$/i,
58134
- /^auth$/i,
58135
- /^auth[_-]?token$/i,
58136
- /^bearer$/i,
58137
- /^api[_-]?key$/i,
58138
- /^access[_-]?token$/i,
58139
- /^refresh[_-]?token$/i,
58140
- /^bridge$/i,
58141
- /^code$/i,
58142
- /^state$/i,
58143
- /^challenge$/i,
58144
- /^link$/i
58145
- ];
58146
- function isCredentialParam(name) {
58147
- return CREDENTIAL_PARAM_PATTERNS.some((re) => re.test(name));
58148
- }
58149
- /**
58150
- * Redact secrets we may surface in logs or telemetry:
58151
- * 1. `userinfo` (`user:password@host`) — the classic RTSP shape.
58152
- * 2. Query-string credential params (`?user=...&password=...`) — the
58153
- * Reolink RTMP shape and several HTTP-pushed-stream APIs.
58154
- *
58155
- * Returns the original string verbatim when the input is not a parseable
58156
- * URL — a log helper must never throw. Param values are replaced with
58157
- * `***` so an operator reading logs still sees WHICH credential was
58158
- * present (useful for debugging "wrong password" issues) without
58159
- * exposing the value.
58160
- *
58161
- * The credential-name match is case-insensitive but anchored to the
58162
- * whole param name; values like `user_id=42` or `topic=password-reset`
58163
- * are NOT masked (those are entity references, not credentials).
58164
- */
58165
- function maskUrlCredentials(rawUrl) {
58166
- if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
58167
- const masked = maskUrlCredentials(`http://relative.invalid${rawUrl}`);
58168
- return masked.startsWith("http://relative.invalid") ? masked.slice(23) : rawUrl;
58169
- }
58170
- try {
58171
- const u = new URL(rawUrl);
58172
- let touched = false;
58173
- if (u.username || u.password) {
58174
- u.username = "";
58175
- u.password = "";
58176
- touched = true;
58177
- }
58178
- for (const key of Array.from(u.searchParams.keys())) if (isCredentialParam(key)) {
58179
- u.searchParams.set(key, "***");
58180
- touched = true;
58181
- }
58182
- return touched ? u.toString() : rawUrl;
58183
- } catch {
58184
- return rawUrl;
58185
- }
58186
- }
58187
58187
  var MB = 1024 * 1024;
58188
58188
  var DEFAULT_POOL_MEMORY_POLICY = {
58189
58189
  sampleIntervalMs: 6e4,
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-8okS4wxl.js");
1
+ const require_dist = require("./dist-CYakbWA8.js");
2
2
  //#region src/detection-pipeline/registry/model-licenses.ts
3
3
  var COCO_DATA = {
4
4
  name: "COCO 2017",
@@ -1,4 +1,4 @@
1
- import { jt as hfModelUrl } from "./dist-TseSUtZJ.mjs";
1
+ import { jt as hfModelUrl } from "./dist-BYCrV0VR.mjs";
2
2
  //#region src/detection-pipeline/registry/model-licenses.ts
3
3
  var COCO_DATA = {
4
4
  name: "COCO 2017",
@@ -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-8okS4wxl.js");
5
+ const require_dist = require("../dist-CYakbWA8.js");
6
6
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Hn as hydrateSchema, Kt as motionDetectionCapability, Pn as BaseAddon, St as evaluateZoneRules, jn as DeviceType } from "../dist-TseSUtZJ.mjs";
1
+ import { An as DeviceType, Gt as motionDetectionCapability, Nn as BaseAddon, St as evaluateZoneRules, Vn as hydrateSchema } from "../dist-BYCrV0VR.mjs";
2
2
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-8okS4wxl.js");
2
+ const require_dist = require("./dist-CYakbWA8.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_fs = require("node:fs");
5
5
  node_fs = require_chunk.__toESM(node_fs, 1);
@@ -1,4 +1,4 @@
1
- import { En as buildFfmpegArgs, Mn as errMsg, Tn as Fmp4BoxSplitter, kn as isSoftwareDecode } from "./dist-TseSUtZJ.mjs";
1
+ import { On as isSoftwareDecode, Tn as buildFfmpegArgs, jn as errMsg, wn as Fmp4BoxSplitter } from "./dist-BYCrV0VR.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { realpathSync } from "node:fs";
4
4
  import path from "node:path";
@@ -3,9 +3,9 @@ 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-8okS4wxl.js");
7
- const require_node = require("../node-BStoDcFQ.js");
8
- const require_custom_model_catalog = require("../custom-model-catalog-BZFt7qC7.js");
6
+ const require_dist = require("../dist-CYakbWA8.js");
7
+ const require_node = require("../node-CTCm8Vtw.js");
8
+ const require_custom_model_catalog = require("../custom-model-catalog-K9Hx82GZ.js");
9
9
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
10
10
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
11
11
  const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
@@ -1,6 +1,6 @@
1
- import { $t as pipelineRunnerCapability, Bn as defineCustomActions, E as FailureCounters, Et as failureContributionCapability, Ft as linkedStepOf, I as PIPELINE_RAMP, It as loadContributionCapability, Kn as nodePin, M as NativeLeaseAdmissionSchema, Mn as errMsg, N as NativeLeaseSettingsSchema, P as NodeBenchRunnerInputSchema, Pn as BaseAddon, Qn as boolean, Rn as createEvent, U as ROOT_SUPPLIABLE_STEP_IDS, Xn as _enum, Xt as pickDetailCropConvention, Z as allowedSuppliedEffect, Zn as array, Zt as pickNativeLeaseOverride, _ as DEFAULT_DETAIL_CROP_CONVENTION, _t as deriveRootFaceRefineRect, a as BackgroundRunRegistry, cr as EventCategory, er as lazy, fn as resolveRootSupply, ht as deriveDetailCropRect, it as cameraOwnsMotionViaSensor, j as MODEL_FORMATS, ln as resolveModelBoundSetting, m as CROP_SUPPLY_SOURCES, nr as number, or as string, pn as rootSuppliesOf, rr as object, rt as buildBenchStepTree, tr as literal, x as DEVICE_RAMP, y as DEFAULT_NATIVE_LEASE_SETTINGS, zn as customAction } from "../dist-TseSUtZJ.mjs";
2
- import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-I5Y2Dzmr.mjs";
3
- import { a as stepsForElectedPool, b as getStepDefinition, d as rootObjectGate, f as localFrameRegistry, h as resolveFrameViewGeometry, i as placeDetailStep, l as resolveDefaultDetectionModel, m as createSessionDecodeCoordinator, p as isSessionDecodeReadGone, s as resolveDeviceEngine, t as CustomModelCatalog, u as macroGateVerdict, y as getStep } from "../custom-model-catalog-Bdp__rWS.mjs";
1
+ import { E as FailureCounters, Et as failureContributionCapability, Ft as linkedStepOf, I as PIPELINE_RAMP, It as loadContributionCapability, Kn as nodePin, Ln as createEvent, M as NativeLeaseAdmissionSchema, N as NativeLeaseSettingsSchema, Nn as BaseAddon, P as NodeBenchRunnerInputSchema, Qn as boolean, Qt as pipelineRunnerCapability, Rn as customAction, U as ROOT_SUPPLIABLE_STEP_IDS, Xn as _enum, Xt as pickNativeLeaseOverride, Yt as pickDetailCropConvention, Z as allowedSuppliedEffect, Zn as array, _ as DEFAULT_DETAIL_CROP_CONVENTION, _t as deriveRootFaceRefineRect, a as BackgroundRunRegistry, cn as resolveModelBoundSetting, cr as EventCategory, dn as resolveRootSupply, er as lazy, fn as rootSuppliesOf, ht as deriveDetailCropRect, it as cameraOwnsMotionViaSensor, j as MODEL_FORMATS, jn as errMsg, m as CROP_SUPPLY_SOURCES, nr as number, or as string, rr as object, rt as buildBenchStepTree, tr as literal, x as DEVICE_RAMP, y as DEFAULT_NATIVE_LEASE_SETTINGS, zn as defineCustomActions } from "../dist-BYCrV0VR.mjs";
2
+ import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-C_qDNN9f.mjs";
3
+ import { a as stepsForElectedPool, b as getStepDefinition, d as rootObjectGate, f as localFrameRegistry, h as resolveFrameViewGeometry, i as placeDetailStep, l as resolveDefaultDetectionModel, m as createSessionDecodeCoordinator, p as isSessionDecodeReadGone, s as resolveDeviceEngine, t as CustomModelCatalog, u as macroGateVerdict, y as getStep } from "../custom-model-catalog-ihWAlDr4.mjs";
4
4
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
6
6
  import { c as withDetectionIntent, o as preRollRequestOf, t as DETECTION_PRE_ROLL_REQUEST_MS } from "../restream-intent-DyGmLAU9.mjs";
@@ -1,4 +1,4 @@
1
- import { Jt as parseProcStatus } from "./dist-TseSUtZJ.mjs";
1
+ import { qt as parseProcStatus } from "./dist-BYCrV0VR.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
4
  //#region src/shared/node-topology-platform.ts
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-8okS4wxl.js");
2
+ const require_dist = require("./dist-CYakbWA8.js");
3
3
  let node_fs = require("node:fs");
4
4
  node_fs = require_chunk.__toESM(node_fs);
5
5
  let node_child_process = require("node:child_process");
@@ -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-8okS4wxl.js");
7
- const require_node = require("../node-BStoDcFQ.js");
6
+ const require_dist = require("../dist-CYakbWA8.js");
7
+ const require_node = require("../node-CTCm8Vtw.js");
8
8
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
9
9
  const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
10
10
  const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
@@ -1,5 +1,5 @@
1
- import { An as DeviceFeature, B as RECORDING_EXPORT_MAX_READ_BYTES, Bn as defineCustomActions, Bt as mayReadLocation, C as EVENT_PAD_MS, Ct as evictionPolicyForMode, Dn as evaluateSensorEdge, F as OpsLogEntrySchema, G as RecordingSignalStatusSchema, H as RECORDING_SOURCE_CAMSTACK_ADDON, Hn as hydrateSchema, Ht as memoizePinnedFfmpeg, It as loadContributionCapability, J as VISIT_MERGE_GAP_MS, Jn as selectAssignedProfileSlots, Kn as nodePin, Mn as errMsg$1, Nt as isFirstLevelMacroClass, Ot as gbToBytes, Pn as BaseAddon, Qn as boolean, S as EVENT_OWNER_TYPES, T as ExportRecordSchema, Tt as exportSubjectOf, Ut as migrateLegacyDeviceSignalConfig, V as RECORDING_SOURCE_CAMSTACK, Vt as mayWriteToLocation, W as RecordingConfigSchema, Wt as modeMayWrite, Xn as _enum, Yn as sleep$1, Zn as array, _n as storageOccupancyCapability, ar as record, cn as resolveLocationMode, cr as EventCategory, dn as resolveRecordingProfiles, gn as storageEvictableCapability, gt as deriveRecordingMode, in as recordingSignalCapability, ir as preprocess, jn as DeviceType, mt as deriveBatteryPresence, nn as recordingCapability, nr as number, nt as batteryCapability, o as BatteryStatusSchema, on as resolveCreateExportSubject, or as string, ot as clipExportFailure, q as VIDEOCLIPS_MAX_OFFER_BYTES, rn as recordingExportCapability, rr as object, s as CLIP_BROKER_PACED_RATES, st as clipExportFailureOf, tn as recordingArchiveCapability, v as DEFAULT_EVENTS_BAND_BUFFER_SEC, wt as evictionPolicyOfLocation, xn as videoclipsCapability, yn as stripRetiredBandPreBufferSec, zn as customAction } from "../dist-TseSUtZJ.mjs";
2
- import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-I5Y2Dzmr.mjs";
1
+ import { An as DeviceType, B as RECORDING_EXPORT_MAX_READ_BYTES, Bt as mayWriteToLocation, C as EVENT_PAD_MS, Ct as evictionPolicyForMode, En as evaluateSensorEdge, F as OpsLogEntrySchema, G as RecordingSignalStatusSchema, H as RECORDING_SOURCE_CAMSTACK_ADDON, Ht as migrateLegacyDeviceSignalConfig, It as loadContributionCapability, J as VISIT_MERGE_GAP_MS, Jn as selectAssignedProfileSlots, Kn as nodePin, Nn as BaseAddon, Nt as isFirstLevelMacroClass, Ot as gbToBytes, Qn as boolean, Rn as customAction, S as EVENT_OWNER_TYPES, T as ExportRecordSchema, Tt as exportSubjectOf, Ut as modeMayWrite, V as RECORDING_SOURCE_CAMSTACK, Vn as hydrateSchema, Vt as memoizePinnedFfmpeg, W as RecordingConfigSchema, Xn as _enum, Yn as sleep$1, Zn as array, an as resolveCreateExportSubject, ar as record, bn as videoclipsCapability, cr as EventCategory, en as recordingArchiveCapability, gn as storageOccupancyCapability, gt as deriveRecordingMode, hn as storageEvictableCapability, ir as preprocess, jn as errMsg$1, kn as DeviceFeature, mt as deriveBatteryPresence, nn as recordingExportCapability, nr as number, nt as batteryCapability, o as BatteryStatusSchema, or as string, ot as clipExportFailure, q as VIDEOCLIPS_MAX_OFFER_BYTES, rn as recordingSignalCapability, rr as object, s as CLIP_BROKER_PACED_RATES, sn as resolveLocationMode, st as clipExportFailureOf, tn as recordingCapability, un as resolveRecordingProfiles, v as DEFAULT_EVENTS_BAND_BUFFER_SEC, vn as stripRetiredBandPreBufferSec, wt as evictionPolicyOfLocation, zn as defineCustomActions, zt as mayReadLocation } from "../dist-BYCrV0VR.mjs";
2
+ import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-C_qDNN9f.mjs";
3
3
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
4
4
  import { d as PREBUFFER_MAX_MS, l as withRecordingIntent, r as RECORDING_PRE_ROLL_REQUEST_MS } from "../restream-intent-DyGmLAU9.mjs";
5
5
  import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-8okS4wxl.js");
2
+ const require_dist = require("./dist-CYakbWA8.js");
3
3
  let node_child_process = require("node:child_process");
4
4
  //#region src/stream-broker/recorded/avcc-annexb.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { r as __toESM, t as __commonJSMin } from "./chunk-DnnnRqeS.mjs";
2
- import { Mn as errMsg } from "./dist-TseSUtZJ.mjs";
2
+ import { jn as errMsg } from "./dist-BYCrV0VR.mjs";
3
3
  import { spawn } from "node:child_process";
4
4
  //#region src/stream-broker/recorded/avcc-annexb.ts
5
5
  /**
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.354",
21
+ version: "1.2.355",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_stream_broker_widgets",
@@ -1,4 +1,4 @@
1
- const require_segment_demux_js = require("../segment-demux-js-BwQRJ24B.js");
1
+ const require_segment_demux_js = require("../segment-demux-js-BgDpp15L.js");
2
2
  let node_worker_threads = require("node:worker_threads");
3
3
  //#region src/stream-broker/recorded/demux-worker-child.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as demuxSegmentFullJs } from "../segment-demux-js-RgT0biID.mjs";
1
+ import { t as demuxSegmentFullJs } from "../segment-demux-js-W5FIgijf.mjs";
2
2
  import { parentPort } from "node:worker_threads";
3
3
  //#region src/stream-broker/recorded/demux-worker-child.ts
4
4
  /**
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.2.354",
39
+ version: "1.2.355",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  singleton: !0,
@@ -3,12 +3,12 @@ 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-8okS4wxl.js");
7
- const require_node = require("../node-BStoDcFQ.js");
6
+ const require_dist = require("../dist-CYakbWA8.js");
7
+ const require_node = require("../node-CTCm8Vtw.js");
8
8
  const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
9
9
  const require_remote_restream = require("../remote-restream-CO36Sr30.js");
10
10
  const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
11
- const require_segment_demux_js = require("../segment-demux-js-BwQRJ24B.js");
11
+ const require_segment_demux_js = require("../segment-demux-js-BgDpp15L.js");
12
12
  let node_os = require("node:os");
13
13
  node_os = require_chunk.__toESM(node_os);
14
14
  let node_crypto = require("node:crypto");
@@ -1,10 +1,10 @@
1
1
  import { r as __toESM } from "../chunk-DnnnRqeS.mjs";
2
- import { $n as discriminatedUnion, An as DeviceFeature, At as gifFromMp4Args, Cn as AUDIO_PRESETS, D as G711_SCALE_CORRECTION_DB, Dt as ffmpegRate, En as buildFfmpegArgs, Fn as CAM_PROFILE_ORDER, Gn as makeSourceBrokerId, Ht as memoizePinnedFfmpeg, In as CamProfileSchema, It as loadContributionCapability, K as RingBuffer, Kn as nodePin, Lt as logChannelsCapability, Mn as errMsg, Nn as BOOT_RECOVERY_BACKOFF_MS, On as invocationFromEncodeProfile, Pn as BaseAddon, Qn as boolean, R as RATE_CONTROL_RELAXED, Rn as createEvent, Sn as webrtcSessionCapability, Un as isEvent, Vn as expandAudioChunkToF32le, Wn as makeProfileBrokerId, X as addonWidgetsSourceCapability, Xn as _enum, Zn as array, ar as record, at as cameraStreamsCapability, bt as egressTransportFromRequest, c as CLIP_STILL_REASON_CODE_HEADER, cr as EventCategory, dt as createLogChannelsProvider, en as rankStreams, ft as declareLogChannel, jn as DeviceType, kn as isSoftwareDecode, kt as gifDeriveSpeed, mt as deriveBatteryPresence, nr as number, nt as batteryCapability, o as BatteryStatusSchema, or as string, qn as parseProfileBrokerId, rr as object, s as CLIP_BROKER_PACED_RATES, sn as resolveEgressDecodeHwAccel, sr as union, st as clipExportFailureOf, tr as literal, ut as createHwAccelCache, vn as streamBrokerCapability, w as EncodeProfileSchema, yt as egressTranscodeSharingKey, z as RATE_CONTROL_TIGHT, zt as maskUrlCredentials } from "../dist-TseSUtZJ.mjs";
3
- import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, o as probeFfmpegBinary, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-I5Y2Dzmr.mjs";
2
+ import { $n as discriminatedUnion, $t as rankStreams, An as DeviceType, At as gifFromMp4Args, Bn as expandAudioChunkToF32le, D as G711_SCALE_CORRECTION_DB, Dn as invocationFromEncodeProfile, Dt as ffmpegRate, Fn as CamProfileSchema, Gn as maskUrlCredentials, Hn as isEvent, It as loadContributionCapability, K as RingBuffer, Kn as nodePin, Ln as createEvent, Lt as logChannelsCapability, Mn as BOOT_RECOVERY_BACKOFF_MS, Nn as BaseAddon, On as isSoftwareDecode, Pn as CAM_PROFILE_ORDER, Qn as boolean, R as RATE_CONTROL_RELAXED, Sn as AUDIO_PRESETS, Tn as buildFfmpegArgs, Un as makeProfileBrokerId, Vt as memoizePinnedFfmpeg, Wn as makeSourceBrokerId, X as addonWidgetsSourceCapability, Xn as _enum, Zn as array, _n as streamBrokerCapability, ar as record, at as cameraStreamsCapability, bt as egressTransportFromRequest, c as CLIP_STILL_REASON_CODE_HEADER, cr as EventCategory, dt as createLogChannelsProvider, ft as declareLogChannel, jn as errMsg, kn as DeviceFeature, kt as gifDeriveSpeed, mt as deriveBatteryPresence, nr as number, nt as batteryCapability, o as BatteryStatusSchema, on as resolveEgressDecodeHwAccel, or as string, qn as parseProfileBrokerId, rr as object, s as CLIP_BROKER_PACED_RATES, sr as union, st as clipExportFailureOf, tr as literal, ut as createHwAccelCache, w as EncodeProfileSchema, xn as webrtcSessionCapability, yt as egressTranscodeSharingKey, z as RATE_CONTROL_TIGHT } from "../dist-BYCrV0VR.mjs";
3
+ import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, o as probeFfmpegBinary, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-C_qDNN9f.mjs";
4
4
  import { a as parseRestreamPath, d as PREBUFFER_MAX_MS, f as capPreRollRequestMs, i as isLiveEdgeOnlyDial, m as resolvePrebuffer, n as NO_DECLARATION, p as prebufferRetentionMs, s as sdpVariantFor, u as withSentryIntent } from "../restream-intent-DyGmLAU9.mjs";
5
5
  import { n as profileForStreamId } from "../remote-restream-BeHi78PZ.mjs";
6
6
  import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
7
- import { a as require_mp4box_all, c as parseNalLengthSize, i as runFfmpegOnce, n as demuxSegmentPreferJs, o as avccSampleToAnnexB, r as FfmpegFallbackRefusedError, s as buildKeyframeAnnexB, t as demuxSegmentFullJs } from "../segment-demux-js-RgT0biID.mjs";
7
+ import { a as require_mp4box_all, c as parseNalLengthSize, i as runFfmpegOnce, n as demuxSegmentPreferJs, o as avccSampleToAnnexB, r as FfmpegFallbackRefusedError, s as buildKeyframeAnnexB, t as demuxSegmentFullJs } from "../segment-demux-js-W5FIgijf.mjs";
8
8
  import { createRequire } from "node:module";
9
9
  import * as os$1 from "node:os";
10
10
  import { networkInterfaces, tmpdir } from "node:os";
@@ -30,7 +30,7 @@ async function d(e) {
30
30
  }
31
31
  }
32
32
  async function f() {
33
- return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DqdW2pDb.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-v95KZ2Rt.mjs")).catch((e) => {
34
34
  throw l = void 0, e;
35
35
  }), l;
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-pipeline",
3
- "version": "1.2.359",
3
+ "version": "1.2.360",
4
4
  "description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",
@@ -168,5 +168,137 @@ class IsolatedSessionTests(unittest.TestCase):
168
168
  proc.kill.assert_called()
169
169
 
170
170
 
171
+ class _FakeEpDevice:
172
+ ep_name = "WebGpuExecutionProvider"
173
+
174
+
175
+ class _FakeSessionOptions:
176
+ def __init__(self) -> None:
177
+ self.intra_op_num_threads = 0
178
+ self.devices: list = []
179
+
180
+ def add_provider_for_devices(self, devices, options) -> None:
181
+ self.devices = list(devices)
182
+
183
+
184
+ class _FakeSession:
185
+ def __init__(self, path: str, sess_options=None, providers=None) -> None:
186
+ self.path = path
187
+ self.sess_options = sess_options
188
+ self._providers = (
189
+ list(providers)
190
+ if providers is not None
191
+ else ["WebGpuExecutionProvider", "CPUExecutionProvider"]
192
+ )
193
+
194
+ def get_providers(self) -> list[str]:
195
+ return self._providers
196
+
197
+
198
+ class _FakeOrt:
199
+ """Mirrors the one ORT behaviour that matters: a second registration RAISES."""
200
+
201
+ SessionOptions = _FakeSessionOptions
202
+ InferenceSession = _FakeSession
203
+
204
+ def __init__(self) -> None:
205
+ self.register_calls = 0
206
+
207
+ def register_execution_provider_library(self, name: str, path: str) -> None:
208
+ self.register_calls += 1
209
+ if self.register_calls > 1:
210
+ raise RuntimeError(f"EP library is already registered under {name}")
211
+
212
+ def get_ep_devices(self) -> list:
213
+ return [_FakeEpDevice()]
214
+
215
+
216
+ class LoaderTests(unittest.TestCase):
217
+ """D845: register once per process; refuse a render node that cannot be opened."""
218
+
219
+ def setUp(self) -> None:
220
+ wi._webgpu_registered = False
221
+ fake_plugin = MagicMock()
222
+ fake_plugin.get_library_path.return_value = "/fake/libwebgpu_ep.so"
223
+ fake_plugin.get_ep_name.return_value = "WebGpuExecutionProvider"
224
+ self._plugin_patch = patch.dict(sys.modules, {"onnxruntime_ep_webgpu": fake_plugin})
225
+ self._plugin_patch.start()
226
+
227
+ def tearDown(self) -> None:
228
+ self._plugin_patch.stop()
229
+ wi._webgpu_registered = False
230
+
231
+ def test_registers_the_plugin_once_across_sessions(self) -> None:
232
+ ort = _FakeOrt()
233
+ s1, p1 = wi.load_onnx_webgpu_session(ort, "/m/a.onnx", render_node_ok=True)
234
+ s2, p2 = wi.load_onnx_webgpu_session(ort, "/m/b.onnx", render_node_ok=True)
235
+ self.assertEqual(ort.register_calls, 1)
236
+ self.assertEqual(p1, ["WebGpuExecutionProvider", "CPUExecutionProvider"])
237
+ self.assertEqual(p2, p1)
238
+ # Both sessions were placed on the WebGPU device, not the CPU fallback.
239
+ self.assertEqual(len(s1.sess_options.devices), 1)
240
+ self.assertEqual(len(s2.sess_options.devices), 1)
241
+
242
+ def test_a_registration_that_raises_is_a_cpu_fallback_not_a_crash(self) -> None:
243
+ """Disarm check: without the guard the second load re-registers and the
244
+ fake raises — the loader must then hand back CPU providers, never a
245
+ WebGPU session built on a poisoned registration."""
246
+ ort = _FakeOrt()
247
+ wi.load_onnx_webgpu_session(ort, "/m/a.onnx", render_node_ok=True)
248
+ wi._webgpu_registered = False # simulate the pre-D845 loader forgetting
249
+ _, providers = wi.load_onnx_webgpu_session(ort, "/m/b.onnx", render_node_ok=True)
250
+ self.assertEqual(ort.register_calls, 2)
251
+ self.assertEqual(providers, [wi.ONNX_CPU_EP])
252
+
253
+ def test_bounds_the_cpu_thread_pool(self) -> None:
254
+ ort = _FakeOrt()
255
+ session, _ = wi.load_onnx_webgpu_session(ort, "/m/a.onnx", render_node_ok=True)
256
+ self.assertEqual(session.sess_options.intra_op_num_threads, wi.WEBGPU_INTRA_OP_THREADS)
257
+
258
+ def test_refuses_without_an_openable_render_node(self) -> None:
259
+ ort = _FakeOrt()
260
+ with self.assertRaises(wi.WebGpuDeviceUnavailable) as ctx:
261
+ wi.load_onnx_webgpu_session(ort, "/m/a.onnx", render_node_ok=False)
262
+ self.assertIn("/dev/dri/renderD", str(ctx.exception))
263
+ self.assertIn("uid=", str(ctx.exception))
264
+ self.assertEqual(ort.register_calls, 0)
265
+
266
+ def test_no_render_node_check_outside_linux(self) -> None:
267
+ ort = _FakeOrt()
268
+ with patch.object(wi, "render_node_accessible", return_value=None):
269
+ _, providers = wi.load_onnx_webgpu_session(ort, "/m/a.onnx")
270
+ self.assertEqual(providers[0], "WebGpuExecutionProvider")
271
+
272
+
273
+ class RenderNodeProbeTests(unittest.TestCase):
274
+ def test_non_linux_is_not_checked(self) -> None:
275
+ self.assertIsNone(wi.render_node_accessible(platform="darwin"))
276
+
277
+ def test_missing_dir_or_no_nodes(self) -> None:
278
+ def missing(_: str) -> list[str]:
279
+ raise FileNotFoundError
280
+
281
+ self.assertFalse(wi.render_node_accessible(platform="linux", listdir=missing))
282
+ self.assertFalse(
283
+ wi.render_node_accessible(platform="linux", listdir=lambda _: ["card1", "by-path"])
284
+ )
285
+
286
+ def test_denied_node_is_false_and_granted_is_true(self) -> None:
287
+ def nodes(_: str) -> list[str]:
288
+ return ["card1", "renderD128"]
289
+
290
+ self.assertFalse(
291
+ wi.render_node_accessible(platform="linux", listdir=nodes, access=lambda p, m: False)
292
+ )
293
+ seen: list[str] = []
294
+
295
+ def grant(p: str, m: int) -> bool:
296
+ seen.append(p)
297
+ return True
298
+
299
+ self.assertTrue(wi.render_node_accessible(platform="linux", listdir=nodes, access=grant))
300
+ self.assertEqual(seen, ["/dev/dri/renderD128"])
301
+
302
+
171
303
  if __name__ == "__main__":
172
304
  unittest.main()
@@ -1,14 +1,30 @@
1
1
  """
2
2
  WebGPU ORT sessions in a dedicated OS process.
3
3
 
4
- ORT WebGPU EP (Dawn/Vulkan) SIGSEGVs when a second InferenceSession is created
5
- in the same Python process (measured amd-proxmox 2026-10-10). Two processes,
6
- one session each, work concurrently. This module:
4
+ One child process per WebGPU model (D823). This module:
7
5
 
8
- - loads a WebGPU session (or CPU fallback) — used by the child worker;
6
+ - loads a WebGPU session — used by the child worker;
9
7
  - hosts the child worker entrypoint (`python webgpu_isolated.py --ipc-in N --ipc-out M`);
10
8
  - exposes `WebGpuIsolatedSession` for the pool parent: one child per model.
11
9
 
10
+ Two facts measured on amd-proxmox 2026-10-13 (D845) shape the loader:
11
+
12
+ 1. The SIGSEGV that motivated D823 was NOT a second session. It is a second
13
+ `register_execution_provider_library` call for a name that is already
14
+ registered: ORT raises "EP library is already registered", and the next
15
+ `InferenceSession` on that EP then dies in `_create_inference_session`.
16
+ Register ONCE per process (`_ensure_webgpu_ep_registered`) and three
17
+ sessions in one process load and run — serialised; concurrent `run()` on
18
+ distinct sessions aborts the process, so a shared process would need one
19
+ lock. The child-per-model layout stays for crash isolation.
20
+ 2. Dawn never reports a software adapter. With `/dev/dri/renderD*` not
21
+ openable (the entrypoint dropped the render group), the session still
22
+ answered `providers=[WebGpuExecutionProvider, …]` and ran at 224 ms per
23
+ yolov9t-320 frame with gpu_busy 0 % — 15× the GPU's 13 ms, for a week,
24
+ looking like a weak iGPU. A render node that cannot be opened is
25
+ therefore REFUSED before any session is built; the error names the
26
+ uid/gid so the fix (device ownership in the entrypoint) is one read away.
27
+
12
28
  IPC uses dedicated pipe fds (`pass_fds`), never stdin/stdout — ORT/Dawn log on
13
29
  fd 1 and a yolov9 raw head on a shared stdout pickle channel truncated the
14
30
  frame (2026-10-10). Pipe `read(n)` can also return short; framing uses
@@ -24,13 +40,77 @@ import struct
24
40
  import subprocess
25
41
  import sys
26
42
  import threading
27
- from typing import Any, BinaryIO, Optional
43
+ from typing import Any, BinaryIO, Callable, Optional
28
44
 
29
45
  ONNX_CPU_EP = "CPUExecutionProvider"
30
46
  ONNX_WEBGPU_EP_REGISTRATION = "webgpu_ep_registration"
31
47
 
48
+ # ORT's CPU thread pool in a WebGPU child only feeds buffer uploads and the
49
+ # rare CPU-placed node; the per-frame cost is Dawn's dispatch on ONE thread.
50
+ # Measured 2026-10-13 (yolov9t-320 warm p50): 14.2 ms at 1, 2 and 6 threads —
51
+ # so the default (one spinning thread per core, per child, ten children on a
52
+ # six-core node) buys nothing and costs CPU. Two keeps a CPU-placed node from
53
+ # serialising fully.
54
+ WEBGPU_INTRA_OP_THREADS = 2
55
+
56
+ RENDER_NODE_DIR = "/dev/dri"
57
+ RENDER_NODE_PREFIX = "renderD"
58
+
32
59
  _HDR = struct.Struct(">I")
33
60
 
61
+ _webgpu_registered = False
62
+ _webgpu_register_lock = threading.Lock()
63
+
64
+
65
+ class WebGpuDeviceUnavailable(RuntimeError):
66
+ """The webgpu device was asked for and cannot reach a GPU. Never served from CPU."""
67
+
68
+
69
+ def render_node_accessible(
70
+ *,
71
+ platform: str = sys.platform,
72
+ listdir: Callable[[str], list[str]] = os.listdir,
73
+ access: Callable[[str, int], bool] = os.access,
74
+ ) -> Optional[bool]:
75
+ """True when some /dev/dri/renderD* is readable+writable by this process.
76
+
77
+ None on a platform without DRM render nodes (nothing to check); False when
78
+ the directory is absent, empty of render nodes, or every node is denied.
79
+ """
80
+ if not platform.startswith("linux"):
81
+ return None
82
+ try:
83
+ names = [n for n in listdir(RENDER_NODE_DIR) if n.startswith(RENDER_NODE_PREFIX)]
84
+ except OSError:
85
+ return False
86
+ if not names:
87
+ return False
88
+ return any(access(os.path.join(RENDER_NODE_DIR, n), os.R_OK | os.W_OK) for n in names)
89
+
90
+
91
+ def _identity() -> str:
92
+ try:
93
+ return f"uid={os.getuid()} gid={os.getgid()} groups={sorted(os.getgroups())}"
94
+ except (AttributeError, OSError):
95
+ return "uid=? gid=? groups=?"
96
+
97
+
98
+ def _ensure_webgpu_ep_registered(ort: Any, webgpu_ep: Any) -> None:
99
+ """Register the plugin EP library at most once per process.
100
+
101
+ A second registration under the same name raises and leaves the next
102
+ session creation to SIGSEGV (the real cause behind D823's "second session
103
+ crashes"). Idempotent across callers and threads.
104
+ """
105
+ global _webgpu_registered
106
+ with _webgpu_register_lock:
107
+ if _webgpu_registered:
108
+ return
109
+ ort.register_execution_provider_library(
110
+ ONNX_WEBGPU_EP_REGISTRATION, webgpu_ep.get_library_path()
111
+ )
112
+ _webgpu_registered = True
113
+
34
114
 
35
115
  def _read_exact(stream: BinaryIO, n: int) -> bytes:
36
116
  """Read exactly n bytes; pipe read() may return short without EOF."""
@@ -59,11 +139,29 @@ def _read_msg(stream: BinaryIO) -> Any:
59
139
  return pickle.loads(data)
60
140
 
61
141
 
62
- def load_onnx_webgpu_session(ort: Any, path: str) -> tuple[Any, list[str]]:
63
- """Build an InferenceSession on the WebGPU plugin EP, CPU fallback on miss.
142
+ def load_onnx_webgpu_session(
143
+ ort: Any,
144
+ path: str,
145
+ *,
146
+ render_node_ok: Optional[bool] = None,
147
+ ) -> tuple[Any, list[str]]:
148
+ """Build an InferenceSession on the WebGPU plugin EP.
64
149
 
65
- Same contract as the former inference_pool._load_onnx_webgpu_session.
150
+ Refuses (`WebGpuDeviceUnavailable`) when no DRM render node is openable —
151
+ Dawn would otherwise serve the session from software and still call it
152
+ WebGPU. A missing plugin or a missing Vulkan adapter still falls back to
153
+ the CPU EP, and says so in `providers`.
66
154
  """
155
+ accessible = render_node_accessible() if render_node_ok is None else render_node_ok
156
+ if accessible is False:
157
+ raise WebGpuDeviceUnavailable(
158
+ f"ONNX WebGPU: no openable {RENDER_NODE_DIR}/{RENDER_NODE_PREFIX}* "
159
+ f"({_identity()}) — Dawn would run this model on a software adapter at "
160
+ f"~15× the GPU latency while reporting WebGpuExecutionProvider; refusing "
161
+ f"device webgpu. The container entrypoint must own or group-grant the "
162
+ f"render node to the camstack user (compose group_add is dropped by gosu)."
163
+ )
164
+
67
165
  try:
68
166
  import onnxruntime_ep_webgpu as webgpu_ep # type: ignore
69
167
  except Exception as err:
@@ -72,15 +170,11 @@ def load_onnx_webgpu_session(ort: Any, path: str) -> tuple[Any, list[str]]:
72
170
  return ort.InferenceSession(path, providers=[ONNX_CPU_EP]), [ONNX_CPU_EP]
73
171
 
74
172
  try:
75
- ort.register_execution_provider_library(
76
- ONNX_WEBGPU_EP_REGISTRATION, webgpu_ep.get_library_path()
77
- )
173
+ _ensure_webgpu_ep_registered(ort, webgpu_ep)
78
174
  except Exception as err:
79
- msg = str(err).lower()
80
- if "already" not in msg and "exist" not in msg:
81
- sys.stderr.write(f"ONNX WebGPU: register failed ({err}); CPU fallback\n")
82
- sys.stderr.flush()
83
- return ort.InferenceSession(path, providers=[ONNX_CPU_EP]), [ONNX_CPU_EP]
175
+ sys.stderr.write(f"ONNX WebGPU: register failed ({err}); CPU fallback\n")
176
+ sys.stderr.flush()
177
+ return ort.InferenceSession(path, providers=[ONNX_CPU_EP]), [ONNX_CPU_EP]
84
178
 
85
179
  ep_name = webgpu_ep.get_ep_name()
86
180
  webgpu_device = next(
@@ -96,10 +190,14 @@ def load_onnx_webgpu_session(ort: Any, path: str) -> tuple[Any, list[str]]:
96
190
  return ort.InferenceSession(path, providers=[ONNX_CPU_EP]), [ONNX_CPU_EP]
97
191
 
98
192
  sess_options = ort.SessionOptions()
193
+ sess_options.intra_op_num_threads = WEBGPU_INTRA_OP_THREADS
99
194
  sess_options.add_provider_for_devices([webgpu_device], {})
100
195
  session = ort.InferenceSession(path, sess_options=sess_options)
101
196
  active = list(session.get_providers())
102
- sys.stderr.write(f"ONNX WebGPU: providers={active} ep={ep_name} device=webgpu\n")
197
+ sys.stderr.write(
198
+ f"ONNX WebGPU: providers={active} ep={ep_name} device=webgpu "
199
+ f"render-node=ok {_identity()}\n"
200
+ )
103
201
  sys.stderr.flush()
104
202
  return session, active
105
203