@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.
- package/THIRD_PARTY_MODELS.md +3 -8
- package/dist/{MotionZonesSettings-DbxM42-u.mjs → MotionZonesSettings-Dglm7NRe.mjs} +2 -2
- package/dist/{PrivacyMaskSettings-B6QbcEYx.mjs → PrivacyMaskSettings-4VnzM7CJ.mjs} +4 -4
- package/dist/{SceneMonitorEditor-C_sWam-G.mjs → SceneMonitorEditor-C2DUWSMy.mjs} +3 -3
- package/dist/_stub.js +5885 -5676
- 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
- package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-_AxpyMKA.mjs +26 -0
- package/dist/{hostInit-Cz9X-HIo.mjs → hostInit-De6t1H5z.mjs} +2 -2
- package/dist/model-studio.addon.js +233 -54
- package/dist/model-studio.addon.mjs +233 -54
- package/dist/{player-overlays-p1wcAk-U.mjs → player-overlays-DnuiNM0v.mjs} +1 -1
- package/dist/remoteEntry.js +1 -1
- package/dist/{responsive-a5o_RHI6.mjs → responsive-CIrYx2bw.mjs} +1 -1
- package/dist/{scene-monitor-copy-DRLPt6W9.mjs → scene-monitor-copy-AfaY2iTT.mjs} +1 -1
- package/dist/{square-Bfh8RaUF.mjs → square-v2U1PMC1.mjs} +1 -1
- package/dist/{trash-2-D-9uzmfU.mjs → trash-2-BQe8qX4b.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-H0G7-sT2.mjs} +1 -1
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cl1eNNmq.mjs +0 -26
|
@@ -10934,7 +10934,18 @@ var LABEL_SUPPLY_STEP_IDS = [
|
|
|
10934
10934
|
];
|
|
10935
10935
|
/** Steps whose box a root detection can BE. */
|
|
10936
10936
|
var CROP_SUPPLY_STEP_IDS = ["plate-detection", "face-detection"];
|
|
10937
|
-
|
|
10937
|
+
/**
|
|
10938
|
+
* Steps whose per-detection MASK a root detection can carry (D737). The live
|
|
10939
|
+
* mask step is `segmentation` — a frame-plane refiner that runs its own model
|
|
10940
|
+
* once per detection crop. (`instance-segmentation` is a dormant step id that
|
|
10941
|
+
* no camera tree carries.)
|
|
10942
|
+
*/
|
|
10943
|
+
var MASK_SUPPLY_STEP_IDS = ["segmentation"];
|
|
10944
|
+
[
|
|
10945
|
+
...LABEL_SUPPLY_STEP_IDS,
|
|
10946
|
+
...CROP_SUPPLY_STEP_IDS,
|
|
10947
|
+
...MASK_SUPPLY_STEP_IDS
|
|
10948
|
+
];
|
|
10938
10949
|
/**
|
|
10939
10950
|
* The EFFECT vocabulary of each label-kind step: exactly the strings the step
|
|
10940
10951
|
* itself emits, in its own spelling. A linked label is one of these and nothing
|
|
@@ -11150,6 +11161,19 @@ var ModelProviderIdSchema = _enum([
|
|
|
11150
11161
|
"custom"
|
|
11151
11162
|
]);
|
|
11152
11163
|
/**
|
|
11164
|
+
* Resolve a catalog entry's picker provider. An explicit stamp always wins;
|
|
11165
|
+
* otherwise Frigate/Scrypted are recognised from id prefix or description.
|
|
11166
|
+
* Unstamped BYO registry rows fall through to `custom`. Family `yolov9` is
|
|
11167
|
+
* NOT a Scrypted signal — CamStack ships that family too.
|
|
11168
|
+
*/
|
|
11169
|
+
function inferModelProvider(entry) {
|
|
11170
|
+
if (entry.provider !== void 0) return entry.provider;
|
|
11171
|
+
const haystack = `${entry.id} ${entry.description ?? ""}`;
|
|
11172
|
+
if (/scrypted/i.test(haystack)) return "scrypted";
|
|
11173
|
+
if (/frigate/i.test(haystack)) return "frigate";
|
|
11174
|
+
return "custom";
|
|
11175
|
+
}
|
|
11176
|
+
/**
|
|
11153
11177
|
* The licence of a model, as a record rather than a bare SPDX string: a
|
|
11154
11178
|
* permissive CODE licence routinely sits on top of restrictive WEIGHTS or
|
|
11155
11179
|
* DATA, and the obligations (attribution, modification notices, source offers)
|
|
@@ -11202,25 +11226,36 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
|
|
|
11202
11226
|
* - `crop`: the root draws the box the step would have found (`license_plate`
|
|
11203
11227
|
* for plate-detection, `face` for face-detection). The box becomes a detail
|
|
11204
11228
|
* child of the `parentMacro` detection that contains it — never a track.
|
|
11229
|
+
* - `mask`: the root's decode emits an instance mask with every detection (a
|
|
11230
|
+
* YOLO26-seg root, D737) — the `segmentation` step's result, carried on the
|
|
11231
|
+
* detection in the shape that step produces. Derived from the model's
|
|
11232
|
+
* postprocessor on read, never stored by an import.
|
|
11205
11233
|
*/
|
|
11206
|
-
var RootStepSupplySchema = discriminatedUnion("kind", [
|
|
11207
|
-
|
|
11208
|
-
|
|
11209
|
-
|
|
11210
|
-
|
|
11211
|
-
|
|
11212
|
-
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
|
|
11234
|
+
var RootStepSupplySchema = discriminatedUnion("kind", [
|
|
11235
|
+
object({
|
|
11236
|
+
kind: literal("label"),
|
|
11237
|
+
stepId: _enum(LABEL_SUPPLY_STEP_IDS),
|
|
11238
|
+
labelToEffect: record(string(), string()),
|
|
11239
|
+
uncovered: array(string()).readonly()
|
|
11240
|
+
}),
|
|
11241
|
+
object({
|
|
11242
|
+
kind: literal("crop"),
|
|
11243
|
+
stepId: _enum(CROP_SUPPLY_STEP_IDS),
|
|
11244
|
+
rootLabels: array(string()).readonly(),
|
|
11245
|
+
parentMacro: _enum(["vehicle", "person"]),
|
|
11246
|
+
childClass: _enum(["plate", "face"]),
|
|
11247
|
+
/**
|
|
11248
|
+
* The root's smaller input side, stamped on read by `rootSuppliesOf` from
|
|
11249
|
+
* the model entry — what a step's `minRootInputPx` gate judges. Never
|
|
11250
|
+
* stored by an import.
|
|
11251
|
+
*/
|
|
11252
|
+
rootInputPx: number().int().positive().optional()
|
|
11253
|
+
}),
|
|
11254
|
+
object({
|
|
11255
|
+
kind: literal("mask"),
|
|
11256
|
+
stepId: _enum(MASK_SUPPLY_STEP_IDS)
|
|
11257
|
+
})
|
|
11258
|
+
]);
|
|
11224
11259
|
var ModelCatalogEntrySchema = object({
|
|
11225
11260
|
id: string(),
|
|
11226
11261
|
name: string(),
|
|
@@ -16549,6 +16584,12 @@ method(_void(), array(string()).readonly(), { auth: "admin" }), method(object({
|
|
|
16549
16584
|
kind: "mutation",
|
|
16550
16585
|
auth: "admin"
|
|
16551
16586
|
});
|
|
16587
|
+
var HUB_ADDRESS_ROLES = [
|
|
16588
|
+
"appConnections",
|
|
16589
|
+
"notificationTaps",
|
|
16590
|
+
"internal",
|
|
16591
|
+
"publicEndpoint"
|
|
16592
|
+
];
|
|
16552
16593
|
/**
|
|
16553
16594
|
* Transport bounds for AI capability methods.
|
|
16554
16595
|
*
|
|
@@ -20726,7 +20767,13 @@ var NcAlarmModeDelaysListSchema = array(object({
|
|
|
20726
20767
|
* Absent = false (logged, not sent). Even when true it is not sent when that same sensor
|
|
20727
20768
|
* is the one that starts the entry delay — the entry-delay notification already names it.
|
|
20728
20769
|
*/
|
|
20729
|
-
notifySensorRewatched: boolean().optional()
|
|
20770
|
+
notifySensorRewatched: boolean().optional(),
|
|
20771
|
+
/**
|
|
20772
|
+
* D735: notify "ignored" when a sensor this arm bypassed really MOVED while armed (its own
|
|
20773
|
+
* change time is after the arm) and set nothing off. Absent = false (logged, not sent). A
|
|
20774
|
+
* re-sync, a recovery from `unavailable` or a duplicate is never announced, switch or not.
|
|
20775
|
+
*/
|
|
20776
|
+
notifySensorIgnored: boolean().optional()
|
|
20730
20777
|
})).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
|
|
20731
20778
|
var NcAlarmModeDelaysEditListSchema = array(object({
|
|
20732
20779
|
mode: AlarmArmModeSchema,
|
|
@@ -20739,7 +20786,9 @@ var NcAlarmModeDelaysEditListSchema = array(object({
|
|
|
20739
20786
|
/** D706: set the switch, or `null` to clear it (back to the default, false). */
|
|
20740
20787
|
notifyDuringEntryDelay: boolean().nullable().optional(),
|
|
20741
20788
|
/** D706: set the switch, or `null` to clear it (back to the default, false). */
|
|
20742
|
-
notifySensorRewatched: boolean().nullable().optional()
|
|
20789
|
+
notifySensorRewatched: boolean().nullable().optional(),
|
|
20790
|
+
/** D735: set the switch, or `null` to clear it (back to the default, false). */
|
|
20791
|
+
notifySensorIgnored: boolean().nullable().optional()
|
|
20743
20792
|
})).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
|
|
20744
20793
|
/** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
|
|
20745
20794
|
var NcAlarmNonBlockingEditSchema = object({
|
|
@@ -31244,7 +31293,7 @@ var GetConnectionEndpointsResultSchema = object({
|
|
|
31244
31293
|
localPort: AdvertisedLocalPortSchema
|
|
31245
31294
|
});
|
|
31246
31295
|
/**
|
|
31247
|
-
* The address flagged for notification
|
|
31296
|
+
* The address flagged for notification taps (`notificationTaps`). `baseUrl: null` = AUTO, which
|
|
31248
31297
|
* since D709 is the CamStack app link; `resolved` reports the effective value,
|
|
31249
31298
|
* so the UI can show it either way.
|
|
31250
31299
|
*/
|
|
@@ -31279,8 +31328,8 @@ var ViewerEndpointsSchema = object({
|
|
|
31279
31328
|
});
|
|
31280
31329
|
/**
|
|
31281
31330
|
* DEPRECATED compatibility shape of `getHubAddresses` (D729 amendment, kept
|
|
31282
|
-
*
|
|
31283
|
-
*
|
|
31331
|
+
* for addons built against it): `cluster` is the `internal` role, `browser`
|
|
31332
|
+
* the `publicEndpoint` role; `override` is the flagged address.
|
|
31284
31333
|
*/
|
|
31285
31334
|
var HubAddressSnapshotSchema = object({
|
|
31286
31335
|
override: string().nullable(),
|
|
@@ -31296,15 +31345,11 @@ var HubAddressesSchema = object({
|
|
|
31296
31345
|
browser: HubAddressSnapshotSchema
|
|
31297
31346
|
});
|
|
31298
31347
|
/**
|
|
31299
|
-
* What an address is used for (`hub-address.ts
|
|
31300
|
-
* `
|
|
31348
|
+
* What an address is used for (`hub-address.ts`, D734). `appConnections` is
|
|
31349
|
+
* many-valued; `notificationTaps`, `publicEndpoint` and `internal` are carried
|
|
31350
|
+
* by at most one address.
|
|
31301
31351
|
*/
|
|
31302
|
-
var HubAddressRoleSchema = _enum(
|
|
31303
|
-
"viewer",
|
|
31304
|
-
"notifications",
|
|
31305
|
-
"agents",
|
|
31306
|
-
"signIn"
|
|
31307
|
-
]);
|
|
31352
|
+
var HubAddressRoleSchema = _enum(HUB_ADDRESS_ROLES);
|
|
31308
31353
|
var RoleSourceSchema = _enum([
|
|
31309
31354
|
"flagged",
|
|
31310
31355
|
"auto",
|
|
@@ -31318,8 +31363,8 @@ var HubAddressRoleResolutionSchema = object({
|
|
|
31318
31363
|
resolved: string().nullable(),
|
|
31319
31364
|
source: RoleSourceSchema
|
|
31320
31365
|
});
|
|
31321
|
-
var
|
|
31322
|
-
/** The addresses flagged `
|
|
31366
|
+
var HubAppConnectionsResolutionSchema = object({
|
|
31367
|
+
/** The addresses flagged `appConnections`, or empty = automatic. */
|
|
31323
31368
|
flagged: array(string()).readonly(),
|
|
31324
31369
|
/** What the apps race right now. */
|
|
31325
31370
|
resolved: array(string()).readonly(),
|
|
@@ -31339,10 +31384,10 @@ var HubAddressBookSchema = object({
|
|
|
31339
31384
|
eligible: array(HubAddressRoleSchema).readonly()
|
|
31340
31385
|
})).readonly(),
|
|
31341
31386
|
roles: object({
|
|
31342
|
-
|
|
31343
|
-
|
|
31344
|
-
|
|
31345
|
-
|
|
31387
|
+
appConnections: HubAppConnectionsResolutionSchema,
|
|
31388
|
+
notificationTaps: HubAddressRoleResolutionSchema,
|
|
31389
|
+
internal: HubAddressRoleResolutionSchema,
|
|
31390
|
+
publicEndpoint: HubAddressRoleResolutionSchema
|
|
31346
31391
|
}),
|
|
31347
31392
|
/** The `hub=` an app link carries (D709), or null. */
|
|
31348
31393
|
appLinkHub: string().nullable()
|
|
@@ -31390,7 +31435,7 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
31390
31435
|
* tunnel always emits `https://` regardless. */
|
|
31391
31436
|
scheme: _enum(["http", "https"]).optional(),
|
|
31392
31437
|
/** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
|
|
31393
|
-
* an address notification
|
|
31438
|
+
* an address notification taps can be flagged with, not a hub
|
|
31394
31439
|
* route. Default `false`: racers and rankers never see it. */
|
|
31395
31440
|
includeAppLink: boolean().optional()
|
|
31396
31441
|
}), 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 +49533,35 @@ function buildModelConvertProvider(deps) {
|
|
|
49488
49533
|
};
|
|
49489
49534
|
} };
|
|
49490
49535
|
}
|
|
49536
|
+
var CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
|
|
49537
|
+
/** Whitespace runs collapse to one space; leading/trailing whitespace goes. */
|
|
49538
|
+
function parseDisplayName(raw) {
|
|
49539
|
+
const name = raw.replace(/\s+/g, " ").trim();
|
|
49540
|
+
if (name.length === 0) return { kind: "reset" };
|
|
49541
|
+
if (CONTROL_CHARS.test(name)) return {
|
|
49542
|
+
kind: "invalid",
|
|
49543
|
+
reason: "The name contains control characters."
|
|
49544
|
+
};
|
|
49545
|
+
if (name.length > 80) return {
|
|
49546
|
+
kind: "invalid",
|
|
49547
|
+
reason: `The name is ${name.length} characters long; the limit is 80.`
|
|
49548
|
+
};
|
|
49549
|
+
return {
|
|
49550
|
+
kind: "set",
|
|
49551
|
+
name
|
|
49552
|
+
};
|
|
49553
|
+
}
|
|
49554
|
+
/**
|
|
49555
|
+
* The optional display name typed on an import card: `undefined` when left
|
|
49556
|
+
* empty (the source's own default applies), the cleaned name otherwise.
|
|
49557
|
+
* Throws with the reason when the input is not acceptable.
|
|
49558
|
+
*/
|
|
49559
|
+
function optionalImportName(raw) {
|
|
49560
|
+
if (raw === void 0) return void 0;
|
|
49561
|
+
const parsed = parseDisplayName(raw);
|
|
49562
|
+
if (parsed.kind === "invalid") throw new Error(`Display name rejected: ${parsed.reason}`);
|
|
49563
|
+
return parsed.kind === "set" ? parsed.name : void 0;
|
|
49564
|
+
}
|
|
49491
49565
|
//#endregion
|
|
49492
49566
|
//#region src/model-registry.ts
|
|
49493
49567
|
/**
|
|
@@ -49519,22 +49593,18 @@ function upsertAvailability(map, modelId, nodeId, entry) {
|
|
|
49519
49593
|
});
|
|
49520
49594
|
}
|
|
49521
49595
|
/**
|
|
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
49596
|
* Insert or replace a descriptor keyed by `entry.id`. When the id already
|
|
49528
49597
|
* exists, **formats are merged** (Scrypted multi-format accumulate) — sibling
|
|
49529
49598
|
* formats on the prior entry are never dropped. Returns a new array.
|
|
49530
49599
|
*/
|
|
49531
|
-
function upsertCustomModel(list, descriptor) {
|
|
49600
|
+
function upsertCustomModel(list, descriptor, nameOrigin = "source") {
|
|
49532
49601
|
const prev = list.find((m) => m.entry.id === descriptor.entry.id);
|
|
49533
49602
|
const merged = prev === void 0 ? descriptor : {
|
|
49534
49603
|
stepId: descriptor.stepId,
|
|
49535
49604
|
entry: {
|
|
49536
49605
|
...prev.entry,
|
|
49537
49606
|
...descriptor.entry,
|
|
49607
|
+
name: nameOrigin === "operator" ? descriptor.entry.name : prev.entry.name,
|
|
49538
49608
|
formats: {
|
|
49539
49609
|
...prev.entry.formats,
|
|
49540
49610
|
...descriptor.entry.formats
|
|
@@ -49543,6 +49613,43 @@ function upsertCustomModel(list, descriptor) {
|
|
|
49543
49613
|
};
|
|
49544
49614
|
return [...list.filter((m) => m.entry.id !== descriptor.entry.id), merged];
|
|
49545
49615
|
}
|
|
49616
|
+
/**
|
|
49617
|
+
* Rename one IMPORTED registry row in place. Only `entry.name` changes — the
|
|
49618
|
+
* id, step, formats and every pin keyed by the id stay exactly as they were.
|
|
49619
|
+
* An empty name resets the row to the name it was imported with. Throws on an
|
|
49620
|
+
* unknown id, a CamStack catalog row, or a name `parseDisplayName` rejects.
|
|
49621
|
+
*/
|
|
49622
|
+
function renameCustomModel(list, importedNames, modelId, rawName) {
|
|
49623
|
+
const row = list.find((m) => m.entry.id === modelId);
|
|
49624
|
+
if (row === void 0) throw new Error(`renameCustomModel: model "${modelId}" is not registered`);
|
|
49625
|
+
if (!isRenameableEntry(row.entry)) throw new Error(`renameCustomModel: "${modelId}" is a CamStack catalog model and keeps its name`);
|
|
49626
|
+
const parsed = parseDisplayName(rawName);
|
|
49627
|
+
if (parsed.kind === "invalid") throw new Error(`renameCustomModel: ${parsed.reason}`);
|
|
49628
|
+
const original = importedNames[modelId] ?? row.entry.name;
|
|
49629
|
+
const name = parsed.kind === "set" ? parsed.name : original;
|
|
49630
|
+
const nextNames = { ...importedNames };
|
|
49631
|
+
if (parsed.kind === "set" && name !== original) nextNames[modelId] = original;
|
|
49632
|
+
else delete nextNames[modelId];
|
|
49633
|
+
return {
|
|
49634
|
+
list: list.map((m) => m.entry.id === modelId ? {
|
|
49635
|
+
stepId: m.stepId,
|
|
49636
|
+
entry: {
|
|
49637
|
+
...m.entry,
|
|
49638
|
+
name
|
|
49639
|
+
}
|
|
49640
|
+
} : m),
|
|
49641
|
+
importedNames: nextNames,
|
|
49642
|
+
name
|
|
49643
|
+
};
|
|
49644
|
+
}
|
|
49645
|
+
/** An imported row can be renamed; a CamStack catalog model cannot. */
|
|
49646
|
+
function isRenameableEntry(entry) {
|
|
49647
|
+
return inferModelProvider({
|
|
49648
|
+
id: entry.id,
|
|
49649
|
+
description: entry.description,
|
|
49650
|
+
provider: entry.provider
|
|
49651
|
+
}) !== "camstack";
|
|
49652
|
+
}
|
|
49546
49653
|
/** Drop the descriptor with the given `entry.id`. Reports whether anything changed. */
|
|
49547
49654
|
function removeCustomModel(list, modelId) {
|
|
49548
49655
|
const next = list.filter((m) => m.entry.id !== modelId);
|
|
@@ -50390,6 +50497,19 @@ var modelStudioActions = defineCustomActions({
|
|
|
50390
50497
|
clearedPins: number().int().nonnegative()
|
|
50391
50498
|
}), { kind: "mutation" }),
|
|
50392
50499
|
/**
|
|
50500
|
+
* Rename an IMPORTED registry row (Frigate, Frigate+, Scrypted, uploads,
|
|
50501
|
+
* conversions) in place. Only `entry.name` changes — never the id, so pins
|
|
50502
|
+
* and availability are untouched. An empty name restores the name the model
|
|
50503
|
+
* was imported with. CamStack catalog models are refused.
|
|
50504
|
+
*/
|
|
50505
|
+
renameCustomModel: customAction(object({
|
|
50506
|
+
modelId: string().min(1),
|
|
50507
|
+
name: string()
|
|
50508
|
+
}), object({
|
|
50509
|
+
modelId: string(),
|
|
50510
|
+
name: string()
|
|
50511
|
+
}), { kind: "mutation" }),
|
|
50512
|
+
/**
|
|
50393
50513
|
* Delete one format of a Library model from one node. Keeps the registry row
|
|
50394
50514
|
* and sibling formats/nodes; clears only that availability chip.
|
|
50395
50515
|
*/
|
|
@@ -50478,6 +50598,8 @@ var modelStudioActions = defineCustomActions({
|
|
|
50478
50598
|
})),
|
|
50479
50599
|
importScryptedCatalogModel: customAction(object({
|
|
50480
50600
|
manifestId: string(),
|
|
50601
|
+
/** Optional display name typed on the import card. */
|
|
50602
|
+
name: string().optional(),
|
|
50481
50603
|
targetNodeId: string().optional()
|
|
50482
50604
|
}), CustomModelDescriptorSchema, { kind: "mutation" }),
|
|
50483
50605
|
/**
|
|
@@ -50507,7 +50629,8 @@ var modelStudioActions = defineCustomActions({
|
|
|
50507
50629
|
modelId: string().optional(),
|
|
50508
50630
|
name: string().optional(),
|
|
50509
50631
|
/**
|
|
50510
|
-
* Native artifact class
|
|
50632
|
+
* Native artifact class of the chosen build (`plusImportLane`: neural →
|
|
50633
|
+
* onnx, coral → tflite). The
|
|
50511
50634
|
* model-detail API does not always carry `supportedDetectors`, so the
|
|
50512
50635
|
* browser passes what the catalog already knew (fixes ssd/yolo-generic
|
|
50513
50636
|
* EdgeTPU rows that `plusTypeTraits` alone would call onnx).
|
|
@@ -50559,6 +50682,8 @@ var modelStudioActions = defineCustomActions({
|
|
|
50559
50682
|
//#region src/model-studio.addon.ts
|
|
50560
50683
|
var KEY_CUSTOM_MODELS = "customModels";
|
|
50561
50684
|
var KEY_AVAILABILITY = "availability";
|
|
50685
|
+
var KEY_IMPORTED_NAMES = "importedNames";
|
|
50686
|
+
var ImportedNamesSchema = record(string(), string());
|
|
50562
50687
|
var CustomModelsSchema = array(CustomModelDescriptorSchema);
|
|
50563
50688
|
var pythonDirCache;
|
|
50564
50689
|
function resolveAddonPythonDir() {
|
|
@@ -50621,12 +50746,18 @@ function spawnRunConvert(jobPath, pythonPath, onProgress, _sessionId) {
|
|
|
50621
50746
|
});
|
|
50622
50747
|
});
|
|
50623
50748
|
}
|
|
50749
|
+
/** A name the operator typed replaces an existing row's name; a derived one never does. */
|
|
50750
|
+
function nameOriginOf(operatorName) {
|
|
50751
|
+
return operatorName !== void 0 ? { nameOrigin: "operator" } : {};
|
|
50752
|
+
}
|
|
50624
50753
|
/** Global-fetch adapter for the injected Frigate+ client seam. */
|
|
50625
50754
|
var plusFetch = async (url, init) => fetch(url, init);
|
|
50626
50755
|
var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
50627
50756
|
id = "model-studio";
|
|
50628
50757
|
modelsState = null;
|
|
50629
50758
|
availabilityState = null;
|
|
50759
|
+
/** modelId → the name a renamed registry row had before its first rename. */
|
|
50760
|
+
importedNamesState = null;
|
|
50630
50761
|
/** Lazily-built chunked-upload store (hub only) — see `upload/model-upload-store.ts`. */
|
|
50631
50762
|
uploadStore = null;
|
|
50632
50763
|
/**
|
|
@@ -50709,6 +50840,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
50709
50840
|
const state = this.state(KEY_CUSTOM_MODELS, CustomModelsSchema, []);
|
|
50710
50841
|
this.modelsState = state;
|
|
50711
50842
|
this.availabilityState = this.state(KEY_AVAILABILITY, ModelAvailabilityMapSchema, {});
|
|
50843
|
+
this.importedNamesState = this.state(KEY_IMPORTED_NAMES, ImportedNamesSchema, {});
|
|
50712
50844
|
const rawNode = this.ctx.kernel?.localNodeId ?? "hub";
|
|
50713
50845
|
const isHub = (rawNode.includes("/") ? rawNode.split("/")[0] : rawNode) === "hub";
|
|
50714
50846
|
const pagesProvider = {
|
|
@@ -50742,7 +50874,11 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
50742
50874
|
return result;
|
|
50743
50875
|
},
|
|
50744
50876
|
abortModelUpload: async (input) => (await this.getUploadStore()).abort(input),
|
|
50745
|
-
registerCustomModel: async (input) => this.registerCustomModel(input, {
|
|
50877
|
+
registerCustomModel: async (input) => this.registerCustomModel(input, {
|
|
50878
|
+
ensureEngines: true,
|
|
50879
|
+
nameOrigin: "operator"
|
|
50880
|
+
}),
|
|
50881
|
+
renameCustomModel: async (input) => this.renameCustomModel_(input),
|
|
50746
50882
|
ensureFormatsThenDistribute: async (input) => this.ensureFormatsThenDistribute_(input.modelId),
|
|
50747
50883
|
ensureModelFormats: async (input) => this.ensureModelFormats_(input.modelId, input.formats),
|
|
50748
50884
|
removeCustomModel: async (input) => this.removeCustomModel(input),
|
|
@@ -50894,6 +51030,35 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
50894
51030
|
if (!this.modelsState) throw new Error("ModelStudioAddon: durable state not initialized");
|
|
50895
51031
|
return this.modelsState;
|
|
50896
51032
|
}
|
|
51033
|
+
get importedNames_() {
|
|
51034
|
+
if (!this.importedNamesState) throw new Error("ModelStudioAddon: imported-names state not initialized");
|
|
51035
|
+
return this.importedNamesState;
|
|
51036
|
+
}
|
|
51037
|
+
/**
|
|
51038
|
+
* Rename an imported registry row in place (`renameCustomModel` decides).
|
|
51039
|
+
* The id is untouched, so pins, availability and files are unaffected; the
|
|
51040
|
+
* pipeline's catalog view picks the new name up on its next registry read.
|
|
51041
|
+
*/
|
|
51042
|
+
async renameCustomModel_(input) {
|
|
51043
|
+
const names = await this.importedNames_.get();
|
|
51044
|
+
const outcome = {};
|
|
51045
|
+
await this.state_.update((prev) => {
|
|
51046
|
+
const renamed = renameCustomModel(prev, names, input.modelId, input.name);
|
|
51047
|
+
outcome.result = renamed;
|
|
51048
|
+
return renamed.list;
|
|
51049
|
+
});
|
|
51050
|
+
const result = outcome.result;
|
|
51051
|
+
if (result === void 0) throw new Error("renameCustomModel: registry update did not run");
|
|
51052
|
+
await this.importedNames_.set(result.importedNames);
|
|
51053
|
+
this.ctx.logger.info("Renamed custom model", { meta: {
|
|
51054
|
+
modelId: input.modelId,
|
|
51055
|
+
name: result.name
|
|
51056
|
+
} });
|
|
51057
|
+
return {
|
|
51058
|
+
modelId: input.modelId,
|
|
51059
|
+
name: result.name
|
|
51060
|
+
};
|
|
51061
|
+
}
|
|
50897
51062
|
get availability_() {
|
|
50898
51063
|
if (!this.availabilityState) throw new Error("ModelStudioAddon: availability state not initialized");
|
|
50899
51064
|
return this.availabilityState;
|
|
@@ -51015,6 +51180,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51015
51180
|
* `targetNodeId` lands the HF URL on that node via download-on-target.
|
|
51016
51181
|
*/
|
|
51017
51182
|
async importScryptedCatalogFromManifest(input) {
|
|
51183
|
+
const name = optionalImportName(input.name);
|
|
51018
51184
|
const entry = getScryptedManifestEntry(input.manifestId);
|
|
51019
51185
|
if (!entry) throw new Error(`importScryptedCatalogModel: unknown manifest id "${input.manifestId}"`);
|
|
51020
51186
|
let drift;
|
|
@@ -51034,8 +51200,15 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51034
51200
|
};
|
|
51035
51201
|
}
|
|
51036
51202
|
if (drift.status !== "ok") throw new Error(`importScryptedCatalogModel: entry "${entry.id}" is blocked by drift (${drift.status}${drift.details ? `: ${drift.details}` : ""})`);
|
|
51037
|
-
const
|
|
51038
|
-
|
|
51203
|
+
const built = buildScryptedRegistryDescriptor(entry);
|
|
51204
|
+
const descriptor = name !== void 0 ? {
|
|
51205
|
+
stepId: built.stepId,
|
|
51206
|
+
entry: {
|
|
51207
|
+
...built.entry,
|
|
51208
|
+
name
|
|
51209
|
+
}
|
|
51210
|
+
} : built;
|
|
51211
|
+
await this.registerCustomModel(descriptor, nameOriginOf(name));
|
|
51039
51212
|
if (input.targetNodeId !== void 0 && input.targetNodeId.length > 0) await this.distributeModel({
|
|
51040
51213
|
modelId: descriptor.entry.id,
|
|
51041
51214
|
nodeId: input.targetNodeId,
|
|
@@ -51057,11 +51230,12 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51057
51230
|
async importFrigateCatalogModel(input) {
|
|
51058
51231
|
const cat = FRIGATE_PUBLIC_CATALOG.find((c) => c.id === input.catalogId);
|
|
51059
51232
|
if (!cat) throw new Error(`importFrigateCatalogModel: unknown catalog id "${input.catalogId}"`);
|
|
51233
|
+
const name = optionalImportName(input.name);
|
|
51060
51234
|
const descriptor = buildFrigateCatalogDescriptor(input.catalogId, {
|
|
51061
51235
|
...input.modelId !== void 0 ? { modelId: input.modelId } : {},
|
|
51062
|
-
...
|
|
51236
|
+
...name !== void 0 ? { name } : {}
|
|
51063
51237
|
});
|
|
51064
|
-
await this.registerCustomModel(descriptor);
|
|
51238
|
+
await this.registerCustomModel(descriptor, nameOriginOf(name));
|
|
51065
51239
|
let bytes = Math.round(cat.sizeMB * 1e6);
|
|
51066
51240
|
if (input.targetNodeId !== void 0 && input.targetNodeId.length > 0) {
|
|
51067
51241
|
const landed = await this.distributeModel({
|
|
@@ -51092,6 +51266,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51092
51266
|
*/
|
|
51093
51267
|
async importFrigatePlusModel(input) {
|
|
51094
51268
|
const modelId = frigatePlusRegistryId(input.plusModelId);
|
|
51269
|
+
const operatorName = optionalImportName(input.name);
|
|
51095
51270
|
const key = this.config.frigatePlusApiKey ?? "";
|
|
51096
51271
|
const { info, downloadUrl } = await requestPlusModelDownload(plusFetch, key, input.plusModelId);
|
|
51097
51272
|
const typeTraits = plusTypeTraits(info.type);
|
|
@@ -51116,12 +51291,15 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51116
51291
|
const sha256 = await ModelStudioAddon.fileSha256(destPath);
|
|
51117
51292
|
const descriptor = plusInfoToDescriptor(info, {
|
|
51118
51293
|
modelId,
|
|
51119
|
-
name:
|
|
51294
|
+
name: operatorName ?? `Frigate+ ${info.type} (${input.plusModelId.slice(0, 8)})`,
|
|
51120
51295
|
filename,
|
|
51121
51296
|
sizeMB: Number((bytes / 1e6).toFixed(1)),
|
|
51122
51297
|
...input.format !== void 0 ? { format: input.format } : {}
|
|
51123
51298
|
});
|
|
51124
|
-
await this.registerCustomModel(descriptor, {
|
|
51299
|
+
await this.registerCustomModel(descriptor, {
|
|
51300
|
+
ensureEngines: false,
|
|
51301
|
+
...nameOriginOf(operatorName)
|
|
51302
|
+
});
|
|
51125
51303
|
await this.availability_.update((prev) => upsertAvailability(prev, modelId, "hub", {
|
|
51126
51304
|
format: traits.format,
|
|
51127
51305
|
sha256,
|
|
@@ -51154,7 +51332,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51154
51332
|
async registerCustomModel(input, opts = {}) {
|
|
51155
51333
|
if (!input.stepId.trim()) throw new Error("registerCustomModel: stepId is required");
|
|
51156
51334
|
if (!input.entry.id.trim()) throw new Error("registerCustomModel: entry.id is required");
|
|
51157
|
-
await this.state_.update((prev) => upsertCustomModel(prev, input));
|
|
51335
|
+
await this.state_.update((prev) => upsertCustomModel(prev, input, opts.nameOrigin));
|
|
51158
51336
|
this.ctx.logger.info("Registered custom model", { meta: {
|
|
51159
51337
|
modelId: input.entry.id,
|
|
51160
51338
|
stepId: input.stepId
|
|
@@ -51411,6 +51589,7 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
|
|
|
51411
51589
|
removed = result.removed;
|
|
51412
51590
|
return result.next;
|
|
51413
51591
|
});
|
|
51592
|
+
await this.importedNames_.update((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => id !== input.modelId)));
|
|
51414
51593
|
this.ctx.logger.info("Removed custom model", { meta: {
|
|
51415
51594
|
modelId: input.modelId,
|
|
51416
51595
|
removed,
|