@camstack/addon-admin-ui 1.1.22 → 1.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/assets/{AddLocationWizard-hVwjbOvf.js → AddLocationWizard-B0D93w1V.js} +1 -1
  2. package/dist/assets/{AddonCollectionPage-CTsqn59E.js → AddonCollectionPage-jQE-FN9F.js} +1 -1
  3. package/dist/assets/Addons-xDCo_Ibx.js +1 -0
  4. package/dist/assets/{AdminTabs-H2eHA_BP.js → AdminTabs-DB_PsPkZ.js} +1 -1
  5. package/dist/assets/{BrokerForm-I-2eiXTD.js → BrokerForm-C1GK18eb.js} +1 -1
  6. package/dist/assets/{Capabilities-KNxdGU5R.js → Capabilities-B_hUdnH-.js} +1 -1
  7. package/dist/assets/{CapabilityBadges-Bp9qwWAG.js → CapabilityBadges-CZscW36S.js} +1 -1
  8. package/dist/assets/Cluster-CQtB-9nH.js +1 -0
  9. package/dist/assets/{Dashboard-CgzNKw4S.js → Dashboard-D4y2xKjN.js} +1 -1
  10. package/dist/assets/{Data-GY9aNQ1Y.js → Data-ClFHo7k7.js} +1 -1
  11. package/dist/assets/{DetectionIntelligence-CbBEvocZ.js → DetectionIntelligence-CYHv0Ld6.js} +1 -1
  12. package/dist/assets/DeviceDetail-DQbcmNRx.js +2 -0
  13. package/dist/assets/{Devices-2wZcDSXn.js → Devices-O-RSaAyc.js} +1 -1
  14. package/dist/assets/{EmbedPlayerPage-BSfF5HmG.js → EmbedPlayerPage-Vwp51w7k.js} +1 -1
  15. package/dist/assets/{FormBuilder-CO6H7q61.js → FormBuilder-DUy1u0HQ.js} +1 -1
  16. package/dist/assets/Identity-BLPS5oFH.js +1 -0
  17. package/dist/assets/{IntegrationDetail-DQI58WRY.js → IntegrationDetail-CLlrQO-I.js} +1 -1
  18. package/dist/assets/{Integrations-Dee1SHhr.js → Integrations-CTOyhVU0.js} +1 -1
  19. package/dist/assets/{Integrations-rZdfMVdZ.js → Integrations-bAY_NNXp.js} +1 -1
  20. package/dist/assets/{Logs-BJ61UdxK.js → Logs-DLDJ1XYV.js} +1 -1
  21. package/dist/assets/{MotionZonesSettings-CYzimYmN.js → MotionZonesSettings-DD82JT6X.js} +1 -1
  22. package/dist/assets/{MyAccess-Bgewpx3A.js → MyAccess-KfKvkEtD.js} +1 -1
  23. package/dist/assets/Network-eBUg-aLx.js +1 -0
  24. package/dist/assets/{NodeAddonsSettingsPanel-CWrLPV6J.js → NodeAddonsSettingsPanel-CAWjHkWe.js} +1 -1
  25. package/dist/assets/{NotificationTargets-lEW7Zldv.js → NotificationTargets-C6VbOPrS.js} +1 -1
  26. package/dist/assets/Pipeline-BJGkzi3T.js +1 -0
  27. package/dist/assets/{PrivacyMaskSettings-CzPxZHZ1.js → PrivacyMaskSettings-D3xSpqST.js} +1 -1
  28. package/dist/assets/{ProviderIcon-Dm-sZ7Df.js → ProviderIcon-Dq4cUz-T.js} +1 -1
  29. package/dist/assets/{ScanNetworkModal-Vcr_-aIb.js → ScanNetworkModal-BG7q8vne.js} +1 -1
  30. package/dist/assets/{Settings-WQCzalu_.js → Settings-BHsqyFJq.js} +1 -1
  31. package/dist/assets/{Showroom-BxISco8h.js → Showroom-BAFyv63G.js} +1 -1
  32. package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-CKTRvTR8.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-yBzXtKyT.js} +1 -1
  33. package/dist/assets/circle-check-big-BzPMhjm9.js +1 -0
  34. package/dist/assets/{copy-CjMMaCPu.js → copy-Bp0A7GL5.js} +1 -1
  35. package/dist/assets/{dist-DsiSY20Z.js → dist-BJkRlU4O.js} +1 -1
  36. package/dist/assets/dist-C2zjNUfm.js +7 -0
  37. package/dist/assets/{download-35ioIRtw.js → download-CBh2vRsp.js} +1 -1
  38. package/dist/assets/{esm-DPIMznCO.js → esm-g63AkxW6.js} +1 -1
  39. package/dist/assets/{hostInit-BQK5GXsV.js → hostInit-qvQlpv8X.js} +1 -1
  40. package/dist/assets/index-Cn4a1biN.js +4 -0
  41. package/dist/assets/index-rzKyP5Rc.css +2 -0
  42. package/dist/assets/{key-round-CHE1k3cQ.js → key-round-C_XEC7Gc.js} +1 -1
  43. package/dist/assets/{mf-entry-bootstrap-0-5940e459.js → mf-entry-bootstrap-0-ad99ca10.js} +2 -2
  44. package/dist/assets/{pencil-C1w2uLp9.js → pencil-DxoCSmVR.js} +1 -1
  45. package/dist/assets/{player-overlays-DlPIT9xj.js → player-overlays-Ct6CLAsl.js} +1 -1
  46. package/dist/assets/plus-EQcjNVys.js +1 -0
  47. package/dist/assets/{radio-DpwfMzDk.js → radio-M3gCeKhM.js} +1 -1
  48. package/dist/assets/{refresh-cw-DfSn-i0F.js → refresh-cw-DBfoJ4On.js} +1 -1
  49. package/dist/assets/remoteEntry-OducpIHg.js +1 -0
  50. package/dist/assets/{rotate-ccw--rHGluty.js → rotate-ccw-C1eVpLBM.js} +1 -1
  51. package/dist/assets/rotate-cw-CKMWN-Uj.js +1 -0
  52. package/dist/assets/search-BfAqdDnY.js +1 -0
  53. package/dist/assets/{server-BlM9U_dS.js → server-DLT_BK-g.js} +1 -1
  54. package/dist/assets/{sparkles-COBUjBX-.js → sparkles-CrRvhOpC.js} +1 -1
  55. package/dist/assets/square-CRlIqw8T.js +1 -0
  56. package/dist/assets/{square-check-big-Dej4DiE7.js → square-check-big-Bu7EFMXK.js} +1 -1
  57. package/dist/assets/src-BfOH_Jmd.js +7 -0
  58. package/dist/assets/src-V0msI9lz.js +67 -0
  59. package/dist/assets/{upload-BJzZajFJ.js → upload-DTudiX4G.js} +1 -1
  60. package/dist/assets/{useDevice-CjqFlWNw.js → useDevice-6T56tUxG.js} +1 -1
  61. package/dist/assets/{useEventInvalidation-CY1yyH4R.js → useEventInvalidation-Czz_xatw.js} +1 -1
  62. package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-Dl7HuNe3.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-TLOoOs7X.js} +2 -2
  63. package/dist/assets/{wifi-Dj3gSpzs.js → wifi-BtR7h5Gk.js} +1 -1
  64. package/dist/assets/{zap-DLkdpIj9.js → zap-BZLRKsVv.js} +1 -1
  65. package/dist/index.html +6 -6
  66. package/dist/server/addon.js +362 -17
  67. package/package.json +1 -1
  68. package/dist/assets/Addons-CCbeVMQt.js +0 -1
  69. package/dist/assets/Cluster-ChnNCri_.js +0 -1
  70. package/dist/assets/DeviceDetail-sgGEfTXB.js +0 -2
  71. package/dist/assets/Identity-Ctx_yZtf.js +0 -1
  72. package/dist/assets/Network-Dc_a0WJ9.js +0 -1
  73. package/dist/assets/Pipeline-CbfX9p5j.js +0 -1
  74. package/dist/assets/circle-check-big-B9mpY_ym.js +0 -1
  75. package/dist/assets/dist-u_nf627j.js +0 -7
  76. package/dist/assets/index-BQGzI2C-.css +0 -2
  77. package/dist/assets/index-BdGL2J0e.js +0 -4
  78. package/dist/assets/plus-D9QBDEjM.js +0 -1
  79. package/dist/assets/remoteEntry-CIdYQQ-f.js +0 -1
  80. package/dist/assets/rotate-cw-DtSHMNUL.js +0 -1
  81. package/dist/assets/search-DgJL1Gey.js +0 -1
  82. package/dist/assets/square-VsNW1mnX.js +0 -1
  83. package/dist/assets/src-C9eW94dA.js +0 -7
  84. package/dist/assets/src-CtvTbpOC.js +0 -61
@@ -4629,7 +4629,7 @@ function _instanceof(cls, params = {}) {
4629
4629
  return inst;
4630
4630
  }
4631
4631
  //#endregion
4632
- //#region ../types/dist/sleep-BO1nweKv.mjs
4632
+ //#region ../types/dist/sleep-BiDFW0E7.mjs
4633
4633
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4634
4634
  EventCategory["SystemBoot"] = "system.boot";
4635
4635
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -5442,6 +5442,100 @@ function createDurableState(deps) {
5442
5442
  };
5443
5443
  }
