@camstack/addon-training 0.1.74 → 0.1.76

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 (20) hide show
  1. package/dist/{MotionZonesSettings-C9bkQa-A.mjs → MotionZonesSettings-D5qNBXG6.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-Q8Q9kHuZ.mjs → PrivacyMaskSettings-D-DPswdN.mjs} +2 -2
  3. package/dist/{SceneMonitorEditor-DVDBQyRh.mjs → SceneMonitorEditor-DtRIp_YE.mjs} +1 -1
  4. package/dist/_stub.js +4370 -4365
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-BEr-JRVK.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-CHXpYatD.mjs} +4 -4
  6. package/dist/_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DoK4LRXI.mjs +26 -0
  7. package/dist/{dist-DlBWQhNT.js → dist-BWgu_wcI.js} +6878 -1593
  8. package/dist/{dist-CZ8OqMuN.mjs → dist-B_SLRS5u.mjs} +6878 -1593
  9. package/dist/{hostInit-Cb-B0_my.mjs → hostInit-B52B1CB4.mjs} +3 -3
  10. package/dist/index.js +1 -1
  11. package/dist/orchestrator/training.addon.js +67 -44
  12. package/dist/orchestrator/training.addon.mjs +67 -44
  13. package/dist/{player-overlays-CGcrSt2j.mjs → player-overlays-DpkZn9vj.mjs} +1 -1
  14. package/dist/providers/modal/training-modal.addon.js +1 -1
  15. package/dist/providers/modal/training-modal.addon.mjs +1 -1
  16. package/dist/remoteEntry.js +1 -1
  17. package/dist/{responsive-Dbc1rEu9.mjs → responsive-rqNDO6E-.mjs} +1 -1
  18. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-DuCpFRYX.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-B3b43ydO.mjs} +1 -1
  19. package/package.json +1 -1
  20. package/dist/_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CSzn01SK.mjs +0 -26
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.2.401",
39
+ version: "1.2.404",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  singleton: !0,
@@ -45,7 +45,7 @@ async function r() {
45
45
  }
46
46
  },
