@camstack/addon-model-studio 1.1.205 → 1.1.207

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 (19) hide show
  1. package/THIRD_PARTY_MODELS.md +3 -8
  2. package/dist/{MotionZonesSettings-DbxM42-u.mjs → MotionZonesSettings-Dglm7NRe.mjs} +2 -2
  3. package/dist/{PrivacyMaskSettings-B6QbcEYx.mjs → PrivacyMaskSettings-4VnzM7CJ.mjs} +4 -4
  4. package/dist/{SceneMonitorEditor-C_sWam-G.mjs → SceneMonitorEditor-C2DUWSMy.mjs} +3 -3
  5. package/dist/_stub.js +5885 -5676
  6. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-CNTVW6pB.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-q5-p_N0m.mjs} +3 -3
  7. package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-_AxpyMKA.mjs +26 -0
  8. package/dist/{hostInit-Cz9X-HIo.mjs → hostInit-De6t1H5z.mjs} +2 -2
  9. package/dist/model-studio.addon.js +233 -54
  10. package/dist/model-studio.addon.mjs +233 -54
  11. package/dist/{player-overlays-p1wcAk-U.mjs → player-overlays-DnuiNM0v.mjs} +1 -1
  12. package/dist/remoteEntry.js +1 -1
  13. package/dist/{responsive-a5o_RHI6.mjs → responsive-CIrYx2bw.mjs} +1 -1
  14. package/dist/{scene-monitor-copy-DRLPt6W9.mjs → scene-monitor-copy-AfaY2iTT.mjs} +1 -1
  15. package/dist/{square-Bfh8RaUF.mjs → square-v2U1PMC1.mjs} +1 -1
  16. package/dist/{trash-2-D-9uzmfU.mjs → trash-2-BQe8qX4b.mjs} +1 -1
  17. 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-H0G7-sT2.mjs} +1 -1
  18. package/package.json +1 -1
  19. package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cl1eNNmq.mjs +0 -26
@@ -10908,7 +10908,18 @@ var LABEL_SUPPLY_STEP_IDS = [
10908
10908
  ];
10909
10909
  /** Steps whose box a root detection can BE. */
10910
10910
  var CROP_SUPPLY_STEP_IDS = ["plate-detection", "face-detection"];
10911
- [...LABEL_SUPPLY_STEP_IDS, ...CROP_SUPPLY_STEP_IDS];
10911
+ /**
10912
+ * Steps whose per-detection MASK a root detection can carry (D737). The live
10913
+ * mask step is `segmentation` — a frame-plane refiner that runs its own model
10914
+ * once per detection crop. (`instance-segmentation` is a dormant step id that
10915
+ * no camera tree carries.)
10916
+ */
10917
+ var MASK_SUPPLY_STEP_IDS = ["segmentation"];
10918
+ [
10919
+ ...LABEL_SUPPLY_STEP_IDS,
10920
+ ...CROP_SUPPLY_STEP_IDS,
10921
+ ...MASK_SUPPLY_STEP_IDS
10922
+ ];
10912
10923
  /**
10913
10924
  * The EFFECT vocabulary of each label-kind step: exactly the strings the step
10914
10925
  * itself emits, in its own spelling. A linked label is one of these and nothing
@@ -11124,6 +11135,19 @@ var ModelProviderIdSchema = _enum([
11124
11135
  "custom"
11125
11136
  ]);
11126
11137
  /**
11138
+ * Resolve a catalog entry's picker provider. An explicit stamp always wins;
11139
+ * otherwise Frigate/Scrypted are recognised from id prefix or description.
11140
+ * Unstamped BYO registry rows fall through to `custom`. Family `yolov9` is
11141
+ * NOT a Scrypted signal — CamStack ships that family too.
11142
+ */
11143
+ function inferModelProvider(entry) {
11144
+ if (entry.provider !== void 0) return entry.provider;
11145
+ const haystack = `${entry.id} ${entry.description ?? ""}`;
11146
+ if (/scrypted/i.test(haystack)) return "scrypted";
11147
+ if (/frigate/i.test(haystack)) return "frigate";
11148
+ return "custom";
11149
+ }
11150
+ /**
11127
11151
  * The licence of a model, as a record rather than a bare SPDX string: a
11128
11152
  * permissive CODE licence routinely sits on top of restrictive WEIGHTS or
11129
11153
  * DATA, and the obligations (attribution, modification notices, source offers)
@@ -11176,25 +11200,36 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
11176
11200
  * - `crop`: the root draws the box the step would have found (`license_plate`
11177
11201
  * for plate-detection, `face` for face-detection). The box becomes a detail
11178
11202
  * child of the `parentMacro` detection that contains it — never a track.
11203
+ * - `mask`: the root's decode emits an instance mask with every detection (a
11204
+ * YOLO26-seg root, D737) — the `segmentation` step's result, carried on the
11205
+ * detection in the shape that step produces. Derived from the model's
11206
+ * postprocessor on read, never stored by an import.
11179
11207
  */
