@camstack/addon-pipeline-orchestrator 1.1.25 → 1.1.27

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.
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.1.27",
21
+ version: "1.1.29",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_pipeline_orchestrator_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.1.23",
36
+ version: "1.1.24",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_pipeline_orchestrator_widgets",
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.1.27",
39
+ version: "1.1.29",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  singleton: !0,
@@ -99,7 +99,7 @@ async function r() {
99
99
  }
100
100
  },
101
101
  "@camstack/ui-library": {
102
- version: "1.1.23",
102
+ version: "1.1.24",
103
103
  scope: "default",
104
104
  shareConfig: {
105
105
  singleton: !0,
package/dist/index.js CHANGED
@@ -7739,6 +7739,36 @@ var EncodeProfileSchema = object({
7739
7739
  */
7740
7740
  outputArgs: array(string()).optional()
7741
7741
  });
7742
+ /**
7743
+ * Per-call node pinning for `ctx.api` capability calls.
7744
+ *
7745
+ * A capability call normally resolves to its DEFAULT provider — a `singleton`
7746
+ * cap resolves to the hub, a device-scoped cap to the device's owning node. To
7747
+ * query a SPECIFIC node's provider instead (e.g. a remote agent's own
7748
+ * in-process `platform-probe` hardware, which the hub cannot probe), pin the
7749
+ * call to that node.
7750
+ *
7751
+ * The nodeId rides OUT-OF-BAND in the tRPC call context (NOT in the validated
7752
+ * method args), so capability method signatures stay `nodeId`-free — node
7753
+ * targeting is a property of the CALL, not of the method. The transport lifts
7754
+ * it from `op.context` onto the `CapCallInput.nodeId` field (`ipcParentLink`),
7755
+ * and the hub parent's `onUnownedCall` passes it to the `CapRouteResolver`,
7756
+ * which classifies a pinned agent node as `agent-child-forward`
7757
+ * (`$agent-cap-fwd.forward` → the agent's in-process provider).
7758
+ *
7759
+ * Usage at a call site:
7760
+ *
7761
+ * await api.platformProbe.getCapabilities.query(undefined, nodePin(nodeId))
7762
+ */
7763
+ /** tRPC `op.context` key carrying a per-call node pin. */
7764
+ var CAP_NODE_PIN_CONTEXT_KEY = "__camstackNodePin";
7765
+ /**
7766
+ * Build the tRPC request options that pin a single capability call to `nodeId`.
7767
+ * Pass as the second argument to `.query(input, …)` / `.mutate(input, …)`.
7768
+ */
7769
+ function nodePin(nodeId) {
7770
+ return { context: { [CAP_NODE_PIN_CONTEXT_KEY]: nodeId } };
7771
+ }
7742
7772
  var YAMNET_TO_MACRO = {
7743
7773
  mapping: {
7744
7774
  Speech: "speech",
@@ -13789,7 +13819,9 @@ method(object({ codec: string() }), boolean()), method(_void(), object({
13789
13819
  id: string(),
13790
13820
  name: string(),
13791
13821
  isPullMode: boolean().optional(),
13792
- priority: number().optional()
13822
+ priority: number().optional(),
13823
+ hwaccel: string().optional(),
13824
+ probedBestHwaccel: string().optional()
13793
13825
  })), method(DecoderSessionConfigSchema, object({
13794
13826
  sessionId: string(),
13795
13827
  nodeId: string()
@@ -24974,7 +25006,6 @@ var OrchestratorDiagnosticsSchema = object({
24974
25006
  enabledAudioNodes: array(string()),
24975
25007
  clusterRoles: object({
24976
25008
  ingestNode: string(),
24977
- recordingNode: string(),
24978
25009
  audioNode: string()
24979
25010
  }),
24980
25011
  assignedDeviceCount: number().int().min(0),
@@ -25135,12 +25166,15 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
25135
25166
  /**
25136
25167
  * Cluster-wide singleton role assignments (placement-model §6). Each role
25137
25168
  * is the ONE node that serves it for every camera. Driven by the
25138
- * `ingestNode` / `recordingNode` / `audioNode` node-selects in the addon
25139
- * global schema; defaults to the hub for all three.
25169
+ * `ingestNode` / `audioNode` node-selects in the addon global schema;
25170
+ * defaults to the hub for both. NOTE: the recording node is NOT a role here
25171
+ * — the `recorder` addon owns it as the single source of truth (its
25172
+ * `recordingNodeId` global setting, obeyed by the recorder's gate). The
25173
+ * Pipeline placement board's "Recording" picker reads/writes that canonical
25174
+ * setting directly via `addonSettings`, not an orchestrator-owned duplicate.
25140
25175
  */
25141
25176
  clusterRoles = {
25142
25177
  ingestNode: "hub",
25143
- recordingNode: "hub",
25144
25178
  audioNode: "hub"
25145
25179
  };
25146
25180
  /**
@@ -26346,22 +26380,19 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
26346
26380
  }));
26347
26381
  }
26348
26382
  /**
26349
- * Read a node's `probedBestHwaccel` from the decoder-ffmpeg addon's per-node
26350
- * settings via the hub-routed `addonSettings` cap. Returns `null` when the
26351
- * value is empty/unset, `undefined` on a read failure (keep the last known).
26352
- * Mirrors {@link readDetectionPipelineEngine}.
26383
+ * Read a node's `probedBestHwaccel` from whatever decoder backend is ACTIVE
26384
+ * on that node — via the `decoder` cap (`getInfo`, nodePin), NOT an addon-id
26385
+ * lookup. Works for any backend (ffmpeg / node-av / future) and no longer
26386
+ * returns empty on a node-av-only node (the old `decoder-ffmpeg`-keyed read
26387
+ * did). Returns `null` when the value is empty/unset, `undefined` on a read
26388
+ * failure (keep the last known). Mirrors {@link readDetectionPipelineEngine}.
26353
26389
  */
26354
26390
  async readNodeDecodeHwaccel(nodeId) {
26355
- const api = this.ctx.api;
26356
- if (!api?.addonSettings) return void 0;
26357
26391
  try {
26358
- const schema = await api.addonSettings.getGlobalSettings.query({
26359
- addonId: "decoder-ffmpeg",
26360
- nodeId
26361
- });
26362
- if (!schema) return void 0;
26363
- for (const s of schema.sections) for (const f of s.fields) if (f.key === "probedBestHwaccel") return typeof f.value === "string" && f.value.length > 0 ? f.value : null;
26364
- return null;
26392
+ const info = await this.ctx.api.decoder.getInfo.query(void 0, nodePin(nodeId));
26393
+ if (!info) return void 0;
26394
+ const probed = info.probedBestHwaccel;
26395
+ return typeof probed === "string" && probed.length > 0 ? probed : null;
26365
26396
  } catch {
26366
26397
  return;
26367
26398
  }
@@ -28117,32 +28148,41 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
28117
28148
  title: "Cluster Roles",
28118
28149
  tab: "pipeline",
28119
28150
  description: "Cluster-wide singleton roles: the single node that serves each role for every camera. Per-node detection/decode/audio CAPABILITIES + weights + camera caps are edited in the per-node capability table (agent settings), not here.",
28120
- fields: [
28121
- {
28122
- key: "ingestNode",
28123
- type: "node-select",
28124
- label: "Ingest Node",
28125
- description: "The node that connects every camera and serves the compressed restream. Defaults to the hub. (Arbitrary ingest ≠ hub is a later phase; kept here so the role is modeled.)",
28126
- default: "hub",
28127
- showOffline: true
28128
- },
28129
- {
28130
- key: "recordingNode",
28131
- type: "node-select",
28132
- label: "Recording Node",
28133
- description: "The node that records every camera (passthrough copy to its storage location). Defaults to the hub.",
28134
- default: "hub",
28135
- showOffline: true
28136
- },
28137
- {
28138
- key: "audioNode",
28139
- type: "node-select",
28140
- label: "Audio Node",
28141
- description: "The node that runs all audio analysis. Defaults to the hub. Must be an audio-capable node.",
28142
- default: "hub",
28143
- showOffline: true
28144
- }
28145
- ]
28151
+ fields: [{
28152
+ key: "ingestNode",
28153
+ type: "node-select",
28154
+ label: "Ingest Node",
28155
+ description: "The node that connects every camera and serves the compressed restream. Defaults to the hub. (Arbitrary ingest ≠ hub is a later phase; kept here so the role is modeled.)",
28156
+ default: "hub",
28157
+ showOffline: true
28158
+ }, {
28159
+ key: "audioNode",
28160
+ type: "node-select",
28161
+ label: "Audio Node",
28162
+ description: "The node that runs all audio analysis. Defaults to the hub. Must be an audio-capable node.",
28163
+ default: "hub",
28164
+ showOffline: true
28165
+ }]
28166
+ },
28167
+ {
28168
+ id: "decoder",
28169
+ title: "Decoder Backend",
28170
+ tab: "pipeline",
28171
+ fields: [{
28172
+ key: "backend",
28173
+ type: "select",
28174
+ label: "Decoder Backend",
28175
+ default: "nodeav",
28176
+ immediate: true,
28177
+ perNode: true,
28178
+ options: [{
28179
+ value: "nodeav",
28180
+ label: "node-av (in-process)"
28181
+ }, {
28182
+ value: "ffmpeg",
28183
+ label: "ffmpeg (subprocess)"
28184
+ }]
28185
+ }]
28146
28186
  },
28147
28187
  {
28148
28188
  id: "load",
@@ -28648,7 +28688,6 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
28648
28688
  };
28649
28689
  this.clusterRoles = {
28650
28690
  ingestNode: readRole("ingestNode"),
28651
- recordingNode: readRole("recordingNode"),
28652
28691
  audioNode: readRole("audioNode")
28653
28692
  };
28654
28693
  this.schedulePendingRetry();
package/dist/index.mjs CHANGED
@@ -7735,6 +7735,36 @@ var EncodeProfileSchema = object({
7735
7735
  */
7736
7736
  outputArgs: array(string()).optional()
7737
7737
  });
7738
+ /**
7739
+ * Per-call node pinning for `ctx.api` capability calls.
7740
+ *
7741
+ * A capability call normally resolves to its DEFAULT provider — a `singleton`
7742
+ * cap resolves to the hub, a device-scoped cap to the device's owning node. To
7743
+ * query a SPECIFIC node's provider instead (e.g. a remote agent's own
7744
+ * in-process `platform-probe` hardware, which the hub cannot probe), pin the
7745
+ * call to that node.
7746
+ *
7747
+ * The nodeId rides OUT-OF-BAND in the tRPC call context (NOT in the validated
7748
+ * method args), so capability method signatures stay `nodeId`-free — node
7749
+ * targeting is a property of the CALL, not of the method. The transport lifts
7750
+ * it from `op.context` onto the `CapCallInput.nodeId` field (`ipcParentLink`),
7751
+ * and the hub parent's `onUnownedCall` passes it to the `CapRouteResolver`,
7752
+ * which classifies a pinned agent node as `agent-child-forward`
7753
+ * (`$agent-cap-fwd.forward` → the agent's in-process provider).
7754
+ *
7755
+ * Usage at a call site:
7756
+ *
7757
+ * await api.platformProbe.getCapabilities.query(undefined, nodePin(nodeId))
7758
+ */
7759
+ /** tRPC `op.context` key carrying a per-call node pin. */
7760
+ var CAP_NODE_PIN_CONTEXT_KEY = "__camstackNodePin";
7761
+ /**
7762
+ * Build the tRPC request options that pin a single capability call to `nodeId`.
7763
+ * Pass as the second argument to `.query(input, …)` / `.mutate(input, …)`.
7764
+ */
7765
+ function nodePin(nodeId) {
7766
+ return { context: { [CAP_NODE_PIN_CONTEXT_KEY]: nodeId } };
7767
+ }
7738
7768
  var YAMNET_TO_MACRO = {
7739
7769
  mapping: {
7740
7770
  Speech: "speech",
@@ -13785,7 +13815,9 @@ method(object({ codec: string() }), boolean()), method(_void(), object({
13785
13815
  id: string(),
13786
13816
  name: string(),
13787
13817
  isPullMode: boolean().optional(),
13788
- priority: number().optional()
13818
+ priority: number().optional(),
13819
+ hwaccel: string().optional(),
13820
+ probedBestHwaccel: string().optional()
13789
13821
  })), method(DecoderSessionConfigSchema, object({
13790
13822
  sessionId: string(),
13791
13823
  nodeId: string()
@@ -24970,7 +25002,6 @@ var OrchestratorDiagnosticsSchema = object({
24970
25002
  enabledAudioNodes: array(string()),
24971
25003
  clusterRoles: object({
24972
25004
  ingestNode: string(),
24973
- recordingNode: string(),
24974
25005
  audioNode: string()
24975
25006
  }),
24976
25007
  assignedDeviceCount: number().int().min(0),
@@ -25131,12 +25162,15 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
25131
25162
  /**
25132
25163
  * Cluster-wide singleton role assignments (placement-model §6). Each role
25133
25164
  * is the ONE node that serves it for every camera. Driven by the
25134
- * `ingestNode` / `recordingNode` / `audioNode` node-selects in the addon
25135
- * global schema; defaults to the hub for all three.
25165
+ * `ingestNode` / `audioNode` node-selects in the addon global schema;
25166
+ * defaults to the hub for both. NOTE: the recording node is NOT a role here
25167
+ * — the `recorder` addon owns it as the single source of truth (its
25168
+ * `recordingNodeId` global setting, obeyed by the recorder's gate). The
25169
+ * Pipeline placement board's "Recording" picker reads/writes that canonical
25170
+ * setting directly via `addonSettings`, not an orchestrator-owned duplicate.
25136
25171
  */
25137
25172
  clusterRoles = {
25138
25173
  ingestNode: "hub",
25139
- recordingNode: "hub",
25140
25174
  audioNode: "hub"
25141
25175
  };
25142
25176
  /**
@@ -26342,22 +26376,19 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
26342
26376
  }));
26343
26377
  }
26344
26378
  /**
26345
- * Read a node's `probedBestHwaccel` from the decoder-ffmpeg addon's per-node
26346
- * settings via the hub-routed `addonSettings` cap. Returns `null` when the
26347
- * value is empty/unset, `undefined` on a read failure (keep the last known).
26348
- * Mirrors {@link readDetectionPipelineEngine}.
26379
+ * Read a node's `probedBestHwaccel` from whatever decoder backend is ACTIVE
26380
+ * on that node — via the `decoder` cap (`getInfo`, nodePin), NOT an addon-id
26381
+ * lookup. Works for any backend (ffmpeg / node-av / future) and no longer
26382
+ * returns empty on a node-av-only node (the old `decoder-ffmpeg`-keyed read
26383
+ * did). Returns `null` when the value is empty/unset, `undefined` on a read
26384
+ * failure (keep the last known). Mirrors {@link readDetectionPipelineEngine}.
26349
26385
  */
26350
26386
  async readNodeDecodeHwaccel(nodeId) {
26351
- const api = this.ctx.api;
26352
- if (!api?.addonSettings) return void 0;
26353
26387
  try {
26354
- const schema = await api.addonSettings.getGlobalSettings.query({
26355
- addonId: "decoder-ffmpeg",
26356
- nodeId
26357
- });
26358
- if (!schema) return void 0;
26359
- for (const s of schema.sections) for (const f of s.fields) if (f.key === "probedBestHwaccel") return typeof f.value === "string" && f.value.length > 0 ? f.value : null;
26360
- return null;
26388
+ const info = await this.ctx.api.decoder.getInfo.query(void 0, nodePin(nodeId));
26389
+ if (!info) return void 0;
26390
+ const probed = info.probedBestHwaccel;
26391
+ return typeof probed === "string" && probed.length > 0 ? probed : null;
26361
26392
  } catch {
26362
26393
  return;
26363
26394
  }
@@ -28113,32 +28144,41 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
28113
28144
  title: "Cluster Roles",
28114
28145
  tab: "pipeline",
28115
28146
  description: "Cluster-wide singleton roles: the single node that serves each role for every camera. Per-node detection/decode/audio CAPABILITIES + weights + camera caps are edited in the per-node capability table (agent settings), not here.",
28116
- fields: [
28117
- {
28118
- key: "ingestNode",
28119
- type: "node-select",
28120
- label: "Ingest Node",
28121
- description: "The node that connects every camera and serves the compressed restream. Defaults to the hub. (Arbitrary ingest ≠ hub is a later phase; kept here so the role is modeled.)",
28122
- default: "hub",
28123
- showOffline: true
28124
- },
28125
- {
28126
- key: "recordingNode",
28127
- type: "node-select",
28128
- label: "Recording Node",
28129
- description: "The node that records every camera (passthrough copy to its storage location). Defaults to the hub.",
28130
- default: "hub",
28131
- showOffline: true
28132
- },
28133
- {
28134
- key: "audioNode",
28135
- type: "node-select",
28136
- label: "Audio Node",
28137
- description: "The node that runs all audio analysis. Defaults to the hub. Must be an audio-capable node.",
28138
- default: "hub",
28139
- showOffline: true
28140
- }
28141
- ]
28147
+ fields: [{
28148
+ key: "ingestNode",
28149
+ type: "node-select",
28150
+ label: "Ingest Node",
28151
+ description: "The node that connects every camera and serves the compressed restream. Defaults to the hub. (Arbitrary ingest ≠ hub is a later phase; kept here so the role is modeled.)",
28152
+ default: "hub",
28153
+ showOffline: true
28154
+ }, {
28155
+ key: "audioNode",
28156
+ type: "node-select",
28157
+ label: "Audio Node",
28158
+ description: "The node that runs all audio analysis. Defaults to the hub. Must be an audio-capable node.",
28159
+ default: "hub",
28160
+ showOffline: true
28161
+ }]
28162
+ },
28163
+ {
28164
+ id: "decoder",
28165
+ title: "Decoder Backend",
28166
+ tab: "pipeline",
28167
+ fields: [{
28168
+ key: "backend",
28169
+ type: "select",
28170
+ label: "Decoder Backend",
28171
+ default: "nodeav",
28172
+ immediate: true,
28173
+ perNode: true,
28174
+ options: [{
28175
+ value: "nodeav",
28176
+ label: "node-av (in-process)"
28177
+ }, {
28178
+ value: "ffmpeg",
28179
+ label: "ffmpeg (subprocess)"
28180
+ }]
28181
+ }]
28142
28182
  },
28143
28183
  {
28144
28184
  id: "load",
@@ -28644,7 +28684,6 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
28644
28684
  };
28645
28685
  this.clusterRoles = {
28646
28686
  ingestNode: readRole("ingestNode"),
28647
- recordingNode: readRole("recordingNode"),
28648
28687
  audioNode: readRole("audioNode")
28649
28688
  };
28650
28689
  this.schedulePendingRetry();
@@ -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_pipeline_orchestrator_widgets-BjQq9CHk.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-NVcZ0pFA.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-orchestrator",
3
- "version": "1.1.25",
3
+ "version": "1.1.27",
4
4
  "description": "Hub-side camera-to-agent load balancer — tracks runner capacity and dispatches attachCamera calls to the optimal pipeline-runner instance",
5
5
  "keywords": [
6
6
  "camstack",