@camstack/addon-model-studio 1.1.205 → 1.1.206

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 (17) hide show
  1. package/dist/{MotionZonesSettings-DbxM42-u.mjs → MotionZonesSettings-C5YcAYX9.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-B6QbcEYx.mjs → PrivacyMaskSettings-B1M6BI5F.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-C_sWam-G.mjs → SceneMonitorEditor-8Nn6yjHl.mjs} +3 -3
  4. package/dist/_stub.js +5099 -4906
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-CNTVW6pB.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-B4UQ5mJi.mjs} +3 -3
  6. package/dist/{_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cl1eNNmq.mjs → _virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-2O40KrYq.mjs} +1 -1
  7. package/dist/{hostInit-Cz9X-HIo.mjs → hostInit-95eIoNUt.mjs} +2 -2
  8. package/dist/model-studio.addon.js +192 -35
  9. package/dist/model-studio.addon.mjs +192 -35
  10. package/dist/{player-overlays-p1wcAk-U.mjs → player-overlays-lpfo2ab8.mjs} +1 -1
  11. package/dist/remoteEntry.js +1 -1
  12. package/dist/{responsive-a5o_RHI6.mjs → responsive-CO_rI6GL.mjs} +1 -1
  13. package/dist/{scene-monitor-copy-DRLPt6W9.mjs → scene-monitor-copy-Dx3Cq-sD.mjs} +1 -1
  14. package/dist/{square-Bfh8RaUF.mjs → square-DQHBxjsn.mjs} +1 -1
  15. package/dist/{trash-2-D-9uzmfU.mjs → trash-2-C6kfv9xl.mjs} +1 -1
  16. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-CrDmlyYW.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-CXb44kDc.mjs} +1 -1
  17. package/package.json +1 -1
@@ -11124,6 +11124,19 @@ var ModelProviderIdSchema = _enum([
11124
11124
  "custom"
11125
11125
  ]);
11126
11126
  /**
11127
+ * Resolve a catalog entry's picker provider. An explicit stamp always wins;
11128
+ * otherwise Frigate/Scrypted are recognised from id prefix or description.
11129
+ * Unstamped BYO registry rows fall through to `custom`. Family `yolov9` is
11130
+ * NOT a Scrypted signal — CamStack ships that family too.
11131
+ */
11132
+ function inferModelProvider(entry) {
11133
+ if (entry.provider !== void 0) return entry.provider;
11134
+ const haystack = `${entry.id} ${entry.description ?? ""}`;
11135
+ if (/scrypted/i.test(haystack)) return "scrypted";
11136
+ if (/frigate/i.test(haystack)) return "frigate";
11137
+ return "custom";
11138
+ }
11139
+ /**
11127
11140
  * The licence of a model, as a record rather than a bare SPDX string: a
11128
11141
  * permissive CODE licence routinely sits on top of restrictive WEIGHTS or
11129
11142
  * DATA, and the obligations (attribution, modification notices, source offers)
@@ -16523,6 +16536,12 @@ method(_void(), array(string()).readonly(), { auth: "admin" }), method(object({
16523
16536
  kind: "mutation",
16524
16537
  auth: "admin"
16525
16538
  });
16539
+ var HUB_ADDRESS_ROLES = [
16540
+ "appConnections",
16541
+ "notificationTaps",
16542
+ "internal",
16543
+ "publicEndpoint"
16544
+ ];
16526
16545
  /**
16527
16546
  * Transport bounds for AI capability methods.
16528
16547
  *
@@ -20700,7 +20719,13 @@ var NcAlarmModeDelaysListSchema = array(object({
20700
20719
  * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
20701
20720
  * is the one that starts the entry delay — the entry-delay notification already names it.
20702
20721
  */
