@camstack/addon-training 0.1.22 → 0.1.23

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 (22) hide show
  1. package/dist/{MotionZonesSettings-DshSNzpp.mjs → MotionZonesSettings-CwGjtvsX.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-DDaIx7j5.mjs → PrivacyMaskSettings-CM_WsrKB.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-CuJ7xjyd.mjs → SceneMonitorEditor-B-sAJ--C.mjs} +3 -3
  4. package/dist/_stub.js +4974 -4879
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-DJDgoXBt.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-DKblQaAP.mjs} +2 -2
  6. package/dist/{_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D7T4QEtw.mjs → _virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-ff7X-Xlf.mjs} +8 -8
  7. package/dist/{dist-DhCvklZW.mjs → dist-CVDWIKl7.mjs} +17 -4
  8. package/dist/{dist-Dd6EAdVX.js → dist-DwF1P1v7.js} +52 -3
  9. package/dist/{hostInit-DR-St7pA.mjs → hostInit-Di0tDODX.mjs} +1 -1
  10. package/dist/index.js +1 -1
  11. package/dist/orchestrator/training.addon.js +8 -1
  12. package/dist/orchestrator/training.addon.mjs +8 -1
  13. package/dist/{player-overlays-DUZMtmvk.mjs → player-overlays-BFAqW2L1.mjs} +1 -1
  14. package/dist/providers/modal/training-modal.addon.js +132 -38
  15. package/dist/providers/modal/training-modal.addon.mjs +132 -38
  16. package/dist/remoteEntry.js +1 -1
  17. package/dist/{responsive-C7QsFtTb.mjs → responsive-BBlQWFFi.mjs} +1 -1
  18. package/dist/{scene-monitor-copy-BtffdnnW.mjs → scene-monitor-copy-D5mKGqt0.mjs} +1 -1
  19. package/dist/{square-e5Ytjod_.mjs → square-D0FYSm8b.mjs} +1 -1
  20. package/dist/{trash-2-Clte6zIF.mjs → trash-2-DdjGvgDw.mjs} +1 -1
  21. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-dy7yMOdw.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-Dw400Y0a.mjs} +1 -1
  22. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../../dist-Dd6EAdVX.js");
2
+ const require_dist = require("../../dist-DwF1P1v7.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");
@@ -11,14 +11,43 @@ let timers_promises = require("timers/promises");
11
11
  let node_console = require("node:console");
12
12
  let node_os = require("node:os");
13
13
  let node_stream = require("node:stream");
14
- //#region src/providers/modal/modal-entries.ts
14
+ //#region src/providers/modal/entry-secret-view.ts
15
15
  /**
16
- * Modal provider entries — one credentials + defaults row per operator
17
- * account. Pure helpers so the store is unit-tested without BaseAddon.
16
+ * Build the {@link SecretFieldView} the Providers edit form needs for an
17
+ * entry's token secret.
18
18
  *
19
- * A pre-multi-entry config (flat tokenId/tokenSecret on the addon) migrates
20
- * to a single entry named {@link DEFAULT_MODAL_ENTRY_NAME}.
19
+ * `readStoredGlobalKey` / `this.config` answer VALUES (D798), never markers —
20
+ * so `toSecretFieldView(plaintext)` alone yields an inline view (`$secret:
21
+ * null`) and `SecretFieldControl` cannot reveal. When the secret is set we
22
+ * prefer the deterministic keychain ref (`ctx.secrets.ref`) so the eye can
23
+ * call `keychain-admin.reveal` (D789).
21
24
  */
25
+ /** View for one entry row, or `null` when nothing is stored. */
26
+ function entryTokenSecretView(tokenSecret, secrets, entryId) {
27
+ if (require_dist.isSecretMarker(tokenSecret)) return require_dist.toSecretFieldView(tokenSecret);
28
+ const plain = typeof tokenSecret === "string" ? tokenSecret.trim() : "";
29
+ if (plain === "") return null;
30
+ if (secrets === void 0) return require_dist.toSecretFieldView(plain);
31
+ return {
32
+ $secret: secrets.ref(require_dist.globalSecretScope(), require_dist.arrayItemFieldPath("entries", entryId, "tokenSecret")),
33
+ v: 1,
34
+ set: true
35
+ };
36
+ }
37
+ /** View for the legacy flat `tokenSecret` (pre-multi-entry Default). */
38
+ function flatTokenSecretView(tokenSecret, secrets) {
39
+ if (require_dist.isSecretMarker(tokenSecret)) return require_dist.toSecretFieldView(tokenSecret);
40
+ const plain = typeof tokenSecret === "string" ? tokenSecret.trim() : "";
41
+ if (plain === "") return null;
42
+ if (secrets === void 0) return require_dist.toSecretFieldView(plain);
43
+ return {
44
+ $secret: secrets.ref(require_dist.globalSecretScope(), "tokenSecret"),
45
+ v: 1,
46
+ set: true
47
+ };
48
+ }
49
+ //#endregion
50
+ //#region src/providers/modal/modal-entries.ts
22
51
  var MODAL_TYPE_ID = "modal";
