@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
|
@@ -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
|
-
|
|
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", [
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
11187
|
-
|
|
11188
|
-
|
|
11189
|
-
|
|
11190
|
-
|
|
11191
|
-
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
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
|
|
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
|
-
*
|
|
31257
|
-
*
|
|
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
|
|
31274
|
-
* `
|
|
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
|
|
31296
|
-
/** The addresses flagged `
|
|
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
|
-
|
|
31317
|
-
|
|
31318
|
-
|
|
31319
|
-
|
|
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
|
|
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
|
|
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, {
|
|
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
|
|
51012
|
-
|
|
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
|
-
...
|
|
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:
|
|
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, {
|
|
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-
|
|
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",
|
package/dist/remoteEntry.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-
|
|
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 {
|
|
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-
|
|
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"
|
|
@@ -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-
|
|
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
|
}
|