20703
- notifySensorRewatched: boolean().optional()
20722
+ notifySensorRewatched: boolean().optional(),
20723
+ /**
20724
+ * D735: notify "ignored" when a sensor this arm bypassed really MOVED while armed (its own
20725
+ * change time is after the arm) and set nothing off. Absent = false (logged, not sent). A
20726
+ * re-sync, a recovery from `unavailable` or a duplicate is never announced, switch or not.
20727
+ */
20728
+ notifySensorIgnored: boolean().optional()
20704
20729
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20705
20730
  var NcAlarmModeDelaysEditListSchema = array(object({
20706
20731
  mode: AlarmArmModeSchema,
@@ -20713,7 +20738,9 @@ var NcAlarmModeDelaysEditListSchema = array(object({
20713
20738
  /** D706: set the switch, or `null` to clear it (back to the default, false). */
20714
20739
  notifyDuringEntryDelay: boolean().nullable().optional(),
20715
20740
  /** D706: set the switch, or `null` to clear it (back to the default, false). */
20716
- notifySensorRewatched: boolean().nullable().optional()
20741
+ notifySensorRewatched: boolean().nullable().optional(),
20742
+ /** D735: set the switch, or `null` to clear it (back to the default, false). */
20743
+ notifySensorIgnored: boolean().nullable().optional()
20717
20744
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20718
20745
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20719
20746
  var NcAlarmNonBlockingEditSchema = object({
@@ -31218,7 +31245,7 @@ var GetConnectionEndpointsResultSchema = object({
31218
31245
  localPort: AdvertisedLocalPortSchema
31219
31246
  });
31220
31247
  /**
31221
- * The address flagged for notification links. `baseUrl: null` = AUTO, which
31248
+ * The address flagged for notification taps (`notificationTaps`). `baseUrl: null` = AUTO, which
31222
31249
  * since D709 is the CamStack app link; `resolved` reports the effective value,
31223
31250
  * so the UI can show it either way.
31224
31251
  */
@@ -31253,8 +31280,8 @@ var ViewerEndpointsSchema = object({
31253
31280
  });
31254
31281
  /**
31255
31282
  * DEPRECATED compatibility shape of `getHubAddresses` (D729 amendment, kept
31256
- * one release for addons built against it): `cluster` is the `agents` role,
31257
- * `browser` the `signIn` role; `override` is the flagged address.
31283
+ * for addons built against it): `cluster` is the `internal` role, `browser`
31284
+ * the `publicEndpoint` role; `override` is the flagged address.
31258
31285
  */
31259
31286
  var HubAddressSnapshotSchema = object({
31260
31287
  override: string().nullable(),
@@ -31270,15 +31297,11 @@ var HubAddressesSchema = object({
31270
31297
  browser: HubAddressSnapshotSchema
31271
31298
  });
31272
31299
  /**
31273
- * What an address is used for (`hub-address.ts`). `viewer` is many-valued;
31274
- * `notifications`, `agents` and `signIn` are carried by at most one address.
31300
+ * What an address is used for (`hub-address.ts`, D734). `appConnections` is
31301
+ * many-valued; `notificationTaps`, `publicEndpoint` and `internal` are carried
31302
+ * by at most one address.
31275
31303
  */
31276
- var HubAddressRoleSchema = _enum([
31277
- "viewer",
31278
- "notifications",
31279
- "agents",
31280
- "signIn"
31281
- ]);
31304
+ var HubAddressRoleSchema = _enum(HUB_ADDRESS_ROLES);
31282
31305
  var RoleSourceSchema = _enum([
31283
31306
  "flagged",
31284
31307
  "auto",
@@ -31292,8 +31315,8 @@ var HubAddressRoleResolutionSchema = object({
31292
31315
  resolved: string().nullable(),
31293
31316
  source: RoleSourceSchema
31294
31317
  });
31295
- var HubViewerResolutionSchema = object({
31296
- /** The addresses flagged `viewer`, or empty = automatic. */
31318
+ var HubAppConnectionsResolutionSchema = object({
31319
+ /** The addresses flagged `appConnections`, or empty = automatic. */
31297
31320
  flagged: array(string()).readonly(),
31298
31321
  /** What the apps race right now. */
31299
31322
  resolved: array(string()).readonly(),
@@ -31313,10 +31336,10 @@ var HubAddressBookSchema = object({
31313
31336
  eligible: array(HubAddressRoleSchema).readonly()
31314
31337
  })).readonly(),
31315
31338
  roles: object({
31316
- viewer: HubViewerResolutionSchema,
31317
- notifications: HubAddressRoleResolutionSchema,
31318
- agents: HubAddressRoleResolutionSchema,
31319
- signIn: HubAddressRoleResolutionSchema
31339
+ appConnections: HubAppConnectionsResolutionSchema,
31340
+ notificationTaps: HubAddressRoleResolutionSchema,
31341
+ internal: HubAddressRoleResolutionSchema,
31342
+ publicEndpoint: HubAddressRoleResolutionSchema
31320
31343
  }),
31321
31344
  /** The `hub=` an app link carries (D709), or null. */
31322
31345
  appLinkHub: string().nullable()
@@ -31364,7 +31387,7 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
31364
31387
  * tunnel always emits `https://` regardless. */
31365
31388
  scheme: _enum(["http", "https"]).optional(),
31366
31389
  /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
31367
- * an address notification links can be flagged with, not a hub
31390
+ * an address notification taps can be flagged with, not a hub
31368
31391
  * route. Default `false`: racers and rankers never see it. */
31369
31392
  includeAppLink: boolean().optional()
31370
31393
  }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), ViewerEndpointsSchema), method(object({ baseUrls: array(string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }), method(object({ scheme: _enum(["http", "https"]).optional() }).optional(), HubAddressBookSchema), method(object({
@@ -49462,6 +49485,35 @@ function buildModelConvertProvider(deps) {
49462
49485
  };
49463
49486
  } };
49464
49487
  }
49488
+ var CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
49489
+ /** Whitespace runs collapse to one space; leading/trailing whitespace goes. */
49490
+ function parseDisplayName(raw) {
49491
+ const name = raw.replace(/\s+/g, " ").trim();
49492
+ if (name.length === 0) return { kind: "reset" };
49493
+ if (CONTROL_CHARS.test(name)) return {
49494
+ kind: "invalid",
49495
+ reason: "The name contains control characters."
49496
+ };
49497
+ if (name.length > 80) return {
49498
+ kind: "invalid",
49499
+ reason: `The name is ${name.length} characters long; the limit is 80.`
49500
+ };
49501
+ return {
49502
+ kind: "set",
49503
+ name
49504
+ };
49505
+ }
49506
+ /**
49507
+ * The optional display name typed on an import card: `undefined` when left
49508
+ * empty (the source's own default applies), the cleaned name otherwise.
49509
+ * Throws with the reason when the input is not acceptable.
49510
+ */
49511
+ function optionalImportName(raw) {
49512
+ if (raw === void 0) return void 0;
49513
+ const parsed = parseDisplayName(raw);
49514
+ if (parsed.kind === "invalid") throw new Error(`Display name rejected: ${parsed.reason}`);
49515
+ return parsed.kind === "set" ? parsed.name : void 0;
49516
+ }
49465
49517
  //#endregion
49466
49518
  //#region src/model-registry.ts
49467
49519
  /**
@@ -49493,22 +49545,18 @@ function upsertAvailability(map, modelId, nodeId, entry) {
49493
49545
  });
49494
49546
  }
49495
49547
  /**
49496
- * Pure registry operations over the custom-model list. Kept side-effect-free
49497
- * so they unit-test without a live `BaseAddon` context — the addon class is a
49498
- * thin durable-state wrapper around these.
49499
- */
49500
- /**
49501
49548
  * Insert or replace a descriptor keyed by `entry.id`. When the id already
49502
49549
  * exists, **formats are merged** (Scrypted multi-format accumulate) — sibling
49503
49550
  * formats on the prior entry are never dropped. Returns a new array.
49504
49551
  */
49505
- function upsertCustomModel(list, descriptor) {
49552
+ function upsertCustomModel(list, descriptor, nameOrigin = "source") {
49506
49553
  const prev = list.find((m) => m.entry.id === descriptor.entry.id);
49507
49554
  const merged = prev === void 0 ? descriptor : {
49508
49555
  stepId: descriptor.stepId,
49509
49556
  entry: {
49510
49557
  ...prev.entry,
49511
49558
  ...descriptor.entry,
49559
+ name: nameOrigin === "operator" ? descriptor.entry.name : prev.entry.name,
49512
49560
  formats: {
49513
49561
  ...prev.entry.formats,
49514
49562
  ...descriptor.entry.formats
@@ -49517,6 +49565,43 @@ function upsertCustomModel(list, descriptor) {
49517
49565
  };
49518
49566
  return [...list.filter((m) => m.entry.id !== descriptor.entry.id), merged];
49519
49567
  }
49568
+ /**
49569
+ * Rename one IMPORTED registry row in place. Only `entry.name` changes — the
49570
+ * id, step, formats and every pin keyed by the id stay exactly as they were.
49571
+ * An empty name resets the row to the name it was imported with. Throws on an
49572
+ * unknown id, a CamStack catalog row, or a name `parseDisplayName` rejects.
49573
+ */
49574
+ function renameCustomModel(list, importedNames, modelId, rawName) {
49575
+ const row = list.find((m) => m.entry.id === modelId);
49576
+ if (row === void 0) throw new Error(`renameCustomModel: model "${modelId}" is not registered`);
49577
+ if (!isRenameableEntry(row.entry)) throw new Error(`renameCustomModel: "${modelId}" is a CamStack catalog model and keeps its name`);
49578
+ const parsed = parseDisplayName(rawName);
49579
+ if (parsed.kind === "invalid") throw new Error(`renameCustomModel: ${parsed.reason}`);
49580
+ const original = importedNames[modelId] ?? row.entry.name;
49581
+ const name = parsed.kind === "set" ? parsed.name : original;
49582
+ const nextNames = { ...importedNames };
49583
+ if (parsed.kind === "set" && name !== original) nextNames[modelId] = original;
49584
+ else delete nextNames[modelId];
49585
+ return {
49586
+ list: list.map((m) => m.entry.id === modelId ? {
49587
+ stepId: m.stepId,
49588
+ entry: {
49589
+ ...m.entry,
49590
+ name
49591
+ }
49592
+ } : m),
49593
+ importedNames: nextNames,
49594
+ name
49595
+ };
49596
+ }
49597
+ /** An imported row can be renamed; a CamStack catalog model cannot. */
49598
+ function isRenameableEntry(entry) {
49599
+ return inferModelProvider({
49600
+ id: entry.id,
49601
+ description: entry.description,
49602
+ provider: entry.provider
49603
+ }) !== "camstack";
49604
+ }
49520
49605
  /** Drop the descriptor with the given `entry.id`. Reports whether anything changed. */
49521
49606
  function removeCustomModel(list, modelId) {
49522
49607
  const next = list.filter((m) => m.entry.id !== modelId);
@@ -50364,6 +50449,19 @@ var modelStudioActions = defineCustomActions({
50364
50449
  clearedPins: number().int().nonnegative()
50365
50450
  }), { kind: "mutation" }),
50366
50451
  /**
50452
+ * Rename an IMPORTED registry row (Frigate, Frigate+, Scrypted, uploads,
50453
+ * conversions) in place. Only `entry.name` changes — never the id, so pins
50454
+ * and availability are untouched. An empty name restores the name the model
50455
+ * was imported with. CamStack catalog models are refused.
50456
+ */
50457
+ renameCustomModel: customAction(object({
50458
+ modelId: string().min(1),
50459
+ name: string()
50460
+ }), object({
50461
+ modelId: string(),
50462
+ name: string()
50463
+ }), { kind: "mutation" }),
50464
+ /**
50367
50465
  * Delete one format of a Library model from one node. Keeps the registry row
50368
50466
  * and sibling formats/nodes; clears only that availability chip.
50369
50467
  */
@@ -50452,6 +50550,8 @@ var modelStudioActions = defineCustomActions({
50452
50550
  })),
50453
50551
  importScryptedCatalogModel: customAction(object({
50454
50552
  manifestId: string(),
50553
+ /** Optional display name typed on the import card. */
50554
+ name: string().optional(),
50455
50555
  targetNodeId: string().optional()
50456
50556
  }), CustomModelDescriptorSchema, { kind: "mutation" }),
50457
50557
  /**
@@ -50481,7 +50581,8 @@ var modelStudioActions = defineCustomActions({
50481
50581
  modelId: string().optional(),
50482
50582
  name: string().optional(),
50483
50583
  /**
50484
- * Native artifact class from the list row (`plusDownloadTraits`). The
50584
+ * Native artifact class of the chosen build (`plusImportLane`: neural →
50585
+ * onnx, coral → tflite). The
50485
50586
  * model-detail API does not always carry `supportedDetectors`, so the
50486
50587
  * browser passes what the catalog already knew (fixes ssd/yolo-generic
50487
50588
  * EdgeTPU rows that `plusTypeTraits` alone would call onnx).
@@ -50533,6 +50634,8 @@ var modelStudioActions = defineCustomActions({
50533
50634
  //#region src/model-studio.addon.ts
50534
50635
  var KEY_CUSTOM_MODELS = "customModels";
50535
50636
  var KEY_AVAILABILITY = "availability";
50637
+ var KEY_IMPORTED_NAMES = "importedNames";
50638
+ var ImportedNamesSchema = record(string(), string());
50536
50639
  var CustomModelsSchema = array(CustomModelDescriptorSchema);
50537
50640
  var pythonDirCache;
50538
50641
  function resolveAddonPythonDir() {
@@ -50595,12 +50698,18 @@ function spawnRunConvert(jobPath, pythonPath, onProgress, _sessionId) {
50595
50698
  });
50596
50699
  });
50597
50700
  }
50701
+ /** A name the operator typed replaces an existing row's name; a derived one never does. */
50702
+ function nameOriginOf(operatorName) {
50703
+ return operatorName !== void 0 ? { nameOrigin: "operator" } : {};
50704
+ }
50598
50705
  /** Global-fetch adapter for the injected Frigate+ client seam. */
50599
50706
  var plusFetch = async (url, init) => fetch(url, init);
50600
50707
  var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
50601
50708
  id = "model-studio";
50602
50709
  modelsState = null;
50603
50710
  availabilityState = null;
50711
+ /** modelId → the name a renamed registry row had before its first rename. */
50712
+ importedNamesState = null;
50604
50713
  /** Lazily-built chunked-upload store (hub only) — see `upload/model-upload-store.ts`. */
50605
50714
  uploadStore = null;
50606
50715
  /**
@@ -50683,6 +50792,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
50683
50792
  const state = this.state(KEY_CUSTOM_MODELS, CustomModelsSchema, []);
50684
50793
  this.modelsState = state;
50685
50794
  this.availabilityState = this.state(KEY_AVAILABILITY, ModelAvailabilityMapSchema, {});
50795
+ this.importedNamesState = this.state(KEY_IMPORTED_NAMES, ImportedNamesSchema, {});
50686
50796
  const rawNode = this.ctx.kernel?.localNodeId ?? "hub";
50687
50797
  const isHub = (rawNode.includes("/") ? rawNode.split("/")[0] : rawNode) === "hub";
50688
50798
  const pagesProvider = {
@@ -50716,7 +50826,11 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
50716
50826
  return result;
50717
50827
  },
50718
50828
  abortModelUpload: async (input) => (await this.getUploadStore()).abort(input),
50719
- registerCustomModel: async (input) => this.registerCustomModel(input, { ensureEngines: true }),
50829
+ registerCustomModel: async (input) => this.registerCustomModel(input, {
50830
+ ensureEngines: true,
50831
+ nameOrigin: "operator"
50832
+ }),
50833
+ renameCustomModel: async (input) => this.renameCustomModel_(input),
50720
50834
  ensureFormatsThenDistribute: async (input) => this.ensureFormatsThenDistribute_(input.modelId),
50721
50835
  ensureModelFormats: async (input) => this.ensureModelFormats_(input.modelId, input.formats),
50722
50836
  removeCustomModel: async (input) => this.removeCustomModel(input),
@@ -50868,6 +50982,35 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
50868
50982
  if (!this.modelsState) throw new Error("ModelStudioAddon: durable state not initialized");
50869
50983
  return this.modelsState;
50870
50984
  }
50985
+ get importedNames_() {
50986
+ if (!this.importedNamesState) throw new Error("ModelStudioAddon: imported-names state not initialized");
50987
+ return this.importedNamesState;
50988
+ }
50989
+ /**
50990
+ * Rename an imported registry row in place (`renameCustomModel` decides).
50991
+ * The id is untouched, so pins, availability and files are unaffected; the
50992
+ * pipeline's catalog view picks the new name up on its next registry read.
50993
+ */
50994
+ async renameCustomModel_(input) {
50995
+ const names = await this.importedNames_.get();
50996
+ const outcome = {};
50997
+ await this.state_.update((prev) => {
50998
+ const renamed = renameCustomModel(prev, names, input.modelId, input.name);
50999
+ outcome.result = renamed;
51000
+ return renamed.list;
51001
+ });
51002
+ const result = outcome.result;
51003
+ if (result === void 0) throw new Error("renameCustomModel: registry update did not run");
51004
+ await this.importedNames_.set(result.importedNames);
51005
+ this.ctx.logger.info("Renamed custom model", { meta: {
51006
+ modelId: input.modelId,
51007
+ name: result.name
51008
+ } });
51009
+ return {
51010
+ modelId: input.modelId,
51011
+ name: result.name
51012
+ };
51013
+ }
50871
51014
  get availability_() {
50872
51015
  if (!this.availabilityState) throw new Error("ModelStudioAddon: availability state not initialized");
50873
51016
  return this.availabilityState;
@@ -50989,6 +51132,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
50989
51132
  * `targetNodeId` lands the HF URL on that node via download-on-target.
50990
51133
  */
50991
51134
  async importScryptedCatalogFromManifest(input) {
51135
+ const name = optionalImportName(input.name);
50992
51136
  const entry = getScryptedManifestEntry(input.manifestId);
50993
51137
  if (!entry) throw new Error(`importScryptedCatalogModel: unknown manifest id "${input.manifestId}"`);
50994
51138
  let drift;
@@ -51008,8 +51152,15 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51008
51152
  };
51009
51153
  }
51010
51154
  if (drift.status !== "ok") throw new Error(`importScryptedCatalogModel: entry "${entry.id}" is blocked by drift (${drift.status}${drift.details ? `: ${drift.details}` : ""})`);
51011
- const descriptor = buildScryptedRegistryDescriptor(entry);
51012
- await this.registerCustomModel(descriptor);
51155
+ const built = buildScryptedRegistryDescriptor(entry);
51156
+ const descriptor = name !== void 0 ? {
51157
+ stepId: built.stepId,
51158
+ entry: {
51159
+ ...built.entry,
51160
+ name
51161
+ }
51162
+ } : built;
51163
+ await this.registerCustomModel(descriptor, nameOriginOf(name));
51013
51164
  if (input.targetNodeId !== void 0 && input.targetNodeId.length > 0) await this.distributeModel({
51014
51165
  modelId: descriptor.entry.id,
51015
51166
  nodeId: input.targetNodeId,
@@ -51031,11 +51182,12 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51031
51182
  async importFrigateCatalogModel(input) {
51032
51183
  const cat = FRIGATE_PUBLIC_CATALOG.find((c) => c.id === input.catalogId);
51033
51184
  if (!cat) throw new Error(`importFrigateCatalogModel: unknown catalog id "${input.catalogId}"`);
51185
+ const name = optionalImportName(input.name);
51034
51186
  const descriptor = buildFrigateCatalogDescriptor(input.catalogId, {
51035
51187
  ...input.modelId !== void 0 ? { modelId: input.modelId } : {},
51036
- ...input.name !== void 0 ? { name: input.name } : {}
51188
+ ...name !== void 0 ? { name } : {}
51037
51189
  });
51038
- await this.registerCustomModel(descriptor);
51190
+ await this.registerCustomModel(descriptor, nameOriginOf(name));
51039
51191
  let bytes = Math.round(cat.sizeMB * 1e6);
51040
51192
  if (input.targetNodeId !== void 0 && input.targetNodeId.length > 0) {
51041
51193
  const landed = await this.distributeModel({
@@ -51066,6 +51218,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51066
51218
  */
51067
51219
  async importFrigatePlusModel(input) {
51068
51220
  const modelId = frigatePlusRegistryId(input.plusModelId);
51221
+ const operatorName = optionalImportName(input.name);
51069
51222
  const key = this.config.frigatePlusApiKey ?? "";
51070
51223
  const { info, downloadUrl } = await requestPlusModelDownload(plusFetch, key, input.plusModelId);
51071
51224
  const typeTraits = plusTypeTraits(info.type);
@@ -51090,12 +51243,15 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51090
51243
  const sha256 = await ModelStudioAddon.fileSha256(destPath);
51091
51244
  const descriptor = plusInfoToDescriptor(info, {
51092
51245
  modelId,
51093
- name: input.name?.trim() || `Frigate+ ${info.type} (${input.plusModelId.slice(0, 8)})`,
51246
+ name: operatorName ?? `Frigate+ ${info.type} (${input.plusModelId.slice(0, 8)})`,
51094
51247
  filename,
51095
51248
  sizeMB: Number((bytes / 1e6).toFixed(1)),
51096
51249
  ...input.format !== void 0 ? { format: input.format } : {}
51097
51250
  });
51098
- await this.registerCustomModel(descriptor, { ensureEngines: false });
51251
+ await this.registerCustomModel(descriptor, {
51252
+ ensureEngines: false,
51253
+ ...nameOriginOf(operatorName)
51254
+ });
51099
51255
  await this.availability_.update((prev) => upsertAvailability(prev, modelId, "hub", {
51100
51256
  format: traits.format,
51101
51257
  sha256,
@@ -51128,7 +51284,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51128
51284
  async registerCustomModel(input, opts = {}) {
51129
51285
  if (!input.stepId.trim()) throw new Error("registerCustomModel: stepId is required");
51130
51286
  if (!input.entry.id.trim()) throw new Error("registerCustomModel: entry.id is required");
51131
- await this.state_.update((prev) => upsertCustomModel(prev, input));
51287
+ await this.state_.update((prev) => upsertCustomModel(prev, input, opts.nameOrigin));
51132
51288
  this.ctx.logger.info("Registered custom model", { meta: {
51133
51289
  modelId: input.entry.id,
51134
51290
  stepId: input.stepId
@@ -51385,6 +51541,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51385
51541
  removed = result.removed;
51386
51542
  return result.next;
51387
51543
  });
51544
+ await this.importedNames_.update((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => id !== input.modelId)));
51388
51545
  this.ctx.logger.info("Removed custom model", { meta: {
51389
51546
  modelId: input.modelId,
51390
51547
  removed,
@@ -1,5 +1,5 @@
1
1
  import { h as e, l as t, u as n, y as r } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare__react__loadShare__.js-DJDHChgO.mjs";
2
- import { o as i, s as a } from "./responsive-a5o_RHI6.mjs";
2
+ import { o as i, s as a } from "./responsive-CO_rI6GL.mjs";
3
3
  import { n as o, r as s, t as c } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-ds_Ehzaa.mjs";
4
4
  var l = a("chevron-down", [["path", {
5
5
  d: "m6 9 6 6 6-6",
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-CrDmlyYW.mjs";
1
+ import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-CXb44kDc.mjs";
2
2
  export { t as get, e as init };
@@ -1,6 +1,6 @@
1
1
  import { c as e, g as t, h as n, l as r, n as i, p as a, r as o, t as s, u as c, y as l } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare__react__loadShare__.js-DJDHChgO.mjs";
2
2
  import "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-ds_Ehzaa.mjs";
3
- import { c as u } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cl1eNNmq.mjs";
3
+ import { c as u } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-2O40KrYq.mjs";
4
4
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.js
5
5
  l();
6
6
  var d = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) === t).join(" ").trim(), f = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), p = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), m = (e) => {
@@ -1,5 +1,5 @@
1
1
  import { c as e, h as t, p as n, y as r } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare__react__loadShare__.js-DJDHChgO.mjs";
2
- import { s as i } from "./responsive-a5o_RHI6.mjs";
2
+ import { s as i } from "./responsive-CO_rI6GL.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 { s as e } from "./responsive-a5o_RHI6.mjs";
1
+ import { s as e } from "./responsive-CO_rI6GL.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 { s as e } from "./responsive-a5o_RHI6.mjs";
1
+ import { s as e } from "./responsive-CO_rI6GL.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_model_studio_page-CNTVW6pB.mjs")).catch((e) => {
2756
+ return tr ||= rr(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-B4UQ5mJi.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-model-studio",
3
- "version": "1.1.205",
3
+ "version": "1.1.206",
4
4
  "description": "Custom detection model registry, conversion & distribution for CamStack",
5
5
  "keywords": [
6
6
  "camstack",