@camstack/addon-pipeline-orchestrator 1.2.619 → 1.2.620

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/index.mjs CHANGED
@@ -77537,28 +77537,33 @@ var DETECTOR_PERF = {
77537
77537
  },
77538
77538
  "amd-igpu": {
77539
77539
  "yolov9t-320": {
77540
- p50Ms: 188,
77541
- fps: 5,
77540
+ p50Ms: 40,
77541
+ fps: 25,
77542
77542
  measured: true
77543
77543
  },
77544
77544
  "yolov9t-640": {
77545
- p50Ms: 671,
77546
- fps: 1,
77545
+ p50Ms: 89,
77546
+ fps: 11,
77547
77547
  measured: true
77548
77548
  },
77549
77549
  "yolov9s-320": {
77550
- p50Ms: 401,
77551
- fps: 2,
77550
+ p50Ms: 65,
77551
+ fps: 15,
77552
77552
  measured: true
77553
77553
  },
77554
77554
  "yolov9s-640": {
77555
- p50Ms: 1434,
77556
- fps: 1,
77555
+ p50Ms: 125,
77556
+ fps: 8,
77557
77557
  measured: true
77558
77558
  },
77559
77559
  "yolov9m-320": {
77560
- p50Ms: 1076,
77561
- fps: 1,
77560
+ p50Ms: 85,
77561
+ fps: 12,
77562
+ measured: true
77563
+ },
77564
+ "yolov9m-640": {
77565
+ p50Ms: 228,
77566
+ fps: 4,
77562
77567
  measured: true
77563
77568
  }
77564
77569
  },
@@ -78085,6 +78090,55 @@ function resolveEffectiveShares(input) {
78085
78090
  deviceWeights
78086
78091
  };
78087
78092
  }
78093
+ /**
78094
+ * Automatic shares from CAPACITY (D755). Pure.
78095
+ *
78096
+ * A node's share is its capacity in parallel sessions over the cluster's; a
78097
+ * device's share inside its node follows its throughput. There is no load
78098
+ * input: nothing here depends on how many sessions ran, only on what each
78099
+ * node can carry.
78100
+ *
78101
+ * Two places a capacity can come from:
78102
+ * - `hardware` — each enabled accelerator at its Auto tune model, from the
78103
+ * measured latency table: ⌊fps × (1 − headroom) / camera fps⌋ sessions;
78104
+ * - `benchmark` — the node's own measured max sessions per device, read
78105
+ * through a {@link NodeCapacitySource}. A node whose results do not cover
78106
+ * every enabled device it would be sized on is sized from `hardware`
78107
+ * instead, and the answer says so per node — never a silent mix.
78108
+ */
78109
+ /**
78110
+ * The measured capacity of `deviceKey` for the model it runs NOW, or
78111
+ * undefined. A cell measured for another model says nothing about this one
78112
+ * (a failed load of a heavier build is not this build's capacity), and a cell
78113
+ * that names no model cannot be matched. The ONE predicate behind both the
78114
+ * automatic shares and the automatic benchmark's "has results".
78115
+ */
78116
+ function capacityForModel(node, deviceKey, modelId) {
78117
+ if (node === void 0 || modelId === null) return void 0;
78118
+ return node.devices.filter((d) => d.deviceKey === deviceKey && d.modelId === modelId).toSorted((a, b) => b.measuredAtMs - a.measuredAtMs)[0];
78119
+ }
78120
+ /**
78121
+ * Every measured (device × model) cell that no device runs now — what
78122
+ * {@link capacityForModel} skips, so the caller can say so once.
78123
+ */
78124
+ function foreignCapacityCells(recommendations, measured) {
78125
+ const out = [];
78126
+ for (const rec of recommendations) {
78127
+ const node = measured.get(rec.nodeId);
78128
+ if (node === void 0) continue;
78129
+ for (const cell of node.devices) {
78130
+ const runs = rec.devices.find((d) => d.deviceKey === cell.deviceKey)?.chosen?.modelId ?? null;
78131
+ if (cell.modelId === null || cell.modelId === runs) continue;
78132
+ out.push({
78133
+ nodeId: rec.nodeId,
78134
+ deviceKey: cell.deviceKey,
78135
+ measuredModelId: cell.modelId,
78136
+ runsModelId: runs
78137
+ });
78138
+ }
78139
+ }
78140
+ return out;
78141
+ }
78088
78142
  /** Integer shares summing to 100 (largest remainder); ties broken by key. */