5444
5444
  /**
5445
+ * Per-node scoping for the shared addon-settings blob.
5446
+ *
5447
+ * An addon's settings store is hub-central (the `addon-settings` cap is
5448
+ * hub-routed — the hub instance answers for every node), so fields whose
5449
+ * value is a NODE fact (decoder backend, engine pick, probed hardware
5450
+ * capabilities) persist under the node-scoped key `<key>@<nodeId>` inside
5451
+ * the single shared blob. The SCHEMA field key stays bare: the write path
5452
+ * ({@link scopePatch}) maps the bare field onto the target node's scoped
5453
+ * key; the read/hydrate path ({@link projectStore}) maps THIS node's value
5454
+ * back, so UI forms and `resolveConfig` only ever see the bare key.
5455
+ *
5456
+ * Fields opt in via the `perNode: true` schema marker on `ConfigFieldBase`
5457
+ * (`interfaces/config-ui.ts`); `BaseAddon` derives the key set from the
5458
+ * schema and routes reads/writes through these helpers.
5459
+ *
5460
+ * ## No bare-key fallback — deliberate
5461
+ *
5462
+ * {@link readNodeValue} reads the node-scoped key ONLY. A node with no
5463
+ * scoped key resolves to `undefined` so the field's schema `default` wins —
5464
+ * NEVER `store[base]` and never another node's value. A bare legacy key in
5465
+ * the store is invisible to every node, hub included, so one node's
5466
+ * selection can never leak onto another. (This generalizes the
5467
+ * `decoder-backend-keys.ts` semantics — the no-fallback variant — over an
5468
+ * arbitrary set of per-node field keys.)
5469
+ *
5470
+ * Pure functions only — no I/O, no imports beyond the language. This is a
5471
+ * LEAF module: import it via its deep path, never from the root barrel.
5472
+ */
5473
+ /**
5474
+ * Normalize a raw kernel node id to the bare node id used for scoping.
5475
+ * `localNodeId` can carry a `<node>/<addon>` suffix on forked child
5476
+ * processes; per-node settings are per-NODE, so strip the addon segment.
5477
+ * `undefined` / `null` / empty falls back to `'hub'`.
5478
+ */
5479
+ function normalizeNodeId(raw) {
5480
+ if (raw === void 0 || raw === null || raw === "") return "hub";
5481
+ const slashIdx = raw.indexOf("/");
5482
+ if (slashIdx < 0) return raw;
5483
+ const bare = raw.slice(0, slashIdx);
5484
+ return bare === "" ? "hub" : bare;
5485
+ }
5486
+ /** The node-scoped store key for a per-node field: `<base>@<nodeId>`. */
5487
+ function nodeScopedKey(base, nodeId) {
5488
+ return `${base}@${normalizeNodeId(nodeId)}`;
5489
+ }
5490
+ /**
5491
+ * Read a node's value for a per-node field from the raw shared store:
5492
+ * the node-scoped key when present, otherwise `undefined`.
5493
+ * Deliberately NO bare-key fallback (see module doc) — the caller lets the
5494
+ * schema `default` win on `undefined`.
5495
+ */
5496
+ function readNodeValue(store, base, nodeId) {
5497
+ return store[nodeScopedKey(base, nodeId)];
5498
+ }
5499
+ /**
5500
+ * Re-map a UI/settings patch so every bare perNode field persists under the
5501
+ * TARGET node's scoped key; all other keys pass through unchanged. Used on
5502
+ * the write path so a save for one node never clobbers another node's value
5503
+ * (and the bare key is never written). Returns a new object — the input
5504
+ * patch is not mutated.
5505
+ */
5506
+ function scopePatch(patch, perNodeKeys, nodeId) {
5507
+ const out = {};
5508
+ for (const [key, value] of Object.entries(patch)) out[perNodeKeys.has(key) ? nodeScopedKey(key, nodeId) : key] = value;
5509
+ return out;
5510
+ }
5511
+ /**
5512
+ * Project the raw shared store onto the bare schema keys for ONE node so a
5513
+ * UI schema (whose field keys are bare) hydrates from that node's own
5514
+ * values:
5515
+ *
5516
+ * - EVERY `@`-scoped key (any node's) is dropped from the pass-through.
5517
+ * - Every bare perNode key is dropped from the pass-through (a stray bare
5518
+ * legacy key must never hydrate any node — no bare fallback).
5519
+ * - THIS node's effective value ({@link readNodeValue}) is then laid onto
5520
+ * each bare perNode key; when the node has no scoped key the bare key is
5521
+ * left ABSENT so the field's schema `default` wins.
5522
+ *
5523
+ * Returns a new object — the input store is not mutated.
5524
+ */
5525
+ function projectStore(store, perNodeKeys, nodeId) {
5526
+ const out = {};
5527
+ for (const [key, value] of Object.entries(store)) {
5528
+ if (key.includes("@")) continue;
5529
+ if (perNodeKeys.has(key)) continue;
5530
+ out[key] = value;
5531
+ }
5532
+ for (const base of perNodeKeys) {
5533
+ const value = readNodeValue(store, base, nodeId);
5534
+ if (value !== void 0) out[base] = value;
5535
+ }
5536
+ return out;
5537
+ }
5538
+ /**
5445
5539
  * Base class for CamStack addons. Eliminates settings boilerplate:
5446
5540
  *
5447
5541
  * - Typed `config` property with automatic resolution from store + defaults
@@ -5609,23 +5703,63 @@ var BaseAddon = class {
5609
5703
  deviceSettingsSchema() {
5610
5704
  return null;
5611
5705
  }
5612
- async getGlobalSettings(overlay, cap, _nodeId) {
5706
+ async getGlobalSettings(overlay, cap, nodeId) {
5613
5707
  const schema = this.globalSettingsSchema(cap);
5614
5708
  if (!schema) return { sections: [] };
5615
- const raw = await this._ctx?.settings?.readAddonStore() ?? {};
5709
+ const projected = await this.resolveGlobalStore(nodeId, cap);
5616
5710
  return hydrateSchema(schema, overlay ? {
5617
- ...raw,
5711
+ ...projected,
5618
5712
  ...overlay
5619
- } : raw);
5713
+ } : projected);
5714
+ }
5715
+ /**
5716
+ * The raw addon store PROJECTED onto the target node's bare per-node keys:
5717
+ * every `perNode: true` field carries THAT node's scoped value on its bare
5718
+ * key (absent scoped key ⇒ key absent, so the schema `default` wins — no
5719
+ * bare fallback), all `@`-scoped and stray bare per-node keys are dropped.
5720
+ * A no-op passthrough when the schema declares no `perNode` field.
5721
+ *
5722
+ * This is the sanctioned way for a `getGlobalSettings` OVERRIDE that needs
5723
+ * the store for custom option logic (option narrowing, value snapping) to
5724
+ * read it per-node — never `ctx.settings.readAddonStore()` directly.
5725
+ * `nodeId` omitted ⇒ the local node (node-blind injection from ctx).
5726
+ */
5727
+ async resolveGlobalStore(nodeId, cap) {
5728
+ const raw = await this._ctx?.settings?.readAddonStore() ?? {};
5729
+ const keys = this.perNodeKeys(cap);
5730
+ const node = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
5731
+ return keys.size > 0 ? projectStore(raw, keys, node) : raw;
5620
5732
  }
