@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.
- package/dist/{MotionZonesSettings-DbxM42-u.mjs → MotionZonesSettings-C5YcAYX9.mjs} +2 -2
- package/dist/{PrivacyMaskSettings-B6QbcEYx.mjs → PrivacyMaskSettings-B1M6BI5F.mjs} +4 -4
- package/dist/{SceneMonitorEditor-C_sWam-G.mjs → SceneMonitorEditor-8Nn6yjHl.mjs} +3 -3
- package/dist/_stub.js +5099 -4906
- 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
- 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
- package/dist/{hostInit-Cz9X-HIo.mjs → hostInit-95eIoNUt.mjs} +2 -2
- package/dist/model-studio.addon.js +192 -35
- package/dist/model-studio.addon.mjs +192 -35
- package/dist/{player-overlays-p1wcAk-U.mjs → player-overlays-lpfo2ab8.mjs} +1 -1
- package/dist/remoteEntry.js +1 -1
- package/dist/{responsive-a5o_RHI6.mjs → responsive-CO_rI6GL.mjs} +1 -1
- package/dist/{scene-monitor-copy-DRLPt6W9.mjs → scene-monitor-copy-Dx3Cq-sD.mjs} +1 -1
- package/dist/{square-Bfh8RaUF.mjs → square-DQHBxjsn.mjs} +1 -1
- package/dist/{trash-2-D-9uzmfU.mjs → trash-2-C6kfv9xl.mjs} +1 -1
- 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
- package/package.json +1 -1
|
@@ -11150,6 +11150,19 @@ var ModelProviderIdSchema = _enum([
|
|
|
11150
11150
|
"custom"
|
|
11151
11151
|
]);
|
|
11152
11152
|
/**
|
|
11153
|
+
* Resolve a catalog entry's picker provider. An explicit stamp always wins;
|
|
11154
|
+
* otherwise Frigate/Scrypted are recognised from id prefix or description.
|
|
11155
|
+
* Unstamped BYO registry rows fall through to `custom`. Family `yolov9` is
|
|
11156
|
+
* NOT a Scrypted signal — CamStack ships that family too.
|
|
11157
|
+
*/
|
|
11158
|
+
function inferModelProvider(entry) {
|
|
11159
|
+
if (entry.provider !== void 0) return entry.provider;
|
|
11160
|
+
const haystack = `${entry.id} ${entry.description ?? ""}`;
|
|
11161
|
+
if (/scrypted/i.test(haystack)) return "scrypted";
|
|
11162
|
+
if (/frigate/i.test(haystack)) return "frigate";
|
|
11163
|
+
return "custom";
|
|
11164
|
+
}
|
|
11165
|
+
/**
|
|
11153
11166
|
* The licence of a model, as a record rather than a bare SPDX string: a
|
|
11154
11167
|
* permissive CODE licence routinely sits on top of restrictive WEIGHTS or
|
|
11155
11168
|
* DATA, and the obligations (attribution, modification notices, source offers)
|
|
@@ -16549,6 +16562,12 @@ method(_void(), array(string()).readonly(), { auth: "admin" }), method(object({
|
|
|
16549
16562
|
kind: "mutation",
|
|
16550
16563
|
auth: "admin"
|
|
16551
16564
|
});
|
|
16565
|
+
var HUB_ADDRESS_ROLES = [
|
|
16566
|
+
"appConnections",
|
|
16567
|
+
"notificationTaps",
|
|
16568
|
+
"internal",
|
|
16569
|
+
"publicEndpoint"
|
|
16570
|
+
];
|
|
16552
16571
|
/**
|
|
16553
16572
|
* Transport bounds for AI capability methods.
|
|
16554
16573
|
*
|
|
@@ -20726,7 +20745,13 @@ var NcAlarmModeDelaysListSchema = array(object({
|
|
|
20726
20745
|
* Absent = false (logged, not sent). Even when true it is not sent when that same sensor
|
|
20727
20746
|
* is the one that starts the entry delay — the entry-delay notification already names it.
|
|
20728
20747
|
*/
|
|
20729
|
-
notifySensorRewatched: boolean().optional()
|
|
20748
|
+
notifySensorRewatched: boolean().optional(),
|
|
20749
|
+
/**
|
|
20750
|
+
* D735: notify "ignored" when a sensor this arm bypassed really MOVED while armed (its own
|
|
20751
|
+
* change time is after the arm) and set nothing off. Absent = false (logged, not sent). A
|
|
20752
|
+
* re-sync, a recovery from `unavailable` or a duplicate is never announced, switch or not.
|
|
20753
|
+
*/
|
|
20754
|
+
notifySensorIgnored: boolean().optional()
|
|
20730
20755
|
})).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
|
|
20731
20756
|
var NcAlarmModeDelaysEditListSchema = array(object({
|
|
20732
20757
|
mode: AlarmArmModeSchema,
|
|
@@ -20739,7 +20764,9 @@ var NcAlarmModeDelaysEditListSchema = array(object({
|
|
|
20739
20764
|
/** D706: set the switch, or `null` to clear it (back to the default, false). */
|
|
20740
20765
|
notifyDuringEntryDelay: boolean().nullable().optional(),
|
|
20741
20766
|
/** D706: set the switch, or `null` to clear it (back to the default, false). */
|
|
20742
|
-
notifySensorRewatched: boolean().nullable().optional()
|
|
20767
|
+
notifySensorRewatched: boolean().nullable().optional(),
|
|
20768
|
+
/** D735: set the switch, or `null` to clear it (back to the default, false). */
|
|
20769
|
+
notifySensorIgnored: boolean().nullable().optional()
|
|
20743
20770
|
})).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
|
|
20744
20771
|
/** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
|
|
20745
20772
|
var NcAlarmNonBlockingEditSchema = object({
|
|
@@ -31244,7 +31271,7 @@ var GetConnectionEndpointsResultSchema = object({
|
|
|
31244
31271
|
localPort: AdvertisedLocalPortSchema
|
|
31245
31272
|
});
|
|
31246
31273
|
/**
|
|
31247
|
-
* The address flagged for notification
|
|
31274
|
+
* The address flagged for notification taps (`notificationTaps`). `baseUrl: null` = AUTO, which
|
|
31248
31275
|
* since D709 is the CamStack app link; `resolved` reports the effective value,
|
|
31249
31276
|
* so the UI can show it either way.
|
|
31250
31277
|
*/
|
|
@@ -31279,8 +31306,8 @@ var ViewerEndpointsSchema = object({
|
|
|
31279
31306
|
});
|
|
31280
31307
|
/**
|
|
31281
31308
|
* DEPRECATED compatibility shape of `getHubAddresses` (D729 amendment, kept
|
|
31282
|
-
*
|
|
31283
|
-
*
|
|
31309
|
+
* for addons built against it): `cluster` is the `internal` role, `browser`
|
|
31310
|
+
* the `publicEndpoint` role; `override` is the flagged address.
|
|
31284
31311
|
*/
|
|
31285
31312
|
var HubAddressSnapshotSchema = object({
|
|
31286
31313
|
override: string().nullable(),
|
|
@@ -31296,15 +31323,11 @@ var HubAddressesSchema = object({
|
|
|
31296
31323
|
browser: HubAddressSnapshotSchema
|
|
31297
31324
|
});
|
|
31298
31325
|
/**
|
|
31299
|
-
* What an address is used for (`hub-address.ts
|
|
31300
|
-
* `
|
|
31326
|
+
* What an address is used for (`hub-address.ts`, D734). `appConnections` is
|
|
31327
|
+
* many-valued; `notificationTaps`, `publicEndpoint` and `internal` are carried
|
|
31328
|
+
* by at most one address.
|
|
31301
31329
|
*/
|
|
31302
|
-
var HubAddressRoleSchema = _enum(
|
|
31303
|
-
"viewer",
|
|
31304
|
-
"notifications",
|
|
31305
|
-
"agents",
|
|
31306
|
-
"signIn"
|
|
31307
|
-
]);
|
|
31330
|
+
var HubAddressRoleSchema = _enum(HUB_ADDRESS_ROLES);
|
|
31308
31331
|
var RoleSourceSchema = _enum([
|
|
31309
31332
|
"flagged",
|
|
31310
31333
|
"auto",
|
|
@@ -31318,8 +31341,8 @@ var HubAddressRoleResolutionSchema = object({
|
|
|
31318
31341
|
resolved: string().nullable(),
|
|
31319
31342
|
source: RoleSourceSchema
|
|
31320
31343
|
});
|
|
31321
|
-
var
|
|
31322
|
-
/** The addresses flagged `
|
|
31344
|
+
var HubAppConnectionsResolutionSchema = object({
|
|
31345
|
+
/** The addresses flagged `appConnections`, or empty = automatic. */
|
|
31323
31346
|
flagged: array(string()).readonly(),
|
|
31324
31347
|
/** What the apps race right now. */
|
|
31325
31348
|
resolved: array(string()).readonly(),
|
|
@@ -31339,10 +31362,10 @@ var HubAddressBookSchema = object({
|
|
|
31339
31362
|
eligible: array(HubAddressRoleSchema).readonly()
|
|
31340
31363
|
})).readonly(),
|
|
31341
31364
|
roles: object({
|
|
31342
|
-
|
|
31343
|
-
|
|
31344
|
-
|
|
31345
|
-
|
|
31365
|
+
appConnections: HubAppConnectionsResolutionSchema,
|
|
31366
|
+
notificationTaps: HubAddressRoleResolutionSchema,
|
|
31367
|
+
internal: HubAddressRoleResolutionSchema,
|
|
31368
|
+
publicEndpoint: HubAddressRoleResolutionSchema
|
|
31346
31369
|
}),
|
|
31347
31370
|
/** The `hub=` an app link carries (D709), or null. */
|
|
31348
31371
|
appLinkHub: string().nullable()
|
|
@@ -31390,7 +31413,7 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
31390
31413
|
* tunnel always emits `https://` regardless. */
|
|
31391
31414
|
scheme: _enum(["http", "https"]).optional(),
|
|
31392
31415
|
/** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
|
|
31393
|
-
* an address notification
|
|
31416
|
+
* an address notification taps can be flagged with, not a hub
|
|
31394
31417
|
* route. Default `false`: racers and rankers never see it. */
|
|
31395
31418
|
includeAppLink: boolean().optional()
|
|
31396
31419
|
}), 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({
|
|
@@ -49488,6 +49511,35 @@ function buildModelConvertProvider(deps) {
|
|
|
49488
49511
|
};
|
|
49489
49512
|
} };
|
|
49490
49513
|
}
|
|
49514
|
+
var CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
|
|
49515
|
+
/** Whitespace runs collapse to one space; leading/trailing whitespace goes. */
|
|
49516
|
+
function parseDisplayName(raw) {
|
|
49517
|
+
const name = raw.replace(/\s+/g, " ").trim();
|
|
49518
|
+
if (name.length === 0) return { kind: "reset" };
|
|
49519
|
+
if (CONTROL_CHARS.test(name)) return {
|
|
49520
|
+
kind: "invalid",
|
|
49521
|
+
reason: "The name contains control characters."
|
|
49522
|
+
};
|
|
49523
|
+
if (name.length > 80) return {
|
|
49524
|
+
kind: "invalid",
|
|
49525
|
+
reason: `The name is ${name.length} characters long; the limit is 80.`
|
|
49526
|
+
};
|
|
49527
|
+
return {
|
|
49528
|
+
kind: "set",
|
|
49529
|
+
name
|
|
49530
|
+
};
|
|
49531
|
+
}
|
|
49532
|
+
/**
|
|
49533
|
+
* The optional display name typed on an import card: `undefined` when left
|
|
49534
|
+
* empty (the source's own default applies), the cleaned name otherwise.
|
|
49535
|
+
* Throws with the reason when the input is not acceptable.
|
|
49536
|
+
*/
|
|
49537
|
+
function optionalImportName(raw) {
|
|
49538
|
+
if (raw === void 0) return void 0;
|
|
49539
|
+
const parsed = parseDisplayName(raw);
|
|
49540
|
+
if (parsed.kind === "invalid") throw new Error(`Display name rejected: ${parsed.reason}`);
|
|
49541
|
+
return parsed.kind === "set" ? parsed.name : void 0;
|
|
49542
|
+
}
|
|
49491
49543
|
//#endregion
|
|
49492
49544
|
//#region src/model-registry.ts
|
|
49493
49545
|
/**
|
|
@@ -49519,22 +49571,18 @@ function upsertAvailability(map, modelId, nodeId, entry) {
|
|
|
49519
49571
|
});
|
|
49520
49572
|
}
|
|
49521
49573
|
/**
|
|
49522
|
-
* Pure registry operations over the custom-model list. Kept side-effect-free
|
|
49523
|
-
* so they unit-test without a live `BaseAddon` context — the addon class is a
|
|
49524
|
-
* thin durable-state wrapper around these.
|
|
49525
|
-
*/
|
|
49526
|
-
/**
|
|
49527
49574
|
* Insert or replace a descriptor keyed by `entry.id`. When the id already
|
|
49528
49575
|
* exists, **formats are merged** (Scrypted multi-format accumulate) — sibling
|
|
49529
49576
|
* formats on the prior entry are never dropped. Returns a new array.
|
|
49530
49577
|
*/
|
|
49531
|
-
function upsertCustomModel(list, descriptor) {
|
|
49578
|
+
function upsertCustomModel(list, descriptor, nameOrigin = "source") {
|
|
49532
49579
|
const prev = list.find((m) => m.entry.id === descriptor.entry.id);
|
|
49533
49580
|
const merged = prev === void 0 ? descriptor : {
|
|
49534
49581
|
stepId: descriptor.stepId,
|
|
49535
49582
|
entry: {
|
|
49536
49583
|
...prev.entry,
|
|
49537
49584
|
...descriptor.entry,
|
|
49585
|
+
name: nameOrigin === "operator" ? descriptor.entry.name : prev.entry.name,
|
|
49538
49586
|
formats: {
|
|
49539
49587
|
...prev.entry.formats,
|
|
49540
49588
|
...descriptor.entry.formats
|
|
@@ -49543,6 +49591,43 @@ function upsertCustomModel(list, descriptor) {
|
|
|
49543
49591
|
};
|
|
49544
49592
|
return [...list.filter((m) => m.entry.id !== descriptor.entry.id), merged];
|
|
49545
49593
|
}
|
|
49594
|
+
/**
|
|
49595
|
+
* Rename one IMPORTED registry row in place. Only `entry.name` changes — the
|
|
49596
|
+
* id, step, formats and every pin keyed by the id stay exactly as they were.
|
|
49597
|
+
* An empty name resets the row to the name it was imported with. Throws on an
|
|
49598
|
+
* unknown id, a CamStack catalog row, or a name `parseDisplayName` rejects.
|
|
49599
|
+
*/
|
|
49600
|
+
function renameCustomModel(list, importedNames, modelId, rawName) {
|
|
49601
|
+
const row = list.find((m) => m.entry.id === modelId);
|
|
49602
|
+
if (row === void 0) throw new Error(`renameCustomModel: model "${modelId}" is not registered`);
|
|
49603
|
+
if (!isRenameableEntry(row.entry)) throw new Error(`renameCustomModel: "${modelId}" is a CamStack catalog model and keeps its name`);
|
|
49604
|
+
const parsed = parseDisplayName(rawName);
|
|
49605
|
+
if (parsed.kind === "invalid") throw new Error(`renameCustomModel: ${parsed.reason}`);
|
|
49606
|
+
const original = importedNames[modelId] ?? row.entry.name;
|
|
49607
|
+
const name = parsed.kind === "set" ? parsed.name : original;
|
|
49608
|
+
const nextNames = { ...importedNames };
|
|
49609
|
+
if (parsed.kind === "set" && name !== original) nextNames[modelId] = original;
|
|
49610
|
+
else delete nextNames[modelId];
|
|
49611
|
+
return {
|
|
49612
|
+
list: list.map((m) => m.entry.id === modelId ? {
|
|
49613
|
+
stepId: m.stepId,
|
|
49614
|
+
entry: {
|
|
49615
|
+
...m.entry,
|
|
49616
|
+
name
|
|
49617
|
+
}
|
|
49618
|
+
} : m),
|
|
49619
|
+
importedNames: nextNames,
|
|
49620
|
+
name
|
|
49621
|
+
};
|
|
49622
|
+
}
|
|
49623
|
+
/** An imported row can be renamed; a CamStack catalog model cannot. */
|
|
49624
|
+
function isRenameableEntry(entry) {
|
|
49625
|
+
return inferModelProvider({
|
|
49626
|
+
id: entry.id,
|
|
49627
|
+
description: entry.description,
|
|
49628
|
+
provider: entry.provider
|
|
49629
|
+
}) !== "camstack";
|
|
49630
|
+
}
|
|
49546
49631
|
/** Drop the descriptor with the given `entry.id`. Reports whether anything changed. */
|
|
49547
49632
|
function removeCustomModel(list, modelId) {
|
|
49548
49633
|
const next = list.filter((m) => m.entry.id !== modelId);
|
|
@@ -50390,6 +50475,19 @@ var modelStudioActions = defineCustomActions({
|
|
|
50390
50475
|
clearedPins: number().int().nonnegative()
|
|
50391
50476
|
}), { kind: "mutation" }),
|
|
50392
50477
|
/**
|
|
50478
|
+
* Rename an IMPORTED registry row (Frigate, Frigate+, Scrypted, uploads,
|
|
50479
|
+
* conversions) in place. Only `entry.name` changes — never the id, so pins
|
|
50480
|
+
* and availability are untouched. An empty name restores the name the model
|
|
50481
|
+
* was imported with. CamStack catalog models are refused.
|
|
50482
|
+
*/
|
|
50483
|
+
renameCustomModel: customAction(object({
|
|
50484
|
+
modelId: string().min(1),
|
|
50485
|
+
name: string()
|
|
50486
|
+
}), object({
|
|
50487
|
+
modelId: string(),
|
|
50488
|
+
name: string()
|
|
50489
|
+
}), { kind: "mutation" }),
|
|
50490
|
+
/**
|
|
50393
50491
|
* Delete one format of a Library model from one node. Keeps the registry row
|
|
50394
50492
|
* and sibling formats/nodes; clears only that availability chip.
|
|
50395
50493
|
*/
|
|
@@ -50478,6 +50576,8 @@ var modelStudioActions = defineCustomActions({
|
|
|
50478
50576
|
})),
|
|
50479
50577
|
importScryptedCatalogModel: customAction(object({
|
|
50480
50578
|
manifestId: string(),
|
|
50579
|
+
/** Optional display name typed on the import card. */
|
|
50580
|
+
name: string().optional(),
|
|
50481
50581
|
targetNodeId: string().optional()
|
|
50482
50582
|
}), CustomModelDescriptorSchema, { kind: "mutation" }),
|
|
50483
50583
|
/**
|
|
@@ -50507,7 +50607,8 @@ var modelStudioActions = defineCustomActions({
|
|
|
50507
50607
|
modelId: string().optional(),
|
|
50508
50608
|
name: string().optional(),
|
|
50509
50609
|
/**
|
|
50510
|
-
* Native artifact class
|
|
50610
|
+
* Native artifact class of the chosen build (`plusImportLane`: neural →
|
|
50611
|
+
* onnx, coral → tflite). The
|
|
50511
50612
|
* model-detail API does not always carry `supportedDetectors`, so the
|
|
50512
50613
|
* browser passes what the catalog already knew (fixes ssd/yolo-generic
|
|
50513
50614
|
* EdgeTPU rows that `plusTypeTraits` alone would call onnx).
|
|
@@ -50559,6 +50660,8 @@ var modelStudioActions = defineCustomActions({
|
|
|
50559
50660
|
//#region src/model-studio.addon.ts
|
|
50560
50661
|
var KEY_CUSTOM_MODELS = "customModels";
|
|
50561
50662
|
var KEY_AVAILABILITY = "availability";
|
|
50663
|
+
var KEY_IMPORTED_NAMES = "importedNames";
|
|
50664
|
+
var ImportedNamesSchema = record(string(), string());
|
|
50562
50665
|
var CustomModelsSchema = array(CustomModelDescriptorSchema);
|
|
50563
50666
|
var pythonDirCache;
|
|
50564
50667
|
function resolveAddonPythonDir() {
|
|
@@ -50621,12 +50724,18 @@ function spawnRunConvert(jobPath, pythonPath, onProgress, _sessionId) {
|
|
|
50621
50724
|
});
|
|
50622
50725
|
});
|
|
50623
50726
|
}
|
|
50727
|
+
/** A name the operator typed replaces an existing row's name; a derived one never does. */
|
|
50728
|
+
function nameOriginOf(operatorName) {
|
|
50729
|
+
return operatorName !== void 0 ? { nameOrigin: "operator" } : {};
|
|
50730
|
+
}
|
|
50624
50731
|
/** Global-fetch adapter for the injected Frigate+ client seam. */
|
|
50625
50732
|
var plusFetch = async (url, init) => fetch(url, init);
|
|
50626
50733
|
var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
50627
50734
|
id = "model-studio";
|
|
50628
50735
|
modelsState = null;
|
|
50629
50736
|
availabilityState = null;
|
|
50737
|
+
/** modelId → the name a renamed registry row had before its first rename. */
|
|
50738
|
+
importedNamesState = null;
|
|
50630
50739
|
/** Lazily-built chunked-upload store (hub only) — see `upload/model-upload-store.ts`. */
|
|
50631
50740
|
uploadStore = null;
|
|
50632
50741
|
/**
|
|
@@ -50709,6 +50818,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
50709
50818
|
const state = this.state(KEY_CUSTOM_MODELS, CustomModelsSchema, []);
|
|
50710
50819
|
this.modelsState = state;
|
|
50711
50820
|
this.availabilityState = this.state(KEY_AVAILABILITY, ModelAvailabilityMapSchema, {});
|
|
50821
|
+
this.importedNamesState = this.state(KEY_IMPORTED_NAMES, ImportedNamesSchema, {});
|
|
50712
50822
|
const rawNode = this.ctx.kernel?.localNodeId ?? "hub";
|
|
50713
50823
|
const isHub = (rawNode.includes("/") ? rawNode.split("/")[0] : rawNode) === "hub";
|
|
50714
50824
|
const pagesProvider = {
|
|
@@ -50742,7 +50852,11 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
50742
50852
|
return result;
|
|
50743
50853
|
},
|
|
50744
50854
|
abortModelUpload: async (input) => (await this.getUploadStore()).abort(input),
|
|
50745
|
-
registerCustomModel: async (input) => this.registerCustomModel(input, {
|
|
50855
|
+
registerCustomModel: async (input) => this.registerCustomModel(input, {
|
|
50856
|
+
ensureEngines: true,
|
|
50857
|
+
nameOrigin: "operator"
|
|
50858
|
+
}),
|
|
50859
|
+
renameCustomModel: async (input) => this.renameCustomModel_(input),
|
|
50746
50860
|
ensureFormatsThenDistribute: async (input) => this.ensureFormatsThenDistribute_(input.modelId),
|
|
50747
50861
|
ensureModelFormats: async (input) => this.ensureModelFormats_(input.modelId, input.formats),
|
|
50748
50862
|
removeCustomModel: async (input) => this.removeCustomModel(input),
|
|
@@ -50894,6 +51008,35 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
50894
51008
|
if (!this.modelsState) throw new Error("ModelStudioAddon: durable state not initialized");
|
|
50895
51009
|
return this.modelsState;
|
|
50896
51010
|
}
|
|
51011
|
+
get importedNames_() {
|
|
51012
|
+
if (!this.importedNamesState) throw new Error("ModelStudioAddon: imported-names state not initialized");
|
|
51013
|
+
return this.importedNamesState;
|
|
51014
|
+
}
|
|
51015
|
+
/**
|
|
51016
|
+
* Rename an imported registry row in place (`renameCustomModel` decides).
|
|
51017
|
+
* The id is untouched, so pins, availability and files are unaffected; the
|
|
51018
|
+
* pipeline's catalog view picks the new name up on its next registry read.
|
|
51019
|
+
*/
|
|
51020
|
+
async renameCustomModel_(input) {
|
|
51021
|
+
const names = await this.importedNames_.get();
|
|
51022
|
+
const outcome = {};
|
|
51023
|
+
await this.state_.update((prev) => {
|
|
51024
|
+
const renamed = renameCustomModel(prev, names, input.modelId, input.name);
|
|
51025
|
+
outcome.result = renamed;
|
|
51026
|
+
return renamed.list;
|
|
51027
|
+
});
|
|
51028
|
+
const result = outcome.result;
|
|
51029
|
+
if (result === void 0) throw new Error("renameCustomModel: registry update did not run");
|
|
51030
|
+
await this.importedNames_.set(result.importedNames);
|
|
51031
|
+
this.ctx.logger.info("Renamed custom model", { meta: {
|
|
51032
|
+
modelId: input.modelId,
|
|
51033
|
+
name: result.name
|
|
51034
|
+
} });
|
|
51035
|
+
return {
|
|
51036
|
+
modelId: input.modelId,
|
|
51037
|
+
name: result.name
|
|
51038
|
+
};
|
|
51039
|
+
}
|
|
50897
51040
|
get availability_() {
|
|
50898
51041
|
if (!this.availabilityState) throw new Error("ModelStudioAddon: availability state not initialized");
|
|
50899
51042
|
return this.availabilityState;
|
|
@@ -51015,6 +51158,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51015
51158
|
* `targetNodeId` lands the HF URL on that node via download-on-target.
|
|
51016
51159
|
*/
|
|
51017
51160
|
async importScryptedCatalogFromManifest(input) {
|
|
51161
|
+
const name = optionalImportName(input.name);
|
|
51018
51162
|
const entry = getScryptedManifestEntry(input.manifestId);
|
|
51019
51163
|
if (!entry) throw new Error(`importScryptedCatalogModel: unknown manifest id "${input.manifestId}"`);
|
|
51020
51164
|
let drift;
|
|
@@ -51034,8 +51178,15 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51034
51178
|
};
|
|
51035
51179
|
}
|
|
51036
51180
|
if (drift.status !== "ok") throw new Error(`importScryptedCatalogModel: entry "${entry.id}" is blocked by drift (${drift.status}${drift.details ? `: ${drift.details}` : ""})`);
|
|
51037
|
-
const
|
|
51038
|
-
|
|
51181
|
+
const built = buildScryptedRegistryDescriptor(entry);
|
|
51182
|
+
const descriptor = name !== void 0 ? {
|
|
51183
|
+
stepId: built.stepId,
|
|
51184
|
+
entry: {
|
|
51185
|
+
...built.entry,
|
|
51186
|
+
name
|
|
51187
|
+
}
|
|
51188
|
+
} : built;
|
|
51189
|
+
await this.registerCustomModel(descriptor, nameOriginOf(name));
|
|
51039
51190
|
if (input.targetNodeId !== void 0 && input.targetNodeId.length > 0) await this.distributeModel({
|
|
51040
51191
|
modelId: descriptor.entry.id,
|
|
51041
51192
|
nodeId: input.targetNodeId,
|
|
@@ -51057,11 +51208,12 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51057
51208
|
async importFrigateCatalogModel(input) {
|
|
51058
51209
|
const cat = FRIGATE_PUBLIC_CATALOG.find((c) => c.id === input.catalogId);
|
|
51059
51210
|
if (!cat) throw new Error(`importFrigateCatalogModel: unknown catalog id "${input.catalogId}"`);
|
|
51211
|
+
const name = optionalImportName(input.name);
|
|
51060
51212
|
const descriptor = buildFrigateCatalogDescriptor(input.catalogId, {
|
|
51061
51213
|
...input.modelId !== void 0 ? { modelId: input.modelId } : {},
|
|
51062
|
-
...
|
|
51214
|
+
...name !== void 0 ? { name } : {}
|
|
51063
51215
|
});
|
|
51064
|
-
await this.registerCustomModel(descriptor);
|
|
51216
|
+
await this.registerCustomModel(descriptor, nameOriginOf(name));
|
|
51065
51217
|
let bytes = Math.round(cat.sizeMB * 1e6);
|
|
51066
51218
|
if (input.targetNodeId !== void 0 && input.targetNodeId.length > 0) {
|
|
51067
51219
|
const landed = await this.distributeModel({
|
|
@@ -51092,6 +51244,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51092
51244
|
*/
|
|
51093
51245
|
async importFrigatePlusModel(input) {
|
|
51094
51246
|
const modelId = frigatePlusRegistryId(input.plusModelId);
|
|
51247
|
+
const operatorName = optionalImportName(input.name);
|
|
51095
51248
|
const key = this.config.frigatePlusApiKey ?? "";
|
|
51096
51249
|
const { info, downloadUrl } = await requestPlusModelDownload(plusFetch, key, input.plusModelId);
|
|
51097
51250
|
const typeTraits = plusTypeTraits(info.type);
|
|
@@ -51116,12 +51269,15 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51116
51269
|
const sha256 = await ModelStudioAddon.fileSha256(destPath);
|
|
51117
51270
|
const descriptor = plusInfoToDescriptor(info, {
|
|
51118
51271
|
modelId,
|
|
51119
|
-
name:
|
|
51272
|
+
name: operatorName ?? `Frigate+ ${info.type} (${input.plusModelId.slice(0, 8)})`,
|
|
51120
51273
|
filename,
|
|
51121
51274
|
sizeMB: Number((bytes / 1e6).toFixed(1)),
|
|
51122
51275
|
...input.format !== void 0 ? { format: input.format } : {}
|
|
51123
51276
|
});
|
|
51124
|
-
await this.registerCustomModel(descriptor, {
|
|
51277
|
+
await this.registerCustomModel(descriptor, {
|
|
51278
|
+
ensureEngines: false,
|
|
51279
|
+
...nameOriginOf(operatorName)
|
|
51280
|
+
});
|
|
51125
51281
|
await this.availability_.update((prev) => upsertAvailability(prev, modelId, "hub", {
|
|
51126
51282
|
format: traits.format,
|
|
51127
51283
|
sha256,
|
|
@@ -51154,7 +51310,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51154
51310
|
async registerCustomModel(input, opts = {}) {
|
|
51155
51311
|
if (!input.stepId.trim()) throw new Error("registerCustomModel: stepId is required");
|
|
51156
51312
|
if (!input.entry.id.trim()) throw new Error("registerCustomModel: entry.id is required");
|
|
51157
|
-
await this.state_.update((prev) => upsertCustomModel(prev, input));
|
|
51313
|
+
await this.state_.update((prev) => upsertCustomModel(prev, input, opts.nameOrigin));
|
|
51158
51314
|
this.ctx.logger.info("Registered custom model", { meta: {
|
|
51159
51315
|
modelId: input.entry.id,
|
|
51160
51316
|
stepId: input.stepId
|
|
@@ -51411,6 +51567,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51411
51567
|
removed = result.removed;
|
|
51412
51568
|
return result.next;
|
|
51413
51569
|
});
|
|
51570
|
+
await this.importedNames_.update((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => id !== input.modelId)));
|
|
51414
51571
|
this.ctx.logger.info("Removed custom model", { meta: {
|
|
51415
51572
|
modelId: input.modelId,
|
|
51416
51573
|
removed,
|