78089
78143
  function toPercentShares(weights) {
78090
78144
  const total = [...weights.values()].reduce((a, b) => a + b, 0);
@@ -78136,7 +78190,11 @@ function sizeNode(rec, input) {
78136
78190
  if (input.source === "hardware") return fromHardware(null);
78137
78191
  if (!rec.reachable) return fromHardware(null);
78138
78192
  const results = input.measured.get(rec.nodeId);
78139
- const byKey = new Map((results?.devices ?? []).map((d) => [d.deviceKey, d]));
78193
+ const byKey = /* @__PURE__ */ new Map();
78194
+ for (const d of rec.devices) {
78195
+ const row = capacityForModel(results, d.deviceKey, d.chosen?.modelId ?? null);
78196
+ if (row !== void 0) byKey.set(d.deviceKey, row);
78197
+ }
78140
78198
  const usable = rec.devices.filter((d) => d.enabled && d.available);
78141
78199
  const mustCover = rec.devices.filter((d) => d.enabled && d.available && d.chosen !== null);
78142
78200
  const covered = usable.filter((d) => byKey.has(d.deviceKey));
@@ -78386,16 +78444,34 @@ function measuredDetectorCells(rows, nodeId, ctx) {
78386
78444
  return out;
78387
78445
  }
78388
78446
  /**
78389
- * Per node, each device at its newest fresh `pipeline` measurement — the
78390
- * `NodeCapacitySource` answer the automatic shares read.
78447
+ * Parallel sessions a `device`-phase cell stands for.
78448
+ *
78449
+ * Both numbers are "streams at the camera rate the hardware carried inside
78450
+ * the frame interval" (`kMax` for the detector alone, `maxSessions` for the
78451
+ * detector plus its enrichment steps) and the benchmark page already shows
78452
+ * `kMax` as "streams", so the unit is the same and the conversion is the
78453
+ * identity. It is an UPPER bound for a session that also runs enrichment —
78454
+ * which is why a `pipeline` cell for the same model always wins over it.
78455
+ */
78456
+ function sessionsOfDeviceCell(kMax) {
78457
+ return Math.max(0, Math.floor(kMax));
78458
+ }
78459
+ /**
78460
+ * Per node, every (device × model) at its best fresh measurement: the newest
78461
+ * `pipeline` cell, else the newest `device` cell read as
78462
+ * {@link sessionsOfDeviceCell}. Several models of one device are kept apart —
78463
+ * the caller picks the one the device runs NOW (a cell for another model says
78464
+ * nothing about it).
78391
78465
  */
78392
78466
  function measuredCapacityFromResults(rows, nodeIds, ctx) {
78393
78467
  const byNode = /* @__PURE__ */ new Map();
78394
78468
  for (const row of freshResults(rows, ctx)) {
78395
- if (!nodeIds.includes(row.nodeId) || row.cell.phase !== "pipeline") continue;
78469
+ if (!nodeIds.includes(row.nodeId)) continue;
78470
+ const candidate = capacityOfRow(row);
78396
78471
  const devices = byNode.get(row.nodeId) ?? /* @__PURE__ */ new Map();
78397
- const held = devices.get(row.cell.deviceKey);
78398
- if (held === void 0 || row.measuredAtMs > held.measuredAtMs) devices.set(row.cell.deviceKey, row);
78472
+ const key = `${candidate.deviceKey}\u0000${candidate.modelId ?? ""}`;
78473
+ const held = devices.get(key);
78474
+ if (held === void 0 || betterCapacityCell(candidate, held)) devices.set(key, candidate);
78399
78475
  byNode.set(row.nodeId, devices);
78400
78476
  }
78401
78477
  const out = /* @__PURE__ */ new Map();
@@ -78404,16 +78480,32 @@ function measuredCapacityFromResults(rows, nodeIds, ctx) {
78404
78480
  if (devices === void 0) continue;
78405
78481
  out.set(nodeId, {
78406
78482
  nodeId,
78407
- devices: [...devices.values()].toSorted((a, b) => a.cell.deviceKey.localeCompare(b.cell.deviceKey)).map((r) => ({
78408
- deviceKey: r.cell.deviceKey,
78409
- maxSessions: r.cell.phase === "pipeline" ? r.cell.maxSessions : 0,
78410
- modelId: r.cell.modelId,
78411
- measuredAtMs: r.measuredAtMs
78412
- }))
78483
+ devices: [...devices.values()].toSorted((a, b) => a.deviceKey.localeCompare(b.deviceKey) || (a.modelId ?? "").localeCompare(b.modelId ?? ""))
78413
78484
  });
78414
78485
  }
78415
78486
  return out;
78416
78487
  }
78488
+ function capacityOfRow(row) {
78489
+ const { cell } = row;
78490
+ return cell.phase === "pipeline" ? {
78491
+ deviceKey: cell.deviceKey,
78492
+ maxSessions: cell.maxSessions,
78493
+ modelId: cell.modelId,
78494
+ measuredAtMs: row.measuredAtMs,
78495
+ phase: "pipeline"
78496
+ } : {
78497
+ deviceKey: cell.deviceKey,
78498
+ maxSessions: sessionsOfDeviceCell(cell.kMax),
78499
+ modelId: cell.modelId,
78500
+ measuredAtMs: row.measuredAtMs,
78501
+ phase: "device"
78502
+ };
78503
+ }
78504
+ /** A pipeline cell beats a device cell; within a phase the newer one wins. */
78505
+ function betterCapacityCell(a, b) {
78506
+ if (a.phase !== b.phase) return a.phase === "pipeline";
78507
+ return a.measuredAtMs > b.measuredAtMs;
78508
+ }
78417
78509
  var BENCH_ROOT_STEP_ID = "object-detection";
78418
78510
  function slotAddons(schema, slot) {
78419
78511
  return schema.slots.find((s) => s.id === slot)?.addons ?? [];
@@ -78997,6 +79089,44 @@ var NodeBenchmarkStore = class {
78997
79089
  return true;
78998
79090
  }
78999
79091
  };
79092
+ //#endregion
79093
+ //#region src/node-benchmark/auto-benchmark.ts
79094
+ /**
79095
+ * The automatic node benchmark: an enabled, available device that has no
79096
+ * measured result — a new node, or a device discovered later — is measured by
79097
+ * a `device`-phase run limited to it, so the automatic shares read a number
79098
+ * instead of the table without the operator starting anything.
79099
+ *
79100
+ * - ONE auto run at a time, and never while any benchmark runs (the service
79101
+ * allows one run cluster-wide);
79102
+ * - a node that is busy is deferred: it carries more than
79103
+ * {@link AUTO_BENCH_MAX_CAMERAS_ON_NODE} cameras (a benchmark moves them
79104
+ * off and a camera with no other frame source would stay and spoil the
79105
+ * numbers). The hub, which usually carries cameras, waits until it is quiet
79106
+ * and the operator can still run it by hand;
79107
+ * - a failed run is retried at most {@link AUTO_BENCH_MAX_ATTEMPTS} times,
79108
+ * {@link AUTO_BENCH_RETRY_MS} apart. The ledger is persisted per (node,
79109
+ * device) and restored at boot — a respawn right after a lost run would
79110
+ * otherwise start the next one at once and hand the device a fresh budget;
79111
+ * a device that gains a usable result is forgotten;
79112
+ * - never for a device the operator disabled, one that is unplugged or
79113
+ * excluded, or one that already has a usable result: a fresh cell for the
79114
+ * model it runs NOW (the predicate the automatic shares read, so the two
79115
+ * cannot disagree). A result for another model does not count — the device
79116
+ * is measured again.
79117
+ *
79118
+ * The planner is pure; the scheduler injects every read and the start.
79119
+ */
79120
+ /**
79121
+ * `nodeId\0deviceKey` of every device that has a usable result: a fresh
79122
+ * measurement for the model it runs now. The SAME predicate
79123
+ * ({@link capacityForModel}) the automatic shares size a node by.
79124
+ */
79125
+ function benchmarkedDevices(runs, measured) {
79126
+ const out = /* @__PURE__ */ new Set();
79127
+ for (const run of runs) if (capacityForModel(measured.get(run.nodeId), run.deviceKey, run.modelId) !== void 0) out.add(benchKey(run.nodeId, run.deviceKey));
79128
+ return out;
79129
+ }
79000
79130
  /** How often the scheduler looks (it is also nudged when a device set changes). */
79001
79131
  var AUTO_BENCH_TICK_MS = 6e4;
79002
79132
  /** Hard deadline of an auto run (a one-model device ramp takes well under this). */
@@ -79083,6 +79213,7 @@ var AutoBenchmarkScheduler = class {
79083
79213
  attempts = /* @__PURE__ */ new Map();
79084
79214
  /** The last decision logged per (node, device): a decision is logged when it CHANGES. */
79085
79215
  lastLogged = /* @__PURE__ */ new Map();
79216
+ restored = null;
79086
79217
  inFlight = null;
79087
79218
  timer = null;
79088
79219
  constructor(deps) {
@@ -79112,6 +79243,27 @@ var AutoBenchmarkScheduler = class {
79112
79243
  });
79113
79244
  return this.inFlight;
79114
79245
  }
79246
+ /** Load the persisted ledger ONCE, before the first decision; a failed read is a fresh budget, said so. */
79247
+ restore() {
79248
+ this.restored ??= (async () => {
79249
+ const persistence = this.deps.persistence;
79250
+ if (persistence === void 0) return;
79251
+ try {
79252
+ for (const [key, attempt] of await persistence.load()) if (!this.attempts.has(key)) this.attempts.set(key, attempt);
79253
+ this.deps.logger.info("auto benchmark: attempts restored", { meta: { devices: this.attempts.size } });
79254
+ } catch (err) {
79255
+ this.deps.logger.warn("auto benchmark: attempt ledger unreadable — starting with none", { meta: { error: errMsg$2(err) } });
79256
+ }
79257
+ })();
79258
+ return this.restored;
79259
+ }
79260
+ async persist() {
79261
+ try {
79262
+ await this.deps.persistence?.save(this.attempts);
79263
+ } catch (err) {
79264
+ this.deps.logger.warn("auto benchmark: attempt ledger not saved — kept in memory", { meta: { error: errMsg$2(err) } });
79265
+ }
79266
+ }
79115
79267
  note(decision, extra = "") {
79116
79268
  const signature = `${decision.kind}:${"reason" in decision ? decision.reason : ""}${extra}`;
79117
79269
  const key = benchKey(decision.nodeId, decision.deviceKey);
@@ -79128,14 +79280,20 @@ var AutoBenchmarkScheduler = class {
79128
79280
  }
79129
79281
  async run(trigger) {
79130
79282
  const nowMs = this.deps.now?.() ?? Date.now();
79283
+ await this.restore();
79131
79284
  const plan = planAutoBenchmark({
79132
79285
  nodes: await this.deps.readNodes(),
79133
- benchmarked: this.deps.benchmarked(),
79286
+ benchmarked: await this.deps.benchmarked(),
79134
79287
  runActive: this.deps.runActive(),
79135
79288
  attempts: this.attempts,
79136
79289
  nowMs
79137
79290
  });
79138
- for (const decision of plan.decisions) if (decision.kind !== "start") this.note(decision);
79291
+ let forgot = false;
79292
+ for (const decision of plan.decisions) {
79293
+ if (decision.kind !== "start") this.note(decision);
79294
+ if (decision.kind === "skip" && decision.reason === "has-results") forgot = this.attempts.delete(benchKey(decision.nodeId, decision.deviceKey)) || forgot;
79295
+ }
79296
+ if (forgot) await this.persist();
79139
79297
  if (plan.start === null) return;
79140
79298
  const { nodeId, deviceKeys } = plan.start;
79141
79299
  const models = await this.deps.pickModels(nodeId, deviceKeys);
@@ -79180,6 +79338,7 @@ var AutoBenchmarkScheduler = class {
79180
79338
  lastAtMs: nowMs
79181
79339
  });
79182
79340
  this.lastLogged.delete(key);
79341
+ await this.persist();
79183
79342
  this.deps.logger.info("auto benchmark: started", {
79184
79343
  tags: { nodeId },
79185
79344
  meta: {
@@ -79193,6 +79352,29 @@ var AutoBenchmarkScheduler = class {
79193
79352
  }
79194
79353
  };
79195
79354
  //#endregion
79355
+ //#region src/node-benchmark/auto-benchmark-attempts.ts
79356
+ function attemptsToBlob(attempts) {
79357
+ const out = {};
79358
+ for (const [key, attempt] of attempts) {
79359
+ const sep = key.indexOf("\0");
79360
+ if (sep < 0) continue;
79361
+ const nodeId = key.slice(0, sep);
79362
+ out[nodeId] = {
79363
+ ...out[nodeId],
79364
+ [key.slice(sep + 1)]: attempt
79365
+ };
79366
+ }
79367
+ return out;
79368
+ }
79369
+ function attemptsFromBlob(blob, keyOf) {
79370
+ const out = /* @__PURE__ */ new Map();
79371
+ for (const [nodeId, devices] of Object.entries(blob)) for (const [deviceKey, attempt] of Object.entries(devices)) out.set(keyOf(nodeId, deviceKey), {
79372
+ count: attempt.count,
79373
+ lastAtMs: attempt.lastAtMs
79374
+ });
79375
+ return out;
79376
+ }
79377
+ //#endregion
79196
79378
  //#region src/node-benchmark/node-benchmark-service.ts
79197
79379
  /**
79198
79380
  * The node benchmark lifecycle (D791) — the orchestrator owns it because it
@@ -89913,7 +90095,7 @@ var NodeStressController = class NodeStressController {
89913
90095
  }
89914
90096
  /** The configured mode, defaulting to {@link DEFAULT_NODE_STRESS_MODE}. */
89915
90097
  mode() {
89916
- return parseNodeStressMode((this.deps.readGlobalSettings() ?? {}).nodeStressMode) ?? "observe";
90098
+ return parseNodeStressMode((this.deps.readGlobalSettings() ?? {}).nodeStressMode) ?? "failover";
89917
90099
  }
89918
90100
  /**
89919
90101
  * Record one camera metrics snapshot. Shares the `PipelineCameraMetricsSnapshot`
@@ -91567,6 +91749,10 @@ async function redispatchAffected(deps, cleared) {
91567
91749
  * schema is kept faithful to the on-disk shape rather than overly strict.
91568
91750
  */
91569
91751
  var NodeBindingsSchema = record(string(), record(string(), string()));
91752
+ var AutoBenchmarkAttemptsSchema = record(string(), record(string(), object({
91753
+ count: number().int().min(0),
91754
+ lastAtMs: number()
91755
+ })));
91570
91756
  var StoredAgentAddonConfigSchema = object({
91571
91757
  enabled: boolean(),
91572
91758
  modelId: string().optional(),
@@ -97069,7 +97255,7 @@ function buildGlobalSettingsSections(options) {
97069
97255
  key: "nodeStressMode",
97070
97256
  type: "select",
97071
97257
  label: "Node stress handling",
97072
- description: "Per-NODE saturation verdict, computed from queue pressure, dropped-frame rate and the delivered-vs-configured fps of ACTIVE cameras only (an idle on-motion camera is never evidence). Observe = the verdict is logged when a node changes state, and nothing moves — this is how the thresholds get measured on your fleet. Failover = a saturated node additionally hands ONE camera at a time to an idle node, under a per-camera cooldown, a ban on returning it to the node it just left, and a cluster-wide cap of 4 moves an hour. A camera is never paused: running degraded beats running blind.",
97258
+ description: "When a node is saturated (queue pressure, dropped frames and delivered-vs-configured fps of active cameras), Failover moves one of its cameras to an idle node. One camera at a time, a cooldown per camera, never back to the node it just left, at most 4 moves an hour across the cluster. A camera is never paused. Observe only logs the verdict; Off does nothing.",
97073
97259
  options: [
97074
97260
  {
97075
97261
  value: "off",
@@ -97084,7 +97270,7 @@ function buildGlobalSettingsSections(options) {
97084
97270
  label: "Failover (relocate a camera)"
97085
97271
  }
97086
97272
  ],
97087
- default: "observe"
97273
+ default: "failover"
97088
97274
  },
97089
97275
  {
97090
97276
  key: "fpsLowWindowMs",
@@ -98022,6 +98208,7 @@ var AutoTuneService = class {
98022
98208
  } catch (err) {
98023
98209
  this.deps.logger.warn("load balancing: benchmark results unreadable — sized from hardware", { meta: { error: errMsg(err) } });
98024
98210
  }
98211
+ this.noteForeignCells(rec.recommendations, measured);
98025
98212
  return computeCapacityShares({
98026
98213
  recommendations: rec.recommendations,
98027
98214
  source,
@@ -98031,6 +98218,28 @@ var AutoTuneService = class {
98031
98218
  minSessionsPerDevice
98032
98219
  });
98033
98220
  }
98221
+ /** Foreign-model cells already reported, so a recompute does not repeat the line. */
98222
+ reportedForeign = /* @__PURE__ */ new Set();
98223
+ /**
98224
+ * A measured cell for a model the device does not run now is ignored by the
98225
+ * shares (D913 follow-up); say so ONCE per (node, device, models), with the
98226
+ * device key — silence would read as "measured and used".
98227
+ */
98228
+ noteForeignCells(recommendations, measured) {
98229
+ for (const cell of foreignCapacityCells(recommendations, measured)) {
98230
+ const key = `${cell.nodeId}|${cell.deviceKey}|${cell.measuredModelId}|${cell.runsModelId ?? ""}`;
98231
+ if (this.reportedForeign.has(key)) continue;
98232
+ this.reportedForeign.add(key);
98233
+ this.deps.logger.info("load balancing: benchmark cell for another model ignored", {
98234
+ tags: { nodeId: cell.nodeId },
98235
+ meta: {
98236
+ deviceKey: cell.deviceKey,
98237
+ measuredModelId: cell.measuredModelId,
98238
+ runsModelId: cell.runsModelId
98239
+ }
98240
+ });
98241
+ }
98242
+ }
98034
98243
  /**
98035
98244
  * Gather every node and recommend. Nothing but hardware, catalog, the sizing
98036
98245
  * and — for a plan — the custom pins goes in. `customPins` maps
@@ -100542,6 +100751,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
100542
100751
  */
100543
100752
  startAutoBenchmark(store) {
100544
100753
  this.autoBenchmark?.stop();
100754
+ const attempts = this.state("autoBenchmarkAttempts", AutoBenchmarkAttemptsSchema, {});
100545
100755
  this.autoBenchmark = new AutoBenchmarkScheduler({
100546
100756
  logger: this.ctx.logger,
100547
100757
  readNodes: async () => {
@@ -100555,7 +100765,19 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
100555
100765
  devices: live[nodeId] ?? []
100556
100766
  })));
100557
100767
  },
100558
- benchmarked: () => new Set(store.listResults().map((r) => benchKey(r.nodeId, r.cell.deviceKey))),
100768
+ benchmarked: async () => {
100769
+ if (store.listResults().length === 0) return /* @__PURE__ */ new Set();
100770
+ const hardware = await this.autoTune().suggestShares({
100771
+ headroom: AUTO_SHARE_HEADROOM,
100772
+ source: "hardware"
100773
+ });
100774
+ const measured = await this.readMeasuredCapacity(hardware.nodes.map((n) => n.nodeId));
100775
+ return benchmarkedDevices(hardware.nodes.flatMap((n) => n.devices.map((d) => ({
100776
+ nodeId: n.nodeId,
100777
+ deviceKey: d.deviceKey,
100778
+ modelId: d.modelId
100779
+ }))), measured);
100780
+ },
100559
100781
  runActive: () => this.benchmarkService().isRunActive(),
100560
100782
  pickModels: async (nodeId, deviceKeys) => {
100561
100783
  const candidates = await this.getNodeBenchmarkCandidates({ nodeId });
@@ -100566,7 +100788,11 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
100566
100788
  }
100567
100789
  return out;
100568
100790
  },
100569
- start: (plan) => this.benchmarkService().start(plan)
100791
+ start: (plan) => this.benchmarkService().start(plan),
100792
+ persistence: {
100793
+ load: async () => attemptsFromBlob(await attempts.get(), benchKey),
100794
+ save: (map) => attempts.set(attemptsToBlob(map))
100795
+ }
100570
100796
  });
100571
100797
  this.autoBenchmark.start();
100572
100798
  this.autoBenchmark.tick("boot");
@@ -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-Brtt25nW.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-C4ACA0hQ.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.2.619",
3
+ "version": "1.2.620",
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",