5621
- async updateGlobalSettings(patch, _nodeId) {
5622
- await this._ctx?.settings?.writeAddonStore(patch);
5733
+ async updateGlobalSettings(patch, nodeId) {
5734
+ const keys = this.perNodeKeys();
5735
+ const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
5736
+ const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
5737
+ const barePatch = patch;
5738
+ const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
5739
+ await this._ctx?.settings?.writeAddonStore(scoped);
5740
+ if (target !== localNode) return;
5623
5741
  await this.resolveConfig();
5624
5742
  await this.onConfigChanged();
5625
5743
  this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
5626
5744
  this.maybeAutoRestart(patch, this.globalSettingsSchema());
5627
5745
  }
5628
5746
  /**
5747
+ * The set of field keys the global settings schema declares `perNode: true`
5748
+ * — derived once per `cap` argument and memoized (schemas are static
5749
+ * declarations). Empty set ⇒ every per-node code path is bypassed and the
5750
+ * settings API behaves exactly like the legacy node-agnostic one.
5751
+ */
5752
+ _perNodeKeysCache = /* @__PURE__ */ new Map();
5753
+ perNodeKeys(cap) {
5754
+ const cacheKey = cap ?? "";
5755
+ const cached = this._perNodeKeysCache.get(cacheKey);
5756
+ if (cached) return cached;
5757
+ const schema = this.globalSettingsSchema(cap);
5758
+ const keys = new Set(schema ? schema.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
5759
+ this._perNodeKeysCache.set(cacheKey, keys);
5760
+ return keys;
5761
+ }
5762
+ /**
5629
5763
  * If any field in `patch` is marked `requiresRestart` in `schema`,
5630
5764
  * schedule an addon restart for the next tick. Deferred via
5631
5765
  * `setImmediate` so the tRPC mutation that triggered the write has
@@ -5778,12 +5912,19 @@ var BaseAddon = class {
5778
5912
  * The merge is shallow: each key in `defaults` is checked against the store.
5779
5913
  * Only keys present in defaults are read — the store can contain extra keys
5780
5914
  * (e.g. from older versions) without polluting the typed config.
5915
+ *
5916
+ * Keys the global settings schema declares `perNode: true` resolve from
5917
+ * THIS node's scoped key (`<key>@<localNode>`) via `readNodeValue` — never
5918
+ * from the bare key — so a per-node field resolves to this node's own
5919
+ * selection at boot (absent scoped key ⇒ the constructor default wins).
5781
5920
  */
5782
5921
  async resolveConfig() {
5783
5922
  const stored = await this.readAddonStoreWithRetry();
5923
+ const perNode = this.perNodeKeys();
5924
+ const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
5784
5925
  const resolved = { ...this.defaults };
5785
5926
  for (const key of Object.keys(this.defaults)) {
5786
- const storedValue = stored[key];
5927
+ const storedValue = perNode.has(key) ? readNodeValue(stored, key, localNode) : stored[key];
5787
5928
  if (storedValue !== void 0 && storedValue !== null) {
5788
5929
  const defaultType = typeof this.defaults[key];
5789
5930
  if (typeof storedValue === defaultType) resolved[key] = storedValue;
@@ -5867,6 +6008,27 @@ var BaseAddon = class {
5867
6008
  }
5868
6009
  };
5869
6010
  /**
6011
+ * Collect the keys of every field marked `perNode: true`, recursing into
6012
+ * layout containers (`group` fields and `sub-tabs` tabs) the same way
6013
+ * `hydrateSchema` does. Valueless structural fields (separator/info/…)
6014
+ * don't declare `perNode` and are excluded by the `in` narrowing.
6015
+ */
6016
+ function collectPerNodeFieldKeys(fields) {
6017
+ const collected = [];
6018
+ for (const field of fields) {
6019
+ if (field.type === "group") {
6020
+ collected.push(...collectPerNodeFieldKeys(field.fields));
6021
+ continue;
6022
+ }
6023
+ if (field.type === "sub-tabs") {
6024
+ for (const tab of field.tabs) collected.push(...collectPerNodeFieldKeys(tab.fields));
6025
+ continue;
6026
+ }
6027
+ if ("perNode" in field && field.perNode === true) collected.push(field.key);
6028
+ }
6029
+ return collected;
6030
+ }
6031
+ /**
5870
6032
  * Normalize an `ICamstackAddon.initialize()` return value into the
5871
6033
  * `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
5872
6034
  * envelopes pass through; void stays void.
@@ -6274,6 +6436,13 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
6274
6436
  /** Single still-image entity (HA `image.*`). Read-only display of an
6275
6437
  * `entity_picture` signed URL the browser loads directly. `image` cap. */
6276
6438
  DeviceType["Image"] = "image";
6439
+ /** Smart pet feeder — cloud-connected food dispenser with a bowl food
6440
+ * level, battery, desiccant life, feeding state and manual-feed /
6441
+ * call-pet / maintenance actions. Installed with the `pet-feeder` cap;
6442
+ * dual-hopper models (D4S/D4SH) expose per-hopper portions. Sources:
6443
+ * native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
6444
+ * integrations sharing the same food/desiccant/hopper surface. */
6445
+ DeviceType["PetFeeder"] = "pet-feeder";
6277
6446
  return DeviceType;
6278
6447
  }({});
6279
6448
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -9928,7 +10097,8 @@ var MotionAnalysisResultSchema = object({
9928
10097
  });
9929
10098
  method(object({
9930
10099
  deviceId: number(),
9931
- frame: FrameInputSchema
10100
+ frame: FrameInputSchema.optional(),
10101
+ frameHandle: FrameHandleSchema.optional()
9932
10102
  }), MotionAnalysisResultSchema, { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), object({
9933
10103
  deviceId: number(),
9934
10104
  detected: boolean(),
@@ -10175,6 +10345,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
10175
10345
  engine: PipelineEngineChoiceSchema.optional(),
10176
10346
  steps: array(PipelineStepInputSchema).min(1),
10177
10347
  frame: FrameInputSchema.optional(),
10348
+ /**
10349
+ * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
10350
+ * the decoded pixels live in. One more member of the one-of
10351
+ * frame/frameHandle/image/imageBase64/referenceImage group.
10352
+ */
10353
+ frameHandle: FrameHandleSchema.optional(),
10178
10354
  imageBase64: string().optional(),
10179
10355
  /**
10180
10356
  * Binary JPEG bytes — preferred over `imageBase64` on internal
@@ -10879,6 +11055,113 @@ object({
10879
11055
  lastFetchedAt: number()
10880
11056
  });
10881
11057
  DeviceType.Sensor;
11058
+ /**
11059
+ * Feeder connectivity / power status — mirrors the HA petkit device-status
11060
+ * enum: `normal` (online, mains), `offline` (not reaching PetKit cloud),
11061
+ * `on_batteries` (running on battery backup). `null` until first reported.
11062
+ */
11063
+ var PetFeederDeviceStatusSchema = _enum([
11064
+ "normal",
11065
+ "offline",
11066
+ "on_batteries"
11067
+ ]);
11068
+ var gramsPortion = number().int().min(4).max(200);
11069
+ object({
11070
+ /** Food currently in the bowl (grams). Null when the device has not
11071
+ * reported a reading yet. On dual-hopper models this is the combined
11072
+ * bowl reading; per-hopper levels live in `food1`/`food2`. */
11073
+ foodLevel: number().nullable(),
11074
+ /** Hopper-1 food level (grams) on dual-hopper feeders; null on
11075
+ * single-hopper models. */
11076
+ food1: number().nullable(),
11077
+ /** Hopper-2 food level (grams) on dual-hopper feeders; null on
11078
+ * single-hopper models. */
11079
+ food2: number().nullable(),
11080
+ /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
11081
+ * (`device_class: problem`, on = low). True when the bowl is empty /
11082
+ * below the feeder's low threshold. */
11083
+ lowFood: boolean(),
11084
+ /** Battery charge 0..100 (%). Null on mains-powered models or when the
11085
+ * device has no battery reading. */
11086
+ batteryPower: number().min(0).max(100).nullable(),
11087
+ /** Days of desiccant life remaining. Null when the model has no
11088
+ * desiccant sensor. */
11089
+ desiccantLeftDays: number().nullable(),
11090
+ /** True while a feed is in progress. */
11091
+ feeding: boolean(),
11092
+ /** Decoded connectivity / power status (HA petkit device-status enum).
11093
+ * Null until the device has reported a status. */
11094
+ status: PetFeederDeviceStatusSchema.nullable(),
11095
+ /** Decoded human-readable fault message. Null (or the device's `no_error`
11096
+ * sentinel) means healthy; a non-null string is an active fault. Pairs
11097
+ * with `errorCode` for consumers that want the raw integer. */
11098
+ error: string().nullable(),
11099
+ /** Raw device error code (0 / null = no error). */
11100
+ errorCode: number().nullable(),
11101
+ /** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
11102
+ isDualHopper: boolean(),
11103
+ /** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
11104
+ childLock: boolean(),
11105
+ /** Front indicator-light setting. */
11106
+ indicatorLight: boolean(),
11107
+ /** Play a chime when dispensing. */
11108
+ feedSound: boolean(),
11109
+ /** Speaker / prompt volume level (device-scaled integer). */
11110
+ volume: number(),
11111
+ /** Ms epoch when the slice was last refreshed from the cloud. */
11112
+ lastFetchedAt: number()
11113
+ });
11114
+ DeviceType.PetFeeder, method(object({
11115
+ deviceId: number().int().nonnegative(),
11116
+ grams: gramsPortion.optional(),
11117
+ hopper1: gramsPortion.optional(),
11118
+ hopper2: gramsPortion.optional()
11119
+ }), _void(), {
11120
+ kind: "mutation",
11121
+ auth: "admin"
11122
+ }), method(object({ deviceId: number().int().nonnegative() }), _void(), {
11123
+ kind: "mutation",
11124
+ auth: "admin"
11125
+ }), method(object({ deviceId: number().int().nonnegative() }), _void(), {
11126
+ kind: "mutation",
11127
+ auth: "admin"
11128
+ }), method(object({ deviceId: number().int().nonnegative() }), _void(), {
11129
+ kind: "mutation",
11130
+ auth: "admin"
11131
+ }), method(object({ deviceId: number().int().nonnegative() }), _void(), {
11132
+ kind: "mutation",
11133
+ auth: "admin"
11134
+ }), method(object({
11135
+ deviceId: number().int().nonnegative(),
11136
+ soundId: number().int().nonnegative()
11137
+ }), _void(), {
11138
+ kind: "mutation",
11139
+ auth: "admin"
11140
+ }), method(object({
11141
+ deviceId: number().int().nonnegative(),
11142
+ on: boolean()
11143
+ }), _void(), {
11144
+ kind: "mutation",
11145
+ auth: "admin"
11146
+ }), method(object({
11147
+ deviceId: number().int().nonnegative(),
11148
+ on: boolean()
11149
+ }), _void(), {
11150
+ kind: "mutation",
11151
+ auth: "admin"
11152
+ }), method(object({
11153
+ deviceId: number().int().nonnegative(),
11154
+ on: boolean()
11155
+ }), _void(), {
11156
+ kind: "mutation",
11157
+ auth: "admin"
11158
+ }), method(object({
11159
+ deviceId: number().int().nonnegative(),
11160
+ level: number().int().nonnegative()
11161
+ }), _void(), {
11162
+ kind: "mutation",
11163
+ auth: "admin"
11164
+ });
10882
11165
  object({
10883
11166
  /** Instantaneous power draw in watts. */
10884
11167
  watts: number().optional(),
@@ -12706,10 +12989,12 @@ method(object({ codec: string() }), boolean()), method(_void(), object({
12706
12989
  url: string()
12707
12990
  }), _void()), method(object({
12708
12991
  sessionId: string(),
12709
- maxCount: number().default(1)
12992
+ maxCount: number().default(1),
12993
+ waitMs: number().optional()
12710
12994
  }), array(DecodedFrameSchema)), method(object({
12711
12995
  sessionId: string(),
12712
- maxCount: number().default(1)
12996
+ maxCount: number().default(1),
12997
+ waitMs: number().optional()
12713
12998
  }), array(FrameHandleSchema)), method(object({ handle: FrameHandleSchema }), DecodedFrameSchema.nullable()), method(object({ sessionId: string() }), ShmRingStatsSchema.nullable()), method(object({
12714
12999
  sessionId: string(),
12715
13000
  config: DecoderSessionConfigSchema.partial()
@@ -17199,7 +17484,10 @@ var HwAccelBackendInputSchema = _enum([
17199
17484
  "webgpu",
17200
17485
  "none"
17201
17486
  ]).nullable().optional();
17202
- var HwAccelResolutionSchema = object({ preferred: array(string()).readonly() });
17487
+ var HwAccelResolutionSchema = object({
17488
+ preferred: array(string()).readonly(),
17489
+ rationale: string()
17490
+ });
17203
17491
  var HardwareEncoderIdSchema = _enum([
17204
17492
  "h264_videotoolbox",
17205
17493
  "hevc_videotoolbox",
@@ -17304,10 +17592,7 @@ var ResolvedInferenceConfigSchema = object({
17304
17592
  format: ModelFormatSchema,
17305
17593
  reason: string()
17306
17594
  });
17307
- method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema), method(object({ requirements: array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema), method(object({
17308
- prefer: HwAccelBackendInputSchema,
17309
- nodeId: string().optional()
17310
- }), HwAccelResolutionSchema), method(_void(), HardwareEncodersSchema), method(_void(), HardwareEncodersSchema, {
17595
+ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema), method(object({ requirements: array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema), method(object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema), method(_void(), HardwareEncodersSchema), method(_void(), HardwareEncodersSchema, {
17311
17596
  kind: "mutation",
17312
17597
  auth: "admin"
17313
17598
  }), method(_void(), HardwareDecodeAccelsSchema), method(_void(), HardwareDecodeAccelsSchema, {
@@ -20272,6 +20557,66 @@ Object.freeze({
20272
20557
  addonId: null,
20273
20558
  access: "create"
20274
20559
  },
20560
+ "petFeeder.callPet": {
20561
+ capName: "pet-feeder",
20562
+ capScope: "device",
20563
+ addonId: null,
20564
+ access: "create"
20565
+ },
20566
+ "petFeeder.cancelFeed": {
20567
+ capName: "pet-feeder",
20568
+ capScope: "device",
20569
+ addonId: null,
20570
+ access: "create"
20571
+ },
20572
+ "petFeeder.feed": {
20573
+ capName: "pet-feeder",
20574
+ capScope: "device",
20575
+ addonId: null,
20576
+ access: "create"
20577
+ },
20578
+ "petFeeder.markFoodReplenished": {
20579
+ capName: "pet-feeder",
20580
+ capScope: "device",
20581
+ addonId: null,
20582
+ access: "create"
20583
+ },
20584
+ "petFeeder.playSound": {
20585
+ capName: "pet-feeder",
20586
+ capScope: "device",
20587
+ addonId: null,
20588
+ access: "create"
20589
+ },
20590
+ "petFeeder.resetDesiccant": {
20591
+ capName: "pet-feeder",
20592
+ capScope: "device",
20593
+ addonId: null,
20594
+ access: "delete"
20595
+ },
20596
+ "petFeeder.setChildLock": {
20597
+ capName: "pet-feeder",
20598
+ capScope: "device",
20599
+ addonId: null,
20600
+ access: "create"
20601
+ },
20602
+ "petFeeder.setFeedSound": {
20603
+ capName: "pet-feeder",
20604
+ capScope: "device",
20605
+ addonId: null,
20606
+ access: "create"
20607
+ },
20608
+ "petFeeder.setIndicatorLight": {
20609
+ capName: "pet-feeder",
20610
+ capScope: "device",
20611
+ addonId: null,
20612
+ access: "create"
20613
+ },
20614
+ "petFeeder.setVolume": {
20615
+ capName: "pet-feeder",
20616
+ capScope: "device",
20617
+ addonId: null,
20618
+ access: "create"
20619
+ },
20275
20620
  "pipelineAnalytics.clearTracks": {
20276
20621
  capName: "pipeline-analytics",
20277
20622
  capScope: "device",
@@ -22246,7 +22591,7 @@ var AdminUIAddon = class extends BaseAddon {
22246
22591
  capability: adminUiCapability,
22247
22592
  provider: {
22248
22593
  getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
22249
- getVersion: async () => ({ version: "1.1.22" })
22594
+ getVersion: async () => ({ version: "1.1.23" })
22250
22595
  }
22251
22596
  }];
22252
22597
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-admin-ui",
3
- "version": "1.1.22",
3
+ "version": "1.1.23",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "CamStack Admin UI — Vite frontend build and server-side addon",
@@ -1 +0,0 @@
1
- import{Ct as e,Dt as t,Et as n,Fr as r,Ot as i,St as a,Zi as o,_t as s,bt as c,ct as l,dt as ee,ea as u,ft as d,g as f,gt as p,kr as m,kt as h,lt as g,m as _,mt as v,pt as y,ra as te,st as ne,ut as re,v as ie,vt as b,wt as ae,yt as oe}from"./src-CtvTbpOC.js";import{I as x,K as S,Q as C,R as se,S as w,W as T,b as ce,o as le,q as E,x as D,z as O}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DTufy7Ue.js";import{L as k}from"./src-C9eW94dA.js";import{p as ue}from"./player-overlays-DlPIT9xj.js";import{t as A}from"./download-35ioIRtw.js";import{t as de}from"./plus-D9QBDEjM.js";import{t as j}from"./refresh-cw-DfSn-i0F.js";import{t as M}from"./search-DgJL1Gey.js";import{t as N}from"./upload-BJzZajFJ.js";import{A as fe,F as P,G as F,P as I,U as pe,V as L,f as me,h as R,i as he,o as z,p as B,s as ge,x as V}from"./index-BdGL2J0e.js";import{t as H}from"./AdminPage-BwmGKnRf.js";import{t as _e}from"./NodeAddonsSettingsPanel-CWrLPV6J.js";var ve=L(`chevron-up`,[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]),U=L(`clock`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`polyline`,{points:`12 6 12 12 16 14`,key:`68esgv`}]]),W=L(`octagon-alert`,[[`path`,{d:`M12 16h.01`,key:`1drbdi`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M15.312 2a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586l-4.688-4.688A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2z`,key:`1fd625`}]]),ye=L(`package`,[[`path`,{d:`M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z`,key:`1a0edw`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}],[`polyline`,{points:`3.29 7 12 12 20.71 7`,key:`ousv84`}],[`path`,{d:`m7.5 4.27 9 5.15`,key:`1c824w`}]]),G=L(`skip-forward`,[[`polygon`,{points:`5 4 15 12 5 20 5 4`,key:`16p6eg`}],[`line`,{x1:`19`,x2:`19`,y1:`5`,y2:`19`,key:`futhcm`}]]);C();var K=new Set([`completed`,`failed`,`partially-failed`,`cancelled`]),q=new Set([`done`,`failed`,`skipped`]);function J(e){return!q.has(e.phase)&&!K.has(e.jobState)}function Y(e,t){let n=new Map,r=[...e].sort((e,t)=>e.createdAtMs-t.createdAtMs);for(let e of r){let r=K.has(e.state);for(let i of e.tasks)r&&i.finishedAtMs!==null&&t-i.finishedAtMs>6e4||n.set(i.packageName,{jobId:e.jobId,packageName:i.packageName,phase:i.phase,fromVersion:i.fromVersion,toVersion:i.toVersion,error:i.error,jobState:e.state,finishedAtMs:i.finishedAtMs})}return n}function be(){let{data:e}=s({activeOnly:!0},{refetchOnWindowFocus:!1,refetchInterval:e=>(e.state.data?.length??0)>0?3e3:!1}),{map:t}=te(k.AddonsJobProgress,e=>e.job.jobId);return T(()=>{let n=new Map;for(let t of e??[])n.set(t.jobId,t);for(let[e,r]of t)n.set(e,r.job);return Y([...n.values()],Date.now())},[e,t])}var X={queued:{icon:U,cls:`bg-muted text-foreground-subtle`,label:`queued`},fetching:{icon:V,cls:`bg-primary/10 text-primary`,label:`fetching`,spin:!0},staged:{icon:V,cls:`bg-primary/10 text-primary`,label:`staged`,spin:!0},validating:{icon:V,cls:`bg-primary/10 text-primary`,label:`validating`,spin:!0},applying:{icon:V,cls:`bg-primary/10 text-primary`,label:`applying`,spin:!0},restarting:{icon:V,cls:`bg-amber-500/10 text-amber-600 dark:text-amber-400`,label:`restarting`,spin:!0},applied:{icon:V,cls:`bg-primary/10 text-primary`,label:`finalizing`,spin:!0},done:{icon:P,cls:`bg-success/10 text-success`,label:`updated`},failed:{icon:W,cls:`bg-danger/10 text-danger`,label:`failed`},skipped:{icon:G,cls:`bg-muted text-foreground-subtle`,label:`skipped`}};function xe({progress:e}){let t=X[e.phase],n=t.icon,r=e.error??`${e.packageName}: ${e.fromVersion??`?`} → ${e.toVersion}`;return w(`span`,{"data-testid":`addon-job-progress`,className:`inline-flex items-center gap-1 px-1.5 py-0.5 text-[10px] rounded shrink-0 ${t.cls}`,title:r,children:[D(n,{className:`w-2.5 h-2.5 ${t.spin?`animate-spin`:``}`}),t.label,w(`span`,{className:`font-mono`,children:[`→ `,e.toVersion]})]})}function Se({packageName:e,currentVersion:t,installSource:n,onInstall:r,onClose:i,onSelect:a,selectedVersion:o}){let s=n===`upload`||n===`local`,{data:c,isLoading:l}=d({name:e});return w(`div`,{className:`fixed z-50 w-72 rounded-lg border border-border bg-surface shadow-xl overflow-hidden`,style:{top:`auto`,bottom:`auto`},ref:e=>{if(!e)return;let t=e.parentElement?.getBoundingClientRect();if(!t)return;let n=window.innerHeight-t.bottom,r=t.top;n<280&&r>n?(e.style.bottom=`${window.innerHeight-t.top+4}px`,e.style.top=`auto`):(e.style.top=`${t.bottom+4}px`,e.style.bottom=`auto`),e.style.right=`${window.innerWidth-t.right}px`},onClick:e=>e.stopPropagation(),children:[w(`div`,{className:`flex items-center justify-between px-3 py-2 border-b border-border`,children:[D(`span`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle font-medium`,children:`Versions`}),D(`button`,{type:`button`,onClick:i,className:`text-foreground-subtle hover:text-foreground`,children:D(he,{className:`h-3 w-3`})})]}),s&&w(`div`,{className:`flex items-start gap-2 px-3 py-2 border-b border-border bg-cyan-500/10 text-[10px]`,children:[D(N,{className:`h-3 w-3 mt-0.5 text-cyan-500 shrink-0`}),w(`div`,{className:`text-cyan-700 dark:text-cyan-200`,children:[w(`span`,{className:`font-semibold`,children:[n===`upload`?`Uploaded copy`:`Local copy`,` in place.`]}),` `,`Re-install `,D(`span`,{className:`font-mono`,children:t}),` from npm to track upstream releases again.`]})]}),l&&w(`div`,{className:`flex items-center justify-center gap-2 py-6 text-[10px] text-foreground-subtle`,children:[D(V,{className:`h-3.5 w-3.5 animate-spin`}),`Loading versions...`]}),c&&D(`div`,{className:`max-h-64 overflow-auto divide-y divide-border`,children:c.map(e=>{let n=e.version===t,i=a!==void 0&&e.version===o,c=/-(alpha|beta|rc|dev|canary|next)/i.test(e.version),l=w(ce,{children:[D(`span`,{className:`font-mono flex-1 text-left ${n?`text-primary font-semibold`:c?`text-amber-400`:`text-foreground`}`,children:e.version}),n&&D(`span`,{className:`text-[9px] rounded-full px-1.5 py-0.5 font-medium bg-primary/10 text-primary`,children:`current`}),e.distTags.map(e=>D(`span`,{className:`text-[9px] rounded-full px-1.5 py-0.5 font-medium ${e===`latest`?`bg-emerald-500/15 text-emerald-400`:e===`beta`?`bg-amber-500/15 text-amber-400`:`bg-gray-500/15 text-gray-400`}`,children:e},e)),e.deprecated&&D(`span`,{className:`text-[9px] text-red-400 line-through`,children:`deprecated`}),D(`span`,{className:`text-[9px] text-foreground-subtle shrink-0`,children:new Date(e.publishedAt).toLocaleDateString()})]});return a?w(`button`,{type:`button`,"aria-label":`Select ${e.version}`,onClick:()=>a(e.version),className:`w-full flex items-center gap-2 px-3 py-2 text-[11px] ${i?`bg-primary/5`:`hover:bg-surface-hover`} transition-colors`,children:[l,i?D(P,{className:`h-3 w-3 text-primary shrink-0`}):D(`span`,{className:`w-3 shrink-0`})]},e.version):w(`div`,{className:`flex items-center gap-2 px-3 py-2 text-[11px] ${n?`bg-primary/5`:`hover:bg-surface-hover`} transition-colors`,children:[l,n&&!s?D(P,{className:`h-3 w-3 text-primary shrink-0`}):D(`button`,{type:`button`,onClick:()=>r(e.version),className:`shrink-0 rounded p-0.5 hover:bg-primary/20 text-foreground-subtle hover:text-primary transition-colors`,title:n?`Re-install ${e.version} from npm`:`Install ${e.version}`,children:D(A,{className:`h-3 w-3`})})]},e.version)})}),c&&c.length===0&&D(`div`,{className:`py-4 text-center text-[10px] text-foreground-subtle`,children:`No versions found`})]})}var Z={queued:{icon:U,bg:`bg-muted text-foreground-subtle`,label:`queued`},updating:{icon:V,bg:`bg-primary/10 text-primary`,label:`updating`,spin:!0},"done-pending-restart":{icon:j,bg:`bg-amber-500/10 text-amber-700`,label:`pending restart`},done:{icon:P,bg:`bg-success/10 text-success`,label:`done`},failed:{icon:W,bg:`bg-danger/10 text-danger`,label:`failed`}};function Ce({item:e}){let t=Z[e.status],n=t.icon;return w(`span`,{className:`inline-flex items-center gap-1 px-1.5 py-0.5 text-[10px] rounded ${t.bg}`,title:e.error,children:[D(n,{className:`w-2.5 h-2.5 ${t.spin?`animate-spin`:``}`}),t.label]})}C();var we=`device-export`;function Te(e){return e.capabilities.some(e=>(typeof e==`string`?e:e.name)===we)}function Ee({isBundle:e,installSource:t,addonId:n,iconPath:r,color:i}){let[a,o]=E(!1),s=i?{backgroundColor:`${i.startsWith(`#`)?i:`#${i}`}26`}:void 0,c=e?`bg-indigo-500/20 text-indigo-700 dark:text-indigo-300`:`bg-purple-500/20 text-purple-700 dark:text-purple-300`,l=n&&r?`/api/addon-assets/${n}/${r}`:null,ee=!!l&&!a;return w(`div`,{className:`relative flex-shrink-0`,children:[D(`div`,{className:`h-9 w-9 rounded-lg flex items-center justify-center ${s?``:c}`,style:s,children:ee?D(`img`,{src:l,alt:n,className:`h-5 w-5`,onError:()=>o(!0)}):D(e?ye:R,{className:`h-4 w-4`})}),t===`local`&&D(`span`,{className:`absolute -top-1.5 -left-1.5 text-[7px] font-bold leading-none bg-orange-500 text-white px-1 py-0.5 rounded -rotate-12 shadow-sm`,children:`DEV`}),t===`npm`&&D(`span`,{className:`absolute -top-1 -left-1 w-4 h-4 rounded bg-[#cb3837] flex items-center justify-center -rotate-12 shadow-sm`,children:D(`svg`,{viewBox:`0 0 780 250`,className:`w-[11px] h-[11px]`,children:D(`path`,{fill:`#fff`,d:`M240 250h100V50h100v200h340V0H0v250z`})})}),t===`upload`&&D(`span`,{className:`absolute -top-1.5 -left-1.5 flex items-center justify-center h-4 w-4 rounded bg-cyan-500 -rotate-12 shadow-sm`,children:D(N,{className:`h-2.5 w-2.5 text-white`})})]})}function De({version:e,availableUpdate:t,selectedVersion:n,isUpdating:r,installSource:i,disabled:a,onUpdate:o,onOpenPicker:s,children:c}){if(r)return w(`span`,{className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border border-blue-500/30 bg-blue-500/10 text-blue-400 shrink-0`,children:[D(V,{className:`h-3 w-3 animate-spin`}),`Updating…`]});if(t){let r=n??t;return w(`div`,{className:`relative inline-flex items-center gap-1 shrink-0`,children:[o&&w(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),o()},disabled:a,className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-semibold border border-blue-500/30 bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 disabled:opacity-50 disabled:cursor-not-allowed transition-colors`,title:`Update to ${r}`,children:[D(A,{className:`h-3 w-3`}),`Update`]}),w(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),s()},disabled:a,"aria-label":`Select version to install`,title:`Installed ${e} — update available. Selected ${r}; tap to choose another version.`,className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-bold font-mono border border-amber-500/40 bg-amber-500/15 text-amber-500 hover:bg-amber-500/25 animate-pulse disabled:opacity-50 disabled:cursor-not-allowed transition-colors`,children:[D(`span`,{className:`font-normal opacity-70`,children:e}),D(`span`,{className:`font-normal opacity-70`,children:`→`}),r,D(I,{className:`h-3 w-3`})]}),c]})}return w(`div`,{className:`relative inline-flex items-center gap-1 shrink-0`,children:[w(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),s()},disabled:a,className:[`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-bold font-mono border transition-colors disabled:opacity-50 disabled:cursor-not-allowed`,i===`local`?`border-orange-500/30 bg-orange-500/10 text-orange-400 hover:bg-orange-500/20`:`border-emerald-500/30 bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20`].join(` `),title:`Version ${e} — tap to view all versions`,children:[e,D(I,{className:`h-3 w-3 opacity-50`})]}),c]})}function Oe({addonId:e}){let t=le(),{data:n=`inherit`}=g({addonId:e}),r=ae({onSuccess:()=>{t.invalidateQueries({queryKey:[[`addons`,`getAddonAutoUpdate`]]})}});return D(`div`,{className:`flex rounded-lg border border-border overflow-hidden text-[10px] font-medium`,children:[`inherit`,`off`,`latest`,`beta`].map(t=>D(`button`,{type:`button`,onClick:n=>{n.stopPropagation(),r.mutate({addonId:e,channel:t})},disabled:r.isPending,className:`px-2.5 py-1 transition-colors capitalize ${n===t?t===`inherit`?`bg-gray-700/50 text-gray-700 dark:text-gray-300`:t===`off`?`bg-gray-700 text-gray-700 dark:text-gray-300`:t===`latest`?`bg-blue-500/20 text-blue-400`:`bg-amber-500/20 text-amber-400`:`bg-transparent text-foreground-subtle hover:bg-surface-hover`} ${t===`inherit`?``:`border-l border-border`}`,children:t},t))})}function ke({addon:e,agents:n=[],clusterStatus:r,hideVersion:i,availableUpdate:s,onUpdate:l,isUpdating:ee,jobProgress:u,bundle:d,bulkItem:p,disabledByBulk:m,isSystem:h}){let[g,v]=E(!1),[te,ne]=E(!1),[re,b]=E(!1),[ae,oe]=E(null);O(()=>{oe(null)},[s]);let x=ae??s,[S,C]=E(!1),[se,T]=E(!1),[k,A]=E(`logs`),M=le(),N=ue(),fe=pe(),P=o(),{manifest:F}=e,L=e.installedOn??[],R=!d&&!F.protected&&F.removable!==!1,z=!!d,V=d?.displayName??F.name,H=d?.packageName??F.packageName,U=d?.version??F.packageVersion??F.version,G=d?.installSource??e.installSource,K=d?d.bundle?.description??d.addons[0]?.manifest.description:F.description,q=t({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]}),M.invalidateQueries({queryKey:[[`addonPages`]]}),M.invalidateQueries({queryKey:[[`capabilities`]]})}}),J=c({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]})}}),Y=y({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]}),M.invalidateQueries({queryKey:[[`addonPages`]]}),M.invalidateQueries({queryKey:[[`capabilities`]]}),M.invalidateQueries({queryKey:[[`integrations`]]})}}),be=a({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]}),M.invalidateQueries({queryKey:[[`alerts`]]})}}),X=e.health,Z=X?.phase===`failed`&&!X.inGracePeriod,we=Z&&X?.nextRetryAt&&!X.retrying;return w(`div`,{className:`rounded-lg border bg-surface overflow-hidden ${Z?`border-red-500/50`:`border-border`}`,children:[Z&&X&&w(`div`,{className:`bg-danger/10 border-b border-danger/30 px-4 py-2.5 flex items-start gap-2.5`,children:[D(W,{className:`h-4 w-4 text-danger shrink-0 mt-0.5`}),w(`div`,{className:`flex-1 min-w-0`,children:[w(`div`,{className:`text-xs font-semibold text-danger`,children:[`Failed to load (attempt #`,X.retryCount,`)`]}),D(`div`,{className:`text-[11px] text-foreground mt-0.5 break-words font-mono`,children:X.lastError?.message??`Unknown error`}),we&&X.nextRetryAt&&w(`div`,{className:`text-[10px] text-foreground-subtle mt-1`,children:[`Auto-retry at `,new Date(X.nextRetryAt).toLocaleTimeString()]})]}),w(`button`,{type:`button`,disabled:be.isPending||X.retrying,onClick:e=>{e.stopPropagation(),be.mutate({packageName:H})},className:`inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-[11px] font-semibold border border-red-400/40 bg-red-400/10 text-red-700 dark:text-red-300 hover:bg-red-400/20 disabled:opacity-50 disabled:cursor-not-allowed transition-colors shrink-0`,title:`Reset retry counter and force immediate reload`,children:[D(j,{className:`h-3 w-3 ${be.isPending||X.retrying?`animate-spin`:``}`}),`Retry now`]})]}),w(`div`,{className:`flex items-start gap-3 px-4 py-3`,children:[D(Ee,{isBundle:z||!!F.components&&F.components.length>1,installSource:G,addonId:z?d.addons[0]?.manifest.id:F.id,iconPath:z?d.bundle?.icon??d.addons[0]?.manifest.icon:F.icon,color:z?d.addons[0]?.manifest.color:F.color}),w(`div`,{className:`flex-1 min-w-0`,children:[w(`div`,{className:`flex items-start justify-between gap-2`,children:[D(`span`,{className:`text-sm font-semibold text-foreground truncate min-w-0`,children:V}),!i&&D(De,{version:U,availableUpdate:s,...x===void 0?{}:{selectedVersion:x},isUpdating:ee,installSource:G,disabled:m,...l&&x!==void 0?{onUpdate:()=>l(x)}:{},onOpenPicker:()=>b(e=>!e),children:re&&D(Se,{packageName:H,currentVersion:U,...G?{installSource:G}:{},...s!==void 0&&x!==void 0?{selectedVersion:x,onSelect:e=>{oe(e),b(!1)}}:{},onInstall:e=>{b(!1),Y.mutate({packageName:H,version:e})},onClose:()=>b(!1)})})]}),w(`div`,{className:`flex flex-wrap items-center gap-x-1.5 gap-y-1 mt-0.5`,children:[H!==V&&D(`span`,{className:`text-[10px] text-foreground-subtle font-mono truncate max-w-full`,children:H}),z&&w(`span`,{className:`text-[10px] rounded-full bg-primary/10 text-primary px-2 py-0.5 font-medium shrink-0 inline-flex items-center gap-1`,children:[D(ye,{className:`h-3 w-3`}),d.addons.length]}),h&&w(`span`,{className:`text-[10px] rounded-full bg-amber-500/15 text-amber-700 dark:text-amber-300 px-2 py-0.5 font-medium shrink-0 inline-flex items-center gap-1`,title:`System package — updating restarts the hub`,children:[D(j,{className:`h-3 w-3`}),`System`]}),p!=null&&D(Ce,{item:p}),u!=null&&D(xe,{progress:u})]}),K&&D(`p`,{className:`text-[10px] text-foreground-subtle mt-1 line-clamp-1`,children:K}),!z&&e.process&&w(`div`,{className:`flex flex-wrap items-center gap-1.5 mt-1`,children:[D(`span`,{className:`text-[9px] rounded-full px-1.5 py-0.5 font-medium ${e.process.mode===`forked`?`bg-blue-500/15 text-blue-400`:`bg-gray-500/15 text-gray-400`}`,children:e.process.mode===`forked`?`forked`:`in-process`}),e.process.pid!=null&&w(`span`,{className:`text-[9px] font-mono text-foreground-subtle`,children:[`PID `,e.process.pid]}),D(`span`,{className:`text-[9px] rounded-full px-1.5 py-0.5 font-medium ${e.process.state===`running`?`bg-emerald-500/15 text-emerald-400`:e.process.state===`crashed`||e.process.state===`failed`?`bg-red-500/15 text-red-400`:`bg-gray-500/15 text-gray-400`}`,children:e.process.state})]})]})]}),!z&&w(`div`,{className:`flex items-center gap-1 px-4 py-1.5 border-t border-border/50 bg-surface-hover/20`,children:[w(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),J.mutate({addonId:F.id})},disabled:J.isPending||m,className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border border-border text-foreground-subtle hover:text-blue-400 hover:border-blue-500/30 hover:bg-blue-500/10 disabled:opacity-50 transition-colors`,children:[D(j,{className:`h-3 w-3 ${J.isPending?`animate-spin`:``}`}),`Restart`]}),w(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),C(e=>!e)},className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border transition-colors ${S?`border-primary/30 bg-primary/10 text-primary`:`border-border text-foreground-subtle hover:text-foreground hover:border-primary/30 hover:bg-primary/10`}`,children:[D(B,{className:`h-3 w-3`}),`Logs`]}),!z&&w(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),T(!0)},className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border transition-colors ${se?`border-primary/30 bg-primary/10 text-primary`:`border-border text-foreground-subtle hover:text-foreground hover:border-primary/30 hover:bg-primary/10`}`,children:[D(me,{className:`h-3 w-3`}),`Settings`]}),D(`div`,{className:`flex-1`}),R&&w(`button`,{type:`button`,onClick:async e=>{e.stopPropagation(),await P({title:`Uninstall ${F.name}?`,message:`This will remove the addon package and all its data. This action cannot be undone.`,confirmLabel:`Uninstall`,variant:`danger`})&&q.mutate({packageName:F.packageName})},disabled:q.isPending||m,className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border border-border text-foreground-subtle hover:text-red-400 hover:border-red-500/30 hover:bg-red-500/10 disabled:opacity-50 transition-colors`,children:[D(ge,{className:`h-3 w-3`}),q.isPending?`Removing...`:`Uninstall`]})]}),r&&r.nodes.length>1&&!z&&w(`div`,{className:`border-t border-border px-4 py-2 bg-muted/10`,children:[D(`div`,{className:`text-[10px] font-medium text-foreground-subtle mb-1.5`,children:`Cluster Deployment`}),D(`div`,{className:`space-y-1`,children:r.nodes.map(e=>w(`div`,{className:`flex items-center gap-2 text-[10px]`,children:[D(`div`,{className:`h-1.5 w-1.5 rounded-full shrink-0 ${e.status===`offline`?`bg-muted-foreground/30`:e.synced?`bg-success`:`bg-amber-500`}`}),D(`span`,{className:`font-medium text-foreground min-w-[60px]`,children:e.name}),D(`span`,{className:`font-mono text-foreground-subtle`,children:e.version}),!e.synced&&e.status!==`offline`&&D(`span`,{className:`text-[9px] px-1 py-0.5 rounded bg-amber-500/10 text-amber-400 border border-amber-500/20`,children:`outdated`}),e.status===`offline`&&D(`span`,{className:`text-[9px] px-1 py-0.5 rounded bg-muted text-muted-foreground`,children:`offline`}),e.synced&&e.status!==`offline`&&D(`span`,{className:`text-[9px] text-success`,children:`synced`})]},e.nodeId))})]}),S&&!z&&w(`div`,{className:`border-t border-border`,children:[w(`div`,{className:`flex items-center px-4 py-1 bg-surface-hover/20 border-b border-border/50`,children:[w(`div`,{className:`flex rounded border border-border overflow-hidden text-[9px]`,children:[D(`button`,{type:`button`,onClick:()=>A(`logs`),className:`px-2 py-0.5 transition-colors ${k===`logs`?`bg-primary/10 text-primary`:`text-foreground-subtle hover:bg-surface-hover`}`,children:`Logs`}),D(`button`,{type:`button`,onClick:()=>A(`events`),className:`px-2 py-0.5 border-l border-border transition-colors ${k===`events`?`bg-primary/10 text-primary`:`text-foreground-subtle hover:bg-surface-hover`}`,children:`Events`})]}),D(`div`,{className:`flex-1`}),D(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),C(!1)},className:`text-foreground-subtle hover:text-foreground`,children:D(he,{className:`h-3.5 w-3.5`})})]}),k===`logs`&&D(f,{addonId:F.id,maxHeight:`max-h-64`,showFilters:!1}),k===`events`&&D(_,{addonId:F.id,category:`addon.*`,maxHeight:`max-h-64`})]}),z&&w(ce,{children:[D(`button`,{type:`button`,onClick:()=>v(e=>!e),className:`w-full flex items-center justify-center border-t border-border py-1 hover:bg-surface-hover/50 transition-colors`,children:D(g?ve:I,{className:`h-3.5 w-3.5 text-foreground-subtle`})}),g&&D(`div`,{className:`border-t border-border px-3 py-2 space-y-2`,children:d.addons.map(e=>D(ke,{addon:e,agents:n,hideVersion:!0},e.manifest.id))})]}),se&&!z&&D(`div`,{className:`fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4`,onClick:()=>T(!1),children:w(`div`,{className:`w-full max-w-lg max-h-[80vh] rounded-xl border border-border bg-surface shadow-2xl flex flex-col overflow-hidden`,onClick:e=>e.stopPropagation(),children:[w(`div`,{className:`flex items-center justify-between px-5 py-3 border-b border-border flex-shrink-0`,children:[w(`div`,{className:`flex items-center gap-2`,children:[D(me,{className:`h-4 w-4 text-primary`}),w(`h2`,{className:`text-sm font-semibold text-foreground`,children:[V,` Settings`]})]}),D(`button`,{onClick:()=>T(!1),className:`text-foreground-subtle hover:text-foreground transition-colors p-1 rounded`,children:D(he,{className:`h-4 w-4`})})]}),w(`div`,{className:`flex-1 overflow-y-auto px-5 py-4 space-y-4`,children:[D(_e,{nodeId:`hub`,addonIds:[F.id],level:`global`}),Te(F)&&w(`div`,{children:[D(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:`Device export`}),D(ie,{addonId:F.id,trpc:N.trpcClient,onOpenDevice:e=>fe(`/devices/${e}`)})]}),G===`npm`&&w(`div`,{children:[D(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:`Auto-Update`}),D(Oe,{addonId:F.id})]}),F.components&&F.components.length>0&&w(`div`,{children:[w(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:[`Components (`,F.components.length,`)`]}),D(`div`,{className:`flex flex-wrap gap-1`,children:F.components.map(e=>D(`span`,{className:`text-[10px] rounded bg-background border border-border px-1.5 py-0.5 text-foreground-subtle font-mono`,children:e},e))})]}),n.length>=2&&w(`div`,{children:[D(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:`Installed on`}),w(`div`,{className:`flex items-center gap-1.5 flex-wrap`,children:[L.length===0&&D(`span`,{className:`text-[10px] text-foreground-subtle`,children:`Hub (local)`}),L.map(e=>D(`span`,{className:`rounded-full bg-surface-hover border border-border px-2 py-0.5 text-[10px] text-foreground-subtle`,children:e},e)),w(`div`,{className:`relative`,children:[w(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),ne(e=>!e)},className:`inline-flex items-center gap-1 rounded-full border border-dashed border-border px-2 py-0.5 text-[10px] text-foreground-subtle hover:text-foreground hover:border-primary transition-colors`,title:`Install on more agents`,children:[D(de,{className:`h-3 w-3`}),`Add agent`]}),te&&w(`div`,{className:`absolute left-0 top-full mt-1 z-10 min-w-[160px] rounded-md border border-border bg-surface shadow-lg overflow-hidden`,children:[n.filter(e=>!e.isHub&&!L.includes(e.name)).map(e=>D(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),ne(!1)},className:`w-full text-left px-3 py-1.5 text-[10px] text-foreground hover:bg-surface-hover transition-colors`,children:e.name},e.id)),n.filter(e=>!e.isHub&&!L.includes(e.name)).length===0&&D(`div`,{className:`px-3 py-1.5 text-[10px] text-foreground-subtle`,children:`No available agents`})]})]})]})]})]}),D(`div`,{className:`flex items-center justify-end px-5 py-3 border-t border-border flex-shrink-0`,children:D(`button`,{type:`button`,onClick:()=>T(!1),className:`rounded-lg border border-border px-3 py-1.5 text-xs font-medium text-foreground-subtle hover:text-foreground hover:bg-surface-hover transition-colors`,children:`Close`})})]})})]})}C();var Ae=`.tgz,.tar.gz,.zip`;function je({onUploadSuccess:e}){let[t,n]=E(!1),r=S(null),i=x(async t=>{n(!0);let r=new FormData;r.append(`file`,t);let i=localStorage.getItem(`camstack_admin_token`)??``;try{(await(await fetch(`/api/addons/upload`,{method:`POST`,headers:{Authorization:`Bearer ${i}`},body:r})).json()).success&&e()}finally{n(!1)}},[e]);return w(ce,{children:[D(`input`,{ref:r,type:`file`,accept:Ae,onChange:x(e=>{let t=e.target.files?.[0];t&&i(t),e.target.value=``},[i]),className:`hidden`}),w(`button`,{type:`button`,onClick:()=>r.current?.click(),disabled:t,className:`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-surface hover:bg-surface-hover border border-border disabled:opacity-50 transition-colors`,children:[t?D(V,{className:`w-3.5 h-3.5 animate-spin`}):D(N,{className:`w-3.5 h-3.5`}),t?`Uploading...`:`Upload`]})]})}function Me({pending:e,onCancel:t,onConfirm:n,isPending:r,error:i}){return D(`div`,{className:`fixed inset-0 z-50 flex items-center justify-center bg-black/60`,children:w(`div`,{className:`bg-surface border border-border rounded-md shadow-xl p-5 max-w-md w-full mx-4 space-y-3`,children:[w(`div`,{className:`flex items-center gap-2`,children:[D(z,{className:`w-4 h-4 text-amber-700`}),D(`h3`,{className:`text-sm font-medium text-foreground`,children:`Update system package?`})]}),w(`div`,{className:`space-y-2 text-[11px] text-foreground-subtle`,children:[w(`div`,{children:[D(`span`,{className:`font-mono text-foreground`,children:e.packageName}),D(`span`,{className:`mx-1`,children:`·`}),w(`span`,{className:`font-mono`,children:[`v`,e.fromVersion]}),D(`span`,{className:`mx-1`,children:`→`}),w(`span`,{className:`font-mono text-amber-700`,children:[`v`,e.toVersion]})]}),D(`div`,{className:`rounded border border-amber-500/30 bg-amber-500/10 p-2 text-amber-800`,children:`The hub will restart immediately after the install. Any in-flight requests will be dropped. The reconnect typically takes ~10 seconds.`}),i!==null&&D(`div`,{className:`rounded border border-danger/40 bg-danger/10 p-2 text-danger`,children:i})]}),w(`div`,{className:`flex justify-end gap-2 pt-1`,children:[D(`button`,{type:`button`,onClick:t,disabled:r,className:`px-3 py-1.5 text-[11px] rounded bg-surface hover:bg-surface-hover border border-border disabled:opacity-50`,children:`Cancel`}),w(`button`,{type:`button`,onClick:n,disabled:r,className:`inline-flex items-center gap-1.5 px-3 py-1.5 text-[11px] rounded bg-amber-500/20 text-amber-800 hover:bg-amber-500/30 border border-amber-500/40 font-medium disabled:opacity-50`,children:[r?D(V,{className:`w-3 h-3 animate-spin`}):D(A,{className:`w-3 h-3`}),`Update & restart`]})]})]})})}C();function Ne(e,t){let n=e??`hub-only`;return n===`any-node`?!0:n===`hub-only`?t:!t}function Q(e){let t=e;if(!t?.manifest)return null;let n={...t.manifest,name:t.manifest.name??t.manifest.id,version:t.manifest.version??`0.0.0`,capabilities:t.manifest.capabilities??[]},r=t.process,i=r?{pid:r.pid,mode:r.mode===`forked`?`forked`:`in-process`,state:r.state??`unknown`}:void 0,a=t.health,o=t.declaration?.execution,s=t.manifest.execution,c=o?.placement??s?.placement;return{manifest:n,enabled:!0,source:t.source,installSource:t.installSource,installedOn:[],...i?{process:i}:{},hasBackup:t.hasBackup,...a?{health:a}:{},...c?{placement:c}:{}}}function Pe(e){let t=new Map,n=[];for(let r of e){let e=r.manifest.packageName;if(!e){n.push(r);continue}let i=t.get(e);if(i)i.addons.push(r);else{let n=r.manifest.bundle,i={packageName:e,displayName:n?.displayName??r.manifest.packageDisplayName??r.manifest.name,version:r.manifest.packageVersion,addons:[r],...r.installSource===void 0?{}:{installSource:r.installSource},...n===void 0?{}:{bundle:n}};t.set(e,i)}}let r=[];for(let e of t.values())e.bundle!==void 0||e.addons.length>=2?r.push({bundle:e,representative:e.addons[0]}):r.push(e.addons[0]);return r.push(...n),r}function $(e){return`bundle`in e}function Fe({addons:t,clusterStatus:r,selectedNode:a,onRefresh:s,isRefreshing:c}){let l=le(),[u,d]=E(``),f=se(u),[m,g]=E(!1),_=S(null);O(()=>{if(!m)return;let e=e=>{_.current&&!_.current.contains(e.target)&&g(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[m]);let{data:v}=b({nodeId:a.id}),te=T(()=>{let e=new Map;for(let t of v??[])e.set(t.name,t.latestVersion);return e},[v]),[ie,ae]=E(()=>new Set),[oe,C]=E(()=>new Map),ce=x(e=>{ae(t=>new Set(t).add(e))},[]),k=x(e=>{ae(t=>{if(!t.has(e))return t;let n=new Set(t);return n.delete(e),n})},[]),ue=x((e,t)=>{C(n=>new Map(n).set(e,t))},[]),de=x(e=>{C(t=>{if(!t.has(e))return t;let n=new Map(t);return n.delete(e),n})},[]),N=h({onMutate:e=>{ce(e.name),de(e.name)},onSuccess:(e,t)=>{k(t.name);let n=e;if(n&&n.success===!1){ue(t.name,n.error??`Update failed (no error message returned by server)`);return}l.invalidateQueries({queryKey:[[`addons`]]}),l.invalidateQueries({queryKey:[[`addons`,`listUpdates`]]})},onError:(e,t)=>{k(t.name),ue(t.name,e instanceof Error?e.message:String(e))}}),I=be(),pe=S(new Set);O(()=>{let e=new Set;for(let[t,n]of I)J(n)&&e.add(t);let t=[...pe.current].some(t=>!e.has(t));pe.current=e,t&&(l.invalidateQueries({queryKey:[[`addons`]]}),l.invalidateQueries({queryKey:[[`addons`,`listUpdates`]]}))},[I,l]);let{data:L}=re(void 0),me=ne({onSuccess:()=>{l.invalidateQueries({queryKey:[[`addons`,`getAutoUpdateSettings`]]})}}),{data:R,isFetching:he}=e({query:f||void 0},{enabled:m}),z=n(),{data:B}=ee(void 0),ge=B?.requestedAt,[H,_e]=E(null),[ve,U]=E(!1);O(()=>{if(B?.kind===`framework-bulk-update`&&B.requestedAt!==H){_e(B.requestedAt),U(!0);let e=window.setTimeout(()=>U(!1),6e3);return()=>window.clearTimeout(e)}},[ge,H,B]);let{data:W}=p(void 0),ye=T(()=>new Map((W??[]).map(e=>[e.packageName,e])),[W]),[G,K]=E(null),[q,Y]=E(null),X=i({onSuccess:()=>{K(null),l.invalidateQueries({queryKey:[[`addons`]]})},onError:e=>{Y(e instanceof Error?e.message:String(e))}}),xe=o(),Se=x(async()=>{let e=v??[];e.length!==0&&(e.some(e=>e.isSystem)&&!await xe({title:`Update all — includes a system package`,message:`This batch updates a system package (@camstack/system). The hub will restart automatically once the framework update completes. In-flight requests are dropped; reconnect typically takes ~10 seconds.`,confirmLabel:`Update all & restart`,variant:`warning`})||await z.mutateAsync({kind:`update`,targets:e.map(e=>({name:e.name,version:e.latestVersion})),nodeIds:[a.id]}))},[v,a.id,z,xe]),Z=y({onSuccess:()=>{l.invalidateQueries({queryKey:[[`addons`]]}),l.invalidateQueries({queryKey:[[`addonPages`]]}),l.invalidateQueries({queryKey:[[`capabilities`]]}),l.invalidateQueries({queryKey:[[`integrations`]]})}}),[Ce,we]=E(`type`),[Te,Ee]=E(()=>typeof window>`u`?`grouped`:window.localStorage.getItem(`camstack.addons.viewMode`)===`flat`?`flat`:`grouped`),De=e=>{Ee(e),typeof window<`u`&&window.localStorage.setItem(`camstack.addons.viewMode`,e)},[Oe,Ae]=F(),Q=Oe.get(`capability`),Fe=T(()=>{let e=a.isHub?t:t.filter(e=>Ne(e.placement,a.isHub));return Q&&(e=e.filter(e=>(e.manifest.capabilities??[]).some(e=>(typeof e==`string`?e:e.name)===Q))),e},[t,a.isHub,Q]),Ie=()=>{let e=new URLSearchParams(Oe);e.delete(`capability`),Ae(e,{replace:!0})},Le=Te===`flat`?Fe:Pe(Fe),Re=T(()=>{let e=[...Le];switch(Ce){case`type`:e.sort((e,t)=>{let n=+!$(e),r=+!$(t);if(n!==r)return n-r;let i=$(e)?e.bundle.displayName:e.manifest.name,a=$(t)?t.bundle.displayName:t.manifest.name;return i.localeCompare(a)});break;case`name`:e.sort((e,t)=>{let n=$(e)?e.bundle.displayName:e.manifest.name,r=$(t)?t.bundle.displayName:t.manifest.name;return n.localeCompare(r)});break;case`source`:e.sort((e,t)=>{let n=$(e)?e.bundle.installSource??``:e.installSource??``,r=$(t)?t.bundle.installSource??``:t.installSource??``;return n.localeCompare(r)});break}return e},[Le,Ce]);return w(`div`,{className:`space-y-4`,children:[[...oe.entries()].map(([e,t])=>D(`div`,{className:`rounded-md border border-danger/30 bg-danger/5 px-3 py-2 text-[11px]`,children:w(`div`,{className:`flex items-start justify-between gap-3`,children:[w(`div`,{className:`space-y-0.5`,children:[w(`div`,{className:`font-medium text-danger`,children:[`Failed to update `,e]}),D(`div`,{className:`text-foreground-subtle font-mono`,children:t})]}),D(`button`,{type:`button`,onClick:()=>de(e),className:`text-foreground-subtle hover:text-foreground text-[10px]`,children:`dismiss`})]})},e)),w(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[w(`div`,{className:`flex items-center gap-1.5`,children:[D(`span`,{className:`text-[10px] text-foreground-subtle`,children:`Sort:`}),w(`select`,{value:Ce,onChange:e=>we(e.target.value),className:`rounded-md border border-border bg-surface px-2 py-1.5 text-[11px] text-foreground focus:outline-none focus:border-primary/50`,children:[D(`option`,{value:`type`,children:`Bundles first`}),D(`option`,{value:`name`,children:`Name`}),D(`option`,{value:`source`,children:`Install source`})]})]}),w(`div`,{className:`flex items-center gap-1.5`,children:[D(`span`,{className:`text-[10px] text-foreground-subtle`,children:`View:`}),D(`div`,{className:`flex rounded-md border border-border overflow-hidden text-[10px] font-medium`,children:[`grouped`,`flat`].map(e=>D(`button`,{type:`button`,onClick:()=>De(e),className:`px-2.5 py-1.5 transition-colors capitalize ${Te===e?`bg-primary/10 text-primary`:`bg-transparent text-foreground-subtle hover:bg-surface-hover`}`,title:e===`grouped`?`Group multi-addon packages into a bundle card`:`Show every addon as its own card`,children:e},e))})]}),a.isHub&&w(`div`,{className:`flex items-center gap-1.5`,children:[D(j,{className:`h-3 w-3 text-foreground-subtle`}),D(`span`,{className:`text-[10px] text-foreground-subtle`,children:`Auto-Update:`}),D(`div`,{className:`flex rounded-md border border-border overflow-hidden text-[10px] font-medium`,children:[`off`,`latest`,`beta`].map(e=>D(`button`,{type:`button`,onClick:()=>me.mutate({channel:e}),disabled:me.isPending,className:`px-2.5 py-1.5 transition-colors capitalize ${L?.channel===e?e===`off`?`bg-gray-700 text-gray-200`:e===`latest`?`bg-blue-500/20 text-blue-400`:`bg-amber-500/20 text-amber-400`:`bg-transparent text-foreground-subtle hover:bg-surface-hover`} ${e===`off`?``:`border-l border-border`}`,children:e},e))})]}),(v?.length??0)>0&&w(`button`,{type:`button`,onClick:()=>void Se(),disabled:z.isPending,className:`flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] rounded-md bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 border border-blue-500/30 disabled:opacity-50 transition-colors font-medium`,children:[z.isPending?D(V,{className:`w-3 h-3 animate-spin`}):D(A,{className:`w-3 h-3`}),z.isPending?`Updating…`:`Update All (${v.length})`]}),w(`button`,{type:`button`,onClick:s,disabled:c||z.isPending,className:`flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] rounded-md bg-surface hover:bg-surface-hover border border-border disabled:opacity-50 transition-colors`,children:[D(j,{className:`w-3 h-3 ${c?`animate-spin`:``}`}),`Refresh`]})]}),w(`div`,{className:`flex items-center gap-3`,children:[w(`div`,{className:`relative flex-1 min-w-[200px]`,ref:_,children:[D(M,{className:`absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-foreground-subtle`}),D(`input`,{type:`text`,value:u,onChange:e=>{d(e.target.value),g(!0)},onFocus:()=>g(!0),placeholder:`Search addons on npm...`,disabled:z.isPending,className:`w-full rounded-lg border border-border bg-background pl-9 pr-3 py-2 text-xs text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-1 focus:ring-primary disabled:opacity-50 disabled:cursor-not-allowed`}),he&&D(V,{className:`absolute right-2.5 top-1/2 -translate-y-1/2 h-3 w-3 text-foreground-subtle animate-spin`}),m&&R&&R.length>0&&w(`div`,{className:`absolute left-0 right-0 top-full mt-1 z-40 rounded-lg border border-border bg-surface shadow-xl overflow-hidden`,children:[R.length>0&&w(`div`,{className:`px-3 py-1.5 text-[10px] uppercase tracking-wide text-foreground-subtle border-b border-border`,children:[`npm results (`,R.length,`)`]}),R&&R.length>0&&D(`div`,{className:`max-h-72 overflow-auto divide-y divide-border`,children:R.map(e=>w(`div`,{className:`flex items-center gap-3 px-3 py-2 hover:bg-surface-hover transition-colors`,children:[D(`div`,{className:`w-6 h-6 rounded-md bg-primary/10 flex items-center justify-center text-primary text-[9px] font-bold shrink-0`,children:e.name.replace(`@camstack/addon-`,``).charAt(0).toUpperCase()}),w(`div`,{className:`flex-1 min-w-0`,children:[w(`div`,{className:`flex items-center gap-2`,children:[D(`span`,{className:`text-[11px] font-semibold truncate`,children:e.name}),w(`span`,{className:`text-[10px] text-foreground-subtle`,children:[`v`,e.version]})]}),e.description&&D(`div`,{className:`text-[10px] text-foreground-subtle truncate`,children:e.description})]}),e.installed?w(`span`,{className:`inline-flex items-center gap-1 px-2 py-1 text-[10px] font-medium rounded-md border border-emerald-500/30 bg-emerald-500/10 text-emerald-400 shrink-0`,children:[D(P,{className:`h-3 w-3`}),e.installedVersion??`Installed`]}):w(`button`,{type:`button`,onClick:()=>Z.mutate({packageName:e.name}),disabled:Z.isPending,className:`inline-flex items-center gap-1 px-2 py-1 text-[10px] font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 transition-colors shrink-0`,children:[Z.isPending?D(V,{className:`h-3 w-3 animate-spin`}):D(A,{className:`h-3 w-3`}),`Install`]})]},e.name))})]})]}),D(je,{onUploadSuccess:()=>{l.invalidateQueries({queryKey:[[`addons`]]}),s()}})]}),Q&&w(`div`,{className:`flex items-center gap-2 px-3 py-2 rounded-lg bg-primary/10 border border-primary/30 text-sm`,children:[D(`span`,{className:`text-foreground-subtle`,children:`Filtering by capability:`}),D(`span`,{className:`font-mono text-primary font-semibold`,children:Q}),D(`button`,{type:`button`,onClick:Ie,className:`ml-auto text-xs text-foreground-subtle hover:text-foreground underline`,children:`Clear`})]}),ve&&w(`div`,{className:`fixed top-4 right-4 z-50 flex items-start gap-2 px-3 py-2.5 rounded-md border border-green-500/30 bg-green-500/10 shadow-lg backdrop-blur-sm max-w-sm pointer-events-auto`,children:[D(fe,{className:`w-4 h-4 shrink-0 mt-0.5 text-green-400`}),w(`div`,{children:[D(`div`,{className:`text-[11px] font-semibold text-foreground`,children:`Bulk update completed`}),D(`div`,{className:`text-[10px] text-foreground-subtle mt-0.5`,children:`System packages updated — hub restarted successfully.`})]})]}),Fe.length===0&&D(`div`,{className:`text-xs text-foreground-subtle`,children:t.length===0?`No addons installed`:Q?`No installed addons declare the "${Q}" capability. Browse the npm catalog above to find a compatible addon.`:`No addons compatible with ${a.name} (placement filter active)`}),D(`div`,{className:`space-y-2`,children:Re.map(e=>{let t=$(e)?e.bundle.packageName:e.manifest.packageName,n=te.get(t),i=$(e)?e.representative:e,o=ye.has(t),s=n?o?e=>K({packageName:t,fromVersion:ye.get(t)?.currentVersion??i.manifest.packageVersion??i.manifest.version,toVersion:e}):e=>N.mutate({name:t,version:e,nodeId:a.id}):void 0,c=I.get(t)??null,l=c!==null&&J(c);return D(ke,{addon:i,clusterStatus:r?.[i.manifest.id],bundle:$(e)?e.bundle:void 0,availableUpdate:n,isUpdating:ie.has(t)||l,jobProgress:c,onUpdate:s,isSystem:o,disabledByBulk:z.isPending},$(e)?t:e.manifest.id)})}),G!==null&&D(Me,{pending:G,onCancel:()=>{K(null),Y(null)},onConfirm:()=>{Y(null),X.mutate({packageName:G.packageName,version:G.toVersion})},isPending:X.isPending,error:q})]})}function Ie(){let e=le(),{data:t,isLoading:n,isError:i}=v(),{data:a}=m(void 0),{data:o}=r(void 0,{refetchInterval:1e4}),s=T(()=>{let e=new Map;e.set(`hub`,{id:`hub`,name:`Hub`,isHub:!0});for(let t of o??[]){let n=String(t.id??``);n&&e.set(n,{id:n,name:String(t.name??n),isHub:!!t.isHub})}return[...e.values()]},[o]),[c,ee]=E(`hub`),d=s.find(e=>e.id===c)??s[0],f=l(),p=oe(),h=f.isPending||p.isPending,g=async()=>{await f.mutateAsync({nodeId:c}),await p.mutateAsync(),e.invalidateQueries({queryKey:[[`addons`]]})},_=S(f);_.current=f,O(()=>{_.current.mutate({nodeId:`hub`})},[]);let y=(t??[]).map(Q).filter(e=>e!==null);return w(H,{children:[w(`div`,{className:`flex items-center justify-between gap-3 flex-wrap`,children:[D(u,{selectedNodeId:c,onSelect:ee}),w(`div`,{className:`flex items-center gap-3 flex-wrap`,children:[!n&&!i&&w(`span`,{className:`text-[10px] rounded-full bg-primary/10 text-primary px-2 py-0.5 font-medium`,children:[y.length,` installed`]}),w(`button`,{type:`button`,onClick:()=>void g(),disabled:h,className:`inline-flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] rounded-md bg-surface hover:bg-surface-hover border border-border disabled:opacity-50 transition-colors`,title:`Re-fetch addons + check npm for new versions`,children:[D(j,{className:`w-3 h-3 ${h?`animate-spin`:``}`}),`Check for updates`]})]})]}),!d.isHub&&w(`div`,{className:`rounded-md border border-warning/30 bg-warning/5 px-3 py-2 text-[11px] text-foreground-subtle`,children:[`Showing only addons compatible with`,` `,D(`span`,{className:`font-medium text-foreground`,children:d.name}),` — agents only run`,` `,D(`code`,{className:`font-mono`,children:`agent-only`}),` and`,` `,D(`code`,{className:`font-mono`,children:`any-node`}),` addons. Install / uninstall is hub-driven; agents auto-sync from the hub.`]}),n&&D(`div`,{className:`text-xs text-foreground-subtle animate-pulse`,children:`Loading…`}),i&&D(`div`,{className:`text-xs text-danger`,children:`Failed to load addons`}),!n&&!i&&D(Fe,{addons:y,clusterStatus:a,selectedNode:d,onRefresh:()=>void g(),isRefreshing:h})]})}export{Ie as AddonsPage};