@camstack/addon-model-studio 1.1.327 → 1.1.329

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.
@@ -1,4 +1,4 @@
1
- import "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-DXGb6Oaj.mjs";
1
+ import "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-Ct9GIyeI.mjs";
2
2
  //#region \0virtual:mf-localSharedImportMap:__mfe_internal__addon_model_studio_page
3
3
  var e = {
4
4
  "@camstack/sdk": {
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.452",
21
+ version: "1.2.453",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_model_studio_page",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.2.357",
36
+ version: "1.2.358",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_model_studio_page",
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.2.452",
39
+ version: "1.2.453",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  singleton: !0,
@@ -81,7 +81,7 @@ async function r() {
81
81
  }
82
82
  },
83
83
  "@camstack/ui-library": {
84
- version: "1.2.357",
84
+ version: "1.2.358",
85
85
  scope: "default",
86
86
  shareConfig: {
87
87
  singleton: !0,
@@ -44698,14 +44698,24 @@ method(object({
44698
44698
  kind: "mutation",
44699
44699
  auth: "admin"
44700
44700
  });
44701
- method(object({
44702
- nodeId: string().min(1),
44703
- /** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
44704
- removedAt: number()
44705
- }), object({ counts: record(string(), number().int().nonnegative()) }), {
44706
- kind: "mutation",
44707
- auth: "admin"
44708
- });
44701
+ var nodeRemovalHookCapability = {
44702
+ name: "node-removal-hook",
44703
+ scope: "system",
44704
+ mode: "collection",
44705
+ internal: true,
44706
+ methods: {
44707
+ /** Remove or re-point everything this addon keeps for the forgotten node. Idempotent. */
44708
+ onNodeRemoved: method(object({
44709
+ nodeId: string().min(1),
44710
+ /** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
44711
+ removedAt: number()
44712
+ }), object({ counts: record(string(), number().int().nonnegative()) }), {
44713
+ kind: "mutation",
44714
+ auth: "admin"
44715
+ }) },
44716
+ /** In-process only — enumerated through `addons.listCapabilityProviders`. */
44717
+ mount: { kind: "skip" }
44718
+ };
44709
44719
  /**
44710
44720
  * `device-removal-hook` — the reliable way an addon learns a device was DELETED,
44711
44721
  * so it can delete what it keeps for that device (D865).
@@ -56925,6 +56935,12 @@ var TerminalInstanceInfoSchema = object({
56925
56935
  instanceId: string(),
56926
56936
  cameraStableId: string(),
56927
56937
  nodeId: string(),
56938
+ /**
56939
+ * False while the node the instance is bound to is not in the cluster
56940
+ * topology (forgotten, renamed, re-paired). The instance is kept; the
56941
+ * operator re-points it with `updateInstance({ targetNodeId })`.
56942
+ */
56943
+ nodeInCluster: boolean(),
56928
56944
  profileId: string(),
56929
56945
  profileLabel: string(),
56930
56946
  name: string(),
@@ -56974,6 +56990,8 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
56974
56990
  auth: "admin"
56975
56991
  }), method(object({
56976
56992
  instanceId: string().min(1),
56993
+ /** Re-point the instance to another node; the profile must exist there. */
56994
+ targetNodeId: string().min(1).max(256).optional(),
56977
56995
  name: string().trim().min(1).max(160).optional(),
56978
56996
  executable: string().max(1024).optional(),
56979
56997
  args: array(string().max(2048)).max(64).optional(),
@@ -87872,6 +87890,41 @@ function stripModelIdFromInferenceDevices(devices, modelId) {
87872
87890
  };
87873
87891
  }
87874
87892
  //#endregion
87893
+ //#region src/node-removal.ts
87894
+ /** `map` without `nodeId`'s entries, and how many (model, node) entries went. */
87895
+ function clearNodeAvailability(map, nodeId) {
87896
+ const next = {};
87897
+ let cleared = 0;
87898
+ for (const [modelId, perNode] of Object.entries(map)) {
87899
+ if (!(nodeId in perNode)) {
87900
+ next[modelId] = perNode;
87901
+ continue;
87902
+ }
87903
+ cleared += 1;
87904
+ const { [nodeId]: _gone, ...rest } = perNode;
87905
+ if (Object.keys(rest).length > 0) next[modelId] = rest;
87906
+ }
87907
+ return {
87908
+ next,
87909
+ cleared
87910
+ };
87911
+ }
87912
+ function createNodeRemovalHook(deps) {
87913
+ return { onNodeRemoved: async ({ nodeId }) => {
87914
+ let cleared = 0;
87915
+ await deps.availability().update((prev) => {
87916
+ const result = clearNodeAvailability(prev, nodeId);
87917
+ cleared = result.cleared;
87918
+ return cleared === 0 ? prev : result.next;
87919
+ });
87920
+ deps.logger.info("cleared model availability of a forgotten node", {
87921
+ tags: { nodeId },
87922
+ meta: { cleared }
87923
+ });
87924
+ return { counts: { "model-availability": cleared } };
87925
+ } };
87926
+ }
87927
+ //#endregion
87875
87928
  //#region src/plus-catalog/plus-catalog-import.ts
87876
87929
  /**
87877
87930
  * Variant-selector axes for an imported plus catalog / plus catalog detector. Family
@@ -89313,13 +89366,22 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
89313
89366
  capability: addonPagesSourceCapability,
89314
89367
  provider: pagesProvider
89315
89368
  }];
89316
- if (isHub) providers.push({
89317
- capability: customModelRegistryCapability,
89318
- provider: {
89319
- listModels: async () => state.get(),
89320
- importModelBundle: async (input) => this.importModelBundle_(input)
89321
- }
89322
- });
89369
+ if (isHub) {
89370
+ providers.push({
89371
+ capability: customModelRegistryCapability,
89372
+ provider: {
89373
+ listModels: async () => state.get(),
89374
+ importModelBundle: async (input) => this.importModelBundle_(input)
89375
+ }
89376
+ });
89377
+ providers.push({
89378
+ capability: nodeRemovalHookCapability,
89379
+ provider: createNodeRemovalHook({
89380
+ availability: () => this.availability_,
89381
+ logger: this.ctx.logger.child("node-removal")
89382
+ })
89383
+ });
89384
+ }
89323
89385
  return {
89324
89386
  providers,
89325
89387
  customActions: isHub ? modelStudioActions : void 0,
@@ -44672,14 +44672,24 @@ method(object({
44672
44672
  kind: "mutation",
44673
44673
  auth: "admin"
44674
44674
  });
44675
- method(object({
44676
- nodeId: string().min(1),
44677
- /** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
44678
- removedAt: number()
44679
- }), object({ counts: record(string(), number().int().nonnegative()) }), {
44680
- kind: "mutation",
44681
- auth: "admin"
44682
- });
44675
+ var nodeRemovalHookCapability = {
44676
+ name: "node-removal-hook",
44677
+ scope: "system",
44678
+ mode: "collection",
44679
+ internal: true,
44680
+ methods: {
44681
+ /** Remove or re-point everything this addon keeps for the forgotten node. Idempotent. */
44682
+ onNodeRemoved: method(object({
44683
+ nodeId: string().min(1),
44684
+ /** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
44685
+ removedAt: number()
44686
+ }), object({ counts: record(string(), number().int().nonnegative()) }), {
44687
+ kind: "mutation",
44688
+ auth: "admin"
44689
+ }) },
44690
+ /** In-process only — enumerated through `addons.listCapabilityProviders`. */
44691
+ mount: { kind: "skip" }
44692
+ };
44683
44693
  /**
44684
44694
  * `device-removal-hook` — the reliable way an addon learns a device was DELETED,
44685
44695
  * so it can delete what it keeps for that device (D865).
@@ -56899,6 +56909,12 @@ var TerminalInstanceInfoSchema = object({
56899
56909
  instanceId: string(),
56900
56910
  cameraStableId: string(),
56901
56911
  nodeId: string(),
56912
+ /**
56913
+ * False while the node the instance is bound to is not in the cluster
56914
+ * topology (forgotten, renamed, re-paired). The instance is kept; the
56915
+ * operator re-points it with `updateInstance({ targetNodeId })`.
56916
+ */
56917
+ nodeInCluster: boolean(),
56902
56918
  profileId: string(),
56903
56919
  profileLabel: string(),
56904
56920
  name: string(),
@@ -56948,6 +56964,8 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
56948
56964
  auth: "admin"
56949
56965
  }), method(object({
56950
56966
  instanceId: string().min(1),
56967
+ /** Re-point the instance to another node; the profile must exist there. */
56968
+ targetNodeId: string().min(1).max(256).optional(),
56951
56969
  name: string().trim().min(1).max(160).optional(),
56952
56970
  executable: string().max(1024).optional(),
56953
56971
  args: array(string().max(2048)).max(64).optional(),
@@ -87846,6 +87864,41 @@ function stripModelIdFromInferenceDevices(devices, modelId) {
87846
87864
  };
87847
87865
  }
87848
87866
  //#endregion
87867
+ //#region src/node-removal.ts
87868
+ /** `map` without `nodeId`'s entries, and how many (model, node) entries went. */
87869
+ function clearNodeAvailability(map, nodeId) {
87870
+ const next = {};
87871
+ let cleared = 0;
87872
+ for (const [modelId, perNode] of Object.entries(map)) {
87873
+ if (!(nodeId in perNode)) {
87874
+ next[modelId] = perNode;
87875
+ continue;
87876
+ }
87877
+ cleared += 1;
87878
+ const { [nodeId]: _gone, ...rest } = perNode;
87879
+ if (Object.keys(rest).length > 0) next[modelId] = rest;
87880
+ }
87881
+ return {
87882
+ next,
87883
+ cleared
87884
+ };
87885
+ }
87886
+ function createNodeRemovalHook(deps) {
87887
+ return { onNodeRemoved: async ({ nodeId }) => {
87888
+ let cleared = 0;
87889
+ await deps.availability().update((prev) => {
87890
+ const result = clearNodeAvailability(prev, nodeId);
87891
+ cleared = result.cleared;
87892
+ return cleared === 0 ? prev : result.next;
87893
+ });
87894
+ deps.logger.info("cleared model availability of a forgotten node", {
87895
+ tags: { nodeId },
87896
+ meta: { cleared }
87897
+ });
87898
+ return { counts: { "model-availability": cleared } };
87899
+ } };
87900
+ }
87901
+ //#endregion
87849
87902
  //#region src/plus-catalog/plus-catalog-import.ts
87850
87903
  /**
87851
87904
  * Variant-selector axes for an imported plus catalog / plus catalog detector. Family
@@ -89287,13 +89340,22 @@ var ModelStudioAddon = class ModelStudioAddon extends BaseAddon {
89287
89340
  capability: addonPagesSourceCapability,
89288
89341
  provider: pagesProvider
89289
89342
  }];
89290
- if (isHub) providers.push({
89291
- capability: customModelRegistryCapability,
89292
- provider: {
89293
- listModels: async () => state.get(),
89294
- importModelBundle: async (input) => this.importModelBundle_(input)
89295
- }
89296
- });
89343
+ if (isHub) {
89344
+ providers.push({
89345
+ capability: customModelRegistryCapability,
89346
+ provider: {
89347
+ listModels: async () => state.get(),
89348
+ importModelBundle: async (input) => this.importModelBundle_(input)
89349
+ }
89350
+ });
89351
+ providers.push({
89352
+ capability: nodeRemovalHookCapability,
89353
+ provider: createNodeRemovalHook({
89354
+ availability: () => this.availability_,
89355
+ logger: this.ctx.logger.child("node-removal")
89356
+ })
89357
+ });
89358
+ }
89297
89359
  return {
89298
89360
  providers,
89299
89361
  customActions: isHub ? modelStudioActions : void 0,
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-DXGb6Oaj.mjs";
1
+ import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-Ct9GIyeI.mjs";
2
2
  export { t as get, e as init };
@@ -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-BP5KTsDn.mjs")).catch((e) => {
2756
+ return tr ||= rr(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-DBwBRZfG.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.327",
3
+ "version": "1.1.329",
4
4
  "description": "Custom detection model registry, conversion & distribution for CamStack",
5
5
  "keywords": [
6
6
  "camstack",
@@ -53,6 +53,9 @@
53
53
  {
54
54
  "name": "model-convert"
55
55
  },
56
+ {
57
+ "name": "node-removal-hook"
58
+ },
56
59
  {
57
60
  "name": "addon-pages-source"
58
61
  }