11180
- var RootStepSupplySchema = discriminatedUnion("kind", [object({
11181
- kind: literal("label"),
11182
- stepId: _enum(LABEL_SUPPLY_STEP_IDS),
11183
- labelToEffect: record(string(), string()),
11184
- uncovered: array(string()).readonly()
11185
- }), object({
11186
- kind: literal("crop"),
11187
- stepId: _enum(CROP_SUPPLY_STEP_IDS),
11188
- rootLabels: array(string()).readonly(),
11189
- parentMacro: _enum(["vehicle", "person"]),
11190
- childClass: _enum(["plate", "face"]),
11191
- /**
11192
- * The root's smaller input side, stamped on read by `rootSuppliesOf` from
11193
- * the model entry — what a step's `minRootInputPx` gate judges. Never
11194
- * stored by an import.
11195
- */
11196
- rootInputPx: number().int().positive().optional()
11197
- })]);
11208
+ var RootStepSupplySchema = discriminatedUnion("kind", [
11209
+ object({
11210
+ kind: literal("label"),
11211
+ stepId: _enum(LABEL_SUPPLY_STEP_IDS),
11212
+ labelToEffect: record(string(), string()),
11213
+ uncovered: array(string()).readonly()
11214
+ }),
11215
+ object({
11216
+ kind: literal("crop"),
11217
+ stepId: _enum(CROP_SUPPLY_STEP_IDS),
11218
+ rootLabels: array(string()).readonly(),
11219
+ parentMacro: _enum(["vehicle", "person"]),
11220
+ childClass: _enum(["plate", "face"]),
11221
+ /**
11222
+ * The root's smaller input side, stamped on read by `rootSuppliesOf` from
11223
+ * the model entry — what a step's `minRootInputPx` gate judges. Never
11224
+ * stored by an import.
11225
+ */
11226
+ rootInputPx: number().int().positive().optional()
11227
+ }),
11228
+ object({
11229
+ kind: literal("mask"),
11230
+ stepId: _enum(MASK_SUPPLY_STEP_IDS)
11231
+ })
11232
+ ]);
11198
11233
  var ModelCatalogEntrySchema = object({
11199
11234
  id: string(),
11200
11235
  name: string(),
@@ -16523,6 +16558,12 @@ method(_void(), array(string()).readonly(), { auth: "admin" }), method(object({
16523
16558
  kind: "mutation",
16524
16559
  auth: "admin"
16525
16560
  });
16561
+ var HUB_ADDRESS_ROLES = [
16562
+ "appConnections",
16563
+ "notificationTaps",
16564
+ "internal",
16565
+ "publicEndpoint"
16566
+ ];
16526
16567
  /**
16527
16568
  * Transport bounds for AI capability methods.
16528
16569
  *
@@ -20700,7 +20741,13 @@ var NcAlarmModeDelaysListSchema = array(object({
20700
20741
  * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
20701
20742
  * is the one that starts the entry delay — the entry-delay notification already names it.
20702
20743
  */
20703
- notifySensorRewatched: boolean().optional()
20744
+ notifySensorRewatched: boolean().optional(),
20745
+ /**
20746
+ * D735: notify "ignored" when a sensor this arm bypassed really MOVED while armed (its own
20747
+ * change time is after the arm) and set nothing off. Absent = false (logged, not sent). A
20748
+ * re-sync, a recovery from `unavailable` or a duplicate is never announced, switch or not.
20749
+ */
20750
+ notifySensorIgnored: boolean().optional()
20704
20751
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20705
20752
  var NcAlarmModeDelaysEditListSchema = array(object({
20706
20753
  mode: AlarmArmModeSchema,
@@ -20713,7 +20760,9 @@ var NcAlarmModeDelaysEditListSchema = array(object({
20713
20760
  /** D706: set the switch, or `null` to clear it (back to the default, false). */
20714
20761
  notifyDuringEntryDelay: boolean().nullable().optional(),
20715
20762
  /** D706: set the switch, or `null` to clear it (back to the default, false). */
20716
- notifySensorRewatched: boolean().nullable().optional()
20763
+ notifySensorRewatched: boolean().nullable().optional(),
20764
+ /** D735: set the switch, or `null` to clear it (back to the default, false). */
20765
+ notifySensorIgnored: boolean().nullable().optional()
20717
20766
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20718
20767
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20719
20768
  var NcAlarmNonBlockingEditSchema = object({
@@ -31218,7 +31267,7 @@ var GetConnectionEndpointsResultSchema = object({
31218
31267
  localPort: AdvertisedLocalPortSchema
31219
31268
  });
31220
31269
  /**
31221
- * The address flagged for notification links. `baseUrl: null` = AUTO, which
31270
+ * The address flagged for notification taps (`notificationTaps`). `baseUrl: null` = AUTO, which
31222
31271
  * since D709 is the CamStack app link; `resolved` reports the effective value,
31223
31272
  * so the UI can show it either way.
31224
31273
  */
@@ -31253,8 +31302,8 @@ var ViewerEndpointsSchema = object({
31253
31302
  });
31254
31303
  /**
31255
31304
  * 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.
31305
+ * for addons built against it): `cluster` is the `internal` role, `browser`
31306
+ * the `publicEndpoint` role; `override` is the flagged address.
31258
31307
  */
31259
31308
  var HubAddressSnapshotSchema = object({
31260
31309
  override: string().nullable(),
@@ -31270,15 +31319,11 @@ var HubAddressesSchema = object({
31270
31319
  browser: HubAddressSnapshotSchema
31271
31320
  });
31272
31321
  /**
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.
31322
+ * What an address is used for (`hub-address.ts`, D734). `appConnections` is
31323
+ * many-valued; `notificationTaps`, `publicEndpoint` and `internal` are carried
31324
+ * by at most one address.
31275
31325
  */
31276
- var HubAddressRoleSchema = _enum([
31277
- "viewer",
31278
- "notifications",
31279
- "agents",
31280
- "signIn"
31281
- ]);
31326
+ var HubAddressRoleSchema = _enum(HUB_ADDRESS_ROLES);
31282
31327
  var RoleSourceSchema = _enum([
31283
31328
  "flagged",
31284
31329
  "auto",
@@ -31292,8 +31337,8 @@ var HubAddressRoleResolutionSchema = object({
31292
31337
  resolved: string().nullable(),
31293
31338
  source: RoleSourceSchema
31294
31339
  });
31295
- var HubViewerResolutionSchema = object({
31296
- /** The addresses flagged `viewer`, or empty = automatic. */
31340
+ var HubAppConnectionsResolutionSchema = object({
31341
+ /** The addresses flagged `appConnections`, or empty = automatic. */
31297
31342
  flagged: array(string()).readonly(),
31298
31343
  /** What the apps race right now. */
31299
31344
  resolved: array(string()).readonly(),
@@ -31313,10 +31358,10 @@ var HubAddressBookSchema = object({
31313
31358
  eligible: array(HubAddressRoleSchema).readonly()
31314
31359
  })).readonly(),
31315
31360
  roles: object({
31316
- viewer: HubViewerResolutionSchema,
31317
- notifications: HubAddressRoleResolutionSchema,
31318
- agents: HubAddressRoleResolutionSchema,
31319
- signIn: HubAddressRoleResolutionSchema
31361
+ appConnections: HubAppConnectionsResolutionSchema,
31362
+ notificationTaps: HubAddressRoleResolutionSchema,
31363
+ internal: HubAddressRoleResolutionSchema,
31364
+ publicEndpoint: HubAddressRoleResolutionSchema
31320
31365
  }),
31321
31366
  /** The `hub=` an app link carries (D709), or null. */
31322
31367
  appLinkHub: string().nullable()
@@ -31364,7 +31409,7 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
31364
31409
  * tunnel always emits `https://` regardless. */
31365
31410
  scheme: _enum(["http", "https"]).optional(),
31366
31411
  /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
31367
- * an address notification links can be flagged with, not a hub
31412
+ * an address notification taps can be flagged with, not a hub
31368
31413
  * route. Default `false`: racers and rankers never see it. */
31369
31414
  includeAppLink: boolean().optional()
31370
31415
  }), 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 +49507,35 @@ function buildModelConvertProvider(deps) {
49462
49507
  };
49463
49508
  } };
49464
49509
  }
49510
+ var CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
49511
+ /** Whitespace runs collapse to one space; leading/trailing whitespace goes. */
49512
+ function parseDisplayName(raw) {
49513
+ const name = raw.replace(/\s+/g, " ").trim();
49514
+ if (name.length === 0) return { kind: "reset" };
49515
+ if (CONTROL_CHARS.test(name)) return {
49516
+ kind: "invalid",
49517
+ reason: "The name contains control characters."
49518
+ };
49519
+ if (name.length > 80) return {
49520
+ kind: "invalid",
49521
+ reason: `The name is ${name.length} characters long; the limit is 80.`
49522
+ };
49523
+ return {
49524
+ kind: "set",
49525
+ name
49526
+ };
49527
+ }
49528
+ /**
49529
+ * The optional display name typed on an import card: `undefined` when left
49530
+ * empty (the source's own default applies), the cleaned name otherwise.
49531
+ * Throws with the reason when the input is not acceptable.
49532
+ */
49533
+ function optionalImportName(raw) {
49534
+ if (raw === void 0) return void 0;
49535
+ const parsed = parseDisplayName(raw);
49536
+ if (parsed.kind === "invalid") throw new Error(`Display name rejected: ${parsed.reason}`);
49537
+ return parsed.kind === "set" ? parsed.name : void 0;
49538
+ }
49465
49539
  //#endregion
49466
49540
  //#region src/model-registry.ts
49467
49541
  /**
@@ -49493,22 +49567,18 @@ function upsertAvailability(map, modelId, nodeId, entry) {
49493
49567
  });
49494
49568
  }
49495
49569
  /**
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
49570
  * Insert or replace a descriptor keyed by `entry.id`. When the id already
49502
49571
  * exists, **formats are merged** (Scrypted multi-format accumulate) — sibling
49503
49572
  * formats on the prior entry are never dropped. Returns a new array.
49504
49573
  */
49505
- function upsertCustomModel(list, descriptor) {
49574
+ function upsertCustomModel(list, descriptor, nameOrigin = "source") {
49506
49575
  const prev = list.find((m) => m.entry.id === descriptor.entry.id);
49507
49576
  const merged = prev === void 0 ? descriptor : {
49508
49577
  stepId: descriptor.stepId,
49509
49578
  entry: {
49510
49579
  ...prev.entry,
49511
49580
  ...descriptor.entry,
49581
+ name: nameOrigin === "operator" ? descriptor.entry.name : prev.entry.name,
49512
49582
  formats: {
49513
49583
  ...prev.entry.formats,
49514
49584
  ...descriptor.entry.formats
@@ -49517,6 +49587,43 @@ function upsertCustomModel(list, descriptor) {
49517
49587
  };
49518
49588
  return [...list.filter((m) => m.entry.id !== descriptor.entry.id), merged];
49519
49589
  }
49590
+ /**
49591
+ * Rename one IMPORTED registry row in place. Only `entry.name` changes — the
49592
+ * id, step, formats and every pin keyed by the id stay exactly as they were.
49593
+ * An empty name resets the row to the name it was imported with. Throws on an
49594
+ * unknown id, a CamStack catalog row, or a name `parseDisplayName` rejects.
49595
+ */
49596
+ function renameCustomModel(list, importedNames, modelId, rawName) {
49597
+ const row = list.find((m) => m.entry.id === modelId);
49598
+ if (row === void 0) throw new Error(`renameCustomModel: model "${modelId}" is not registered`);
49599
+ if (!isRenameableEntry(row.entry)) throw new Error(`renameCustomModel: "${modelId}" is a CamStack catalog model and keeps its name`);
49600
+ const parsed = parseDisplayName(rawName);
49601
+ if (parsed.kind === "invalid") throw new Error(`renameCustomModel: ${parsed.reason}`);
49602
+ const original = importedNames[modelId] ?? row.entry.name;
49603
+ const name = parsed.kind === "set" ? parsed.name : original;
49604
+ const nextNames = { ...importedNames };
49605
+ if (parsed.kind === "set" && name !== original) nextNames[modelId] = original;
49606
+ else delete nextNames[modelId];
49607
+ return {
49608
+ list: list.map((m) => m.entry.id === modelId ? {
49609
+ stepId: m.stepId,
49610
+ entry: {
49611
+ ...m.entry,
49612
+ name
49613
+ }
49614
+ } : m),
49615
+ importedNames: nextNames,
49616
+ name
49617
+ };
49618
+ }
49619
+ /** An imported row can be renamed; a CamStack catalog model cannot. */
49620
+ function isRenameableEntry(entry) {
49621
+ return inferModelProvider({
49622
+ id: entry.id,
49623
+ description: entry.description,
49624
+ provider: entry.provider
49625
+ }) !== "camstack";
49626
+ }
49520
49627
  /** Drop the descriptor with the given `entry.id`. Reports whether anything changed. */
49521
49628
  function removeCustomModel(list, modelId) {
49522
49629
  const next = list.filter((m) => m.entry.id !== modelId);
@@ -50364,6 +50471,19 @@ var modelStudioActions = defineCustomActions({
50364
50471
  clearedPins: number().int().nonnegative()
50365
50472
  }), { kind: "mutation" }),
50366
50473
  /**
50474
+ * Rename an IMPORTED registry row (Frigate, Frigate+, Scrypted, uploads,
50475
+ * conversions) in place. Only `entry.name` changes — never the id, so pins
50476
+ * and availability are untouched. An empty name restores the name the model
50477
+ * was imported with. CamStack catalog models are refused.
50478
+ */
50479
+ renameCustomModel: customAction(object({
50480
+ modelId: string().min(1),
50481
+ name: string()
50482
+ }), object({
50483
+ modelId: string(),
50484
+ name: string()
50485
+ }), { kind: "mutation" }),
50486
+ /**
50367
50487
  * Delete one format of a Library model from one node. Keeps the registry row
50368
50488
  * and sibling formats/nodes; clears only that availability chip.
50369
50489
  */
@@ -50452,6 +50572,8 @@ var modelStudioActions = defineCustomActions({
50452
50572
  })),
50453
50573
  importScryptedCatalogModel: customAction(object({
50454
50574
  manifestId: string(),
50575
+ /** Optional display name typed on the import card. */
50576
+ name: string().optional(),
50455
50577
  targetNodeId: string().optional()
50456
50578
  }), CustomModelDescriptorSchema, { kind: "mutation" }),
50457
50579
  /**
@@ -50481,7 +50603,8 @@ var modelStudioActions = defineCustomActions({
50481
50603
  modelId: string().optional(),
50482
50604
  name: string().optional(),
50483
50605
  /**
50484
- * Native artifact class from the list row (`plusDownloadTraits`). The
50606
+ * Native artifact class of the chosen build (`plusImportLane`: neural →
50607
+ * onnx, coral → tflite). The
50485
50608
  * model-detail API does not always carry `supportedDetectors`, so the
50486
50609
  * browser passes what the catalog already knew (fixes ssd/yolo-generic
50487
50610
  * EdgeTPU rows that `plusTypeTraits` alone would call onnx).
@@ -50533,6 +50656,8 @@ var modelStudioActions = defineCustomActions({
50533
50656
  //#region src/model-studio.addon.ts
50534
50657
  var KEY_CUSTOM_MODELS = "customModels";
50535
50658
  var KEY_AVAILABILITY = "availability";
50659
+ var KEY_IMPORTED_NAMES = "importedNames";
50660
+ var ImportedNamesSchema = record(string(), string());
50536
50661
  var CustomModelsSchema = array(CustomModelDescriptorSchema);
50537
50662
  var pythonDirCache;
50538
50663
  function resolveAddonPythonDir() {
@@ -50595,12 +50720,18 @@ function spawnRunConvert(jobPath, pythonPath, onProgress, _sessionId) {
50595
50720
  });
50596
50721
  });
50597
50722
  }
50723
+ /** A name the operator typed replaces an existing row's name; a derived one never does. */
50724
+ function nameOriginOf(operatorName) {
50725
+ return operatorName !== void 0 ? { nameOrigin: "operator" } : {};
50726
+ }
50598
50727
  /** Global-fetch adapter for the injected Frigate+ client seam. */
50599
50728
  var plusFetch = async (url, init) => fetch(url, init);
50600
50729
  var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
50601
50730
  id = "model-studio";
50602
50731
  modelsState = null;
50603
50732
  availabilityState = null;
50733
+ /** modelId → the name a renamed registry row had before its first rename. */
50734
+ importedNamesState = null;
50604
50735
  /** Lazily-built chunked-upload store (hub only) — see `upload/model-upload-store.ts`. */
50605
50736
  uploadStore = null;
50606
50737
  /**
@@ -50683,6 +50814,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
50683
50814
  const state = this.state(KEY_CUSTOM_MODELS, CustomModelsSchema, []);
50684
50815
  this.modelsState = state;
50685
50816
  this.availabilityState = this.state(KEY_AVAILABILITY, ModelAvailabilityMapSchema, {});
50817
+ this.importedNamesState = this.state(KEY_IMPORTED_NAMES, ImportedNamesSchema, {});
50686
50818
  const rawNode = this.ctx.kernel?.localNodeId ?? "hub";
50687
50819
  const isHub = (rawNode.includes("/") ? rawNode.split("/")[0] : rawNode) === "hub";
50688
50820
  const pagesProvider = {
@@ -50716,7 +50848,11 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
50716
50848
  return result;
50717
50849
  },
50718
50850
  abortModelUpload: async (input) => (await this.getUploadStore()).abort(input),
50719
- registerCustomModel: async (input) => this.registerCustomModel(input, { ensureEngines: true }),
50851
+ registerCustomModel: async (input) => this.registerCustomModel(input, {
50852
+ ensureEngines: true,
50853
+ nameOrigin: "operator"
50854
+ }),
50855
+ renameCustomModel: async (input) => this.renameCustomModel_(input),
50720
50856
  ensureFormatsThenDistribute: async (input) => this.ensureFormatsThenDistribute_(input.modelId),
50721
50857
  ensureModelFormats: async (input) => this.ensureModelFormats_(input.modelId, input.formats),
50722
50858
  removeCustomModel: async (input) => this.removeCustomModel(input),
@@ -50868,6 +51004,35 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
50868
51004
  if (!this.modelsState) throw new Error("ModelStudioAddon: durable state not initialized");
50869
51005
  return this.modelsState;
50870
51006
  }
51007
+ get importedNames_() {
51008
+ if (!this.importedNamesState) throw new Error("ModelStudioAddon: imported-names state not initialized");
51009
+ return this.importedNamesState;
51010
+ }
51011
+ /**
51012
+ * Rename an imported registry row in place (`renameCustomModel` decides).
51013
+ * The id is untouched, so pins, availability and files are unaffected; the
51014
+ * pipeline's catalog view picks the new name up on its next registry read.
51015
+ */
51016
+ async renameCustomModel_(input) {
51017
+ const names = await this.importedNames_.get();
51018
+ const outcome = {};
51019
+ await this.state_.update((prev) => {
51020
+ const renamed = renameCustomModel(prev, names, input.modelId, input.name);
51021
+ outcome.result = renamed;
51022
+ return renamed.list;
51023
+ });
51024
+ const result = outcome.result;
51025
+ if (result === void 0) throw new Error("renameCustomModel: registry update did not run");
51026
+ await this.importedNames_.set(result.importedNames);
51027
+ this.ctx.logger.info("Renamed custom model", { meta: {
51028
+ modelId: input.modelId,
51029
+ name: result.name
51030
+ } });
51031
+ return {
51032
+ modelId: input.modelId,
51033
+ name: result.name
51034
+ };
51035
+ }
50871
51036
  get availability_() {
50872
51037
  if (!this.availabilityState) throw new Error("ModelStudioAddon: availability state not initialized");
50873
51038
  return this.availabilityState;
@@ -50989,6 +51154,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
50989
51154
  * `targetNodeId` lands the HF URL on that node via download-on-target.
50990
51155
  */
50991
51156
  async importScryptedCatalogFromManifest(input) {
51157
+ const name = optionalImportName(input.name);
50992
51158
  const entry = getScryptedManifestEntry(input.manifestId);
50993
51159
  if (!entry) throw new Error(`importScryptedCatalogModel: unknown manifest id "${input.manifestId}"`);
50994
51160
  let drift;
@@ -51008,8 +51174,15 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51008
51174
  };
51009
51175
  }
51010
51176
  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);
51177
+ const built = buildScryptedRegistryDescriptor(entry);
51178
+ const descriptor = name !== void 0 ? {
51179
+ stepId: built.stepId,
51180
+ entry: {
51181
+ ...built.entry,
51182
+ name
51183
+ }
51184
+ } : built;
51185
+ await this.registerCustomModel(descriptor, nameOriginOf(name));
51013
51186
  if (input.targetNodeId !== void 0 && input.targetNodeId.length > 0) await this.distributeModel({
51014
51187
  modelId: descriptor.entry.id,
51015
51188
  nodeId: input.targetNodeId,
@@ -51031,11 +51204,12 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51031
51204
  async importFrigateCatalogModel(input) {
51032
51205
  const cat = FRIGATE_PUBLIC_CATALOG.find((c) => c.id === input.catalogId);
51033
51206
  if (!cat) throw new Error(`importFrigateCatalogModel: unknown catalog id "${input.catalogId}"`);
51207
+ const name = optionalImportName(input.name);
51034
51208
  const descriptor = buildFrigateCatalogDescriptor(input.catalogId, {
51035
51209
  ...input.modelId !== void 0 ? { modelId: input.modelId } : {},
51036
- ...input.name !== void 0 ? { name: input.name } : {}
51210
+ ...name !== void 0 ? { name } : {}
51037
51211
  });
51038
- await this.registerCustomModel(descriptor);
51212
+ await this.registerCustomModel(descriptor, nameOriginOf(name));
51039
51213
  let bytes = Math.round(cat.sizeMB * 1e6);
51040
51214
  if (input.targetNodeId !== void 0 && input.targetNodeId.length > 0) {
51041
51215
  const landed = await this.distributeModel({
@@ -51066,6 +51240,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51066
51240
  */
51067
51241
  async importFrigatePlusModel(input) {
51068
51242
  const modelId = frigatePlusRegistryId(input.plusModelId);
51243
+ const operatorName = optionalImportName(input.name);
51069
51244
  const key = this.config.frigatePlusApiKey ?? "";
51070
51245
  const { info, downloadUrl } = await requestPlusModelDownload(plusFetch, key, input.plusModelId);
51071
51246
  const typeTraits = plusTypeTraits(info.type);
@@ -51090,12 +51265,15 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51090
51265
  const sha256 = await ModelStudioAddon.fileSha256(destPath);
51091
51266
  const descriptor = plusInfoToDescriptor(info, {
51092
51267
  modelId,
51093
- name: input.name?.trim() || `Frigate+ ${info.type} (${input.plusModelId.slice(0, 8)})`,
51268
+ name: operatorName ?? `Frigate+ ${info.type} (${input.plusModelId.slice(0, 8)})`,
51094
51269
  filename,
51095
51270
  sizeMB: Number((bytes / 1e6).toFixed(1)),
51096
51271
  ...input.format !== void 0 ? { format: input.format } : {}
51097
51272
  });
51098
- await this.registerCustomModel(descriptor, { ensureEngines: false });
51273
+ await this.registerCustomModel(descriptor, {
51274
+ ensureEngines: false,
51275
+ ...nameOriginOf(operatorName)
51276
+ });
51099
51277
  await this.availability_.update((prev) => upsertAvailability(prev, modelId, "hub", {
51100
51278
  format: traits.format,
51101
51279
  sha256,
@@ -51128,7 +51306,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51128
51306
  async registerCustomModel(input, opts = {}) {
51129
51307
  if (!input.stepId.trim()) throw new Error("registerCustomModel: stepId is required");
51130
51308
  if (!input.entry.id.trim()) throw new Error("registerCustomModel: entry.id is required");
51131
- await this.state_.update((prev) => upsertCustomModel(prev, input));
51309
+ await this.state_.update((prev) => upsertCustomModel(prev, input, opts.nameOrigin));
51132
51310
  this.ctx.logger.info("Registered custom model", { meta: {
51133
51311
  modelId: input.entry.id,
51134
51312
  stepId: input.stepId
@@ -51385,6 +51563,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
51385
51563
  removed = result.removed;
51386
51564
  return result.next;
51387
51565
  });
51566
+ await this.importedNames_.update((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => id !== input.modelId)));
51388
51567
  this.ctx.logger.info("Removed custom model", { meta: {
51389
51568
  modelId: input.modelId,
51390
51569
  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-CIrYx2bw.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-H0G7-sT2.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 { l as u } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-_AxpyMKA.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-CIrYx2bw.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-CIrYx2bw.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-CIrYx2bw.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-q5-p_N0m.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.207",
4
4
  "description": "Custom detection model registry, conversion & distribution for CamStack",
5
5
  "keywords": [
6
6
  "camstack",