@camstack/addon-pipeline-orchestrator 1.1.15 → 1.1.16
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.
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-9JducZh4.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-C2Ftp01d.mjs} +1 -1
- package/dist/{hostInit-Dh1lreoG.mjs → hostInit-DuphCRk3.mjs} +1 -1
- package/dist/index.js +63 -24
- package/dist/index.mjs +63 -24
- package/dist/remoteEntry.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23037,6 +23037,25 @@ function computeDispatchGap(desired, known) {
|
|
|
23037
23037
|
return [...desired].filter((id) => !known.has(id));
|
|
23038
23038
|
}
|
|
23039
23039
|
//#endregion
|
|
23040
|
+
//#region src/frame-source-eligibility.ts
|
|
23041
|
+
/**
|
|
23042
|
+
* The set of nodes that can obtain a given camera's decoded frames.
|
|
23043
|
+
*
|
|
23044
|
+
* - No `sourceOwnerNodeId` → `enabledDecoderNodes` unchanged (today's
|
|
23045
|
+
* semantics; `remoteSourcingNodes` cannot restrict anything when no owner
|
|
23046
|
+
* restricts first — it only ever WIDENS an owner-restricted set).
|
|
23047
|
+
* - With an owner → `enabledDecoderNodes ∩ ({owner} ∪ remoteSourcingNodes)`.
|
|
23048
|
+
*
|
|
23049
|
+
* Pure: never mutates its inputs; result preserves `enabledDecoderNodes`
|
|
23050
|
+
* order.
|
|
23051
|
+
*/
|
|
23052
|
+
function computeFrameSourceNodes(input) {
|
|
23053
|
+
const { enabledDecoderNodes, sourceOwnerNodeId, remoteSourcingNodes } = input;
|
|
23054
|
+
if (sourceOwnerNodeId === void 0) return enabledDecoderNodes;
|
|
23055
|
+
const remote = remoteSourcingNodes ?? [];
|
|
23056
|
+
return enabledDecoderNodes.filter((nodeId) => nodeId === sourceOwnerNodeId || remote.includes(nodeId));
|
|
23057
|
+
}
|
|
23058
|
+
//#endregion
|
|
23040
23059
|
//#region src/load-balancer.ts
|
|
23041
23060
|
/**
|
|
23042
23061
|
* Compute the L2 capacity score for a runner node. Lower is better.
|
|
@@ -24526,7 +24545,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24526
24545
|
nodes: await this.collectAgentLoad({ onlyEnabled: true }),
|
|
24527
24546
|
preferredAgent,
|
|
24528
24547
|
nodeCaps: await this.buildNodeCaps(),
|
|
24529
|
-
eligibleNodes: this.detectionEligibleNodes()
|
|
24548
|
+
eligibleNodes: this.detectionEligibleNodes(runnerConfig.deviceId)
|
|
24530
24549
|
});
|
|
24531
24550
|
if (!decision) throw new Error(`dispatchCamera: no runner available for ${runnerConfig.deviceId}`);
|
|
24532
24551
|
if (decision.kind === "pending") {
|
|
@@ -24591,7 +24610,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24591
24610
|
}
|
|
24592
24611
|
async assignPipeline(input) {
|
|
24593
24612
|
if (!this.ctx) throw new Error("PipelineOrchestrator: assignPipeline called before initialize");
|
|
24594
|
-
const eligible = this.detectionEligibleNodes();
|
|
24613
|
+
const eligible = this.detectionEligibleNodes(input.deviceId);
|
|
24595
24614
|
if (!eligible.includes(input.agentNodeId)) throw new Error(`Cannot pin camera ${input.deviceId} detection to '${input.agentNodeId}': the node cannot obtain this camera's decoded frames (frame-source nodes: ${eligible.join(", ") || "none"}). Add it to Enabled Decoder Nodes first.`);
|
|
24596
24615
|
await this.ctx.settings?.writeDeviceStore(input.deviceId, { [PREFERRED_AGENT_SETTING]: input.agentNodeId }).catch((err) => {
|
|
24597
24616
|
const msg = errMsg(err);
|
|
@@ -24639,7 +24658,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24639
24658
|
nodes: await this.collectAgentLoad({ onlyEnabled: true }),
|
|
24640
24659
|
preferredAgent: null,
|
|
24641
24660
|
nodeCaps: await this.buildNodeCaps(),
|
|
24642
|
-
eligibleNodes: this.detectionEligibleNodes()
|
|
24661
|
+
eligibleNodes: this.detectionEligibleNodes(input.deviceId)
|
|
24643
24662
|
});
|
|
24644
24663
|
if (!decision) this.ctx.logger.warn("unassignPipeline: no runner available, leaving unassigned", { tags: { deviceId: input.deviceId } });
|
|
24645
24664
|
else if (decision.kind === "pending") {
|
|
@@ -24695,7 +24714,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24695
24714
|
nodes: loads,
|
|
24696
24715
|
preferredAgent: await this.readPreferredAgent(deviceId),
|
|
24697
24716
|
nodeCaps,
|
|
24698
|
-
eligibleNodes: this.detectionEligibleNodes()
|
|
24717
|
+
eligibleNodes: this.detectionEligibleNodes(deviceId)
|
|
24699
24718
|
});
|
|
24700
24719
|
if (!decision) continue;
|
|
24701
24720
|
if (decision.kind === "pending") {
|
|
@@ -24910,34 +24929,54 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24910
24929
|
return this.enabledNodes.includes(nodeId);
|
|
24911
24930
|
}
|
|
24912
24931
|
/**
|
|
24913
|
-
* The
|
|
24932
|
+
* The node that owns `deviceId`'s source pull (dials the real RTSP, hosts
|
|
24933
|
+
* the broker). **P2a (today): intentionally `undefined`** — per-camera
|
|
24934
|
+
* source ownership is not modeled yet, and an absent owner makes
|
|
24935
|
+
* `computeFrameSourceNodes` collapse to the global `enabledDecoderNodes`
|
|
24936
|
+
* (bit-identical to pre-P2a behavior, the safe-rollout invariant).
|
|
24914
24937
|
*
|
|
24915
|
-
*
|
|
24916
|
-
*
|
|
24917
|
-
*
|
|
24918
|
-
* `
|
|
24938
|
+
* Deliberately NOT `'hub'` yet: with an owner set and no
|
|
24939
|
+
* `remoteSourcingNodes`, the predicate would restrict eligibility to the
|
|
24940
|
+
* hub even when agents are decoder-enabled — a behavior change reserved for
|
|
24941
|
+
* P2d, which backs this seam with the `assignSource`/`getSourceAssignment`
|
|
24942
|
+
* cap surface (restream-owner design §2.3).
|
|
24943
|
+
*/
|
|
24944
|
+
sourceOwner(_deviceId) {}
|
|
24945
|
+
/**
|
|
24946
|
+
* The set of nodes that can OBTAIN a camera's decoded frames — PER-CAMERA
|
|
24947
|
+
* since P2a (restream-owner design §2.3), delegating to the pure
|
|
24948
|
+
* `computeFrameSourceNodes` predicate: a node qualifies iff it can decode
|
|
24949
|
+
* locally (`enabledDecoderNodes`) AND (it owns the camera's source pull OR
|
|
24950
|
+
* it may dial the owner's restream — `remoteSourcingNodes`, a P2c/P2d
|
|
24951
|
+
* rollout knob not wired yet).
|
|
24919
24952
|
*
|
|
24920
|
-
* **
|
|
24921
|
-
*
|
|
24922
|
-
*
|
|
24923
|
-
*
|
|
24924
|
-
|
|
24925
|
-
|
|
24926
|
-
|
|
24953
|
+
* **Today's effective value:** `sourceOwner()` returns `undefined` for
|
|
24954
|
+
* every camera, so this is exactly `enabledDecoderNodes` — identical to the
|
|
24955
|
+
* pre-P2a global predicate. Every predicate change edits the pure module
|
|
24956
|
+
* (and this ONE method); `deviceId` is threaded from all placement sites so
|
|
24957
|
+
* later phases turn per-camera without re-touching call sites.
|
|
24958
|
+
*/
|
|
24959
|
+
frameSourceNodes(deviceId) {
|
|
24960
|
+
return computeFrameSourceNodes({
|
|
24961
|
+
enabledDecoderNodes: this.enabledDecoderNodes,
|
|
24962
|
+
sourceOwnerNodeId: this.sourceOwner(deviceId)
|
|
24963
|
+
});
|
|
24927
24964
|
}
|
|
24928
24965
|
/**
|
|
24929
24966
|
* Nodes eligible to run a camera's detection pipeline: the whitelist of
|
|
24930
24967
|
* detection-enabled nodes (`enabledNodes`) intersected with the nodes that
|
|
24931
|
-
* can obtain the camera's frames (`frameSourceNodes()`). A node
|
|
24932
|
-
* detection-enabled but cannot source frames (or vice-versa) is
|
|
24933
|
-
* valid placement — the balancer receives this as `eligibleNodes`
|
|
24934
|
-
* anything outside it as unassignable (`no-frame-source`).
|
|
24968
|
+
* can obtain the camera's frames (`frameSourceNodes(deviceId)`). A node
|
|
24969
|
+
* that is detection-enabled but cannot source frames (or vice-versa) is
|
|
24970
|
+
* NEVER a valid placement — the balancer receives this as `eligibleNodes`
|
|
24971
|
+
* and treats anything outside it as unassignable (`no-frame-source`).
|
|
24935
24972
|
*
|
|
24936
24973
|
* Single choke point for T2/T10: passed to every `balance()` call site and
|
|
24937
|
-
* enforced by `assignPipeline` before persisting a pin.
|
|
24974
|
+
* enforced by `assignPipeline` before persisting a pin. `deviceId` is
|
|
24975
|
+
* optional only for node-scoped checks (reconnect-restore guard); camera
|
|
24976
|
+
* placement sites always pass it.
|
|
24938
24977
|
*/
|
|
24939
|
-
detectionEligibleNodes() {
|
|
24940
|
-
const frameSource = this.frameSourceNodes();
|
|
24978
|
+
detectionEligibleNodes(deviceId) {
|
|
24979
|
+
const frameSource = this.frameSourceNodes(deviceId);
|
|
24941
24980
|
return this.enabledNodes.filter((nodeId) => frameSource.includes(nodeId));
|
|
24942
24981
|
}
|
|
24943
24982
|
/**
|
|
@@ -25192,7 +25231,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25192
25231
|
nodes: loads,
|
|
25193
25232
|
preferredAgent: null,
|
|
25194
25233
|
nodeCaps: await this.buildNodeCaps(),
|
|
25195
|
-
eligibleNodes: this.detectionEligibleNodes()
|
|
25234
|
+
eligibleNodes: this.detectionEligibleNodes(deviceId)
|
|
25196
25235
|
});
|
|
25197
25236
|
if (!decision) {
|
|
25198
25237
|
this.ctx.logger.error("Failover: no online runner", { tags: { deviceId } });
|
package/dist/index.mjs
CHANGED
|
@@ -23033,6 +23033,25 @@ function computeDispatchGap(desired, known) {
|
|
|
23033
23033
|
return [...desired].filter((id) => !known.has(id));
|
|
23034
23034
|
}
|
|
23035
23035
|
//#endregion
|
|
23036
|
+
//#region src/frame-source-eligibility.ts
|
|
23037
|
+
/**
|
|
23038
|
+
* The set of nodes that can obtain a given camera's decoded frames.
|
|
23039
|
+
*
|
|
23040
|
+
* - No `sourceOwnerNodeId` → `enabledDecoderNodes` unchanged (today's
|
|
23041
|
+
* semantics; `remoteSourcingNodes` cannot restrict anything when no owner
|
|
23042
|
+
* restricts first — it only ever WIDENS an owner-restricted set).
|
|
23043
|
+
* - With an owner → `enabledDecoderNodes ∩ ({owner} ∪ remoteSourcingNodes)`.
|
|
23044
|
+
*
|
|
23045
|
+
* Pure: never mutates its inputs; result preserves `enabledDecoderNodes`
|
|
23046
|
+
* order.
|
|
23047
|
+
*/
|
|
23048
|
+
function computeFrameSourceNodes(input) {
|
|
23049
|
+
const { enabledDecoderNodes, sourceOwnerNodeId, remoteSourcingNodes } = input;
|
|
23050
|
+
if (sourceOwnerNodeId === void 0) return enabledDecoderNodes;
|
|
23051
|
+
const remote = remoteSourcingNodes ?? [];
|
|
23052
|
+
return enabledDecoderNodes.filter((nodeId) => nodeId === sourceOwnerNodeId || remote.includes(nodeId));
|
|
23053
|
+
}
|
|
23054
|
+
//#endregion
|
|
23036
23055
|
//#region src/load-balancer.ts
|
|
23037
23056
|
/**
|
|
23038
23057
|
* Compute the L2 capacity score for a runner node. Lower is better.
|
|
@@ -24522,7 +24541,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24522
24541
|
nodes: await this.collectAgentLoad({ onlyEnabled: true }),
|
|
24523
24542
|
preferredAgent,
|
|
24524
24543
|
nodeCaps: await this.buildNodeCaps(),
|
|
24525
|
-
eligibleNodes: this.detectionEligibleNodes()
|
|
24544
|
+
eligibleNodes: this.detectionEligibleNodes(runnerConfig.deviceId)
|
|
24526
24545
|
});
|
|
24527
24546
|
if (!decision) throw new Error(`dispatchCamera: no runner available for ${runnerConfig.deviceId}`);
|
|
24528
24547
|
if (decision.kind === "pending") {
|
|
@@ -24587,7 +24606,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24587
24606
|
}
|
|
24588
24607
|
async assignPipeline(input) {
|
|
24589
24608
|
if (!this.ctx) throw new Error("PipelineOrchestrator: assignPipeline called before initialize");
|
|
24590
|
-
const eligible = this.detectionEligibleNodes();
|
|
24609
|
+
const eligible = this.detectionEligibleNodes(input.deviceId);
|
|
24591
24610
|
if (!eligible.includes(input.agentNodeId)) throw new Error(`Cannot pin camera ${input.deviceId} detection to '${input.agentNodeId}': the node cannot obtain this camera's decoded frames (frame-source nodes: ${eligible.join(", ") || "none"}). Add it to Enabled Decoder Nodes first.`);
|
|
24592
24611
|
await this.ctx.settings?.writeDeviceStore(input.deviceId, { [PREFERRED_AGENT_SETTING]: input.agentNodeId }).catch((err) => {
|
|
24593
24612
|
const msg = errMsg(err);
|
|
@@ -24635,7 +24654,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24635
24654
|
nodes: await this.collectAgentLoad({ onlyEnabled: true }),
|
|
24636
24655
|
preferredAgent: null,
|
|
24637
24656
|
nodeCaps: await this.buildNodeCaps(),
|
|
24638
|
-
eligibleNodes: this.detectionEligibleNodes()
|
|
24657
|
+
eligibleNodes: this.detectionEligibleNodes(input.deviceId)
|
|
24639
24658
|
});
|
|
24640
24659
|
if (!decision) this.ctx.logger.warn("unassignPipeline: no runner available, leaving unassigned", { tags: { deviceId: input.deviceId } });
|
|
24641
24660
|
else if (decision.kind === "pending") {
|
|
@@ -24691,7 +24710,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24691
24710
|
nodes: loads,
|
|
24692
24711
|
preferredAgent: await this.readPreferredAgent(deviceId),
|
|
24693
24712
|
nodeCaps,
|
|
24694
|
-
eligibleNodes: this.detectionEligibleNodes()
|
|
24713
|
+
eligibleNodes: this.detectionEligibleNodes(deviceId)
|
|
24695
24714
|
});
|
|
24696
24715
|
if (!decision) continue;
|
|
24697
24716
|
if (decision.kind === "pending") {
|
|
@@ -24906,34 +24925,54 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24906
24925
|
return this.enabledNodes.includes(nodeId);
|
|
24907
24926
|
}
|
|
24908
24927
|
/**
|
|
24909
|
-
* The
|
|
24928
|
+
* The node that owns `deviceId`'s source pull (dials the real RTSP, hosts
|
|
24929
|
+
* the broker). **P2a (today): intentionally `undefined`** — per-camera
|
|
24930
|
+
* source ownership is not modeled yet, and an absent owner makes
|
|
24931
|
+
* `computeFrameSourceNodes` collapse to the global `enabledDecoderNodes`
|
|
24932
|
+
* (bit-identical to pre-P2a behavior, the safe-rollout invariant).
|
|
24910
24933
|
*
|
|
24911
|
-
*
|
|
24912
|
-
*
|
|
24913
|
-
*
|
|
24914
|
-
* `
|
|
24934
|
+
* Deliberately NOT `'hub'` yet: with an owner set and no
|
|
24935
|
+
* `remoteSourcingNodes`, the predicate would restrict eligibility to the
|
|
24936
|
+
* hub even when agents are decoder-enabled — a behavior change reserved for
|
|
24937
|
+
* P2d, which backs this seam with the `assignSource`/`getSourceAssignment`
|
|
24938
|
+
* cap surface (restream-owner design §2.3).
|
|
24939
|
+
*/
|
|
24940
|
+
sourceOwner(_deviceId) {}
|
|
24941
|
+
/**
|
|
24942
|
+
* The set of nodes that can OBTAIN a camera's decoded frames — PER-CAMERA
|
|
24943
|
+
* since P2a (restream-owner design §2.3), delegating to the pure
|
|
24944
|
+
* `computeFrameSourceNodes` predicate: a node qualifies iff it can decode
|
|
24945
|
+
* locally (`enabledDecoderNodes`) AND (it owns the camera's source pull OR
|
|
24946
|
+
* it may dial the owner's restream — `remoteSourcingNodes`, a P2c/P2d
|
|
24947
|
+
* rollout knob not wired yet).
|
|
24915
24948
|
*
|
|
24916
|
-
* **
|
|
24917
|
-
*
|
|
24918
|
-
*
|
|
24919
|
-
*
|
|
24920
|
-
|
|
24921
|
-
|
|
24922
|
-
|
|
24949
|
+
* **Today's effective value:** `sourceOwner()` returns `undefined` for
|
|
24950
|
+
* every camera, so this is exactly `enabledDecoderNodes` — identical to the
|
|
24951
|
+
* pre-P2a global predicate. Every predicate change edits the pure module
|
|
24952
|
+
* (and this ONE method); `deviceId` is threaded from all placement sites so
|
|
24953
|
+
* later phases turn per-camera without re-touching call sites.
|
|
24954
|
+
*/
|
|
24955
|
+
frameSourceNodes(deviceId) {
|
|
24956
|
+
return computeFrameSourceNodes({
|
|
24957
|
+
enabledDecoderNodes: this.enabledDecoderNodes,
|
|
24958
|
+
sourceOwnerNodeId: this.sourceOwner(deviceId)
|
|
24959
|
+
});
|
|
24923
24960
|
}
|
|
24924
24961
|
/**
|
|
24925
24962
|
* Nodes eligible to run a camera's detection pipeline: the whitelist of
|
|
24926
24963
|
* detection-enabled nodes (`enabledNodes`) intersected with the nodes that
|
|
24927
|
-
* can obtain the camera's frames (`frameSourceNodes()`). A node
|
|
24928
|
-
* detection-enabled but cannot source frames (or vice-versa) is
|
|
24929
|
-
* valid placement — the balancer receives this as `eligibleNodes`
|
|
24930
|
-
* anything outside it as unassignable (`no-frame-source`).
|
|
24964
|
+
* can obtain the camera's frames (`frameSourceNodes(deviceId)`). A node
|
|
24965
|
+
* that is detection-enabled but cannot source frames (or vice-versa) is
|
|
24966
|
+
* NEVER a valid placement — the balancer receives this as `eligibleNodes`
|
|
24967
|
+
* and treats anything outside it as unassignable (`no-frame-source`).
|
|
24931
24968
|
*
|
|
24932
24969
|
* Single choke point for T2/T10: passed to every `balance()` call site and
|
|
24933
|
-
* enforced by `assignPipeline` before persisting a pin.
|
|
24970
|
+
* enforced by `assignPipeline` before persisting a pin. `deviceId` is
|
|
24971
|
+
* optional only for node-scoped checks (reconnect-restore guard); camera
|
|
24972
|
+
* placement sites always pass it.
|
|
24934
24973
|
*/
|
|
24935
|
-
detectionEligibleNodes() {
|
|
24936
|
-
const frameSource = this.frameSourceNodes();
|
|
24974
|
+
detectionEligibleNodes(deviceId) {
|
|
24975
|
+
const frameSource = this.frameSourceNodes(deviceId);
|
|
24937
24976
|
return this.enabledNodes.filter((nodeId) => frameSource.includes(nodeId));
|
|
24938
24977
|
}
|
|
24939
24978
|
/**
|
|
@@ -25188,7 +25227,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25188
25227
|
nodes: loads,
|
|
25189
25228
|
preferredAgent: null,
|
|
25190
25229
|
nodeCaps: await this.buildNodeCaps(),
|
|
25191
|
-
eligibleNodes: this.detectionEligibleNodes()
|
|
25230
|
+
eligibleNodes: this.detectionEligibleNodes(deviceId)
|
|
25192
25231
|
});
|
|
25193
25232
|
if (!decision) {
|
|
25194
25233
|
this.ctx.logger.error("Failover: no online runner", { tags: { deviceId } });
|
package/dist/remoteEntry.js
CHANGED
|
@@ -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-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-C2Ftp01d.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.
|
|
3
|
+
"version": "1.1.16",
|
|
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",
|