23
52
  var DEFAULT_MODAL_ENTRY_NAME = "Default";
24
53
  var MODAL_ICON_ASSET = "assets/training-modal/icon.svg";
@@ -29,22 +58,44 @@ var DEFAULT_GRACE = 24;
29
58
  function legacyConfigured(config) {
30
59
  return (config.tokenId ?? "").trim() !== "" || (config.tokenSecret ?? "").trim() !== "";
31
60
  }
61
+ /**
62
+ * Keychain / secretStatePaths may omit an empty `tokenSecret` from a stored
63
+ * row (writing `''` is KEEP, not "store empty"). Readers must never see
64
+ * `undefined` and then call `.trim()`.
65
+ */
66
+ function secretAsString(value) {
67
+ return typeof value === "string" ? value : "";
68
+ }
69
+ function normalizeModalEntry(entry) {
70
+ return {
71
+ id: entry.id,
72
+ name: entry.name ?? "Default",
73
+ tokenId: entry.tokenId ?? "",
74
+ tokenSecret: secretAsString(entry.tokenSecret),
75
+ environment: entry.environment ?? "",
76
+ gpuType: entry.gpuType ?? DEFAULT_GPU,
77
+ region: entry.region ?? "",
78
+ costCapUsd: entry.costCapUsd ?? DEFAULT_COST,
79
+ maxRuntimeHours: entry.maxRuntimeHours ?? DEFAULT_HOURS,
80
+ failedDatasetGraceHours: entry.failedDatasetGraceHours ?? DEFAULT_GRACE
81
+ };
82
+ }
32
83
  /** Entries the operator sees — synthesises Default from legacy flat fields. */
33
84
  function effectiveModalEntries(config) {
34
- if (config.entries.length > 0) return config.entries;
85
+ if (config.entries.length > 0) return config.entries.map((e) => normalizeModalEntry(e));
35
86
  if (!legacyConfigured(config)) return [];
36
- return [{
87
+ return [normalizeModalEntry({
37
88
  id: "legacy-default",
38
89
  name: DEFAULT_MODAL_ENTRY_NAME,
39
- tokenId: config.tokenId ?? "",
40
- tokenSecret: config.tokenSecret ?? "",
41
- environment: config.environment ?? "",
42
- gpuType: config.gpuType ?? DEFAULT_GPU,
43
- region: config.region ?? "",
44
- costCapUsd: config.costCapUsd ?? DEFAULT_COST,
45
- maxRuntimeHours: config.maxRuntimeHours ?? DEFAULT_HOURS,
46
- failedDatasetGraceHours: config.failedDatasetGraceHours ?? DEFAULT_GRACE
47
- }];
90
+ tokenId: config.tokenId,
91
+ tokenSecret: config.tokenSecret,
92
+ environment: config.environment,
93
+ gpuType: config.gpuType,
94
+ region: config.region,
95
+ costCapUsd: config.costCapUsd,
96
+ maxRuntimeHours: config.maxRuntimeHours,
97
+ failedDatasetGraceHours: config.failedDatasetGraceHours
98
+ })];
48
99
  }