47
47
  "@camstack/sdk": {
48
- version: "1.2.150",
48
+ version: "1.2.151",
49
49
  scope: "default",
50
50
  shareConfig: {
51
51
  singleton: !0,
@@ -81,7 +81,7 @@ async function r() {
81
81
  }
82
82
  },
83
83
  "@camstack/ui-library": {
84
- version: "1.2.317",
84
+ version: "1.2.319",
85
85
  scope: "default",
86
86
  shareConfig: {
87
87
  singleton: !0,
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- require("./dist-DlBWQhNT.js");
2
+ require("./dist-BWgu_wcI.js");
3
3
  const require_orchestrator_training_addon = require("./orchestrator/training.addon.js");
4
4
  const require_providers_modal_training_modal_addon = require("./providers/modal/training-modal.addon.js");
5
5
  //#region src/providers/fake/fake-provider.ts
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../dist-DlBWQhNT.js");
2
+ const require_dist = require("../dist-BWgu_wcI.js");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_dist.__toESM(node_path);
5
5
  let node_crypto = require("node:crypto");
@@ -180,23 +180,31 @@ function pinPort(api) {
180
180
  }
181
181
  return out;
182
182
  },
183
- getOverride: async (deviceId, nodeId, deviceKey) => {
184
- const patch = (await orchestrator.getCameraStepOverrides.query({ deviceId }))?.[nodeId]?.[deviceKey]?.[DETECTOR_STEP];
185
- if (patch === void 0) return null;
186
- return {
187
- ...patch.modelId !== void 0 ? { modelId: patch.modelId } : {},
188
- ...patch.settings !== void 0 ? { settings: { ...patch.settings } } : {}
189
- };
183
+ getPin: async (nodeId, deviceKey) => {
184
+ return (await orchestrator.getAgentSettings.query({ agentNodeId: nodeId }))?.inferenceDevices?.[deviceKey]?.steps?.["object-detection"]?.modelId ?? null;
190
185
  },
191
- setOverride: async (deviceId, nodeId, deviceKey, patch) => {
192
- await orchestrator.setCameraStepOverride.mutate({
193
- deviceId,
186
+ setPin: async (nodeId, deviceKey, modelId) => {
187
+ const devices = (await orchestrator.getAgentSettings.query({ agentNodeId: nodeId }))?.inferenceDevices ?? {};
188
+ const entry = devices[deviceKey] ?? { enabled: true };
189
+ const { [DETECTOR_STEP]: current, ...otherSteps } = entry.steps ?? {};
190
+ const { modelId: _old, ...keep } = current ?? {};
191
+ const detector = modelId === null ? keep : {
192
+ ...keep,
193
+ modelId
194
+ };
195
+ const steps = Object.keys(detector).length > 0 ? {
196
+ ...otherSteps,
197
+ [DETECTOR_STEP]: detector
198
+ } : otherSteps;
199
+ const { steps: _oldSteps, ...rest } = entry;
200
+ await orchestrator.setAgentInferenceDevices.mutate({
194
201
  agentNodeId: nodeId,
195
- deviceKey,
196
- addonId: DETECTOR_STEP,
197
- patch: patch === null ? null : {
198
- ...patch.modelId !== void 0 ? { modelId: patch.modelId } : {},
199
- ...patch.settings !== void 0 ? { settings: { ...patch.settings } } : {}
202
+ inferenceDevices: {
203
+ ...devices,
204
+ [deviceKey]: {
205
+ ...rest,
206
+ ...Object.keys(steps).length > 0 ? { steps } : {}
207
+ }
200
208
  }
201
209
  });
202
210
  }
@@ -1423,16 +1431,16 @@ function uniqueBaseModelIds(ids) {
1423
1431
  //#region src/orchestrator/return-flow.ts
1424
1432
  /**
1425
1433
  * The return flow (D805, D806): a verified result → Model Studio → a canary
1426
- * on chosen cameras → rollback. Every step is an operator click; nothing here
1434
+ * on chosen accelerators → rollback. Every step is an operator click; nothing here
1427
1435
  * runs on its own (operator decision 2026-10-08: never auto-register).
1428
1436
  *
1429
1437
  * Register hands the staged bundle to `custom-model-registry.importModelBundle`
1430
1438
  * over `ctx.peerBytes`, with the catalog rows completed here (labels, licence,
1431
1439
  * the base's D741 floors) and every format URL rewritten to
1432
- * `camstack-local://`. A canary pins the camera's root detector, on every
1433
- * accelerator it can land on, to the variant that matches what that
1434
- * accelerator ran (INT8 where the base pin was INT8), and remembers EXACTLY
1435
- * what it replaced so a rollback restores it.
1440
+ * `camstack-local://`. A canary pins the root detector of each chosen
1441
+ * ACCELERATOR — a model is chosen by the accelerator that runs it (D927) — to
1442
+ * the variant that matches what that accelerator ran (INT8 where its pin was
1443
+ * INT8), and remembers EXACTLY what it replaced so a rollback restores it.
1436
1444
  */
1437
1445
  var DETECTOR_STEP_ID = "object-detection";
1438
1446
  var TRAINER_SOURCE_URL = "https://huggingface.co/camstack/camstack-trainer";
@@ -1480,14 +1488,25 @@ function catalogEntryFromDraft(input) {
1480
1488
  formats
1481
1489
  };
1482
1490
  }
1483
- /** Which registered variant each (camera, accelerator) gets: INT8 where the base pin was INT8. */
1491
+ function acceleratorKey(a) {
1492
+ return `${a.nodeId}|${a.deviceKey}`;
1493
+ }
1494
+ /**
1495
+ * Which registered variant each chosen accelerator gets: INT8 where its pin
1496
+ * was INT8. An accelerator that does not run the root detector is REFUSED by
1497
+ * name — a canary that silently skipped one would report a pin it never made.
1498
+ */
1484
1499
  function planCanary(input) {
1485
- return input.deviceIds.flatMap((deviceId) => input.devices.map((d) => ({
1486
- deviceId,
1487
- nodeId: d.nodeId,
1488
- deviceKey: d.deviceKey,
1489
- modelId: input.int8Id !== null && d.pinnedModelId?.endsWith("-int8") === true ? input.int8Id : input.baseId
1490
- })));
1500
+ const byKey = new Map(input.devices.map((d) => [acceleratorKey(d), d]));
1501
+ return input.accelerators.map((a) => {
1502
+ const d = byKey.get(acceleratorKey(a));
1503
+ if (d === void 0) throw new Error(`${a.nodeId} · ${a.deviceKey} does not run the root detector`);
1504
+ return {
1505
+ nodeId: d.nodeId,
1506
+ deviceKey: d.deviceKey,
1507
+ modelId: input.int8Id !== null && d.pinnedModelId?.endsWith("-int8") === true ? input.int8Id : input.baseId
1508
+ };
1509
+ });
1491
1510
  }
1492
1511
  var ReturnFlow = class {
1493
1512
  ports;
@@ -1503,7 +1522,7 @@ var ReturnFlow = class {
1503
1522
  const parsed = require_dist.TrainingResultSchema.safeParse(JSON.parse(text));
1504
1523
  return parsed.success ? parsed.data : null;
1505
1524
  }
1506
- async register(id, canaryDeviceIds) {
1525
+ async register(id, canaryAccelerators) {
1507
1526
  const job = await this.requireJob(id);
1508
1527
  if (job.state !== "ready") throw new Error(`only a ready result can be registered (this one is ${job.state})`);
1509
1528
  const result = await this.readResult(job);
@@ -1555,53 +1574,52 @@ var ReturnFlow = class {
1555
1574
  modelIds: [...answer.modelIds],
1556
1575
  distributed: answer.distributed.length
1557
1576
  } });
1558
- if (canaryDeviceIds.length > 0) return this.canary(id, canaryDeviceIds);
1577
+ if (canaryAccelerators.length > 0) return this.canary(id, canaryAccelerators);
1559
1578
  return registered ?? job;
1560
1579
  }
1561
- async canary(id, deviceIds) {
1580
+ async canary(id, accelerators) {
1562
1581
  const job = await this.requireJob(id);
1563
1582
  if (job.state !== "registered" && job.state !== "canary") throw new Error("register the result before a canary");
1564
1583
  const [baseId, int8Id] = job.registeredModelIds;
1565
1584
  if (baseId === void 0) throw new Error("no registered model to canary");
1566
- const already = new Set(job.canary?.deviceIds ?? []);
1567
- const fresh = deviceIds.filter((d) => !already.has(d));
1585
+ const already = new Set((job.canary?.accelerators ?? []).map(acceleratorKey));
1568
1586
  const writes = planCanary({
1569
- deviceIds: fresh,
1587
+ accelerators: accelerators.filter((a) => !already.has(acceleratorKey(a))),
1570
1588
  devices: await this.ports.pins.listDetectorDevices(),
1571
1589
  baseId,
1572
1590
  int8Id: int8Id ?? null
1573
1591
  });
1574
1592
  const previous = [];
1575
1593
  for (const w of writes) {
1576
- const before = await this.ports.pins.getOverride(w.deviceId, w.nodeId, w.deviceKey);
1577
1594
  previous.push({
1578
- deviceId: w.deviceId,
1579
1595
  nodeId: w.nodeId,
1580
1596
  deviceKey: w.deviceKey,
1581
- patch: before === null ? null : { ...before }
1597
+ modelId: await this.ports.pins.getPin(w.nodeId, w.deviceKey)
1582
1598
  });
1583
- await this.ports.pins.setOverride(w.deviceId, w.nodeId, w.deviceKey, { modelId: w.modelId });
1599
+ await this.ports.pins.setPin(w.nodeId, w.deviceKey, w.modelId);
1584
1600
  }
1585
1601
  const updated = await this.mutator.update(id, (j) => ({
1586
1602
  ...j,
1587
1603
  state: "canary",
1588
1604
  canary: {
1589
- deviceIds: [...already, ...fresh],
1605
+ accelerators: [...j.canary?.accelerators ?? [], ...writes.map((w) => ({
1606
+ nodeId: w.nodeId,
1607
+ deviceKey: w.deviceKey
1608
+ }))],
1590
1609
  since: j.canary?.since ?? this.ports.now(),
1591
1610
  previous: [...j.canary?.previous ?? [], ...previous]
1592
1611
  }
1593
1612
  }));
1594
1613
  this.ports.logger.info("training canary pinned", { meta: {
1595
1614
  jobId: id,
1596
- cameras: fresh,
1597
- writes: writes.length
1615
+ accelerators: writes.map((w) => acceleratorKey(w))
1598
1616
  } });
1599
1617
  return updated ?? job;
1600
1618
  }
1601
1619
  async rollback(id) {
1602
1620
  const job = await this.requireJob(id);
1603
1621
  if (job.canary === null) return job;
1604
- for (const p of job.canary.previous) await this.ports.pins.setOverride(p.deviceId, p.nodeId, p.deviceKey, p.patch);
1622
+ for (const p of job.canary.previous) await this.ports.pins.setPin(p.nodeId, p.deviceKey, p.modelId);
1605
1623
  const updated = await this.mutator.update(id, (j) => ({
1606
1624
  ...j,
1607
1625
  state: "registered",
@@ -1789,8 +1807,13 @@ var TrainingAddon = class extends require_dist.BaseAddon {
1789
1807
  const job = await ports.jobs.get(id);
1790
1808
  return job === null ? null : flow.readResult(job);
1791
1809
  },
1792
- registerResult: async ({ id, canaryDeviceIds }) => flow.register(id, canaryDeviceIds),
1793
- canaryResult: async ({ id, deviceIds }) => flow.canary(id, deviceIds),
1810
+ listCanaryTargets: async () => (await ports.pins.listDetectorDevices()).map((d) => ({
1811
+ nodeId: d.nodeId,
1812
+ deviceKey: d.deviceKey,
1813
+ pinnedModelId: d.pinnedModelId
1814
+ })),
1815
+ registerResult: async ({ id, canaryAccelerators }) => flow.register(id, canaryAccelerators),
1816
+ canaryResult: async ({ id, accelerators }) => flow.canary(id, accelerators),
1794
1817
  rollbackCanary: async ({ id }) => flow.rollback(id),
1795
1818
  discardResult: async ({ id }) => flow.discard(id)
1796
1819
  };
@@ -1,4 +1,4 @@
1
- import { a as addonPagesSourceCapability, i as TrainingResultSchema, l as BaseAddon, m as defineRecordKinds, n as TrainingJobSchema, o as parseTrainingEventLine, r as TrainingJobSpecSchema, s as trainingCapability, t as TRAINING_JOB_SCHEMA, u as RECORD_MAX_DATA_BYTES } from "../dist-CZ8OqMuN.mjs";
1
+ import { a as addonPagesSourceCapability, i as TrainingResultSchema, l as BaseAddon, m as defineRecordKinds, n as TrainingJobSchema, o as parseTrainingEventLine, r as TrainingJobSpecSchema, s as trainingCapability, t as TRAINING_JOB_SCHEMA, u as RECORD_MAX_DATA_BYTES } from "../dist-B_SLRS5u.mjs";
2
2
  import path from "node:path";
3
3
  import { createHash } from "node:crypto";
4
4
  import { createReadStream } from "node:fs";
@@ -178,23 +178,31 @@ function pinPort(api) {
178
178
  }
179
179
  return out;
180
180
  },
181
- getOverride: async (deviceId, nodeId, deviceKey) => {
182
- const patch = (await orchestrator.getCameraStepOverrides.query({ deviceId }))?.[nodeId]?.[deviceKey]?.[DETECTOR_STEP];
183
- if (patch === void 0) return null;
184
- return {
185
- ...patch.modelId !== void 0 ? { modelId: patch.modelId } : {},
186
- ...patch.settings !== void 0 ? { settings: { ...patch.settings } } : {}
187
- };
181
+ getPin: async (nodeId, deviceKey) => {
182
+ return (await orchestrator.getAgentSettings.query({ agentNodeId: nodeId }))?.inferenceDevices?.[deviceKey]?.steps?.["object-detection"]?.modelId ?? null;
188
183
  },
189
- setOverride: async (deviceId, nodeId, deviceKey, patch) => {
190
- await orchestrator.setCameraStepOverride.mutate({
191
- deviceId,
184
+ setPin: async (nodeId, deviceKey, modelId) => {
185
+ const devices = (await orchestrator.getAgentSettings.query({ agentNodeId: nodeId }))?.inferenceDevices ?? {};
186
+ const entry = devices[deviceKey] ?? { enabled: true };
187
+ const { [DETECTOR_STEP]: current, ...otherSteps } = entry.steps ?? {};
188
+ const { modelId: _old, ...keep } = current ?? {};
189
+ const detector = modelId === null ? keep : {
190
+ ...keep,
191
+ modelId
192
+ };
193
+ const steps = Object.keys(detector).length > 0 ? {
194
+ ...otherSteps,
195
+ [DETECTOR_STEP]: detector
196
+ } : otherSteps;
197
+ const { steps: _oldSteps, ...rest } = entry;
198
+ await orchestrator.setAgentInferenceDevices.mutate({
192
199
  agentNodeId: nodeId,
193
- deviceKey,
194
- addonId: DETECTOR_STEP,
195
- patch: patch === null ? null : {
196
- ...patch.modelId !== void 0 ? { modelId: patch.modelId } : {},
197
- ...patch.settings !== void 0 ? { settings: { ...patch.settings } } : {}
200
+ inferenceDevices: {
201
+ ...devices,
202
+ [deviceKey]: {
203
+ ...rest,
204
+ ...Object.keys(steps).length > 0 ? { steps } : {}
205
+ }
198
206
  }
199
207
  });
200
208
  }
@@ -1421,16 +1429,16 @@ function uniqueBaseModelIds(ids) {
1421
1429
  //#region src/orchestrator/return-flow.ts
1422
1430
  /**
1423
1431
  * The return flow (D805, D806): a verified result → Model Studio → a canary
1424
- * on chosen cameras → rollback. Every step is an operator click; nothing here
1432
+ * on chosen accelerators → rollback. Every step is an operator click; nothing here
1425
1433
  * runs on its own (operator decision 2026-10-08: never auto-register).
1426
1434
  *
1427
1435
  * Register hands the staged bundle to `custom-model-registry.importModelBundle`
1428
1436
  * over `ctx.peerBytes`, with the catalog rows completed here (labels, licence,
1429
1437
  * the base's D741 floors) and every format URL rewritten to
1430
- * `camstack-local://`. A canary pins the camera's root detector, on every
1431
- * accelerator it can land on, to the variant that matches what that
1432
- * accelerator ran (INT8 where the base pin was INT8), and remembers EXACTLY
1433
- * what it replaced so a rollback restores it.
1438
+ * `camstack-local://`. A canary pins the root detector of each chosen
1439
+ * ACCELERATOR — a model is chosen by the accelerator that runs it (D927) — to
1440
+ * the variant that matches what that accelerator ran (INT8 where its pin was
1441
+ * INT8), and remembers EXACTLY what it replaced so a rollback restores it.
1434
1442
  */
1435
1443
  var DETECTOR_STEP_ID = "object-detection";
1436
1444
  var TRAINER_SOURCE_URL = "https://huggingface.co/camstack/camstack-trainer";
@@ -1478,14 +1486,25 @@ function catalogEntryFromDraft(input) {
1478
1486
  formats
1479
1487
  };
1480
1488
  }
1481
- /** Which registered variant each (camera, accelerator) gets: INT8 where the base pin was INT8. */
1489
+ function acceleratorKey(a) {
1490
+ return `${a.nodeId}|${a.deviceKey}`;
1491
+ }
1492
+ /**
1493
+ * Which registered variant each chosen accelerator gets: INT8 where its pin
1494
+ * was INT8. An accelerator that does not run the root detector is REFUSED by
1495
+ * name — a canary that silently skipped one would report a pin it never made.
1496
+ */
1482
1497
  function planCanary(input) {
1483
- return input.deviceIds.flatMap((deviceId) => input.devices.map((d) => ({
1484
- deviceId,
1485
- nodeId: d.nodeId,
1486
- deviceKey: d.deviceKey,
1487
- modelId: input.int8Id !== null && d.pinnedModelId?.endsWith("-int8") === true ? input.int8Id : input.baseId
1488
- })));
1498
+ const byKey = new Map(input.devices.map((d) => [acceleratorKey(d), d]));
1499
+ return input.accelerators.map((a) => {
1500
+ const d = byKey.get(acceleratorKey(a));
1501
+ if (d === void 0) throw new Error(`${a.nodeId} · ${a.deviceKey} does not run the root detector`);
1502
+ return {
1503
+ nodeId: d.nodeId,
1504
+ deviceKey: d.deviceKey,
1505
+ modelId: input.int8Id !== null && d.pinnedModelId?.endsWith("-int8") === true ? input.int8Id : input.baseId
1506
+ };
1507
+ });
1489
1508
  }
1490
1509
  var ReturnFlow = class {
1491
1510
  ports;
@@ -1501,7 +1520,7 @@ var ReturnFlow = class {
1501
1520
  const parsed = TrainingResultSchema.safeParse(JSON.parse(text));
1502
1521
  return parsed.success ? parsed.data : null;
1503
1522
  }
1504
- async register(id, canaryDeviceIds) {
1523
+ async register(id, canaryAccelerators) {
1505
1524
  const job = await this.requireJob(id);
1506
1525
  if (job.state !== "ready") throw new Error(`only a ready result can be registered (this one is ${job.state})`);
1507
1526
  const result = await this.readResult(job);
@@ -1553,53 +1572,52 @@ var ReturnFlow = class {
1553
1572
  modelIds: [...answer.modelIds],
1554
1573
  distributed: answer.distributed.length
1555
1574
  } });
1556
- if (canaryDeviceIds.length > 0) return this.canary(id, canaryDeviceIds);
1575
+ if (canaryAccelerators.length > 0) return this.canary(id, canaryAccelerators);
1557
1576
  return registered ?? job;
1558
1577
  }
1559
- async canary(id, deviceIds) {
1578
+ async canary(id, accelerators) {
1560
1579
  const job = await this.requireJob(id);
1561
1580
  if (job.state !== "registered" && job.state !== "canary") throw new Error("register the result before a canary");
1562
1581
  const [baseId, int8Id] = job.registeredModelIds;
1563
1582
  if (baseId === void 0) throw new Error("no registered model to canary");
1564
- const already = new Set(job.canary?.deviceIds ?? []);
1565
- const fresh = deviceIds.filter((d) => !already.has(d));
1583
+ const already = new Set((job.canary?.accelerators ?? []).map(acceleratorKey));
1566
1584
  const writes = planCanary({
1567
- deviceIds: fresh,
1585
+ accelerators: accelerators.filter((a) => !already.has(acceleratorKey(a))),
1568
1586
  devices: await this.ports.pins.listDetectorDevices(),
1569
1587
  baseId,
1570
1588
  int8Id: int8Id ?? null
1571
1589
  });
1572
1590
  const previous = [];
1573
1591
  for (const w of writes) {
1574
- const before = await this.ports.pins.getOverride(w.deviceId, w.nodeId, w.deviceKey);
1575
1592
  previous.push({
1576
- deviceId: w.deviceId,
1577
1593
  nodeId: w.nodeId,
1578
1594
  deviceKey: w.deviceKey,
1579
- patch: before === null ? null : { ...before }
1595
+ modelId: await this.ports.pins.getPin(w.nodeId, w.deviceKey)
1580
1596
  });
1581
- await this.ports.pins.setOverride(w.deviceId, w.nodeId, w.deviceKey, { modelId: w.modelId });
1597
+ await this.ports.pins.setPin(w.nodeId, w.deviceKey, w.modelId);
1582
1598
  }
1583
1599
  const updated = await this.mutator.update(id, (j) => ({
1584
1600
  ...j,
1585
1601
  state: "canary",
1586
1602
  canary: {
1587
- deviceIds: [...already, ...fresh],
1603
+ accelerators: [...j.canary?.accelerators ?? [], ...writes.map((w) => ({
1604
+ nodeId: w.nodeId,
1605
+ deviceKey: w.deviceKey
1606
+ }))],
1588
1607
  since: j.canary?.since ?? this.ports.now(),
1589
1608
  previous: [...j.canary?.previous ?? [], ...previous]
1590
1609
  }
1591
1610
  }));
1592
1611
  this.ports.logger.info("training canary pinned", { meta: {
1593
1612
  jobId: id,
1594
- cameras: fresh,
1595
- writes: writes.length
1613
+ accelerators: writes.map((w) => acceleratorKey(w))
1596
1614
  } });
1597
1615
  return updated ?? job;
1598
1616
  }
1599
1617
  async rollback(id) {
1600
1618
  const job = await this.requireJob(id);
1601
1619
  if (job.canary === null) return job;
1602
- for (const p of job.canary.previous) await this.ports.pins.setOverride(p.deviceId, p.nodeId, p.deviceKey, p.patch);
1620
+ for (const p of job.canary.previous) await this.ports.pins.setPin(p.nodeId, p.deviceKey, p.modelId);
1603
1621
  const updated = await this.mutator.update(id, (j) => ({
1604
1622
  ...j,
1605
1623
  state: "registered",
@@ -1787,8 +1805,13 @@ var TrainingAddon = class extends BaseAddon {
1787
1805
  const job = await ports.jobs.get(id);
1788
1806
  return job === null ? null : flow.readResult(job);
1789
1807
  },
1790
- registerResult: async ({ id, canaryDeviceIds }) => flow.register(id, canaryDeviceIds),
1791
- canaryResult: async ({ id, deviceIds }) => flow.canary(id, deviceIds),
1808
+ listCanaryTargets: async () => (await ports.pins.listDetectorDevices()).map((d) => ({
1809
+ nodeId: d.nodeId,
1810
+ deviceKey: d.deviceKey,
1811
+ pinnedModelId: d.pinnedModelId
1812
+ })),
1813
+ registerResult: async ({ id, canaryAccelerators }) => flow.register(id, canaryAccelerators),
1814
+ canaryResult: async ({ id, accelerators }) => flow.canary(id, accelerators),
1792
1815
  rollbackCanary: async ({ id }) => flow.rollback(id),
1793
1816
  discardResult: async ({ id }) => flow.discard(id)
1794
1817
  };
@@ -1,7 +1,7 @@
1
1
  import { d as e, u as t, w as n, y as r } from "./_virtual_mf___mfe_internal__addon_training_page__loadShare__react__loadShare__.js-CEvdvp1T.mjs";
2
2
  import { n as i, r as a, t as o } from "./_virtual_mf___mfe_internal__addon_training_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-DnrbBvrw.mjs";
3
3
  import { n as s, t as c } from "./chevron-right-cA8Hucbi.mjs";
4
- import { h as l } from "./responsive-Dbc1rEu9.mjs";
4
+ import { h as l } from "./responsive-rqNDO6E-.mjs";
5
5
  //#region ../ui-library/src/composites/widget-panel.tsx
6
6
  n();
7
7
  function u({ title: e, icon: t, headerRight: n, collapsible: l = !1, defaultOpen: u = !0, open: d, onOpenChange: f, bodyClassName: p = "p-3", children: m }) {
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../../dist-DlBWQhNT.js");
2
+ const require_dist = require("../../dist-BWgu_wcI.js");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_dist.__toESM(node_path, 1);
5
5
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { C as record, S as object, _ as isSecretClear, b as boolean, c as trainingProviderCapability, d as SECRET_CLEAR, f as arrayItemFieldPath, g as globalSecretScope, h as entryBody, l as BaseAddon, p as defineEntryKind, v as isSecretMarker, w as string, x as number, y as toSecretFieldView } from "../../dist-CZ8OqMuN.mjs";
1
+ import { C as record, S as object, _ as isSecretClear, b as boolean, c as trainingProviderCapability, d as SECRET_CLEAR, f as arrayItemFieldPath, g as globalSecretScope, h as entryBody, l as BaseAddon, p as defineEntryKind, v as isSecretMarker, w as string, x as number, y as toSecretFieldView } from "../../dist-B_SLRS5u.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import path, { dirname, posix } from "node:path";
4
4
  import { createHash, createPrivateKey, createPublicKey, createSign, randomBytes, randomUUID } from "node:crypto";
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-DuCpFRYX.mjs";
1
+ import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-B3b43ydO.mjs";
2
2
  export { t as get, e as init };
@@ -1,6 +1,6 @@
1
1
  import { b as e, d as t, g as n, l as r, n as i, u as a, w as o, y as s } from "./_virtual_mf___mfe_internal__addon_training_page__loadShare__react__loadShare__.js-CEvdvp1T.mjs";
2
2
  import "./_virtual_mf___mfe_internal__addon_training_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-DnrbBvrw.mjs";
3
- import { x as c } from "./_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CSzn01SK.mjs";
3
+ import { S as c } from "./_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DoK4LRXI.mjs";
4
4
  //#region ../ui-library/src/lib/shared-context.ts
5
5
  o();
6
6
  function l(e, t) {
@@ -2762,7 +2762,7 @@ async function or(e) {
2762
2762
  }
2763
2763
  }
2764
2764
  async function sr() {
2765
- return ir ||= or(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-BEr-JRVK.mjs")).catch((e) => {
2765
+ return ir ||= or(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-CHXpYatD.mjs")).catch((e) => {
2766
2766
  throw ir = void 0, e;
2767
2767
  }), ir;
2768
2768
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-training",
3
- "version": "0.1.74",
3
+ "version": "0.1.76",
4
4
  "description": "Cloud training for CamStack: fine-tune the root detector on your own frames, on your own GPU-cloud account",
5
5
  "keywords": [
6
6
  "camstack",