49
100
  function findModalEntry(config, entryId) {
50
101
  return effectiveModalEntries(config).find((e) => e.id === entryId) ?? null;
@@ -381,7 +432,10 @@ var ModalTrainingProvider = class {
381
432
  constructor(deps) {
382
433
  this.deps = deps;
383
434
  }
384
- describe = async () => this.deps.listEntries().map((entry) => this.descriptorOf(entry));
435
+ describe = async () => {
436
+ const views = await this.deps.secretViews();
437
+ return this.deps.listEntries().map((entry) => this.descriptorOf(entry, views.get(entry.id) ?? null));
438
+ };
385
439
  describeTypes = async () => [{
386
440
  typeId: MODAL_TYPE_ID,
387
441
  label: "Modal",
@@ -397,10 +451,11 @@ var ModalTrainingProvider = class {
397
451
  }));
398
452
  createEntry = async (input) => this.deps.createEntry(input);
399
453
  updateEntry = async (input) => {
454
+ const secret = input.tokenSecret === void 0 ? {} : require_dist.isSecretClear(input.tokenSecret) ? { clearTokenSecret: true } : input.tokenSecret === "" ? {} : { tokenSecret: input.tokenSecret };
400
455
  await this.deps.updateEntry(input.entryId, {
401
456
  name: input.name,
402
457
  ...input.tokenId !== void 0 ? { tokenId: input.tokenId } : {},
403
- ...input.tokenSecret !== void 0 ? { tokenSecret: input.tokenSecret } : {},
458
+ ...secret,
404
459
  ...input.environment !== void 0 ? { environment: input.environment } : {},
405
460
  ...input.gpuType !== void 0 ? { gpuType: input.gpuType } : {},
406
461
  ...input.region !== void 0 ? { region: input.region } : {},
@@ -687,9 +742,12 @@ var ModalTrainingProvider = class {
687
742
  }))];
688
743
  };
689
744
  entryConfigured(entry) {
690
- return entry.tokenId.trim() !== "" && entry.tokenSecret.trim() !== "";
745
+ const secret = typeof entry.tokenSecret === "string" ? entry.tokenSecret : "";
746
+ return (entry.tokenId ?? "").trim() !== "" && secret.trim() !== "";
691
747
  }
692
- descriptorOf(entry) {
748
+ descriptorOf(entry, tokenSecretView) {
749
+ const tokenId = entry.tokenId ?? "";
750
+ const environment = entry.environment ?? "";
693
751
  return {
694
752
  addonId: this.deps.addonId,
695
753
  entryId: entry.id,
@@ -706,15 +764,16 @@ var ModalTrainingProvider = class {
706
764
  dataResidencyNote: "Modal keeps the frames on a Volume of your workspace for the length of the job; the training container sees them only while it runs.",
707
765
  configured: this.entryConfigured(entry),
708
766
  defaults: {
709
- gpuType: entry.gpuType,
710
- region: entry.region,
711
- costCapUsd: entry.costCapUsd,
712
- maxRuntimeSec: Math.round(entry.maxRuntimeHours * 3600),
713
- failedDatasetGraceHours: entry.failedDatasetGraceHours
767
+ gpuType: entry.gpuType ?? "L4",
768
+ region: entry.region ?? "",
769
+ costCapUsd: entry.costCapUsd ?? 5,
770
+ maxRuntimeSec: Math.round((entry.maxRuntimeHours ?? 4) * 3600),
771
+ failedDatasetGraceHours: entry.failedDatasetGraceHours ?? 24
714
772
  },
715
- tokenId: entry.tokenId,
716
- environment: entry.environment,
717
- hasTokenSecret: entry.tokenSecret.trim() !== ""
773
+ tokenId,
774
+ environment,
775
+ hasTokenSecret: tokenSecretView !== null,
776
+ tokenSecretView
718
777
  };
719
778
  }
720
779
  requireGateway(entryId) {
@@ -88588,9 +88647,15 @@ var TrainingModalAddon = class extends require_dist.BaseAddon {
88588
88647
  }
88589
88648
  async onInitialize() {
88590
88649
  let next = this.config;
88591
- if (next.entries.length === 0) next = migrateLegacyModalConfig(next, () => (0, node_crypto.randomUUID)());
88650
+ let migratedLegacy = false;
88651
+ if (next.entries.length === 0) {
88652
+ const migrated = migrateLegacyModalConfig(next, () => (0, node_crypto.randomUUID)());
88653
+ migratedLegacy = migrated !== next;
88654
+ next = migrated;
88655
+ }
88592
88656
  next = ensureModalEntryIds(next, () => (0, node_crypto.randomUUID)());
88593
88657
  if (next !== this.config) await this.updateGlobalSettings(next);
88658
+ if (migratedLegacy) await this.clearSecrets(["tokenSecret"]);
88594
88659
  this.ledgerState = this.state(LEDGER_KEY, LedgerSchema, {});
88595
88660
  const peer = this.ctx.peerBytes;
88596
88661
  if (peer === void 0) throw new Error("training-modal needs the peer-bytes facility");
@@ -88606,17 +88671,27 @@ var TrainingModalAddon = class extends require_dist.BaseAddon {
88606
88671
  return { entryId: result.entryId };
88607
88672
  },
88608
88673
  updateEntry: async (entryId, patch) => {
88609
- const result = updateModalEntry(this.config, entryId, patch);
88674
+ const { clearTokenSecret, ...rest } = patch;
88675
+ const result = updateModalEntry(this.config, entryId, rest);
88610
88676
  if (!result.ok) throw new Error(`cannot update entry: ${result.reason}`);
88611
- await this.updateGlobalSettings(result.config);
88677
+ if (clearTokenSecret === true) await this.updateGlobalSettings({
88678
+ ...result.config,
88679
+ entries: result.config.entries.map((entry) => entry.id === entryId ? {
88680
+ ...entry,
88681
+ tokenSecret: require_dist.SECRET_CLEAR
88682
+ } : entry)
88683
+ });
88684
+ else await this.updateGlobalSettings(result.config);
88612
88685
  this.closeGateway(entryId);
88613
88686
  },
88614
88687
  deleteEntry: async (entryId) => {
88615
88688
  const result = deleteModalEntry(this.config, entryId);
88616
88689
  if (!result.ok) throw new Error(`cannot delete entry: ${result.reason}`);
88617
88690
  await this.updateGlobalSettings(result.config);
88691
+ await this.clearSecrets(["tokenSecret"]);
88618
88692
  this.closeGateway(entryId);
88619
88693
  },
88694
+ secretViews: () => this.readEntrySecretViews(),
88620
88695
  ledger: this.ledger(),
88621
88696
  peer,
88622
88697
  fetchText,
@@ -88625,7 +88700,10 @@ var TrainingModalAddon = class extends require_dist.BaseAddon {
88625
88700
  });
88626
88701
  this.ctx.logger.info("Modal training provider ready", { meta: {
88627
88702
  entries: effectiveModalEntries(this.config).length,
88628
- configured: effectiveModalEntries(this.config).some((e) => e.tokenId.trim() !== "" && e.tokenSecret.trim() !== "")
88703
+ configured: effectiveModalEntries(this.config).some((e) => {
88704
+ const secret = typeof e.tokenSecret === "string" ? e.tokenSecret : "";
88705
+ return (e.tokenId ?? "").trim() !== "" && secret.trim() !== "";
88706
+ })
88629
88707
  } });
88630
88708
  return { providers: [{
88631
88709
  capability: require_dist.trainingProviderCapability,
@@ -88643,15 +88721,18 @@ var TrainingModalAddon = class extends require_dist.BaseAddon {
88643
88721
  gatewayFor(entryId) {
88644
88722
  const entry = findModalEntry(this.config, entryId);
88645
88723
  if (entry === null) return null;
88646
- if (entry.tokenId.trim() === "" || entry.tokenSecret.trim() === "") return null;
88647
- const key = `${entry.tokenId}\u0000${entry.environment}`;
88724
+ const tokenId = (entry.tokenId ?? "").trim();
88725
+ const tokenSecret = (typeof entry.tokenSecret === "string" ? entry.tokenSecret : "").trim();
88726
+ if (tokenId === "" || tokenSecret === "") return null;
88727
+ const environment = (entry.environment ?? "").trim();
88728
+ const key = `${tokenId}\u0000${environment}`;
88648
88729
  const cached = this.gateways.get(entryId);
88649
88730
  if (cached !== void 0 && cached.key === key) return cached.gateway;
88650
88731
  cached?.gateway.close();
88651
88732
  const gateway = new ModalSdkGateway({
88652
- tokenId: entry.tokenId.trim(),
88653
- tokenSecret: entry.tokenSecret.trim(),
88654
- environment: entry.environment.trim()
88733
+ tokenId,
88734
+ tokenSecret,
88735
+ environment
88655
88736
  });
88656
88737
  this.gateways.set(entryId, {
88657
88738
  key,
@@ -88665,6 +88746,19 @@ var TrainingModalAddon = class extends require_dist.BaseAddon {
88665
88746
  cached.gateway.close();
88666
88747
  this.gateways.delete(entryId);
88667
88748
  }
88749
+ /**
88750
+ * Keychain views for the Providers edit form. Addon-facing reads answer
88751
+ * VALUES (D798), so we cannot recover markers from `readStoredGlobalKey`.
88752
+ * When a secret is present on the resolved entry we emit the deterministic
88753
+ * keychain ref (`ctx.secrets.ref`) so `SecretFieldControl` can reveal.
88754
+ */
88755
+ async readEntrySecretViews() {
88756
+ const map = /* @__PURE__ */ new Map();
88757
+ const secrets = this.ctx.secrets;
88758
+ for (const entry of effectiveModalEntries(this.config)) map.set(entry.id, entryTokenSecretView(entry.tokenSecret, secrets, entry.id));
88759
+ map.set("legacy-default", flatTokenSecretView(this.config.tokenSecret, secrets));
88760
+ return map;
88761
+ }
88668
88762
  ledger() {
88669
88763
  const state = () => {
88670
88764
  if (this.ledgerState === null) throw new Error("training-modal ledger not initialised");
@@ -1,4 +1,4 @@
1
- import { c as trainingProviderCapability, d as number, f as object, l as BaseAddon, m as string, p as record, u as boolean } from "../../dist-DhCvklZW.mjs";
1
+ import { _ as number, b as string, c as trainingProviderCapability, d as arrayItemFieldPath, f as globalSecretScope, g as boolean, h as toSecretFieldView, l as BaseAddon, m as isSecretMarker, p as isSecretClear, u as SECRET_CLEAR, v as object, y as record } from "../../dist-CVDWIKl7.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";
@@ -39,14 +39,43 @@ var __copyProps = (to, from, except, desc) => {
39
39
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
40
40
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
41
41
  //#endregion
42
- //#region src/providers/modal/modal-entries.ts
42
+ //#region src/providers/modal/entry-secret-view.ts
43
43
  /**
44
- * Modal provider entries — one credentials + defaults row per operator
45
- * account. Pure helpers so the store is unit-tested without BaseAddon.
44
+ * Build the {@link SecretFieldView} the Providers edit form needs for an
45
+ * entry's token secret.
46
46
  *
47
- * A pre-multi-entry config (flat tokenId/tokenSecret on the addon) migrates
48
- * to a single entry named {@link DEFAULT_MODAL_ENTRY_NAME}.
47
+ * `readStoredGlobalKey` / `this.config` answer VALUES (D798), never markers —
48
+ * so `toSecretFieldView(plaintext)` alone yields an inline view (`$secret:
49
+ * null`) and `SecretFieldControl` cannot reveal. When the secret is set we
50
+ * prefer the deterministic keychain ref (`ctx.secrets.ref`) so the eye can
51
+ * call `keychain-admin.reveal` (D789).
49
52
  */
53
+ /** View for one entry row, or `null` when nothing is stored. */
54
+ function entryTokenSecretView(tokenSecret, secrets, entryId) {
55
+ if (isSecretMarker(tokenSecret)) return toSecretFieldView(tokenSecret);
56
+ const plain = typeof tokenSecret === "string" ? tokenSecret.trim() : "";
57
+ if (plain === "") return null;
58
+ if (secrets === void 0) return toSecretFieldView(plain);
59
+ return {
60
+ $secret: secrets.ref(globalSecretScope(), arrayItemFieldPath("entries", entryId, "tokenSecret")),
61
+ v: 1,
62
+ set: true
63
+ };
64
+ }
65
+ /** View for the legacy flat `tokenSecret` (pre-multi-entry Default). */
66
+ function flatTokenSecretView(tokenSecret, secrets) {
67
+ if (isSecretMarker(tokenSecret)) return toSecretFieldView(tokenSecret);
68
+ const plain = typeof tokenSecret === "string" ? tokenSecret.trim() : "";
69
+ if (plain === "") return null;
70
+ if (secrets === void 0) return toSecretFieldView(plain);
71
+ return {
72
+ $secret: secrets.ref(globalSecretScope(), "tokenSecret"),
73
+ v: 1,
74
+ set: true
75
+ };
76
+ }
77
+ //#endregion
78
+ //#region src/providers/modal/modal-entries.ts
50
79
  var MODAL_TYPE_ID = "modal";
51
80
  var DEFAULT_MODAL_ENTRY_NAME = "Default";
52
81
  var MODAL_ICON_ASSET = "assets/training-modal/icon.svg";
@@ -57,22 +86,44 @@ var DEFAULT_GRACE = 24;
57
86
  function legacyConfigured(config) {
58
87
  return (config.tokenId ?? "").trim() !== "" || (config.tokenSecret ?? "").trim() !== "";
59
88
  }
89
+ /**
90
+ * Keychain / secretStatePaths may omit an empty `tokenSecret` from a stored
91
+ * row (writing `''` is KEEP, not "store empty"). Readers must never see
92
+ * `undefined` and then call `.trim()`.
93
+ */
94
+ function secretAsString(value) {
95
+ return typeof value === "string" ? value : "";
96
+ }
97
+ function normalizeModalEntry(entry) {
98
+ return {
99
+ id: entry.id,
100
+ name: entry.name ?? "Default",
101
+ tokenId: entry.tokenId ?? "",
102
+ tokenSecret: secretAsString(entry.tokenSecret),
103
+ environment: entry.environment ?? "",
104
+ gpuType: entry.gpuType ?? DEFAULT_GPU,
105
+ region: entry.region ?? "",
106
+ costCapUsd: entry.costCapUsd ?? DEFAULT_COST,
107
+ maxRuntimeHours: entry.maxRuntimeHours ?? DEFAULT_HOURS,
108
+ failedDatasetGraceHours: entry.failedDatasetGraceHours ?? DEFAULT_GRACE
109
+ };
110
+ }
60
111
  /** Entries the operator sees — synthesises Default from legacy flat fields. */
61
112
  function effectiveModalEntries(config) {
62
- if (config.entries.length > 0) return config.entries;
113
+ if (config.entries.length > 0) return config.entries.map((e) => normalizeModalEntry(e));
63
114
  if (!legacyConfigured(config)) return [];
64
- return [{
115
+ return [normalizeModalEntry({
65
116
  id: "legacy-default",
66
117
  name: DEFAULT_MODAL_ENTRY_NAME,
67
- tokenId: config.tokenId ?? "",
68
- tokenSecret: config.tokenSecret ?? "",
69
- environment: config.environment ?? "",
70
- gpuType: config.gpuType ?? DEFAULT_GPU,
71
- region: config.region ?? "",
72
- costCapUsd: config.costCapUsd ?? DEFAULT_COST,
73
- maxRuntimeHours: config.maxRuntimeHours ?? DEFAULT_HOURS,
74
- failedDatasetGraceHours: config.failedDatasetGraceHours ?? DEFAULT_GRACE
75
- }];
118
+ tokenId: config.tokenId,
119
+ tokenSecret: config.tokenSecret,
120
+ environment: config.environment,
121
+ gpuType: config.gpuType,
122
+ region: config.region,
123
+ costCapUsd: config.costCapUsd,
124
+ maxRuntimeHours: config.maxRuntimeHours,
125
+ failedDatasetGraceHours: config.failedDatasetGraceHours
126
+ })];
76
127
  }
77
128
  function findModalEntry(config, entryId) {
78
129
  return effectiveModalEntries(config).find((e) => e.id === entryId) ?? null;
@@ -409,7 +460,10 @@ var ModalTrainingProvider = class {
409
460
  constructor(deps) {
410
461
  this.deps = deps;
411
462
  }
412
- describe = async () => this.deps.listEntries().map((entry) => this.descriptorOf(entry));
463
+ describe = async () => {
464
+ const views = await this.deps.secretViews();
465
+ return this.deps.listEntries().map((entry) => this.descriptorOf(entry, views.get(entry.id) ?? null));
466
+ };
413
467
  describeTypes = async () => [{
414
468
  typeId: MODAL_TYPE_ID,
415
469
  label: "Modal",
@@ -425,10 +479,11 @@ var ModalTrainingProvider = class {
425
479
  }));
426
480
  createEntry = async (input) => this.deps.createEntry(input);
427
481
  updateEntry = async (input) => {
482
+ const secret = input.tokenSecret === void 0 ? {} : isSecretClear(input.tokenSecret) ? { clearTokenSecret: true } : input.tokenSecret === "" ? {} : { tokenSecret: input.tokenSecret };
428
483
  await this.deps.updateEntry(input.entryId, {
429
484
  name: input.name,
430
485
  ...input.tokenId !== void 0 ? { tokenId: input.tokenId } : {},
431
- ...input.tokenSecret !== void 0 ? { tokenSecret: input.tokenSecret } : {},
486
+ ...secret,
432
487
  ...input.environment !== void 0 ? { environment: input.environment } : {},
433
488
  ...input.gpuType !== void 0 ? { gpuType: input.gpuType } : {},
434
489
  ...input.region !== void 0 ? { region: input.region } : {},
@@ -715,9 +770,12 @@ var ModalTrainingProvider = class {
715
770
  }))];
716
771
  };
717
772
  entryConfigured(entry) {
718
- return entry.tokenId.trim() !== "" && entry.tokenSecret.trim() !== "";
773
+ const secret = typeof entry.tokenSecret === "string" ? entry.tokenSecret : "";
774
+ return (entry.tokenId ?? "").trim() !== "" && secret.trim() !== "";
719
775
  }
720
- descriptorOf(entry) {
776
+ descriptorOf(entry, tokenSecretView) {
777
+ const tokenId = entry.tokenId ?? "";
778
+ const environment = entry.environment ?? "";
721
779
  return {
722
780
  addonId: this.deps.addonId,
723
781
  entryId: entry.id,
@@ -734,15 +792,16 @@ var ModalTrainingProvider = class {
734
792
  dataResidencyNote: "Modal keeps the frames on a Volume of your workspace for the length of the job; the training container sees them only while it runs.",
735
793
  configured: this.entryConfigured(entry),
736
794
  defaults: {
737
- gpuType: entry.gpuType,
738
- region: entry.region,
739
- costCapUsd: entry.costCapUsd,
740
- maxRuntimeSec: Math.round(entry.maxRuntimeHours * 3600),
741
- failedDatasetGraceHours: entry.failedDatasetGraceHours
795
+ gpuType: entry.gpuType ?? "L4",
796
+ region: entry.region ?? "",
797
+ costCapUsd: entry.costCapUsd ?? 5,
798
+ maxRuntimeSec: Math.round((entry.maxRuntimeHours ?? 4) * 3600),
799
+ failedDatasetGraceHours: entry.failedDatasetGraceHours ?? 24
742
800
  },
743
- tokenId: entry.tokenId,
744
- environment: entry.environment,
745
- hasTokenSecret: entry.tokenSecret.trim() !== ""
801
+ tokenId,
802
+ environment,
803
+ hasTokenSecret: tokenSecretView !== null,
804
+ tokenSecretView
746
805
  };
747
806
  }
748
807
  requireGateway(entryId) {
@@ -88616,9 +88675,15 @@ var TrainingModalAddon = class extends BaseAddon {
88616
88675
  }
88617
88676
  async onInitialize() {
88618
88677
  let next = this.config;
88619
- if (next.entries.length === 0) next = migrateLegacyModalConfig(next, () => randomUUID());
88678
+ let migratedLegacy = false;
88679
+ if (next.entries.length === 0) {
88680
+ const migrated = migrateLegacyModalConfig(next, () => randomUUID());
88681
+ migratedLegacy = migrated !== next;
88682
+ next = migrated;
88683
+ }
88620
88684
  next = ensureModalEntryIds(next, () => randomUUID());
88621
88685
  if (next !== this.config) await this.updateGlobalSettings(next);
88686
+ if (migratedLegacy) await this.clearSecrets(["tokenSecret"]);
88622
88687
  this.ledgerState = this.state(LEDGER_KEY, LedgerSchema, {});
88623
88688
  const peer = this.ctx.peerBytes;
88624
88689
  if (peer === void 0) throw new Error("training-modal needs the peer-bytes facility");
@@ -88634,17 +88699,27 @@ var TrainingModalAddon = class extends BaseAddon {
88634
88699
  return { entryId: result.entryId };
88635
88700
  },
88636
88701
  updateEntry: async (entryId, patch) => {
88637
- const result = updateModalEntry(this.config, entryId, patch);
88702
+ const { clearTokenSecret, ...rest } = patch;
88703
+ const result = updateModalEntry(this.config, entryId, rest);
88638
88704
  if (!result.ok) throw new Error(`cannot update entry: ${result.reason}`);
88639
- await this.updateGlobalSettings(result.config);
88705
+ if (clearTokenSecret === true) await this.updateGlobalSettings({
88706
+ ...result.config,
88707
+ entries: result.config.entries.map((entry) => entry.id === entryId ? {
88708
+ ...entry,
88709
+ tokenSecret: SECRET_CLEAR
88710
+ } : entry)
88711
+ });
88712
+ else await this.updateGlobalSettings(result.config);
88640
88713
  this.closeGateway(entryId);
88641
88714
  },
88642
88715
  deleteEntry: async (entryId) => {
88643
88716
  const result = deleteModalEntry(this.config, entryId);
88644
88717
  if (!result.ok) throw new Error(`cannot delete entry: ${result.reason}`);
88645
88718
  await this.updateGlobalSettings(result.config);
88719
+ await this.clearSecrets(["tokenSecret"]);
88646
88720
  this.closeGateway(entryId);
88647
88721
  },
88722
+ secretViews: () => this.readEntrySecretViews(),
88648
88723
  ledger: this.ledger(),
88649
88724
  peer,
88650
88725
  fetchText,
@@ -88653,7 +88728,10 @@ var TrainingModalAddon = class extends BaseAddon {
88653
88728
  });
88654
88729
  this.ctx.logger.info("Modal training provider ready", { meta: {
88655
88730
  entries: effectiveModalEntries(this.config).length,
88656
- configured: effectiveModalEntries(this.config).some((e) => e.tokenId.trim() !== "" && e.tokenSecret.trim() !== "")
88731
+ configured: effectiveModalEntries(this.config).some((e) => {
88732
+ const secret = typeof e.tokenSecret === "string" ? e.tokenSecret : "";
88733
+ return (e.tokenId ?? "").trim() !== "" && secret.trim() !== "";
88734
+ })
88657
88735
  } });
88658
88736
  return { providers: [{
88659
88737
  capability: trainingProviderCapability,
@@ -88671,15 +88749,18 @@ var TrainingModalAddon = class extends BaseAddon {
88671
88749
  gatewayFor(entryId) {
88672
88750
  const entry = findModalEntry(this.config, entryId);
88673
88751
  if (entry === null) return null;
88674
- if (entry.tokenId.trim() === "" || entry.tokenSecret.trim() === "") return null;
88675
- const key = `${entry.tokenId}\u0000${entry.environment}`;
88752
+ const tokenId = (entry.tokenId ?? "").trim();
88753
+ const tokenSecret = (typeof entry.tokenSecret === "string" ? entry.tokenSecret : "").trim();
88754
+ if (tokenId === "" || tokenSecret === "") return null;
88755
+ const environment = (entry.environment ?? "").trim();
88756
+ const key = `${tokenId}\u0000${environment}`;
88676
88757
  const cached = this.gateways.get(entryId);
88677
88758
  if (cached !== void 0 && cached.key === key) return cached.gateway;
88678
88759
  cached?.gateway.close();
88679
88760
  const gateway = new ModalSdkGateway({
88680
- tokenId: entry.tokenId.trim(),
88681
- tokenSecret: entry.tokenSecret.trim(),
88682
- environment: entry.environment.trim()
88761
+ tokenId,
88762
+ tokenSecret,
88763
+ environment
88683
88764
  });
88684
88765
  this.gateways.set(entryId, {
88685
88766
  key,
@@ -88693,6 +88774,19 @@ var TrainingModalAddon = class extends BaseAddon {
88693
88774
  cached.gateway.close();
88694
88775
  this.gateways.delete(entryId);
88695
88776
  }
88777
+ /**
88778
+ * Keychain views for the Providers edit form. Addon-facing reads answer
88779
+ * VALUES (D798), so we cannot recover markers from `readStoredGlobalKey`.
88780
+ * When a secret is present on the resolved entry we emit the deterministic
88781
+ * keychain ref (`ctx.secrets.ref`) so `SecretFieldControl` can reveal.
88782
+ */
88783
+ async readEntrySecretViews() {
88784
+ const map = /* @__PURE__ */ new Map();
88785
+ const secrets = this.ctx.secrets;
88786
+ for (const entry of effectiveModalEntries(this.config)) map.set(entry.id, entryTokenSecretView(entry.tokenSecret, secrets, entry.id));
88787
+ map.set("legacy-default", flatTokenSecretView(this.config.tokenSecret, secrets));
88788
+ return map;
88789
+ }
88696
88790
  ledger() {
88697
88791
  const state = () => {
88698
88792
  if (this.ledgerState === null) throw new Error("training-modal ledger not initialised");
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-dy7yMOdw.mjs";
1
+ import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-Dw400Y0a.mjs";
2
2
  export { t as get, e as init };
@@ -1,5 +1,5 @@
1
1
  import { _ as e, b as t, c as n, g as r, l as i, m as a, n as o, r as s, t as c, u as l } from "./_virtual_mf___mfe_internal__addon_training_page__loadShare__react__loadShare__.js-BT_wL8s3.mjs";
2
- import { i as u } from "./_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D7T4QEtw.mjs";
2
+ import { a as u } from "./_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-ff7X-Xlf.mjs";
3
3
  import "./_virtual_mf___mfe_internal__addon_training_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-DDBEvLdo.mjs";
4
4
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.js
5
5
  t();
@@ -1,5 +1,5 @@
1
1
  import { b as e, c as t, g as n, m as r } from "./_virtual_mf___mfe_internal__addon_training_page__loadShare__react__loadShare__.js-BT_wL8s3.mjs";
2
- import { y as i } from "./responsive-C7QsFtTb.mjs";
2
+ import { y as i } from "./responsive-BBlQWFFi.mjs";
3
3
  var a = i("camera", [["path", {
4
4
  d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z",
5
5
  key: "18u6gg"
@@ -1,4 +1,4 @@
1
- import { y as e } from "./responsive-C7QsFtTb.mjs";
1
+ import { y as e } from "./responsive-BBlQWFFi.mjs";
2
2
  var t = e("eye-off", [
3
3
  ["path", {
4
4
  d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49",
@@ -1,4 +1,4 @@
1
- import { y as e } from "./responsive-C7QsFtTb.mjs";
1
+ import { y as e } from "./responsive-BBlQWFFi.mjs";
2
2
  var t = e("trash-2", [
3
3
  ["path", {
4
4
  d: "M10 11v6",
@@ -2753,7 +2753,7 @@ async function rr(e) {
2753
2753
  }
2754
2754
  }
2755
2755
  async function ir() {
2756
- return tr ||= rr(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-DJDgoXBt.mjs")).catch((e) => {
2756
+ return tr ||= rr(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-DKblQaAP.mjs")).catch((e) => {
2757
2757
  throw tr = void 0, e;
2758
2758
  }), tr;
2759
2759
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-training",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
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",