@camstack/addon-admin-ui 1.2.108 → 1.2.110

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 (116) hide show
  1. package/dist/assets/{AddLocationWizard-C1QuDd5l.js → AddLocationWizard-B2pYT5l3.js} +1 -1
  2. package/dist/assets/{AddonCollectionPage-CAMY7BSp.js → AddonCollectionPage-BM--U5wi.js} +1 -1
  3. package/dist/assets/Addons-C1fP2vbH.js +1 -0
  4. package/dist/assets/{AdminTabs-DIHki8py.js → AdminTabs-CLeY5YpY.js} +1 -1
  5. package/dist/assets/{AiProfiles-DiQNZe9S.js → AiProfiles-B4OqaOyL.js} +2 -2
  6. package/dist/assets/{BrokerForm-mfcucCqe.js → BrokerForm-BBcYgkU0.js} +1 -1
  7. package/dist/assets/{Build-Bd3bm4rZ.js → Build-Clz-9wEa.js} +4 -4
  8. package/dist/assets/{Capabilities-CT-vp-yT.js → Capabilities-GMmZShHl.js} +1 -1
  9. package/dist/assets/{CapabilityBadges-5yUqlEI8.js → CapabilityBadges-ls5u4iSS.js} +1 -1
  10. package/dist/assets/Cluster-B0miuhVw.js +11 -0
  11. package/dist/assets/{Data-DLHWqZce.js → Data-BsJaaX8N.js} +1 -1
  12. package/dist/assets/DetectionIntelligence-DwAMtJfh.js +1 -0
  13. package/dist/assets/{DeviceAggregateSection-CJc0z5bD.js → DeviceAggregateSection-CfbUJ--n.js} +1 -1
  14. package/dist/assets/DeviceDetail-kxj_2AT4.js +2 -0
  15. package/dist/assets/Devices-CrvA2yT8.js +1 -0
  16. package/dist/assets/EmbedPlayerPage-5qdw9AQ5.js +1 -0
  17. package/dist/assets/Events-C0HDOEKf.js +1 -0
  18. package/dist/assets/{FormBuilder-DfwoGzWg.js → FormBuilder-Sl0aUAjm.js} +1 -1
  19. package/dist/assets/Identity-3wRL60eh.js +1 -0
  20. package/dist/assets/IntegrationDetail-DYdXG7zw.js +1 -0
  21. package/dist/assets/Integrations-DWirlHH2.js +1 -0
  22. package/dist/assets/Integrations-GhhEgWTs.js +1 -0
  23. package/dist/assets/{Logs-BI78-0VJ.js → Logs-OCVO9mBC.js} +1 -1
  24. package/dist/assets/{MotionZonesSettings-gEbDj9Pj.js → MotionZonesSettings-DztkSAzw.js} +1 -1
  25. package/dist/assets/{MyAccess-Dq5YfsIV.js → MyAccess-C1FnwEV1.js} +1 -1
  26. package/dist/assets/Network-C1tyUle7.js +1 -0
  27. package/dist/assets/NodeAddonsSettingsPanel-C51CLAUV.js +1 -0
  28. package/dist/assets/NodeInferenceDevices-BheDiA_Y.js +1 -0
  29. package/dist/assets/Notifications-2oJHn7yF.js +1 -0
  30. package/dist/assets/Pipeline-BWgUjIBu.js +1 -0
  31. package/dist/assets/{Plates-LVOTLRQ_.js → Plates-B1CFXuYH.js} +1 -1
  32. package/dist/assets/{PrivacyMaskSettings-ByMo4smL.js → PrivacyMaskSettings-BhcRGnjg.js} +1 -1
  33. package/dist/assets/{ProviderIcon-BuQT-C1f.js → ProviderIcon-CSGCWRat.js} +1 -1
  34. package/dist/assets/Recordings-CVBJ-fTE.js +1 -0
  35. package/dist/assets/{ScanNetworkModal-DO_NYOIO.js → ScanNetworkModal-DP3Q1lJf.js} +1 -1
  36. package/dist/assets/{SceneMonitorEditor-COTXt9Av.js → SceneMonitorEditor-CC_SRLkY.js} +1 -1
  37. package/dist/assets/Scenes-CMobnBno.js +1 -0
  38. package/dist/assets/{Settings-UaBYAyll.js → Settings-Boxw0bXQ.js} +1 -1
  39. package/dist/assets/{Showroom-BO0DQHWe.js → Showroom-BLra8xK_.js} +1 -1
  40. package/dist/assets/{Terminal-D6BI2-dB.js → Terminal-DJKkbuyK.js} +3 -3
  41. package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-6VfZqYHt.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-MSGGSFff.js} +1 -1
  42. package/dist/assets/{bell-ring-COnGH5xz.js → bell-ring-BrNXSpTM.js} +1 -1
  43. package/dist/assets/{boxes-BqAi_ZYu.js → boxes-BBQZcZmf.js} +1 -1
  44. package/dist/assets/circle-check-big-BA325w7j.js +1 -0
  45. package/dist/assets/{circle-x-CAQ2kQrS.js → circle-x-duYSDFk4.js} +1 -1
  46. package/dist/assets/clock-C2SeIGR3.js +1 -0
  47. package/dist/assets/{copy-D25tAWbr.js → copy-sWO8ojL1.js} +1 -1
  48. package/dist/assets/{debounced-invalidate-Dd9oPYKq.js → debounced-invalidate-CsEaF8m7.js} +1 -1
  49. package/dist/assets/{dist-Cz-CwPmU.js → dist-B2d_n790.js} +1 -1
  50. package/dist/assets/dist-CwHxBEa6.js +349 -0
  51. package/dist/assets/{download-DC6CvEwh.js → download-BG5cgMLB.js} +1 -1
  52. package/dist/assets/{face-bbox-HBoy_dVM.js → face-bbox-CuCo2m34.js} +1 -1
  53. package/dist/assets/{hostInit-BZyW6qkD.js → hostInit-BXhxZCos.js} +1 -1
  54. package/dist/assets/index-TIovJNJd.js +4 -0
  55. package/dist/assets/{key-round-CkImMp76.js → key-round-DFB6Ddil.js} +1 -1
  56. package/dist/assets/{layers-DR9qbaZc.js → layers-Dg2FntHR.js} +1 -1
  57. package/dist/assets/{mf-entry-bootstrap-0-ed3637b6.js → mf-entry-bootstrap-0-78ce8935.js} +2 -2
  58. package/dist/assets/{pencil-D-rxGNNf.js → pencil-DfqU4z4Q.js} +1 -1
  59. package/dist/assets/play-B850xDzN.js +1 -0
  60. package/dist/assets/{player-overlays-wniETo4u.js → player-overlays-BrCS3AFF.js} +1 -1
  61. package/dist/assets/plus-C1RBhRwm.js +1 -0
  62. package/dist/assets/power-DSSFcNbH.js +1 -0
  63. package/dist/assets/{radio-dB0UNm47.js → radio-PpN-ewHt.js} +1 -1
  64. package/dist/assets/{refresh-cw-srKCTv_-.js → refresh-cw-BP3l62v-.js} +1 -1
  65. package/dist/assets/remoteEntry-y4f0U6fp.js +1 -0
  66. package/dist/assets/rotate-ccw-BiOviRuW.js +1 -0
  67. package/dist/assets/rotate-cw-PvJE7LD3.js +1 -0
  68. package/dist/assets/{send-DouvNKBS.js → send-BhFSH1Dx.js} +1 -1
  69. package/dist/assets/{server-BQtqKhWd.js → server-DTziPc-j.js} +1 -1
  70. package/dist/assets/{shield-Df5MsuC2.js → shield-DloVBwF4.js} +1 -1
  71. package/dist/assets/square-BxxA8zwF.js +1 -0
  72. package/dist/assets/{square-check-big-DRcEphPQ.js → square-check-big-DCkSuowJ.js} +1 -1
  73. package/dist/assets/{src-IW6snE8b.js → src-CTpgEWRF.js} +17 -17
  74. package/dist/assets/src-Db_n2ue2.js +349 -0
  75. package/dist/assets/{star-DSf9DLF0.js → star-mh10UPuW.js} +1 -1
  76. package/dist/assets/{undo-2-D8E8Za4o.js → undo-2-BdzAmJva.js} +1 -1
  77. package/dist/assets/{use-scenes-feed-DZbJbkzy.js → use-scenes-feed-DuRQEHSx.js} +1 -1
  78. package/dist/assets/useEventInvalidation-DI7nUUUv.js +1 -0
  79. package/dist/assets/{video-DMGYxQJX.js → video-B8iXv4k7.js} +1 -1
  80. package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-ByOZvjdD.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-vbnrVEeZ.js} +2 -2
  81. package/dist/assets/{wifi-DPLmik5h.js → wifi-CA7EJ_s4.js} +1 -1
  82. package/dist/assets/{zap-D-TFU8vN.js → zap-Bm8mq3x-.js} +1 -1
  83. package/dist/index.html +5 -5
  84. package/dist/server/addon.js +270 -2
  85. package/package.json +1 -1
  86. package/dist/assets/Addons-bWB0ZmER.js +0 -1
  87. package/dist/assets/Cluster-BPFw3CEh.js +0 -11
  88. package/dist/assets/DetectionIntelligence-B4PBUQWJ.js +0 -1
  89. package/dist/assets/DeviceDetail-bORj-EYN.js +0 -2
  90. package/dist/assets/Devices-D88uoW-K.js +0 -1
  91. package/dist/assets/EmbedPlayerPage-BQ5bsryo.js +0 -1
  92. package/dist/assets/Events-D9XRueHk.js +0 -1
  93. package/dist/assets/Identity-43Eya081.js +0 -1
  94. package/dist/assets/IntegrationDetail-CwEQCSYB.js +0 -1
  95. package/dist/assets/Integrations-Bj0zg-09.js +0 -1
  96. package/dist/assets/Integrations-tWtIaZju.js +0 -1
  97. package/dist/assets/Network-0uDqECDb.js +0 -1
  98. package/dist/assets/NodeAddonsSettingsPanel-CAyKEQpD.js +0 -1
  99. package/dist/assets/NodeInferenceDevices-CnDRCNxR.js +0 -1
  100. package/dist/assets/Notifications-zU06XSrO.js +0 -1
  101. package/dist/assets/Pipeline-B5A-GVGG.js +0 -1
  102. package/dist/assets/Recordings-TSgV5nm4.js +0 -1
  103. package/dist/assets/Scenes-DgDtCzMz.js +0 -1
  104. package/dist/assets/circle-check-big-BQVyFP9A.js +0 -1
  105. package/dist/assets/clock-DGWYKseh.js +0 -1
  106. package/dist/assets/dist-B_SWpX_l.js +0 -349
  107. package/dist/assets/index-Cp84SBzW.js +0 -4
  108. package/dist/assets/play-s0iikQHY.js +0 -1
  109. package/dist/assets/plus-BVcSfMMt.js +0 -1
  110. package/dist/assets/power-CcdufSlh.js +0 -1
  111. package/dist/assets/remoteEntry-B2zHiyBK.js +0 -1
  112. package/dist/assets/rotate-ccw-CY-1yf9N.js +0 -1
  113. package/dist/assets/rotate-cw-DKLLB8dt.js +0 -1
  114. package/dist/assets/square-CFDIb2ZA.js +0 -1
  115. package/dist/assets/src-CsxD-27Y.js +0 -349
  116. package/dist/assets/useEventInvalidation-CsNvM4qI.js +0 -1
@@ -5917,6 +5917,40 @@ var BaseAddon = class {
5917
5917
  deviceSettingsSchema() {
5918
5918
  return null;
5919
5919
  }
5920
+ /**
5921
+ * INTEGRATION-LEVEL SETTINGS — declare which of this addon's global sections
5922
+ * ARE the configuration of its integration.
5923
+ *
5924
+ * Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
5925
+ * operator should find on the addon's integration page (System →
5926
+ * Integrations → <name>) rather than only in the cluster-wide list of every
5927
+ * addon. Empty (the default) means the addon has no integration-level
5928
+ * settings and no such surface is offered — this is opt-in, because whether
5929
+ * an addon's configuration IS its integration's configuration depends on the
5930
+ * nature of the integration.
5931
+ *
5932
+ * WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
5933
+ * the ONE global schema, in the ONE addon store, written by the ONE
5934
+ * `updateGlobalSettings` path. There is deliberately no
5935
+ * `updateIntegrationSettings`: a second write path is how a surface acquires
5936
+ * a second store key, and this repo has shipped that twice (`btmPath@hub`,
5937
+ * D266). Selecting sections cannot introduce a key that selecting cannot.
5938
+ *
5939
+ * WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
5940
+ * `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
5941
+ * removed with the reason recorded at
5942
+ * `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
5943
+ * determined by WHICH schema it lives in, not by a field-level marker."* A
5944
+ * marker sprinkled across sections also has to borrow a field that already
5945
+ * means something else; borrowing `section.tab` put the literal word
5946
+ * "integration" into an operator-facing tab bar, because `tab` means "how to
5947
+ * GROUP this visually" and cannot also mean "where this lives" (D269
5948
+ * supersedes D268). One declaration, in one place, next to the schema whose
5949
+ * ids it names.
5950
+ */
5951
+ integrationSettingSections() {
5952
+ return [];
5953
+ }
5920
5954
  async getGlobalSettings(overlay, cap, nodeId) {
5921
5955
  const schema = this.globalSettingsSchema(cap);
5922
5956
  if (!schema) return { sections: [] };
@@ -5927,6 +5961,55 @@ var BaseAddon = class {
5927
5961
  } : projected);
5928
5962
  }
5929
5963
  /**
5964
+ * The integration-level view of this addon's settings: exactly the sections
5965
+ * named by {@link integrationSettingSections}, hydrated from the SAME store
5966
+ * `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
5967
+ *
5968
+ * Returns `null` when the addon declared nothing — an addon that opts out has
5969
+ * no integration settings surface at all, rather than an empty one that reads
5970
+ * as a failed load.
5971
+ *
5972
+ * Three properties hold BY CONSTRUCTION, which is why they are here in core
5973
+ * and not in whichever UI happens to render this:
5974
+ *
5975
+ * 1. **One key.** The payload is a SUBSET of the global schema, so a field
5976
+ * shown here is the same field, with the same bare key, that the addon's
5977
+ * own page shows. There is no integration-specific writer — callers save
5978
+ * through `updateGlobalSettings` — so a second store key is unreachable,
5979
+ * not merely discouraged.
5980
+ * 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
5981
+ * is `<key>@<nodeId>` and an integration is not a node; whichever node
5982
+ * such a field silently picked would be a wrong answer for the operator
5983
+ * who opened the page (D266).
5984
+ * 3. **No silent typo.** A declared id that names no section throws. The
5985
+ * alternative — skip it — turns a rename into a surface that quietly
5986
+ * empties, which looks exactly like an addon with nothing to configure.
5987
+ */
5988
+ async getIntegrationSettings(nodeId) {
5989
+ const declared = this.integrationSettingSections();
5990
+ if (declared.length === 0) return null;
5991
+ const schema = this.globalSettingsSchema();
5992
+ if (!schema) throw new Error(`${this.constructor.name}: integrationSettingSections() names [${declared.join(", ")}] but globalSettingsSchema() returns null.`);
5993
+ const byId = new Map(schema.sections.map((section) => [section.id, section]));
5994
+ const sections = [];
5995
+ for (const id of declared) {
5996
+ const section = byId.get(id);
5997
+ if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
5998
+ const fields = dropPerNodeFields(section.fields);
5999
+ if (fields.length === 0) continue;
6000
+ sections.push({
6001
+ ...section,
6002
+ fields
6003
+ });
6004
+ }
6005
+ if (sections.length === 0) return null;
6006
+ const projected = await this.resolveGlobalStore(nodeId);
6007
+ return hydrateSchema({
6008
+ ...schema,
6009
+ sections
6010
+ }, projected);
6011
+ }
6012
+ /**
5930
6013
  * The raw addon store PROJECTED onto the target node's bare per-node keys:
5931
6014
  * every `perNode: true` field carries THAT node's scoped value on its bare
5932
6015
  * key (absent scoped key ⇒ key absent, so the schema `default` wins — no
@@ -6230,6 +6313,41 @@ var BaseAddon = class {
6230
6313
  * `hydrateSchema` does. Valueless structural fields (separator/info/…)
6231
6314
  * don't declare `perNode` and are excluded by the `in` narrowing.
6232
6315
  */
6316
+ /**
6317
+ * The same fields with every `perNode: true` one removed, recursing into layout
6318
+ * containers exactly as {@link collectPerNodeFieldKeys} does. A container left
6319
+ * with no child is dropped rather than rendered empty.
6320
+ *
6321
+ * Used by `getIntegrationSettings`: an integration is not a node, so a field
6322
+ * whose store key is `<key>@<nodeId>` has no node to belong to there.
6323
+ */
6324
+ function dropPerNodeFields(fields) {
6325
+ const kept = [];
6326
+ for (const field of fields) {
6327
+ if (field.type === "group") {
6328
+ const inner = dropPerNodeFields(field.fields);
6329
+ if (inner.length > 0) kept.push({
6330
+ ...field,
6331
+ fields: inner
6332
+ });
6333
+ continue;
6334
+ }
6335
+ if (field.type === "sub-tabs") {
6336
+ const tabs = field.tabs.map((tab) => ({
6337
+ ...tab,
6338
+ fields: dropPerNodeFields(tab.fields)
6339
+ })).filter((tab) => tab.fields.length > 0);
6340
+ if (tabs.length > 0) kept.push({
6341
+ ...field,
6342
+ tabs
6343
+ });
6344
+ continue;
6345
+ }
6346
+ if ("perNode" in field && field.perNode === true) continue;
6347
+ kept.push(field);
6348
+ }
6349
+ return kept;
6350
+ }
6233
6351
  function collectPerNodeFieldKeys(fields) {
6234
6352
  const collected = [];
6235
6353
  for (const field of fields) {
@@ -9401,6 +9519,9 @@ method(object({
9401
9519
  kind: "mutation",
9402
9520
  auth: "admin"
9403
9521
  }), method(object({
9522
+ addonId: string(),
9523
+ nodeId: string().optional()
9524
+ }), SettingsSchemaWithValuesSchema.nullable()), method(object({
9404
9525
  addonId: string(),
9405
9526
  deviceId: number(),
9406
9527
  nodeId: string().optional()
@@ -13085,6 +13206,114 @@ method(LogEntrySchema, _void(), { kind: "mutation" }), method(object({
13085
13206
  limit: number().optional(),
13086
13207
  tags: record(string(), string()).optional()
13087
13208
  }), array(LogEntrySchema).readonly());
13209
+ /**
13210
+ * `failure-contribution` — the capability an addon reports its OWN losses
13211
+ * through, per camera, with the denominator attached. It stores nothing.
13212
+ *
13213
+ * ## The twin of `load-contribution`, and why it is a twin and not a field
13214
+ *
13215
+ * `load-contribution` answers *what did this camera COST*. This answers *what
13216
+ * did this camera LOSE*. The reporting discipline is identical and deliberately
13217
+ * copied: the contributor reports what it already knows, hub-main adds only
13218
+ * `addonId`, nothing needs global knowledge, and there is no central list for
13219
+ * somebody to forget to edit.
13220
+ *
13221
+ * They are not merged, because their invariants are opposites:
13222
+ *
13223
+ * - a `load-contribution` measurement is **absent, never zero** — a zero would
13224
+ * claim a camera cost nothing, which is a measurement nobody made;
13225
+ * - a `failure-contribution` zero is the **most valuable value on the
13226
+ * surface** — `attempts: 400, succeeded: 400` is the proof a fix landed,
13227
+ * and it is exactly what an absent entry cannot say.
13228
+ *
13229
+ * Putting a loss counter on a cost entry would also break the reconciliation
13230
+ * that gives `load-contribution` its point: contributions are subtracted from
13231
+ * `metrics.node-processes-snapshot` to find processes nobody claims. A failure
13232
+ * has no process.
13233
+ *
13234
+ * ## Why not a log line, since the counters already exist
13235
+ *
13236
+ * Several of these paths already counted themselves — `CaptureScheduler`'s
13237
+ * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
13238
+ * ends in a log line, and a log line is the thing the operator asked to stop
13239
+ * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
13240
+ * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
13241
+ * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
13242
+ * media blackout were both diagnosed. The counters stay; this is where they can
13243
+ * be READ.
13244
+ *
13245
+ * ## The rate is served with its denominator or not at all
13246
+ *
13247
+ * Every entry carries `attempts` and `succeeded`. A miss count alone is
13248
+ * unreadable: on 2026-08-28 the enrichment-crop miss count read as "35x worse
13249
+ * than yesterday" and was **flat across twelve hours** once divided by the
13250
+ * successes on the same path. A surface that publishes only the numerator
13251
+ * reproduces that mistake on every read.
13252
+ *
13253
+ * ## Shape
13254
+ *
13255
+ * Copied from `load-contribution.cap.ts` (`mode: 'collection'`,
13256
+ * `internal: true`, `mount: { kind: 'skip' }`): no tRPC route of its own and no
13257
+ * generated hooks, while `addons.listCapabilityProviders` still enumerates it
13258
+ * and the hub's `CapabilityRegistry` still holds an RPC proxy per provider — so
13259
+ * a forked runner's entries reach hub-main over transport that already exists.
13260
+ * No new UDS message, no second registry (D3). The operator reads the assembled
13261
+ * result through `system.getFailureContributions`.
13262
+ */
13263
+ var FailureReasonCountSchema = object({
13264
+ /**
13265
+ * Why the attempt did not land, in the contributor's own vocabulary —
13266
+ * `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
13267
+ * strings that already appear in this repo's logs and, where one exists, the
13268
+ * same string the per-track `previewMissReason` records (D276): a second
13269
+ * vocabulary for the same loss would make the row and the counter
13270
+ * un-joinable.
13271
+ */
13272
+ reason: string(),
13273
+ count: number().int().nonnegative()
13274
+ });
13275
+ var FailureContributionSchema = object({
13276
+ /**
13277
+ * The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
13278
+ * `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
13279
+ * `unit` free: the families are owned by different addons and a shared enum
13280
+ * is a central list that rots invisibly.
13281
+ */
13282
+ family: string(),
13283
+ /**
13284
+ * The NUMERIC device id — the same value every log line carries as
13285
+ * `tags.deviceId`. Never nullable and never absent: a contributor that
13286
+ * cannot name the camera must not emit the entry, because a fleet total
13287
+ * cannot answer the only question anybody asks of this surface.
13288
+ */
13289
+ deviceId: number().int().positive(),
13290
+ /**
13291
+ * A second dimension inside the family: the model / step id for an inference
13292
+ * timeout, so "which camera AND which model" is one read. Absent when the
13293
+ * family has a single variant.
13294
+ */
13295
+ variant: string().optional(),
13296
+ /**
13297
+ * Epoch ms this counter started — the INCARNATION MARKER. A consumer
13298
+ * differencing two reads must drop the interval when it changes, because the
13299
+ * counter restarted from zero in a respawned runner. Same discipline as
13300
+ * `LoadContribution.startedAtMs`.
13301
+ */
13302
+ sinceMs: number(),
13303
+ /** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
13304
+ atMs: number(),
13305
+ /**
13306
+ * THE DENOMINATOR — every attempt on this path for this camera in the
13307
+ * window. A failure count published without it is the mistake this schema
13308
+ * exists to make impossible.
13309
+ */
13310
+ attempts: number().int().nonnegative(),
13311
+ /** Attempts that landed. `attempts - succeeded` is the loss. */
13312
+ succeeded: number().int().nonnegative(),
13313
+ /** The loss, partitioned. Sums to `attempts - succeeded`. */
13314
+ reasons: array(FailureReasonCountSchema).readonly()
13315
+ });
13316
+ method(_void(), array(FailureContributionSchema).readonly());
13088
13317
  var LoadContributionSchema = object({
13089
13318
  role: _enum([
13090
13319
  "decode",
@@ -17597,6 +17826,20 @@ var TrackSchema = object({
17597
17826
  * `=== true` and render nothing otherwise — never infer "no rider".
17598
17827
  */
17599
17828
  hasRider: boolean().optional(),
17829
+ /**
17830
+ * WHY this track ended without a NATIVE best-shot tile
17831
+ * ([D276](../decisions/adr-0276-a-stand-in-tile-is-provisional-and-a-close-says-why.md)) —
17832
+ * a composed token line (`no-key-frame capture=keyframe:native-missx4`,
17833
+ * `derive-returned-null tile=standin`, …) written at close and CLEARED by
17834
+ * the late-keyFrame upgrade when a native tile lands after all. The
17835
+ * operator-facing answer to "perché manca l'immagine?" on a track whose
17836
+ * tile is a face/plate stand-in, a raster crop, or an icon.
17837
+ *
17838
+ * **Absent ≠ "missed silently"**: a row written before the column, a hub
17839
+ * that predates the field, and every track whose tile landed native all
17840
+ * omit it. Render nothing when absent.
17841
+ */
17842
+ previewMissReason: string().optional(),
17600
17843
  ...TrackFlagFields,
17601
17844
  ...TrackRetrainFields
17602
17845
  });
@@ -26823,6 +27066,13 @@ var LoggingSettingsPatchSchema = object({
26823
27066
  * anyone but its owner.
26824
27067
  */
26825
27068
  var ReportedLoadContributionSchema = LoadContributionSchema.extend({ addonId: string() });
27069
+ /**
27070
+ * One per-camera failure counter, plus WHO reported it.
27071
+ *
27072
+ * Same rule as {@link ReportedLoadContributionSchema}: `addonId` is stamped by
27073
+ * the hub as it enumerates providers, never by the contributor.
27074
+ */
27075
+ var ReportedFailureContributionSchema = FailureContributionSchema.extend({ addonId: string() });
26826
27076
  var GetLoggingSettingsInputSchema = object({
26827
27077
  scopeNodeId: string().optional(),
26828
27078
  /**
@@ -26881,7 +27131,7 @@ method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), met
26881
27131
  }), method(_void(), SiteLocationStatusSchema, {
26882
27132
  kind: "mutation",
26883
27133
  auth: "admin"
26884
- }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
27134
+ }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(_void(), array(ReportedFailureContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
26885
27135
  kind: "mutation",
26886
27136
  auth: "admin"
26887
27137
  });
@@ -27795,6 +28045,12 @@ Object.freeze({
27795
28045
  addonId: null,
27796
28046
  access: "view"
27797
28047
  },
28048
+ "addonSettings.getIntegrationSettings": {
28049
+ capName: "addon-settings",
28050
+ capScope: "system",
28051
+ addonId: null,
28052
+ access: "view"
28053
+ },
27798
28054
  "addonSettings.updateDeviceSettings": {
27799
28055
  capName: "addon-settings",
27800
28056
  capScope: "system",
@@ -29457,6 +29713,12 @@ Object.freeze({
29457
29713
  addonId: null,
29458
29714
  access: "create"
29459
29715
  },
29716
+ "failureContribution.list": {
29717
+ capName: "failure-contribution",
29718
+ capScope: "system",
29719
+ addonId: null,
29720
+ access: "view"
29721
+ },
29460
29722
  "fanControl.setDirection": {
29461
29723
  capName: "fan-control",
29462
29724
  capScope: "device",
@@ -32763,6 +33025,12 @@ Object.freeze({
32763
33025
  addonId: null,
32764
33026
  access: "create"
32765
33027
  },
33028
+ "system.getFailureContributions": {
33029
+ capName: "system",
33030
+ capScope: "system",
33031
+ addonId: null,
33032
+ access: "view"
33033
+ },
32766
33034
  "system.getLoadContributions": {
32767
33035
  capName: "system",
32768
33036
  capScope: "system",
@@ -35668,7 +35936,7 @@ var AdminUIAddon = class extends BaseAddon {
35668
35936
  capability: adminUiCapability,
35669
35937
  provider: {
35670
35938
  getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
35671
- getVersion: async () => ({ version: "1.2.108" })
35939
+ getVersion: async () => ({ version: "1.2.110" })
35672
35940
  }
35673
35941
  }];
35674
35942
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-admin-ui",
3
- "version": "1.2.108",
3
+ "version": "1.2.110",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Admin UI — Vite frontend build and server-side addon",
@@ -1 +0,0 @@
1
- import{I as e,K as t,Q as n,R as r,S as i,W as a,b as o,o as s,q as c,x as l,z as u}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{Z as d}from"./player-overlays-wniETo4u.js";import{An as f,Bn as p,C as m,Cn as h,En as g,Fn as _,Fs as v,In as y,J as ee,Nn as b,On as x,Pn as S,Rn as te,Sn as ne,Tn as re,Zi as C,bn as w,g as ie,jn as ae,kn as oe,ln as se,na as T,q as ce,wn as E,xn as D,yn as le,zn as ue}from"./src-IW6snE8b.js";import{Vt as O}from"./src-CsxD-27Y.js";import{t as k}from"./clock-DGWYKseh.js";import{t as A}from"./download-DC6CvEwh.js";import{t as de}from"./plus-BVcSfMMt.js";import{t as j}from"./refresh-cw-srKCTv_-.js";import{C as M,J as N,K as P,P as fe,R as F,X as pe,_ as I,f as me,m as he,o as L,p as R,r as z,z as B}from"./index-Cp84SBzW.js";import{t as V}from"./AdminPage-CCD07UMs.js";import{t as H}from"./NodeAddonsSettingsPanel-CAyKEQpD.js";var ge=P(`chevron-up`,[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]),U=P(`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`}]]),_e=P(`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`}]]),W=P(`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`}]]),G=P(`upload`,[[`path`,{d:`M12 3v12`,key:`1x0j5s`}],[`path`,{d:`m17 8-5-5-5 5`,key:`7q97r8`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}]]);n();var K=new Set([`completed`,`failed`,`partially-failed`,`cancelled`]),q=new Set([`done`,`failed`,`skipped`]);function ve(e){return!q.has(e.phase)&&!K.has(e.jobState)}function J(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 ye(){let{data:e}=x({activeOnly:!0},{refetchOnWindowFocus:!1,refetchInterval:e=>(e.state.data?.length??0)>0?3e3:!1}),{map:t}=v(O.AddonsJobProgress,e=>e.job.jobId);return a(()=>{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 J([...n.values()],Date.now())},[e,t])}var be={queued:{icon:k,cls:`bg-muted text-foreground-subtle`,label:`queued`},fetching:{icon:M,cls:`bg-primary/10 text-primary`,label:`fetching`,spin:!0},staged:{icon:M,cls:`bg-primary/10 text-primary`,label:`staged`,spin:!0},validating:{icon:M,cls:`bg-primary/10 text-primary`,label:`validating`,spin:!0},applying:{icon:M,cls:`bg-primary/10 text-primary`,label:`applying`,spin:!0},restarting:{icon:M,cls:`bg-amber-500/10 text-amber-600 dark:text-amber-400`,label:`restarting`,spin:!0},applied:{icon:M,cls:`bg-primary/10 text-primary`,label:`finalizing`,spin:!0},done:{icon:B,cls:`bg-success/10 text-success`,label:`updated`},failed:{icon:U,cls:`bg-danger/10 text-danger`,label:`failed`},skipped:{icon:W,cls:`bg-muted text-foreground-subtle`,label:`skipped`}};function xe({progress:e}){let t=be[e.phase],n=t.icon,r=e.error??`${e.packageName}: ${e.fromVersion??`?`} → ${e.toVersion}`;return i(`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:[l(n,{className:`w-2.5 h-2.5 ${t.spin?`animate-spin`:``}`}),t.label,i(`span`,{className:`font-mono`,children:[`→ `,e.toVersion]})]})}function Se({packageName:e,currentVersion:t,installSource:n,onInstall:r,onClose:a,onSelect:s,selectedVersion:c}){let u=n===`upload`||n===`local`,{data:d,isLoading:f}=E({name:e});return i(`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:[i(`div`,{className:`flex items-center justify-between px-3 py-2 border-b border-border`,children:[l(`span`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle font-medium`,children:`Versions`}),l(`button`,{type:`button`,onClick:a,className:`text-foreground-subtle hover:text-foreground`,children:l(z,{className:`h-3 w-3`})})]}),u&&i(`div`,{className:`flex items-start gap-2 px-3 py-2 border-b border-border bg-cyan-500/10 text-[10px]`,children:[l(G,{className:`h-3 w-3 mt-0.5 text-cyan-500 shrink-0`}),i(`div`,{className:`text-cyan-700 dark:text-cyan-200`,children:[i(`span`,{className:`font-semibold`,children:[n===`upload`?`Uploaded copy`:`Local copy`,` in place.`]}),` `,`Re-install `,l(`span`,{className:`font-mono`,children:t}),` from npm to track upstream releases again.`]})]}),f&&i(`div`,{className:`flex items-center justify-center gap-2 py-6 text-[10px] text-foreground-subtle`,children:[l(M,{className:`h-3.5 w-3.5 animate-spin`}),`Loading versions...`]}),d&&l(`div`,{className:`max-h-64 overflow-auto divide-y divide-border`,children:d.map(e=>{let n=e.version===t,a=s!==void 0&&e.version===c,d=/-(alpha|beta|rc|dev|canary|next)/i.test(e.version),f=i(o,{children:[l(`span`,{className:`font-mono flex-1 text-left ${n?`text-primary font-semibold`:d?`text-amber-400`:`text-foreground`}`,children:e.version}),n&&l(`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=>l(`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&&l(`span`,{className:`text-[9px] text-red-400 line-through`,children:`deprecated`}),l(`span`,{className:`text-[9px] text-foreground-subtle shrink-0`,children:new Date(e.publishedAt).toLocaleDateString()})]});return s?i(`button`,{type:`button`,"aria-label":`Select ${e.version}`,onClick:()=>s(e.version),className:`w-full flex items-center gap-2 px-3 py-2 text-[11px] ${a?`bg-primary/5`:`hover:bg-surface-hover`} transition-colors`,children:[f,a?l(B,{className:`h-3 w-3 text-primary shrink-0`}):l(`span`,{className:`w-3 shrink-0`})]},e.version):i(`div`,{className:`flex items-center gap-2 px-3 py-2 text-[11px] ${n?`bg-primary/5`:`hover:bg-surface-hover`} transition-colors`,children:[f,n&&!u?l(B,{className:`h-3 w-3 text-primary shrink-0`}):l(`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:l(A,{className:`h-3 w-3`})})]},e.version)})}),d&&d.length===0&&l(`div`,{className:`py-4 text-center text-[10px] text-foreground-subtle`,children:`No versions found`})]})}var Y={queued:{icon:k,bg:`bg-muted text-foreground-subtle`,label:`queued`},updating:{icon:M,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:B,bg:`bg-success/10 text-success`,label:`done`},failed:{icon:U,bg:`bg-danger/10 text-danger`,label:`failed`}};function Ce({item:e}){let t=Y[e.status],n=t.icon;return i(`span`,{className:`inline-flex items-center gap-1 px-1.5 py-0.5 text-[10px] rounded ${t.bg}`,title:e.error,children:[l(n,{className:`w-2.5 h-2.5 ${t.spin?`animate-spin`:``}`}),t.label]})}n();var X=`device-export`;function Z(e){return e.capabilities.some(e=>(typeof e==`string`?e:e.name)===X)}function we({isBundle:e,installSource:t,addonId:n,iconPath:r,color:a}){let[o,s]=c(!1),u=a?{backgroundColor:`${a.startsWith(`#`)?a:`#${a}`}26`}:void 0,d=e?`bg-indigo-500/20 text-indigo-700 dark:text-indigo-300`:`bg-purple-500/20 text-purple-700 dark:text-purple-300`,f=n&&r?`/api/addon-assets/${n}/${r}`:null,p=!!f&&!o;return i(`div`,{className:`relative flex-shrink-0`,children:[l(`div`,{className:`h-9 w-9 rounded-lg flex items-center justify-center ${u?``:d}`,style:u,children:p?l(`img`,{src:f,alt:n,className:`h-5 w-5`,onError:()=>s(!0)}):l(e?_e:I,{className:`h-4 w-4`})}),t===`local`&&l(`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`&&l(`span`,{className:`absolute -top-1 -left-1 w-4 h-4 rounded bg-[#cb3837] flex items-center justify-center -rotate-12 shadow-sm`,children:l(`svg`,{viewBox:`0 0 780 250`,className:`w-[11px] h-[11px]`,children:l(`path`,{fill:`#fff`,d:`M240 250h100V50h100v200h340V0H0v250z`})})}),t===`upload`&&l(`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:l(G,{className:`h-2.5 w-2.5 text-white`})})]})}function Te({version:e,availableUpdate:t,selectedVersion:n,isUpdating:r,installSource:a,disabled:o,onUpdate:s,onOpenPicker:c,children:u}){if(r)return i(`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:[l(M,{className:`h-3 w-3 animate-spin`}),`Updating…`]});if(t){let r=n??t;return i(`div`,{className:`relative inline-flex items-center gap-1 shrink-0`,children:[s&&i(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),s()},disabled:o,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:[l(A,{className:`h-3 w-3`}),`Update`]}),i(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),c()},disabled:o,"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:[l(`span`,{className:`font-normal opacity-70`,children:e}),l(`span`,{className:`font-normal opacity-70`,children:`→`}),r,l(F,{className:`h-3 w-3`})]}),u]})}return i(`div`,{className:`relative inline-flex items-center gap-1 shrink-0`,children:[i(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),c()},disabled:o,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`,a===`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,l(F,{className:`h-3 w-3 opacity-50`})]}),u]})}function Ee({addonId:e}){let t=s(),{data:n=`inherit`}=D({addonId:e}),r=_({onSuccess:()=>{t.invalidateQueries({queryKey:[[`addons`,`getAddonAutoUpdate`]]})}});return l(`div`,{className:`flex rounded-lg border border-border overflow-hidden text-[10px] font-medium`,children:[`inherit`,`off`,`latest`,`beta`].map(t=>l(`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 De({addon:e,agents:t=[],clusterStatus:n,hideVersion:r,availableUpdate:a,onUpdate:f,isUpdating:p,jobProgress:h,bundle:g,bulkItem:_,disabledByBulk:v,isSystem:y}){let[x,S]=c(!1),[te,ne]=c(!1),[C,w]=c(!1),[ie,oe]=c(null);u(()=>{oe(null)},[a]);let T=ie??a,[E,D]=c(!1),[le,O]=c(!1),[k,A]=c(`logs`),M=s(),P=d(),fe=N(),pe=se(),{manifest:I}=e,R=e.installedOn??[],B=!g&&!I.protected&&I.removable!==!1,V=!!g,W=g?.displayName??I.name,G=g?.packageName??I.packageName,K=g?.version??I.packageVersion??I.version,q=g?.installSource??e.installSource,ve=g?g.bundle?.description??g.addons[0]?.manifest.description:I.description,J=ue({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]}),M.invalidateQueries({queryKey:[[`addonPages`]]}),M.invalidateQueries({queryKey:[[`capabilities`]]})}}),ye=ae({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]})}}),be=re({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]}),M.invalidateQueries({queryKey:[[`addonPages`]]}),M.invalidateQueries({queryKey:[[`capabilities`]]}),M.invalidateQueries({queryKey:[[`integrations`]]})}}),Y=b({onSuccess:()=>{M.invalidateQueries({queryKey:[[`addons`]]}),M.invalidateQueries({queryKey:[[`alerts`]]})}}),X=e.health,Q=X?.phase===`failed`&&!X.inGracePeriod,Oe=Q&&X?.nextRetryAt&&!X.retrying;return i(`div`,{className:`rounded-lg border bg-surface overflow-hidden ${Q?`border-red-500/50`:`border-border`}`,children:[Q&&X&&i(`div`,{className:`bg-danger/10 border-b border-danger/30 px-4 py-2.5 flex items-start gap-2.5`,children:[l(U,{className:`h-4 w-4 text-danger shrink-0 mt-0.5`}),i(`div`,{className:`flex-1 min-w-0`,children:[i(`div`,{className:`text-xs font-semibold text-danger`,children:[`Failed to load (attempt #`,X.retryCount,`)`]}),l(`div`,{className:`text-[11px] text-foreground mt-0.5 break-words font-mono`,children:X.lastError?.message??`Unknown error`}),Oe&&X.nextRetryAt&&i(`div`,{className:`text-[10px] text-foreground-subtle mt-1`,children:[`Auto-retry at `,new Date(X.nextRetryAt).toLocaleTimeString()]})]}),i(`button`,{type:`button`,disabled:Y.isPending||X.retrying,onClick:e=>{e.stopPropagation(),Y.mutate({packageName:G})},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:[l(j,{className:`h-3 w-3 ${Y.isPending||X.retrying?`animate-spin`:``}`}),`Retry now`]})]}),i(`div`,{className:`flex items-start gap-3 px-4 py-3`,children:[l(we,{isBundle:V||!!I.components&&I.components.length>1,installSource:q,addonId:V?g.addons[0]?.manifest.id:I.id,iconPath:V?g.bundle?.icon??g.addons[0]?.manifest.icon:I.icon,color:V?g.addons[0]?.manifest.color:I.color}),i(`div`,{className:`flex-1 min-w-0`,children:[i(`div`,{className:`flex items-start justify-between gap-2`,children:[l(`span`,{className:`text-sm font-semibold text-foreground truncate min-w-0`,children:W}),!r&&l(Te,{version:K,availableUpdate:a,...T===void 0?{}:{selectedVersion:T},isUpdating:p,installSource:q,disabled:v,...f&&T!==void 0?{onUpdate:()=>f(T)}:{},onOpenPicker:()=>w(e=>!e),children:C&&l(Se,{packageName:G,currentVersion:K,...q?{installSource:q}:{},...a!==void 0&&T!==void 0?{selectedVersion:T,onSelect:e=>{oe(e),w(!1)}}:{},onInstall:e=>{w(!1),be.mutate({packageName:G,version:e})},onClose:()=>w(!1)})})]}),i(`div`,{className:`flex flex-wrap items-center gap-x-1.5 gap-y-1 mt-0.5`,children:[G!==W&&l(`span`,{className:`text-[10px] text-foreground-subtle font-mono truncate max-w-full`,children:G}),V&&i(`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:[l(_e,{className:`h-3 w-3`}),g.addons.length]}),y&&i(`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:[l(j,{className:`h-3 w-3`}),`System`]}),_!=null&&l(Ce,{item:_}),h!=null&&l(xe,{progress:h})]}),ve&&l(`p`,{className:`text-[10px] text-foreground-subtle mt-1 line-clamp-1`,children:ve}),!V&&e.process&&i(`div`,{className:`flex flex-wrap items-center gap-1.5 mt-1`,children:[l(`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&&i(`span`,{className:`text-[9px] font-mono text-foreground-subtle`,children:[`PID `,e.process.pid]}),l(`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})]})]})]}),!V&&i(`div`,{className:`flex items-center gap-1 px-4 py-1.5 border-t border-border/50 bg-surface-hover/20`,children:[i(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),ye.mutate({addonId:I.id})},disabled:ye.isPending||v,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:[l(j,{className:`h-3 w-3 ${ye.isPending?`animate-spin`:``}`}),`Restart`]}),i(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),D(e=>!e)},className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border transition-colors ${E?`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:[l(he,{className:`h-3 w-3`}),`Logs`]}),!V&&i(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),O(!0)},className:`inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium border transition-colors ${le?`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:[l(me,{className:`h-3 w-3`}),`Settings`]}),l(`div`,{className:`flex-1`}),B&&i(`button`,{type:`button`,onClick:async e=>{e.stopPropagation(),await pe({title:`Uninstall ${I.name}?`,message:`This will remove the addon package and all its data. This action cannot be undone.`,confirmLabel:`Uninstall`,variant:`danger`})&&J.mutate({packageName:I.packageName})},disabled:J.isPending||v,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:[l(L,{className:`h-3 w-3`}),J.isPending?`Removing...`:`Uninstall`]})]}),n&&n.nodes.length>1&&!V&&i(`div`,{className:`border-t border-border px-4 py-2 bg-muted/10`,children:[l(`div`,{className:`text-[10px] font-medium text-foreground-subtle mb-1.5`,children:`Cluster Deployment`}),l(`div`,{className:`space-y-1`,children:n.nodes.map(e=>i(`div`,{className:`flex items-center gap-2 text-[10px]`,children:[l(`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`}`}),l(`span`,{className:`font-medium text-foreground min-w-[60px]`,children:e.name}),l(`span`,{className:`font-mono text-foreground-subtle`,children:e.version}),!e.synced&&e.status!==`offline`&&l(`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`&&l(`span`,{className:`text-[9px] px-1 py-0.5 rounded bg-muted text-muted-foreground`,children:`offline`}),e.synced&&e.status!==`offline`&&l(`span`,{className:`text-[9px] text-success`,children:`synced`})]},e.nodeId))})]}),E&&!V&&i(`div`,{className:`border-t border-border`,children:[i(`div`,{className:`flex items-center px-4 py-1 bg-surface-hover/20 border-b border-border/50`,children:[i(`div`,{className:`flex rounded border border-border overflow-hidden text-[9px]`,children:[l(`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`}),l(`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`})]}),l(`div`,{className:`flex-1`}),l(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),D(!1)},className:`text-foreground-subtle hover:text-foreground`,children:l(z,{className:`h-3.5 w-3.5`})})]}),k===`logs`&&l(ee,{addonId:I.id,maxHeight:`max-h-64`,showFilters:!1}),k===`events`&&l(ce,{addonId:I.id,category:`addon.*`,maxHeight:`max-h-64`})]}),V&&i(o,{children:[l(`button`,{type:`button`,onClick:()=>S(e=>!e),className:`w-full flex items-center justify-center border-t border-border py-1 hover:bg-surface-hover/50 transition-colors`,children:l(x?ge:F,{className:`h-3.5 w-3.5 text-foreground-subtle`})}),x&&l(`div`,{className:`border-t border-border px-3 py-2 space-y-2`,children:g.addons.map(e=>l(De,{addon:e,agents:t,hideVersion:!0},e.manifest.id))})]}),le&&!V&&l(`div`,{className:`fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4`,onClick:()=>O(!1),children:i(`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:[i(`div`,{className:`flex items-center justify-between px-5 py-3 border-b border-border flex-shrink-0`,children:[i(`div`,{className:`flex items-center gap-2`,children:[l(me,{className:`h-4 w-4 text-primary`}),i(`h2`,{className:`text-sm font-semibold text-foreground`,children:[W,` Settings`]})]}),l(`button`,{onClick:()=>O(!1),className:`text-foreground-subtle hover:text-foreground transition-colors p-1 rounded`,children:l(z,{className:`h-4 w-4`})})]}),i(`div`,{className:`flex-1 overflow-y-auto px-5 py-4 space-y-4`,children:[l(`div`,{className:`text-[10px] text-foreground-subtle mb-2`,children:`Cluster-wide settings — one value for every node. Per-node settings live under Cluster → the node → Settings.`}),l(H,{nodeId:null,addonIds:[I.id],level:`global`,fieldScope:`cluster`,emptyMessage:`This addon exposes no cluster-wide settings. Anything it can configure is per-node — open it from Cluster → the node → Settings.`}),Z(I)&&i(`div`,{children:[l(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:`Device export`}),l(m,{addonId:I.id,trpc:P.trpcClient,onOpenDevice:e=>fe(`/devices/${e}`)})]}),q===`npm`&&i(`div`,{children:[l(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:`Auto-Update`}),l(Ee,{addonId:I.id})]}),I.components&&I.components.length>0&&i(`div`,{children:[i(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:[`Components (`,I.components.length,`)`]}),l(`div`,{className:`flex flex-wrap gap-1`,children:I.components.map(e=>l(`span`,{className:`text-[10px] rounded bg-background border border-border px-1.5 py-0.5 text-foreground-subtle font-mono`,children:e},e))})]}),t.length>=2&&i(`div`,{children:[l(`div`,{className:`text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-2`,children:`Installed on`}),i(`div`,{className:`flex items-center gap-1.5 flex-wrap`,children:[R.length===0&&l(`span`,{className:`text-[10px] text-foreground-subtle`,children:`Hub (local)`}),R.map(e=>l(`span`,{className:`rounded-full bg-surface-hover border border-border px-2 py-0.5 text-[10px] text-foreground-subtle`,children:e},e)),i(`div`,{className:`relative`,children:[i(`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:[l(de,{className:`h-3 w-3`}),`Add agent`]}),te&&i(`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:[t.filter(e=>!e.isHub&&!R.includes(e.name)).map(e=>l(`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)),t.filter(e=>!e.isHub&&!R.includes(e.name)).length===0&&l(`div`,{className:`px-3 py-1.5 text-[10px] text-foreground-subtle`,children:`No available agents`})]})]})]})]})]}),l(`div`,{className:`flex items-center justify-end px-5 py-3 border-t border-border flex-shrink-0`,children:l(`button`,{type:`button`,onClick:()=>O(!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`})})]})})]})}n();var Q=`.tgz,.tar.gz,.zip`;function Oe({onUploadSuccess:n}){let[r,a]=c(!1),s=t(null),u=e(async e=>{a(!0);let t=new FormData;t.append(`file`,e);let r=localStorage.getItem(`camstack_admin_token`)??``;try{(await(await fetch(`/api/addons/upload`,{method:`POST`,headers:{Authorization:`Bearer ${r}`},body:t})).json()).success&&n()}finally{a(!1)}},[n]);return i(o,{children:[l(`input`,{ref:s,type:`file`,accept:Q,onChange:e(e=>{let t=e.target.files?.[0];t&&u(t),e.target.value=``},[u]),className:`hidden`}),i(`button`,{type:`button`,onClick:()=>s.current?.click(),disabled:r,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:[r?l(M,{className:`w-3.5 h-3.5 animate-spin`}):l(G,{className:`w-3.5 h-3.5`}),r?`Uploading...`:`Upload`]})]})}n();var ke=[{seconds:900,label:`15m`},{seconds:3600,label:`1h`},{seconds:21600,label:`6h`},{seconds:43200,label:`12h`},{seconds:86400,label:`24h`}],Ae=new Set([`@camstack/system`,`@camstack/types`,`@camstack/sdk`,`@camstack/shm-ring`,`@camstack/ui-library`]);function je(e){return e!==void 0&&Ae.has(e)}function Me(e,t){let n=e??`hub-only`;return n===`any-node`?!0:n===`hub-only`?t:!t}function Ne(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:n,clusterStatus:o,selectedNode:d,onRefresh:f,isRefreshing:m}){let g=s(),[_,v]=c(``),ee=r(_),[b,x]=c(!1),C=t(null);u(()=>{if(!b)return;let e=e=>{C.current&&!C.current.contains(e.target)&&x(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[b]);let{data:w}=oe({nodeId:d.id}),ie=a(()=>{let e=new Map;for(let t of w??[])e.set(t.name,t.latestVersion);return e},[w]),ae=a(()=>(w??[]).filter(e=>!e.isSystem).length,[w]),[T,ce]=c(()=>new Set),[E,D]=c(()=>new Map),ue=e(e=>{ce(t=>new Set(t).add(e))},[]),O=e(e=>{ce(t=>{if(!t.has(e))return t;let n=new Set(t);return n.delete(e),n})},[]),k=e((e,t)=>{D(n=>new Map(n).set(e,t))},[]),de=e(e=>{D(t=>{if(!t.has(e))return t;let n=new Map(t);return n.delete(e),n})},[]),N=p({onMutate:e=>{ue(e.name),de(e.name)},onSuccess:(e,t)=>{O(t.name);let n=e;if(n&&n.success===!1){k(t.name,n.error??`Update failed (no error message returned by server)`);return}g.invalidateQueries({queryKey:[[`addons`]]}),g.invalidateQueries({queryKey:[[`addons`,`listUpdates`]]})},onError:(e,t)=>{O(t.name),k(t.name,e instanceof Error?e.message:String(e))}}),P=ye(),F=t(new Set);u(()=>{let e=new Set;for(let[t,n]of P)ve(n)&&e.add(t);let t=[...F.current].some(t=>!e.has(t));F.current=e,t&&(g.invalidateQueries({queryKey:[[`addons`]]}),g.invalidateQueries({queryKey:[[`addons`,`listUpdates`]]}))},[P,g]);let{data:I}=ne(void 0),me=le({onSuccess:()=>{g.invalidateQueries({queryKey:[[`addons`,`getAutoUpdateSettings`]]})}}),he=y({onSuccess:()=>{g.invalidateQueries({queryKey:[[`addons`,`getAutoUpdateSettings`]]})}}),{data:L,isFetching:z}=S({query:ee||void 0},{enabled:b}),V=te(),{data:H}=h(void 0),ge=H?.requestedAt,[U,_e]=c(null),[W,G]=c(!1);u(()=>{if(H?.kind===`framework-bulk-update`&&H.requestedAt!==U){_e(H.requestedAt),G(!0);let e=window.setTimeout(()=>G(!1),6e3);return()=>window.clearTimeout(e)}},[ge,U,H]),se();let K=e(async()=>{let e=(w??[]).filter(e=>!e.isSystem);if(e.length!==0){if(!d.isHub){for(let t of e)await N.mutateAsync({name:t.name,version:t.latestVersion,nodeId:d.id});return}await V.mutateAsync({kind:`update`,targets:e.map(e=>({name:e.name,version:e.latestVersion})),nodeIds:[d.id]})}},[w,d.id,d.isHub,V,N]),q=re({onSuccess:()=>{g.invalidateQueries({queryKey:[[`addons`]]}),g.invalidateQueries({queryKey:[[`addonPages`]]}),g.invalidateQueries({queryKey:[[`capabilities`]]}),g.invalidateQueries({queryKey:[[`integrations`]]})}}),[J,be]=c(`type`),[xe,Se]=c(()=>typeof window>`u`?`grouped`:window.localStorage.getItem(`camstack.addons.viewMode`)===`flat`?`flat`:`grouped`),Y=e=>{Se(e),typeof window<`u`&&window.localStorage.setItem(`camstack.addons.viewMode`,e)},[Ce,X]=pe(),Z=Ce.get(`capability`),we=a(()=>{let e=n.filter(e=>!je(e.manifest.packageName));return d.isHub||(e=e.filter(e=>Me(e.placement,d.isHub))),Z&&(e=e.filter(e=>(e.manifest.capabilities??[]).some(e=>(typeof e==`string`?e:e.name)===Z))),e},[n,d.isHub,Z]),Te=()=>{let e=new URLSearchParams(Ce);e.delete(`capability`),X(e,{replace:!0})},Ee=xe===`flat`?we:Pe(we),Q=a(()=>{let e=[...Ee];switch(J){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},[Ee,J]);return i(`div`,{className:`space-y-4`,children:[[...E.entries()].map(([e,t])=>l(`div`,{className:`rounded-md border border-danger/30 bg-danger/5 px-3 py-2 text-[11px]`,children:i(`div`,{className:`flex items-start justify-between gap-3`,children:[i(`div`,{className:`space-y-0.5`,children:[i(`div`,{className:`font-medium text-danger`,children:[`Failed to update `,e]}),l(`div`,{className:`text-foreground-subtle font-mono`,children:t})]}),l(`button`,{type:`button`,onClick:()=>de(e),className:`text-foreground-subtle hover:text-foreground text-[10px]`,children:`dismiss`})]})},e)),i(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[i(`div`,{className:`flex items-center gap-1.5`,children:[l(`span`,{className:`text-[10px] text-foreground-subtle`,children:`Sort:`}),i(`select`,{value:J,onChange:e=>be(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:[l(`option`,{value:`type`,children:`Bundles first`}),l(`option`,{value:`name`,children:`Name`}),l(`option`,{value:`source`,children:`Install source`})]})]}),i(`div`,{className:`flex items-center gap-1.5`,children:[l(`span`,{className:`text-[10px] text-foreground-subtle`,children:`View:`}),l(`div`,{className:`flex rounded-md border border-border overflow-hidden text-[10px] font-medium`,children:[`grouped`,`flat`].map(e=>l(`button`,{type:`button`,onClick:()=>Y(e),className:`px-2.5 py-1.5 transition-colors capitalize ${xe===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))})]}),d.isHub&&i(`div`,{className:`flex items-center gap-1.5`,children:[l(j,{className:`h-3 w-3 text-foreground-subtle`}),l(`span`,{className:`text-[10px] text-foreground-subtle`,children:`Auto-Update:`}),l(`div`,{className:`flex rounded-md border border-border overflow-hidden text-[10px] font-medium`,children:[`off`,`latest`,`beta`].map(e=>l(`button`,{type:`button`,onClick:()=>me.mutate({channel:e}),disabled:me.isPending,className:`px-2.5 py-1.5 transition-colors capitalize ${I?.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))})]}),d.isHub&&i(`div`,{className:`flex items-center gap-1.5`,children:[l(`span`,{className:`text-[10px] text-foreground-subtle`,children:`Check every:`}),l(`select`,{className:`rounded-md border border-border bg-transparent px-1.5 py-1 text-[10px] font-medium text-foreground-subtle`,value:I?.updateCheckIntervalSeconds??21600,disabled:he.isPending||I===void 0,onChange:e=>{I!==void 0&&he.mutate({channel:I.channel,updateCheckIntervalSeconds:Number(e.target.value)})},children:ke.map(e=>l(`option`,{value:e.seconds,children:e.label},e.seconds))})]}),ae>0&&i(`button`,{type:`button`,onClick:()=>void K(),disabled:V.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:[V.isPending?l(M,{className:`w-3 h-3 animate-spin`}):l(A,{className:`w-3 h-3`}),V.isPending?`Updating…`:`Update All (${ae})`]}),i(`button`,{type:`button`,onClick:f,disabled:m||V.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:[l(j,{className:`w-3 h-3 ${m?`animate-spin`:``}`}),`Refresh`]})]}),i(`div`,{className:`flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3`,children:[i(`div`,{className:`relative flex-1 min-w-0 sm:min-w-[200px]`,ref:C,children:[l(R,{className:`absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-foreground-subtle`}),l(`input`,{type:`text`,value:_,onChange:e=>{v(e.target.value),x(!0)},onFocus:()=>x(!0),placeholder:`Search addons on npm...`,disabled:V.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`}),z&&l(M,{className:`absolute right-2.5 top-1/2 -translate-y-1/2 h-3 w-3 text-foreground-subtle animate-spin`}),b&&L&&L.length>0&&i(`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:[L.length>0&&i(`div`,{className:`px-3 py-1.5 text-[10px] uppercase tracking-wide text-foreground-subtle border-b border-border`,children:[`npm results (`,L.length,`)`]}),L&&L.length>0&&l(`div`,{className:`max-h-72 overflow-auto divide-y divide-border`,children:L.map(e=>i(`div`,{className:`flex items-center gap-3 px-3 py-2 hover:bg-surface-hover transition-colors`,children:[l(`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()}),i(`div`,{className:`flex-1 min-w-0`,children:[i(`div`,{className:`flex items-baseline gap-2 min-w-0`,children:[l(`span`,{className:`text-[11px] font-semibold break-words`,children:e.name.replace(/^@camstack\/(addon-)?/,``)}),i(`span`,{className:`text-[10px] text-foreground-subtle shrink-0`,children:[`v`,e.version]})]}),l(`div`,{className:`font-mono text-[9px] text-foreground-subtle/80 truncate`,children:e.name}),e.description&&l(`div`,{className:`text-[10px] text-foreground-subtle truncate`,children:e.description})]}),e.installed?i(`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:[l(B,{className:`h-3 w-3`}),e.installedVersion??`Installed`]}):i(`button`,{type:`button`,onClick:()=>q.mutate({packageName:e.name}),disabled:q.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:[q.isPending?l(M,{className:`h-3 w-3 animate-spin`}):l(A,{className:`h-3 w-3`}),`Install`]})]},e.name))})]})]}),l(Oe,{onUploadSuccess:()=>{g.invalidateQueries({queryKey:[[`addons`]]}),f()}})]}),Z&&i(`div`,{className:`flex items-center gap-2 px-3 py-2 rounded-lg bg-primary/10 border border-primary/30 text-sm`,children:[l(`span`,{className:`text-foreground-subtle`,children:`Filtering by capability:`}),l(`span`,{className:`font-mono text-primary font-semibold`,children:Z}),l(`button`,{type:`button`,onClick:Te,className:`ml-auto text-xs text-foreground-subtle hover:text-foreground underline`,children:`Clear`})]}),W&&i(`div`,{className:`fixed top-[calc(env(safe-area-inset-top)+1rem)] right-[calc(env(safe-area-inset-right)+1rem)] 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:[l(fe,{className:`w-4 h-4 shrink-0 mt-0.5 text-green-400`}),i(`div`,{children:[l(`div`,{className:`text-[11px] font-semibold text-foreground`,children:`Bulk update completed`}),l(`div`,{className:`text-[10px] text-foreground-subtle mt-0.5`,children:`System packages updated — hub restarted successfully.`})]})]}),we.length===0&&l(`div`,{className:`text-xs text-foreground-subtle`,children:n.length===0?`No addons installed`:Z?`No installed addons declare the "${Z}" capability. Browse the npm catalog above to find a compatible addon.`:`No addons compatible with ${d.name} (placement filter active)`}),l(`div`,{className:`space-y-2`,children:Q.map(e=>{let t=$(e)?e.bundle.packageName:e.manifest.packageName,n=ie.get(t),r=$(e)?e.representative:e,i=n?e=>N.mutate({name:t,version:e,nodeId:d.id}):void 0,a=P.get(t)??null,s=a!==null&&ve(a);return l(De,{addon:r,clusterStatus:o?.[r.manifest.id],bundle:$(e)?e.bundle:void 0,availableUpdate:n,isUpdating:T.has(t)||s,jobProgress:a,onUpdate:i,disabledByBulk:V.isPending},$(e)?t:e.manifest.id)})})]})}function Ie(){let e=s(),{data:n,isLoading:r,isError:o}=g(),{data:d}=C(void 0),{data:p}=T(void 0,{refetchInterval:1e4}),m=a(()=>{let e=new Map;e.set(`hub`,{id:`hub`,name:`Hub`,isHub:!0});for(let t of p??[]){let n=String(t.id??``);n&&e.set(n,{id:n,name:String(t.name??n),isHub:!!t.isHub})}return[...e.values()]},[p]),[h,_]=c(`hub`),v=m.find(e=>e.id===h)??m[0],y=w(),ee=f(),b=y.isPending||ee.isPending,x=async()=>{await y.mutateAsync({nodeId:h}),await ee.mutateAsync(),e.invalidateQueries({queryKey:[[`addons`]]})},S=t(y);S.current=y,u(()=>{S.current.mutate({nodeId:`hub`})},[]);let te=(n??[]).map(Ne).filter(e=>e!==null);return i(V,{children:[i(`div`,{className:`flex items-center justify-between gap-3 flex-wrap`,children:[l(ie,{selectedNodeId:h,onSelect:_}),i(`div`,{className:`flex items-center gap-3 flex-wrap`,children:[!r&&!o&&i(`span`,{className:`text-[10px] rounded-full bg-primary/10 text-primary px-2 py-0.5 font-medium`,children:[te.length,` installed`]}),i(`button`,{type:`button`,onClick:()=>void x(),disabled:b,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:[l(j,{className:`w-3 h-3 ${b?`animate-spin`:``}`}),`Check for updates`]})]})]}),!v.isHub&&i(`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`,` `,l(`span`,{className:`font-medium text-foreground`,children:v.name}),` — agents only run`,` `,l(`code`,{className:`font-mono`,children:`agent-only`}),` and`,` `,l(`code`,{className:`font-mono`,children:`any-node`}),` addons. Install / uninstall is hub-driven; agents auto-sync from the hub.`]}),r&&l(`div`,{className:`text-xs text-foreground-subtle animate-pulse`,children:`Loading…`}),o&&l(`div`,{className:`text-xs text-danger`,children:`Failed to load addons`}),!r&&!o&&l(Fe,{addons:te,clusterStatus:d,selectedNode:v,onRefresh:()=>void x(),isRefreshing:b})]})}export{Ie as AddonsPage};
@@ -1,11 +0,0 @@
1
- import{I as e,K as t,Q as n,S as r,V as i,W as a,b as o,o as s,q as c,x as l,z as u}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{Z as d}from"./player-overlays-wniETo4u.js";import{$i as f,Ct as p,Fo as m,Gi as h,Io as g,Is as _,J as v,Ki as y,Lo as b,Po as x,Ps as S,Qi as C,Ra as w,St as ee,Xo as T,bt as te,ea as ne,et as E,ln as D,na as re,q as ie,ro as ae,ta as oe,tt as O,xt as se}from"./src-IW6snE8b.js";import{Et as ce}from"./src-CsxD-27Y.js";import{t as le}from"./pencil-D-rxGNNf.js";import{t as ue}from"./radio-dB0UNm47.js";import{t as de}from"./refresh-cw-srKCTv_-.js";import{t as k}from"./rotate-cw-DKLLB8dt.js";import{t as fe}from"./server-BQtqKhWd.js";import{C as A,H as pe,I as me,K as j,N as he,R as ge,a as M,m as _e,o as ve,p as ye,r as N,t as P,z as be}from"./index-Cp84SBzW.js";import{t as xe}from"./NodeAddonsSettingsPanel-CAyKEQpD.js";import{t as F}from"./format-size-DIxKXxtj.js";var I=j(`arrow-down-to-line`,[[`path`,{d:`M12 17V3`,key:`1cwfxf`}],[`path`,{d:`m6 11 6 6 6-6`,key:`12ii2o`}],[`path`,{d:`M19 21H5`,key:`150jfl`}]]),Se=j(`hard-drive-download`,[[`path`,{d:`M12 2v8`,key:`1q4o3n`}],[`path`,{d:`m16 6-4 4-4-4`,key:`6wukr`}],[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,key:`w68u3i`}],[`path`,{d:`M6 18h.01`,key:`uhywen`}],[`path`,{d:`M10 18h.01`,key:`h775k`}]]),Ce=j(`memory-stick`,[[`path`,{d:`M6 19v-3`,key:`1nvgqn`}],[`path`,{d:`M10 19v-3`,key:`iu8nkm`}],[`path`,{d:`M14 19v-3`,key:`kcehxu`}],[`path`,{d:`M18 19v-3`,key:`1vh91z`}],[`path`,{d:`M8 11V9`,key:`63erz4`}],[`path`,{d:`M16 11V9`,key:`fru6f3`}],[`path`,{d:`M12 11V9`,key:`ha00sb`}],[`path`,{d:`M2 15h20`,key:`16ne18`}],[`path`,{d:`M2 7a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v1.1a2 2 0 0 0 0 3.837V17a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-5.1a2 2 0 0 0 0-3.837Z`,key:`lhddv3`}]]);n();function we(e){if(e<60)return`${e}s`;let t=Math.floor(e/60);if(t<60)return`${t}m`;let n=Math.floor(t/60);return n<24?`${n}h ${t%60}m`:`${Math.floor(n/24)}d ${n%24}h`}function Te({state:e}){return l(`span`,{className:`text-[10px] px-1.5 py-0.5 rounded-full ${e===`running`?`bg-success/10 text-success`:e===`crashed`||e===`failed`?`bg-destructive/10 text-destructive`:`bg-muted text-muted-foreground`}`,children:e})}var Ee=[`trace`,`debug`,`info`,`warn`,`error`];function De({proc:e,nodeId:t,defaultExpanded:n,activePanel:i,onTogglePanel:a}){let o=e.services??[],s=o.length===1,[u,d]=c(s||n),[m,h]=c(null),[g,_]=c(`info`),y=e.name.includes(`(core)`),b=f(),x=ne(),S=oe(),C=async e=>{h(e);try{await b.mutateAsync({nodeId:t,addonId:e})}catch(e){console.error(`Failed to restart addon:`,e)}finally{h(null)}},w=async()=>{h(e.name);try{await x.mutateAsync({nodeId:t,processName:e.name})}catch(e){console.error(`Failed to restart process:`,e)}finally{h(null)}},T=async n=>{let r=`${t}/${e.name}`;try{await S.mutateAsync({nodeId:r,level:n}),_(n)}catch(e){console.error(`Failed to set log level:`,e)}};return r(`div`,{className:`border border-border/50 rounded-lg overflow-hidden`,children:[r(`div`,{role:s?void 0:`button`,tabIndex:s?void 0:0,onClick:s?void 0:()=>d(!u),onKeyDown:s?void 0:e=>{(e.key===`Enter`||e.key===` `)&&d(!u)},className:`flex items-center gap-2 w-full px-3 py-2 bg-muted/20 transition-colors text-left select-none ${s?``:`hover:bg-muted/40 cursor-pointer`}`,children:[s?l(`span`,{className:`h-3 w-3 shrink-0`}):l(u?ge:me,{className:`h-3 w-3 text-muted-foreground shrink-0`}),y?l(fe,{className:`h-3.5 w-3.5 text-primary shrink-0`}):l(pe,{className:`h-3.5 w-3.5 text-amber-500 shrink-0`}),l(`span`,{className:`text-xs font-semibold flex-1`,children:e.name}),r(`span`,{className:`text-[10px] text-muted-foreground tabular-nums`,children:[`PID `,e.pid]}),l(Te,{state:e.state}),r(`span`,{className:`text-[10px] text-muted-foreground tabular-nums`,children:[e.cpuPercent,`% CPU`]}),l(`span`,{className:`text-[10px] text-muted-foreground tabular-nums`,children:F(e.memoryRss)}),l(`span`,{className:`text-[10px] text-muted-foreground tabular-nums`,children:we(e.uptimeSeconds)}),!y&&r(`div`,{className:`flex items-center gap-1`,onClick:e=>e.stopPropagation(),children:[r(te,{children:[l(p,{className:`text-[9px] px-1.5 py-0.5 rounded border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors font-mono`,title:`Set log level`,children:g}),l(se,{align:`end`,className:`min-w-[70px] py-0.5`,children:Ee.map(e=>l(ee,{onClick:()=>T(e),className:`h-auto px-2 py-1 text-[10px] font-mono ${e===g?`text-primary font-semibold`:``}`,children:e},e))})]}),l(`button`,{onClick:w,disabled:m===e.name,className:`p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50`,title:`Restart process`,children:l(k,{className:`h-3 w-3 ${m===e.name?`animate-spin`:``}`})})]})]}),u&&o.length>0&&l(`div`,{className:`divide-y divide-border/30`,children:o.map(e=>r(`div`,{children:[r(`div`,{className:`flex items-center gap-2 px-3 py-1.5 pl-9 hover:bg-muted/10 transition-colors`,children:[l(`div`,{className:`h-1.5 w-1.5 rounded-full shrink-0 ${e.status===`running`?`bg-success`:`bg-muted-foreground/30`}`}),l(`span`,{className:`text-xs font-medium flex-1 min-w-0 truncate`,children:e.addonId}),e.capabilities.length>0&&r(`div`,{className:`flex gap-1 flex-shrink-0`,children:[e.capabilities.slice(0,3).map(e=>l(`span`,{className:`text-[9px] px-1 py-0.5 rounded bg-blue-500/10 text-blue-400 truncate max-w-[80px]`,children:e},e)),e.capabilities.length>3&&r(`span`,{className:`text-[9px] text-muted-foreground`,children:[`+`,e.capabilities.length-3]})]}),r(`div`,{className:`flex items-center gap-1 shrink-0`,children:[l(`button`,{onClick:()=>a(e.addonId,`logs`),className:`p-0.5 rounded transition-colors ${i?.addonId===e.addonId&&i.type===`logs`?`bg-primary/10 text-primary`:`hover:bg-muted text-muted-foreground hover:text-foreground`}`,title:`Show logs for ${e.addonId}`,children:l(_e,{className:`h-3 w-3`})}),l(`button`,{onClick:()=>a(e.addonId,`events`),className:`p-0.5 rounded transition-colors ${i?.addonId===e.addonId&&i.type===`events`?`bg-primary/10 text-primary`:`hover:bg-muted text-muted-foreground hover:text-foreground`}`,title:`Show events for ${e.addonId}`,children:l(ue,{className:`h-3 w-3`})}),l(`button`,{onClick:()=>C(e.addonId),disabled:m===e.addonId,className:`p-0.5 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50`,title:`Restart ${e.addonId}`,children:l(k,{className:`h-3 w-3 ${m===e.addonId?`animate-spin`:``}`})})]})]}),i?.addonId===e.addonId&&i.type===`logs`&&l(`div`,{className:`border-t border-border/40 bg-muted/5`,children:l(v,{addonId:e.addonId,agentId:t,maxHeight:`max-h-48`,showFilters:!1,onClose:()=>a(e.addonId,`logs`)})}),i?.addonId===e.addonId&&i.type===`events`&&l(`div`,{className:`border-t border-border/40 bg-muted/5`,children:l(ie,{addonId:e.addonId,category:`addon.*`,maxHeight:`max-h-48`,onClose:()=>a(e.addonId,`events`)})})]},e.addonId))}),u&&o.length===0&&l(`div`,{className:`px-3 py-2 pl-9 text-[10px] text-muted-foreground`,children:`No services`})]})}function Oe({nodeId:e,processes:t}){let[n,r]=c(null),i=(e,t)=>{r(n=>n?.addonId===e&&n.type===t?null:{addonId:e,type:t})};return t.length===0?l(`div`,{className:`py-4 text-xs text-muted-foreground text-center`,children:`No processes on this node`}):l(`div`,{className:`space-y-2 py-1`,children:t.map((t,r)=>l(De,{proc:t,nodeId:e,defaultExpanded:r===0,activePanel:n,onTogglePanel:i},t.name))})}var ke={system:0,managed:1,root:2};n();function Ae(e){return e<60?`${e}s`:e<3600?`${Math.floor(e/60)}m ${e%60}s`:e<86400?`${Math.floor(e/3600)}h ${Math.floor(e%3600/60)}m`:`${Math.floor(e/86400)}d ${Math.floor(e%86400/3600)}h`}function je(e,t=80){return e.length<=t?e:`…${e.slice(e.length-t+1)}`}function Me({nodeId:e}){let t=s(),n=S(`metrics.node-processes-snapshot`,t=>t.nodeId===e),{data:i}=y({nodeId:e}),o=n?.processes??i,c=a(()=>o?[...o]:[],[o]),u=a(()=>[{key:`classification`,header:`Class`,sortValue:e=>ke[e.classification],render:e=>l(`span`,{className:`inline-block rounded-full px-1.5 py-0.5 text-[10px] font-medium ${e.classification===`root`?`bg-sky-500/20 text-sky-400`:e.classification===`system`?`bg-foreground-subtle/20 text-foreground-subtle`:`bg-success/20 text-success`}`,children:e.classification})},{key:`pid`,header:`PID`,align:`right`,sortValue:e=>e.pid,render:e=>l(`span`,{className:`font-mono`,children:e.pid})},{key:`ppid`,header:`PPID`,align:`right`,sortValue:e=>e.ppid,render:e=>l(`span`,{className:`font-mono text-foreground-subtle`,children:e.ppid})},{key:`addonId`,header:`Addon`,sortValue:e=>e.addonId,render:e=>l(`span`,{className:`text-foreground-subtle text-[11px]`,children:e.addonId??`—`})},{key:`command`,header:`Command`,nowrap:!1,render:e=>l(`span`,{className:`font-mono text-[10px] text-foreground-subtle break-all`,children:je(e.command)})},{key:`cpuPercent`,header:`CPU%`,align:`right`,sortValue:e=>e.cpuPercent,render:e=>e.cpuPercent.toFixed(1)},{key:`memoryRssBytes`,header:`RSS`,align:`right`,sortValue:e=>e.memoryRssBytes,render:e=>F(e.memoryRssBytes)},{key:`uptimeSec`,header:`Uptime`,align:`right`,sortValue:e=>e.uptimeSec,render:e=>l(`span`,{className:`text-foreground-subtle`,children:Ae(e.uptimeSec)})}],[]);return r(`div`,{className:`space-y-4`,children:[l(`div`,{className:`flex items-start justify-end gap-4`,children:r(`button`,{type:`button`,onClick:()=>t.invalidateQueries({queryKey:[[`metricsProvider`,`listNodeProcesses`]]}),className:`inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[11px] text-foreground hover:bg-surface-hover`,children:[l(de,{className:`h-3 w-3`}),` Refresh`]})}),l(E,{columns:u,rows:c,rowKey:e=>e.pid,minWidthPx:880,mobileMode:`scroll`,maxBodyHeight:O,emptyMessage:`Scanning…`})]})}var L={width:320,height:64},R=5,z=L.width+34,B=L.height+R+1,Ne=[1,2,5,10],Pe=[1,2,4,8,16,32,64,128,256,512,1024],V=[1,1.5,2,2.5,3,4,5],H=[4,3,5,2],U=1e-9;function Fe(e,t){if(!Number.isFinite(e)||e<=0)return!1;if(t===`bytes`){if(!Number.isInteger(e))return!1;let t=e/1024**Math.floor(Math.log(e)/Math.log(1024));return V.some(e=>Math.abs(t-e)<U)}let n=e/10**Math.floor(Math.log10(e));return V.some(e=>Math.abs(n-e)<U)}function W(e,t,n){let r=0;for(let t of e)t!==null&&t>r&&(r=t);if(r<=t)return t;if(n===`bytes`){let e=1024**Math.floor(Math.log(r)/Math.log(1024));for(let t of Pe){let n=t*e;if(n>=r)return n}return 1024*e}let i=10**Math.floor(Math.log10(r));for(let e of Ne){let t=e*i;if(t>=r)return t}return 10*i}function Ie(e,t){if(!Number.isFinite(e)||e<=0)return[];let n=H.find(n=>Fe(e/n,t))??H[H.length-1],r=e/n,i=[];for(let e=1;e<=n;e++)i.push(r*e);return i}var Le=2.5;function Re(e){return e*Le}function G(e,t,n,r){if(e.length===0)return[];let i=e[0].atMs,a=e.at(-1).atMs-i,o=n<=0?1:n,s=r!==void 0&&r>0?r:null;return e.map((r,c)=>{let l=a>0?(r.atMs-i)/a*t.width:c/Math.max(1,e.length-1)*t.width,u=c>0?e[c-1]:void 0;return{x:l,y:r.value===null?null:t.height-Math.min(r.value,n)/o*t.height,gapBefore:s!==null&&u!==void 0&&r.atMs-u.atMs>s}})}function ze(e){let t=[],n=!1;for(let r of e){if(r.y===null){n=!1;continue}r.gapBefore&&(n=!1),t.push(`${n?`L`:`M`}${r.x.toFixed(1)},${r.y.toFixed(1)}`),n=!0}return t.join(` `)}function Be(e,t){let n=[],r=[],i=()=>{if(r.length<2){r=[];return}let e=r.map((e,t)=>`${t===0?`M`:`L`}${e.x.toFixed(1)},${e.top.toFixed(1)}`),t=[...r].reverse().map(e=>`L${e.x.toFixed(1)},${e.bottom.toFixed(1)}`);n.push(`${e.join(` `)} ${t.join(` `)} Z`),r=[]};for(let n=0;n<e.length;n++){let a=e[n],o=t[n];if(!a||!o||a.y===null||o.y===null){i();continue}(a.gapBefore||o.gapBefore)&&i(),r.push({x:a.x,top:a.y,bottom:o.y})}return i(),n.join(` `)}function Ve(e){let t=[];for(let n=1;n<e.length;n++){let r=e[n];r.gapBefore&&t.push({fromX:e[n-1].x,toX:r.x})}return t}function K(e,t){if(e.length===0)return null;let n=0,r=1/0;for(let i=0;i<e.length;i++){let a=Math.abs(e[i].x-t);a<r&&(r=a,n=i)}return n}n();var q={main:`var(--load-series-main)`,gc:`var(--load-series-gc)`},He=`
2
- .load-chart-scope { --load-series-main: #2a78d6; --load-series-gc: #e87ba4; }
3
- @media (prefers-color-scheme: dark) {
4
- :root:not([data-theme="light"]) .load-chart-scope {
5
- --load-series-main: #3987e5; --load-series-gc: #d55181;
6
- }
7
- }
8
- :root[data-theme="dark"] .load-chart-scope {
9
- --load-series-main: #3987e5; --load-series-gc: #d55181;
10
- }
11
- `,J=L,Ue={height:B};function We({title:e,points:n,mainLabel:s,gcLabel:u,formatValue:d,emptyMessage:f,gapMs:p}){let m=i(),h=t(null),[g,_]=c(null),v=a(()=>{let e=n.map(e=>({atMs:e.atMs,value:e.main===null||e.gc===null?null:e.main+e.gc})),t=n.map(e=>({atMs:e.atMs,value:e.main})),r=W(e.map(e=>e.value),25,`linear`),i=G(e,J,r,p),a=G(t,J,r,p),o=a.map(e=>({x:e.x,y:e.y===null?null:J.height,gapBefore:e.gapBefore}));return{max:r,ticks:Ie(r,`linear`),upper:i,middle:a,base:o,gaps:Ve(i)}},[n,p]),y=n.at(-1),b=(g===null?void 0:n[g.index])??y;return n.length===0||v.upper.every(e=>e.y===null)?l(X,{title:e,children:l(Je,{message:f})}):l(X,{title:e,legend:r(o,{children:[l(qe,{color:q.main,label:s}),l(qe,{color:q.gc,label:u})]}),readout:b&&b.main!==null&&b.gc!==null?r(o,{children:[l(Z,{color:q.main,text:d(b.main)}),l(Z,{color:q.gc,text:d(b.gc)})]}):null,children:r(`svg`,{ref:h,viewBox:`0 0 ${z} ${B}`,preserveAspectRatio:`none`,className:`w-full`,style:Ue,role:`img`,"aria-label":`${e}: ${s} and ${u} over time`,onMouseLeave:()=>_(null),onMouseMove:e=>{let t=e.currentTarget.getBoundingClientRect();if(t.width===0)return;let n=(e.clientX-t.left)/t.width*z-34,r=K(v.upper,n);r!==null&&_({index:r,xPx:v.upper[r].x})},children:[l(Ge,{ticks:v.ticks,max:v.max,formatValue:d}),r(`g`,{transform:`translate(34,${R})`,clipPath:`url(#${m})`,children:[l(`clipPath`,{id:m,children:l(`rect`,{x:0,y:0,width:J.width,height:J.height})}),l(Ke,{spans:v.gaps}),l(`path`,{d:Be(v.middle,v.base),fill:q.main,fillOpacity:.85}),l(`path`,{d:Be(v.upper,v.middle),fill:q.gc,fillOpacity:.85,stroke:`var(--color-surface, transparent)`,strokeWidth:2}),l(`path`,{d:ze(v.upper),fill:`none`,stroke:q.gc,strokeWidth:2}),g!==null&&l(`line`,{x1:g.xPx,x2:g.xPx,y1:0,y2:J.height,stroke:`currentColor`,strokeWidth:1,className:`text-foreground-subtle`})]})]})})}function Y({title:e,points:n,formatValue:i,emptyMessage:o,axisFloor:s,axisScale:u=`linear`,gapMs:d}){let f=t(null),[p,m]=c(null),h=a(()=>{let e=W(n.map(e=>e.value),s,u),t=G(n,J,e,d);return{max:e,ticks:Ie(e,u),projected:t,gaps:Ve(t)}},[n,s,u,d]),g=(p===null?n.at(-1):n[p.index])??void 0;return n.length===0||h.projected.every(e=>e.y===null)?l(X,{title:e,children:l(Je,{message:o})}):l(X,{title:e,readout:g&&g.value!==null?l(Z,{color:q.main,text:i(g.value)}):null,children:r(`svg`,{ref:f,viewBox:`0 0 ${z} ${B}`,preserveAspectRatio:`none`,className:`w-full`,style:Ue,role:`img`,"aria-label":e,onMouseLeave:()=>m(null),onMouseMove:e=>{let t=e.currentTarget.getBoundingClientRect();if(t.width===0)return;let n=(e.clientX-t.left)/t.width*z-34,r=K(h.projected,n);r!==null&&m({index:r,xPx:h.projected[r].x})},children:[l(Ge,{ticks:h.ticks,max:h.max,formatValue:i}),r(`g`,{transform:`translate(34,${R})`,children:[l(Ke,{spans:h.gaps}),l(`path`,{d:ze(h.projected),fill:`none`,stroke:q.main,strokeWidth:2}),p!==null&&l(`line`,{x1:p.xPx,x2:p.xPx,y1:0,y2:J.height,stroke:`currentColor`,strokeWidth:1,className:`text-foreground-subtle`})]})]})})}function Ge({ticks:e,max:t,formatValue:n}){let i=t<=0?1:t;return l(`g`,{className:`text-foreground-subtle`,children:e.map(e=>{let t=R+J.height-e/i*J.height;return r(`g`,{children:[l(`line`,{x1:34,x2:34+J.width,y1:t,y2:t,stroke:`currentColor`,strokeWidth:.5,strokeOpacity:.3}),l(`text`,{x:0,y:t+3,fontSize:8,fill:`currentColor`,children:n(e)})]},e)})})}function Ke({spans:e}){return e.length===0?null:l(`g`,{className:`text-foreground-subtle`,children:e.map(e=>l(`rect`,{x:e.fromX,y:0,width:Math.max(1,e.toX-e.fromX),height:J.height,fill:`currentColor`,fillOpacity:.12,children:l(`title`,{children:`No samples in this interval — nothing was measured, nothing is drawn.`})},`${e.fromX}-${e.toX}`))})}function X({title:e,legend:t,readout:n,children:i}){return r(`div`,{className:`load-chart-scope min-w-0`,children:[r(`div`,{className:`flex items-baseline justify-between gap-2 mb-1`,children:[l(`span`,{className:`text-[10px] uppercase tracking-wider text-foreground-subtle`,children:e}),l(`div`,{className:`flex items-center gap-2`,children:n})]}),i,t&&l(`div`,{className:`flex items-center gap-3 mt-1`,children:t})]})}function qe({color:e,label:t}){return r(`span`,{className:`inline-flex items-center gap-1 text-[9px] text-foreground-subtle`,children:[l(`span`,{className:`inline-block h-2 w-2 rounded-[2px]`,style:{backgroundColor:e}}),t]})}function Z({color:e,text:t}){return r(`span`,{className:`inline-flex items-center gap-1 text-[11px] tabular-nums text-foreground`,children:[l(`span`,{className:`inline-block h-2 w-2 rounded-[2px]`,style:{backgroundColor:e}}),t]})}function Je({message:e}){return l(`div`,{className:`h-16 flex items-center justify-center rounded border border-dashed border-border text-[10px] text-foreground-subtle text-center px-2`,children:e})}function Ye(e,t){let n=e.at(-1);return n===void 0?[t]:t.atMs>n.atMs?[...e,t]:e.some(e=>e.atMs===t.atMs)?e:[...e,t].toSorted((e,t)=>e.atMs-t.atMs)}function Xe(e,t,n,r){let i=e.get(t),a=Ye(i?.points??[],r);if(i!==void 0&&a===i.points)return e;let o=new Map(e);return o.set(t,{key:t,kind:n,points:a.slice(Math.max(0,a.length-720))}),o}function Ze(e,t,n){let r=e;for(let e of ce(t,n))r=Xe(r,e.key,e.kind,e.point);return r}function Qe(e,t){let n=e;for(let e of t)for(let t of e.points)n=Xe(n,e.key,e.kind,t);return n}function $e(e){let t=null;for(let n of e.values()){let e=n.points.at(-1);e!==void 0&&(t===null||e.atMs>t)&&(t=e.atMs)}return t}var et=12e4;function tt(e,t){return e===null?!1:t-e>et}function nt(e){let t=e=>{let t=e.points.at(-1);return t===void 0?-1:t.cpuMainPercent!==null&&t.cpuGcPercent!==null?t.cpuMainPercent+t.cpuGcPercent:t.cpuLifetimePercent};return[...e.values()].toSorted((e,n)=>{let r=t(n)-t(e);return r===0?e.key.localeCompare(n.key):r})}function rt(e){for(let t of e.values()){let e=t.points.at(-1);if(e&&e.cpuMainPercent!==null)return!0}return!1}var it={key:`cpu`,dir:`desc`};function at(e){let t=e.points.at(-1);return t===void 0?null:t.cpuMainPercent!==null&&t.cpuGcPercent!==null?t.cpuMainPercent+t.cpuGcPercent:t.cpuLifetimePercent}function ot(e){return e.points.at(-1)?.memoryRssBytes??null}function st(e,t,n){let r=n.trim().toLowerCase();return r===``?!0:e.toLowerCase().includes(r)||t.toLowerCase().includes(r)}function ct(e,t){return t===`memory`?ot(e):at(e)}function lt(e,t,n){let r=e.filter(e=>t.selectedKeys.size>0&&!t.selectedKeys.has(e.key)?!1:st(n(e),e.key,t.search)),i=(e,t)=>e.key.localeCompare(t.key);return[...r].toSorted((e,r)=>{if(t.sort.key===`name`){let a=n(e).localeCompare(n(r)),o=t.sort.dir===`asc`?a:-a;return o===0?i(e,r):o}let a=ct(e,t.sort.key),o=ct(r,t.sort.key);if(a===null&&o===null)return i(e,r);if(a===null)return 1;if(o===null)return-1;let s=t.sort.dir===`desc`?o-a:a-o;return s===0?i(e,r):s})}n();function ut(e,n=720){let r=S(`metrics.node-processes-snapshot`,t=>t.nodeId===e),[i,a]=c(new Map),[o,s]=c(void 0),l=t(null),{data:d,isPending:f}=h({forNodeId:e,sinceMs:o,maxPoints:n});u(()=>{d!==void 0&&a(e=>{let t=Qe(e,d.series);return l.current=$e(t),t})},[d]);let p=r?.timestamp;u(()=>{r==null||p===void 0||(tt(l.current,p)&&s(l.current??0),a(e=>{let t=Ze(e,r.processes,p);return l.current=$e(t),t}))},[p]),u(()=>{a(new Map),s(void 0),l.current=null},[e]);let m=0,g=null;for(let e of i.values()){m=Math.max(m,e.points.length);let t=e.points.at(0);t!==void 0&&(g===null||t.atMs<g)&&(g=t.atMs)}return{series:nt(i),sampleCount:m,threadSplitAvailable:rt(i),oldestAtMs:g,loadingRetained:f,durable:d?.durable??null,bucketMs:d?.bucketMs??null,cadenceMs:d?.cadenceMs??null,retainedSamples:d?.retainedSamples??null}}function dt(e){let t=e.indexOf(`@`);return t===-1?{addonId:e,nodeId:null}:{addonId:e.slice(0,t),nodeId:e.slice(t+1)}}function ft(e,t){let n=dt(e);return n.nodeId===null?t.isHub:n.nodeId===t.nodeId}function pt(e,t){return e.filter(e=>ft(e.addonId,t))}function mt(e){return dt(e).addonId}var ht=`__unclaimed__`;function gt(e,t){return{kind:e,deviceId:t,cpu:0,cpuKnown:!0,rss:0,rssKnown:!0,count:0,roles:new Set}}function _t(e,t){return{key:e,kind:t.kind,deviceId:t.deviceId,cpuSeconds:t.cpuKnown?Math.round(t.cpu*100)/100:null,rssBytes:t.rssKnown?t.rss:null,unitCount:t.count,roles:[...t.roles].sort()}}function vt(e,t){let n=new Map,r=new Set;for(let t of e){t.pid!==void 0&&r.add(t.pid);let e=t.deviceId!==null&&t.attribution!==`unattributable`,i=e?`device:${t.deviceId}`:`shared:${t.role}`,a=n.get(i)??gt(e?`camera`:`shared`,e?t.deviceId:null);a.count+=1,a.roles.add(t.role),t.cpuSeconds===void 0?a.cpuKnown=!1:a.cpu+=t.cpuSeconds,t.rssBytes===void 0?a.rssKnown=!1:a.rss+=t.rssBytes,n.set(i,a)}let i=[...n.entries()].map(([e,t])=>_t(e,t));if(t===void 0)return i;let a=t.filter(e=>!r.has(e.pid));if(a.length===0)return i;let o=gt(`unclaimed`,null);for(let e of a)o.count+=1,o.rss+=e.memoryRssBytes;return o.cpuKnown=!1,[...i,_t(ht,o)]}function yt(e,t){let n=new Set;for(let t of e)t.pid!==void 0&&n.add(t.pid);return{claimed:t.filter(e=>n.has(e.pid)).length,observed:t.length}}var bt=[{family:`decode`,attribution:`measured`,unit:`one forked decode-worker child per decode SESSION`,perCamera:!0,wiredToday:!0,missingLink:``,evidence:[`packages/addon-pipeline/src/session-decode/fork-decode-worker.ts:118`,`packages/addon-pipeline/src/session-decode/fork-decode-worker.ts:131`,`packages/addon-pipeline/src/pipeline-runner/index.ts:2765`,`packages/types/src/process/child-cost-registry.ts:188`]},{family:`transcode`,attribution:`measured`,unit:`one ffmpeg child per (camera, encode plan)`,perCamera:!0,wiredToday:!0,missingLink:``,evidence:[`packages/addon-pipeline/src/stream-broker/stream-broker/egress-transcode-manager.ts:960`,`packages/addon-pipeline/src/stream-broker/stream-broker/transcode-egress.ts:282`,`packages/addon-pipeline/src/stream-broker/stream-broker/transcode-egress.ts:324`,`packages/types/src/ffmpeg/sharing-key.ts:139`]},{family:`recording`,attribution:`measured`,unit:`one passthrough ffmpeg per (camera, profile)`,perCamera:!0,wiredToday:!0,missingLink:``,evidence:[`packages/addon-pipeline/src/recorder/addon/segment-writer.ts:33`,`packages/addon-pipeline/src/recorder/addon/segment-writer.ts:217`,`packages/addon-pipeline/src/recorder/addon/segment-writer.ts:279`,`packages/addon-pipeline/src/recorder/addon/recording-controller.ts:1710`]},{family:`detection`,attribution:`accounted`,unit:`a SHARED inference pool per (node, runtime, device) — no per-camera process`,perCamera:!1,wiredToday:!0,missingLink:"Nothing is missing for COUNTING — per-camera inference milliseconds, processed/shed frames and bytes-to-pool are already accumulated. What is missing is any basis for CPU seconds: `deviceId` reaches the pool as a diagnostic label and selects nothing, so cost can only ever be apportioned, never measured. Since 2026-08-27 the pool DECLARES this itself, contributing `deviceId: null` + `attribution: unattributable` rather than leaving an observer to infer it — the renunciation is data, and it is still refused by the chart gate.",evidence:[`packages/addon-pipeline/src/detection-pipeline/provider.ts:540`,`packages/addon-pipeline/src/detection-pipeline/engine/shared-inference-pool.ts:413`,`packages/types/src/interfaces/inference-engine.ts:24`,`packages/addon-pipeline/src/pipeline-runner/timing-sampler.ts:63`,`packages/addon-pipeline/src/pipeline-runner/runner.ts:353`,`packages/addon-pipeline/src/pipeline-runner/runner.ts:849`,`packages/addon-pipeline/src/pipeline-runner/frame-lazy-metrics.ts:100`]},{family:`streaming`,attribution:`measured`,unit:"one StreamBroker per `${deviceId}/${camStreamId}`, with an exact RTP ring byte counter",perCamera:!0,wiredToday:!1,missingLink:"The ffmpeg-backed ingest is now reported per camera, but that is the minority path. `rtpRingBytes` — the exact figure for the in-process case — is maintained on every push and drop and is still private and absent from `getStats()`. The three `EncodedRingBuffer`s track packets and duration and have no byte accumulator at all; the only byte figure they offer is `preRollBudgetBytes`, which is a BUDGET, not an occupancy.",evidence:[`packages/addon-pipeline/src/stream-broker/stream-broker/stream-broker.ts:2895`,`packages/addon-pipeline/src/stream-broker/rtsp/ffmpeg-push-reader.ts:189`,`packages/addon-pipeline/src/stream-broker/stream-broker/stream-broker.ts:494`,`packages/addon-pipeline/src/stream-broker/stream-broker/stream-broker.ts:498`,`packages/addon-pipeline/src/stream-broker/stream-broker/stream-broker.ts:2505`,`packages/addon-pipeline/src/stream-broker/stream-broker/pre-roll.ts:269`]}];function xt(e){return e.attribution===`measured`&&e.wiredToday}function St(){let e=new Set;for(let t of bt)xt(t)&&e.add(t.family);return e}var Ct=St();function Q(e){return!Ct.has(e.role)||e.deviceId===null?!1:e.attribution===`measured`}function wt(e){return`${e.addonId}${e.role}${e.unit}${e.pid??`-`}`}function Tt(){return{cpuSeconds:0,cpuKnown:!0,rssBytes:0,rssKnown:!0,unitCount:0,roles:new Set}}function Et(e,t,n){if(t.cpuSeconds===void 0)return null;if(e===void 0)return t.startedAtMs===void 0||t.startedAtMs<n?null:t.cpuSeconds;if(e.cpuSeconds===void 0||e.startedAtMs===void 0||t.startedAtMs===void 0||e.startedAtMs!==t.startedAtMs)return null;let r=t.cpuSeconds-e.cpuSeconds;return r<0?null:r}function Dt(e){return Math.round(e*10)/10}function Ot(e,t){let n=t.atMs-e.atMs;if(n<=0)return[];let r=n/1e3,i=new Map;for(let t of e.entries)Q(t)&&i.set(wt(t),t);let a=new Map,o=new Set;for(let n of t.entries){if(!Q(n)||n.deviceId===null)continue;let t=wt(n);o.add(t);let r=a.get(n.deviceId)??Tt();r.unitCount+=1,r.roles.add(n.role);let s=Et(i.get(t),n,e.atMs);s===null?r.cpuKnown=!1:r.cpuSeconds+=s,n.rssBytes===void 0?r.rssKnown=!1:r.rssBytes+=n.rssBytes,a.set(n.deviceId,r)}for(let[e,t]of i){if(o.has(e)||t.deviceId===null)continue;let n=a.get(t.deviceId);n!==void 0&&(n.cpuKnown=!1)}return[...a.entries()].map(([e,n])=>({deviceId:e,point:{atMs:t.atMs,cpuPercent:n.cpuKnown?Dt(n.cpuSeconds/r*100):null,rssBytes:n.rssKnown?n.rssBytes:null,unitCount:n.unitCount,roles:[...n.roles].toSorted()}}))}function kt(e,t){if(t.length===0)return e;let n=new Map(e);for(let e of t){let t=n.get(e.deviceId),r=t?.points.at(-1);if(r!==void 0&&e.point.atMs<=r.atMs)continue;let i=[...t?.points??[],e.point];n.set(e.deviceId,{deviceId:e.deviceId,points:i.slice(Math.max(0,i.length-720))})}return n}function At(e){let t=e=>{for(let t=e.points.length-1;t>=0;t--){let n=e.points[t]?.cpuPercent;if(n!=null)return n}return-1};return[...e.values()].toSorted((e,n)=>{let r=t(n)-t(e);return r===0?e.deviceId-n.deviceId:r})}n();function jt(e){let n=S(`metrics.node-processes-snapshot`,t=>t.nodeId===e.nodeId),r=T(void 0,{staleTime:1/0,refetchOnWindowFocus:!1}),[i,a]=c(new Map),[o,s]=c(null),[l,d]=c(0),f=t(null),p=t(r.refetch);p.current=r.refetch;let m=n?.timestamp;u(()=>{m!==void 0&&p.current()},[m]),u(()=>{a(new Map),s(null),d(0),f.current=null},[e.nodeId,e.isHub]);let h=r.data,g=r.dataUpdatedAt;u(()=>{if(h===void 0||g===0)return;let t={atMs:g,entries:pt(h,e)},n=f.current;if(f.current=t,n===null){s(g);return}let r=Ot(n,t);r.length!==0&&(d(e=>e+1),a(e=>kt(e,r)))},[h,g,e.nodeId,e.isHub]);let _=h===void 0?[]:pt(h,e),v=n?.processes,y=vt(_,v),b=v===void 0?null:yt(_,v),x=_.filter(e=>e.deviceId!==null&&!Q(e));return{series:At(i),sharedBuckets:y.filter(e=>e.kind===`shared`),unclaimed:y.find(e=>e.kind===`unclaimed`)??null,coverage:b,refusedEntries:x,windowStartedAtMs:o,windowCount:l,loading:r.isPending,error:r.error===null?null:r.error.message}}var Mt=25,Nt=64*1024*1024;function Pt({scope:e}){let{t}=P(),n=jt(e),i=[...St()].toSorted().join(`, `);return r(`section`,{className:`space-y-3`,children:[r(`div`,{className:`space-y-1`,children:[l(`h3`,{className:`text-xs font-medium text-foreground`,children:t(`cameraLoad.title`,`Per-camera load — measured, on this node`)}),l(`p`,{className:`text-[11px] text-foreground-subtle`,children:t(`cameraLoad.intro`,"Each addon reports its OWN cost through the `load-contribution` capability — the recorder does not have to be told which camera its ffmpeg serves, it spawned it. Nothing here joins a pid to a camera. Only families that are measured AND readable today are drawn ({{roles}}); every other family is named at the bottom of this panel, with the reason, rather than estimated.",{roles:i})}),r(`p`,{className:`text-[11px] text-foreground-subtle`,children:[t(`cameraLoad.windowNote`,`CPU is a DELTA of the cumulative counters, over the same fixed cadence the node already emits its process snapshot on — this panel adds no sampling and reads nothing while it is closed. Contributions are not stored anywhere, so unlike the per-addon charts above this window starts when you open this panel.`),n.windowStartedAtMs!==null&&r(o,{children:[` `,t(`cameraLoad.windowSince`,`Differencing since {{since}} ({{count}} windows).`,{since:new Date(n.windowStartedAtMs).toLocaleTimeString(),count:n.windowCount})]})]})]}),n.error!==null&&r(`div`,{className:`flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-[11px] text-destructive`,children:[l(M,{className:`h-3 w-3 mt-0.5 shrink-0`}),l(`span`,{children:t(`cameraLoad.readFailed`,`Could not read the load contributions: {{error}}. This is a failed read, NOT an idle cluster.`,{error:n.error})})]}),l(Ft,{state:n}),n.series.length===0&&n.error===null&&l(`div`,{className:`rounded-md border border-dashed border-border px-3 py-6 text-center text-[11px] text-foreground-subtle`,children:n.loading?t(`cameraLoad.loading`,`Reading what the addons report…`):t(`cameraLoad.waitingWindow`,`No per-camera window yet. A rate needs two readings, and the second arrives with the next process snapshot. A node running no decode, transcode or recording child stays empty — which is the truth, not a failure.`)}),l(`div`,{className:`space-y-3`,children:n.series.map(e=>r(`div`,{className:`rounded-lg border border-border p-3`,children:[r(`div`,{className:`flex items-baseline justify-between gap-2 mb-2`,children:[l(`span`,{className:`text-xs font-medium text-foreground`,children:t(`cameraLoad.deviceLabel`,`Camera {{deviceId}}`,{deviceId:e.deviceId})}),l(`span`,{className:`text-[10px] text-foreground-subtle`,children:t(`cameraLoad.unitCount`,`{{count}} unit(s): {{roles}}`,{count:e.points.at(-1)?.unitCount??0,roles:(e.points.at(-1)?.roles??[]).join(`, `)})})]}),r(`div`,{className:`grid grid-cols-1 md:grid-cols-2 gap-4`,children:[l(Y,{title:t(`cameraLoad.cpuTitle`,`CPU % of one core (delta)`),points:e.points.map(e=>({atMs:e.atMs,value:e.cpuPercent})),formatValue:e=>`${e.toFixed(1)}%`,axisFloor:Mt,emptyMessage:t(`cameraLoad.cpuUnavailable`,`No measurable window yet — every window so far contained a unit that started, stopped or reported no counter.`)}),l(Y,{title:t(`cameraLoad.memoryTitle`,`Memory RSS`),points:e.points.map(e=>({atMs:e.atMs,value:e.rssBytes})),formatValue:F,axisFloor:Nt,emptyMessage:t(`cameraLoad.memoryUnavailable`,`No memory reported for this camera.`)})]})]},e.deviceId))}),l(It,{buckets:n.sharedBuckets}),l(Rt,{entries:n.refusedEntries})]})}function Ft({state:e}){let{t}=P(),n=e.coverage;if(n===null)return l(`p`,{className:`text-[11px] text-foreground-subtle`,children:t(`cameraLoad.coverageUnknown`,`No process snapshot from this node yet, so nothing can be said about what is unclaimed. That is different from "everything is claimed".`)});let i=e.unclaimed;return r(`div`,{className:`rounded-md border border-border px-3 py-2 space-y-1`,children:[l(`p`,{className:`text-[11px] text-foreground`,children:t(`cameraLoad.coverage`,`Addons claim {{claimed}} of the {{observed}} camstack processes this node reports.`,{claimed:n.claimed,observed:n.observed})}),i===null?l(`p`,{className:`text-[10px] text-foreground-subtle`,children:t(`cameraLoad.noUnclaimed`,`Nothing is unclaimed on this node.`)}):l(`p`,{className:`text-[10px] text-amber-400`,children:t(`cameraLoad.unclaimed`,`{{count}} process(es) nobody claims, holding {{rss}}. Either a leak or a cost family nobody has taught to report — they stay in their own bucket and are never folded into a camera. CPU is unknown for them: the observer measures RSS and reports no cumulative CPU seconds.`,{count:i.unitCount,rss:i.rssBytes===null?`—`:F(i.rssBytes)})})]})}function It({buckets:e}){let{t}=P();return e.length===0?null:r(`div`,{className:`rounded-md border border-border px-3 py-2 space-y-1`,children:[l(`p`,{className:`text-[11px] font-medium text-foreground`,children:t(`cameraLoad.sharedTitle`,`Shared — declared as belonging to no camera`)}),l(`p`,{className:`text-[10px] text-foreground-subtle`,children:t(`cameraLoad.sharedNote`,`Reported so the shared cost is visible as shared instead of invisible. It is NOT divided across the cameras present: the inference pool is one process serving all of them, and an apportioned share is an estimate, not a measurement. These totals are cumulative since each unit started — not a rate.`)}),l(`ul`,{className:`space-y-0.5`,children:e.map(e=>l(`li`,{className:`text-[10px] text-foreground-subtle`,children:t(`cameraLoad.sharedRow`,`{{roles}} — {{units}} unit(s), {{cpu}} CPU-seconds, {{rss}}`,{roles:e.roles.join(`, `),units:e.unitCount,cpu:e.cpuSeconds===null?`—`:e.cpuSeconds.toFixed(0),rss:e.rssBytes===null?`—`:F(e.rssBytes)})},e.key))})]})}function Lt(e){let t=new Map;for(let n of e){let e=t.get(n.role)??{count:0,addons:new Set};e.count+=1,e.addons.add(mt(n.addonId)),t.set(n.role,e)}return t}function Rt({entries:e}){let{t}=P(),n=Lt(e),i=bt.filter(e=>!xt(e));return i.length===0?null:r(`div`,{className:`rounded-md border border-dashed border-border px-3 py-2 space-y-1`,children:[l(`p`,{className:`text-[11px] font-medium text-foreground`,children:t(`cameraLoad.notChartedTitle`,`Not charted per camera — and why`)}),l(`ul`,{className:`space-y-1`,children:i.map(e=>{let i=n.get(e.family);return r(`li`,{className:`text-[10px] text-foreground-subtle`,children:[l(`span`,{className:`font-medium capitalize text-foreground`,children:e.family}),` `,e.missingLink,i!==void 0&&r(o,{children:[` `,t(`cameraLoad.notChartedReported`,`Reported anyway this window: {{count}} entr(ies) from {{addons}}, all declined.`,{count:i.count,addons:[...i.addons].toSorted().join(`, `)})]})]},e.family)})})]})}n();var zt=256*1024*1024,Bt=1e4;function Vt({nodeId:e,isHub:t}){let{t:n}=P(),{series:i,sampleCount:o,threadSplitAvailable:s,oldestAtMs:u,loadingRetained:d,bucketMs:f,cadenceMs:p}=ut(e),[m,h]=c(``),[g,_]=c(new Set),[v,y]=c(it),b=e=>e.key===`__root__`?n(`clusterLoad.bucketRoot`,`Node supervisor`):e.key===`__unattributed__`?n(`clusterLoad.bucketUnattributed`,`Unattributed processes`):e.key,x=Re(f??p??Bt),S=a(()=>lt(i,{search:m,selectedKeys:g,sort:v},b),[i,m,g,v]),C=e=>{_(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})};return r(`div`,{className:`space-y-4`,children:[l(`style`,{children:He}),l(Ht,{oldestAtMs:u,bucketMs:f,cadenceMs:p}),!s&&o>0&&r(`div`,{className:`flex items-start gap-2 rounded-md border border-amber-500/40 bg-amber-500/5 px-3 py-2 text-[11px] text-amber-400`,children:[l(M,{className:`h-3 w-3 mt-0.5 shrink-0`}),l(`span`,{children:n(`clusterLoad.noThreadSplit`,`No thread-level CPU split on this node — /proc/<pid>/task is Linux-only, and the first sample after a restart has no window to measure. CPU below falls back to the ps lifetime AVERAGE, which is not a rate.`)})]}),o===0&&l(`div`,{className:`rounded-md border border-dashed border-border px-3 py-6 text-center text-[11px] text-foreground-subtle`,children:d?n(`clusterLoad.loadingRetained`,`Reading the retained series from this node…`):n(`clusterLoad.waitingFirstSample`,`No retained samples for this node — waiting for the next snapshot (up to 20s). The chart needs two to draw a rate.`)}),i.length>0&&r(`div`,{className:`space-y-2`,children:[r(`div`,{className:`flex flex-wrap items-center gap-2`,children:[r(`label`,{className:`relative flex-1 min-w-[160px]`,children:[l(ye,{className:`pointer-events-none absolute left-2 top-1/2 h-3 w-3 -translate-y-1/2 text-foreground-subtle`}),l(`input`,{type:`search`,value:m,onChange:e=>h(e.target.value),placeholder:n(`clusterLoad.searchPlaceholder`,`Find a runner…`),"aria-label":n(`clusterLoad.searchLabel`,`Find a runner`),className:`w-full rounded border border-border bg-background py-1 pl-7 pr-2 text-[11px] text-foreground placeholder:text-foreground-subtle`})]}),l(Ut,{sort:v,onChange:y})]}),r(`div`,{className:`flex flex-wrap items-center gap-1`,children:[i.map(e=>{let t=g.has(e.key);return l(`button`,{type:`button`,"aria-pressed":t,onClick:()=>C(e.key),className:`rounded-full border px-2 py-0.5 text-[10px] transition-colors ${t?`border-primary bg-primary/10 text-primary`:`border-border text-foreground-subtle hover:text-foreground`}`,children:b(e)},e.key)}),g.size>0&&r(`button`,{type:`button`,onClick:()=>_(new Set),className:`inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 text-[10px] text-foreground-subtle hover:text-foreground`,children:[l(N,{className:`h-2.5 w-2.5`}),n(`clusterLoad.clearFilter`,`All runners`)]})]})]}),i.length>0&&S.length===0&&l(`div`,{className:`rounded-md border border-dashed border-border px-3 py-6 text-center text-[11px] text-foreground-subtle`,children:n(`clusterLoad.noMatches`,`No runner matches this search on {{count}} present. The node is reporting — the filter is hiding them.`,{count:i.length})}),l(`div`,{className:`space-y-3`,children:S.map(e=>r(`div`,{className:`rounded-lg border border-border p-3`,children:[r(`div`,{className:`flex items-baseline justify-between gap-2 mb-2`,children:[l(`span`,{className:`text-xs font-medium text-foreground`,children:b(e)}),l(`span`,{className:`text-[10px] text-foreground-subtle`,children:n(`clusterLoad.processCount`,`{{count}} process(es)`,{count:e.points.at(-1)?.processCount??0})})]}),r(`div`,{className:`grid grid-cols-1 md:grid-cols-2 gap-4`,children:[l(We,{title:s?n(`clusterLoad.cpuTitle`,`CPU % of one core`):n(`clusterLoad.cpuTitleLifetime`,`CPU % (ps lifetime average)`),mainLabel:n(`clusterLoad.seriesMain`,`Main threads`),gcLabel:n(`clusterLoad.seriesGc`,`GC / V8 helpers`),points:e.points.map(e=>({atMs:e.atMs,main:e.cpuMainPercent,gc:e.cpuGcPercent})),formatValue:e=>`${e.toFixed(0)}%`,gapMs:x,emptyMessage:n(`clusterLoad.cpuUnavailable`,`No thread split on this node. Lifetime average: {{avg}}%`,{avg:(e.points.at(-1)?.cpuLifetimePercent??0).toFixed(1)})}),l(Y,{title:n(`clusterLoad.memoryTitle`,`Memory RSS`),points:e.points.map(e=>({atMs:e.atMs,value:e.memoryRssBytes})),formatValue:F,axisFloor:zt,axisScale:`bytes`,gapMs:x,emptyMessage:n(`clusterLoad.memoryUnavailable`,`No memory samples yet.`)})]})]},e.key))}),l(Pt,{scope:{nodeId:e,isHub:t}})]})}function Ht({oldestAtMs:e,bucketMs:t,cadenceMs:n}){let{t:i}=P();if(e===null&&t===null)return null;let a=t!==null&&n!==null&&t>n;return r(`p`,{className:`text-[10px] text-foreground-subtle`,children:[e!==null&&i(`clusterLoad.captionSince`,`Since {{since}}`,{since:new Date(e).toLocaleTimeString()}),e!==null&&t!==null&&` · `,t!==null&&(a?i(`clusterLoad.captionBucket`,`1 point = {{bucket}}s (bucket MAXIMUM, not a mean)`,{bucket:Math.round(t/1e3)}):i(`clusterLoad.captionRaw`,`1 point = 1 snapshot`))]})}function Ut({sort:e,onChange:t}){let{t:n}=P(),i=[{key:`cpu`,label:n(`clusterLoad.sortCpu`,`CPU`)},{key:`memory`,label:n(`clusterLoad.sortMemory`,`Memory`)},{key:`name`,label:n(`clusterLoad.sortName`,`Name`)}];return r(`div`,{className:`flex items-center gap-1`,children:[l(`span`,{className:`text-[10px] text-foreground-subtle`,children:n(`clusterLoad.sortBy`,`Sort by`)}),i.map(n=>{let i=e.key===n.key;return r(`button`,{type:`button`,"aria-pressed":i,onClick:()=>t(i?{key:n.key,dir:e.dir===`desc`?`asc`:`desc`}:{key:n.key,dir:n.key===`name`?`asc`:`desc`}),className:`rounded border px-1.5 py-0.5 text-[10px] transition-colors ${i?`border-primary bg-primary/10 text-primary`:`border-border text-foreground-subtle hover:text-foreground`}`,children:[n.label,i&&(e.dir===`desc`?` ↓`:` ↑`)]},n.key)})]})}var Wt=`No per-node settings on this node. Every other setting these addons expose is one value for the whole cluster — edit those in System → Settings, not here.`;function Gt({nodeId:e,addonIds:t}){return t.length===0?l(`div`,{className:`py-4 text-xs text-muted-foreground text-center`,children:`No addons on this node`}):l(`div`,{className:`w-full py-2`,children:l(xe,{nodeId:e,addonIds:t,level:`global`,fieldScope:`perNode`,emptyMessage:Wt,selfTabbed:!0})})}n();function Kt({nodeId:e,addons:t}){let n=s(),{data:r,isLoading:i}=w({nodeId:e}),o=a(()=>{let e=new Map;for(let n of t)for(let t of n.capabilities){let r=e.get(t)??[];r.push(n.id),e.set(t,r)}return[...e.entries()].toSorted(([e],[t])=>e.localeCompare(t)).map(([e,t])=>({capName:e,providers:t}))},[t]),c=ae({onSuccess:()=>{n.invalidateQueries({queryKey:[[`pipelineOrchestrator`,`getCapabilityBindings`]]})}});return o.length===0?l(`div`,{className:`py-4 text-xs text-muted-foreground text-center`,children:`No capabilities on this node`}):l(E,{columns:[{key:`capName`,header:`Capability`,width:`w-1/2`,sortValue:e=>e.capName,render:e=>l(`span`,{className:`font-medium`,children:e.capName})},{key:`provider`,header:`Bound provider`,render:t=>{let n=r?.[t.capName]??t.providers[0]??``,a=t.providers.length===1;return l(`select`,{value:n,onChange:n=>c.mutate({nodeId:e,capName:t.capName,addonId:n.target.value}),disabled:a||c.isPending||i,className:`w-full rounded-md border border-border bg-surface px-2 py-1 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary disabled:opacity-60`,title:a?`Only one provider installed — choice becomes available once a second addon registers this capability`:void 0,children:t.providers.map(e=>l(`option`,{value:e,children:e},e))})}}],rows:o,rowKey:e=>e.capName,minWidthPx:520,mobileMode:`scroll`,maxBodyHeight:O})}n();function qt(e){if(e==null)return`—`;let t=Math.floor(e/1e3),n=Math.floor(t/86400),r=Math.floor(t%86400/3600),i=Math.floor(t%3600/60);return n>0?`${n}d ${r}h ${i}m`:r>0?`${r}h ${i}m`:`${i}m`}function Jt(e){let[t,n]=c(!1),[i,a]=c(e.name),o=s(),u=C({onSuccess:()=>{o.invalidateQueries({queryKey:[[`nodes`,`topology`]]})}}),d=(e.localIps??[]).filter(e=>!e.includes(`:`)),f=(e.localIps??[]).filter(e=>e.includes(`:`)),p=async()=>{if(!i.trim()||i===e.name){n(!1);return}try{await u.mutateAsync({nodeId:e.nodeId,name:i.trim()}),n(!1)}catch(e){console.error(`Rename failed:`,e)}},m=[[`Node ID`,l(`span`,{className:`font-mono text-xs`,children:e.nodeId})],[`Display Name`,t?r(`div`,{className:`flex items-center gap-1.5`,children:[l(`input`,{type:`text`,className:`flex-1 px-2 py-0.5 text-xs border border-border rounded bg-background focus:outline-none focus:ring-1 focus:ring-primary`,value:i,onChange:e=>a(e.target.value),onKeyDown:e=>{e.key===`Enter`&&p(),e.key===`Escape`&&n(!1)},disabled:u.isPending,autoFocus:!0}),l(`button`,{onClick:()=>void p(),disabled:u.isPending,className:`p-0.5 text-success hover:bg-success/10 rounded`,title:`Save`,children:l(be,{className:`h-3.5 w-3.5`})}),l(`button`,{onClick:()=>n(!1),className:`p-0.5 text-muted-foreground hover:bg-muted rounded`,title:`Cancel`,children:l(N,{className:`h-3.5 w-3.5`})})]}):r(`div`,{className:`flex items-center gap-1.5`,children:[l(`span`,{className:`text-xs`,children:e.name}),!e.isHub&&l(`button`,{onClick:()=>{a(e.name),n(!0)},className:`p-0.5 text-muted-foreground hover:text-primary rounded`,title:`Rename`,children:l(le,{className:`h-3 w-3`})})]})],[`Hostname`,l(`span`,{className:`text-xs`,children:e.hostname})],[`Role`,e.isHub?l(`span`,{className:`text-[10px] px-1.5 py-0.5 rounded bg-primary/10 text-primary font-medium`,children:`Hub`}):l(`span`,{className:`text-[10px] px-1.5 py-0.5 rounded bg-muted text-muted-foreground font-medium`,children:`Agent`})],[`Root Package`,e.rootPackage?r(`span`,{className:`font-mono text-xs`,children:[e.rootPackage.name,`@`,e.rootPackage.version]}):l(`span`,{className:`text-xs text-muted-foreground`,children:`—`})],[`Platform`,r(`span`,{className:`text-xs`,children:[e.platform,` / `,e.arch]})],[`CPU`,r(`span`,{className:`text-xs`,children:[e.cpuModel??`—`,` (`,e.cpuCores,` cores)`]})],[`Memory`,l(`span`,{className:`text-xs`,children:e.memoryMB>0?`${(e.memoryMB/1024).toFixed(1)} GB`:`—`})],[`Uptime`,l(`span`,{className:`text-xs`,children:qt(e.uptime)})],[`Engines`,e.engines.length>0?l(`div`,{className:`flex flex-wrap gap-1`,children:e.engines.map(e=>l(`span`,{className:`text-[9px] px-1.5 py-0.5 rounded-full bg-purple-500/10 text-purple-400 font-medium`,children:e},e))}):l(`span`,{className:`text-xs text-muted-foreground`,children:`—`})]];return d.length>0&&m.push([`IPv4`,l(`div`,{className:`flex flex-wrap gap-1.5`,children:d.map((e,t)=>l(`span`,{className:`font-mono text-xs px-1.5 py-0.5 rounded bg-muted`,children:e},`${e}-${t}`))})]),f.length>0&&m.push([`IPv6`,l(`div`,{className:`flex flex-wrap gap-1.5`,children:f.map((e,t)=>l(`span`,{className:`font-mono text-[10px] px-1.5 py-0.5 rounded bg-muted truncate max-w-[280px]`,title:e,children:e},`${e}-${t}`))})]),l(`div`,{className:`py-2`,children:l(`table`,{className:`w-full text-sm`,children:l(`tbody`,{children:m.map(([e,t],n)=>r(`tr`,{className:n%2==0?`bg-muted/20`:``,children:[l(`td`,{className:`py-1.5 px-2 text-xs font-medium text-muted-foreground w-28 align-top whitespace-nowrap`,children:e}),l(`td`,{className:`py-1.5 px-2`,children:t})]},e))})})})}n();var Yt=[[`serverManagement`,`getServerPackageStatus`]];function Xt({label:e,tone:t}){return l(`span`,{className:`inline-flex items-center rounded px-2 py-0.5 text-[11px] font-medium ${{neutral:`bg-surface-muted text-foreground-subtle`,ok:`bg-emerald-500/15 text-emerald-500`,info:`bg-sky-500/15 text-sky-500`,warn:`bg-amber-500/15 text-amber-500`,error:`bg-red-500/15 text-red-500`}[t]}`,children:e})}function $({label:e,value:t}){return r(`div`,{className:`flex items-center justify-between gap-4 text-sm`,children:[l(`span`,{className:`text-foreground-subtle`,children:e}),l(`span`,{className:`font-mono text-foreground`,children:t??`—`})]})}function Zt({node:t}){let{t:n}=P(),i=s(),a=D(),[o,u]=c(!1),d=t.isHub?void 0:{nodeId:t.id},f=g(d,{refetchInterval:1e4,enabled:t.isOnline,retry:1}),p=f.data??null,h=e(()=>{i.invalidateQueries({queryKey:Yt})},[i]),_=m({onSettled:h}),v=x({onSettled:h}),y=b({onSettled:h}),S=e(async()=>{p?.latestVersion!=null&&await a({title:n(`serverManagement.confirmUpdateTitle`,`Update node root package`),message:n(`serverManagement.confirmUpdateBody`,`Install {{pkg}}@{{version}} on "{{node}}" and restart to apply.`,{pkg:p.packageName,version:p.latestVersion,node:t.name}),confirmLabel:n(`serverManagement.confirmUpdateAction`,`Update & restart`)})&&(await v.mutateAsync({version:p.latestVersion,...d})).restarting&&u(!0)},[v,a,t.name,d,p,n]),C=e(async()=>{await a({title:n(`serverManagement.confirmRestartTitle`,`Restart node`),message:n(`serverManagement.confirmRestartBody`,`Restart "{{node}}"? In-flight requests to this node are dropped.`,{node:t.name}),confirmLabel:n(`serverManagement.confirmRestartAction`,`Restart`),variant:`warning`})&&(await y.mutateAsync(d)).restarting&&u(!0)},[a,t.name,y,d,n]),w=()=>{switch(p?.bootMode){case`data-root`:return{label:n(`serverManagement.bootDataRoot`,`runtime-updatable`),tone:`ok`};case`workspace`:return{label:n(`serverManagement.bootWorkspace`,`dev workspace`),tone:`info`};default:return{label:n(`serverManagement.bootBaked`,`baked image seed`),tone:`neutral`}}},ee=()=>{if(p==null)return null;switch(p.updateState){case`checking`:return{label:n(`serverManagement.stateChecking`,`checking…`),tone:`info`};case`staging`:return{label:n(`serverManagement.stateStaging`,`updating…`),tone:`info`};case`pending-restart`:return{label:n(`serverManagement.statePending`,`restart pending`),tone:`warn`};default:return p.updateAvailable?{label:n(`serverManagement.stateUpdateAvailable`,`update available`),tone:`warn`}:{label:n(`serverManagement.stateUpToDate`,`up to date`),tone:`ok`}}},T=p?.bootMode===`workspace`,te=t.isOnline&&p!=null&&!T&&p.updateAvailable&&p.updateState===`idle`&&!v.isPending,ne=t.isOnline&&p!=null&&p.updateState===`idle`&&!y.isPending,E=t.isOnline?ee():null;return r(`div`,{className:`space-y-4 py-2`,children:[r(`div`,{className:`flex items-center justify-between gap-3`,children:[r(`div`,{className:`flex items-center gap-2 min-w-0`,children:[l(`span`,{className:`font-mono text-xs text-foreground-subtle truncate`,children:p?.packageName??t.rootPackage?.name??`@camstack/server`}),t.isOnline&&p!=null&&l(Xt,{...w()})]}),E!=null&&l(Xt,{...E})]}),!t.isOnline&&l($,{label:n(`serverManagement.lastReportedVersion`,`Last reported version`),value:t.rootPackage?.version??null}),t.isOnline&&f.isLoading&&r(`div`,{className:`flex items-center gap-2 text-sm text-foreground-subtle`,children:[l(A,{className:`h-4 w-4 animate-spin`}),n(`common.loading`,`Loading…`)]}),t.isOnline&&f.isError&&r(`div`,{className:`space-y-1.5`,children:[l(`p`,{className:`text-sm text-red-500`,children:n(`serverManagement.loadError`,`Could not load the node root-package status.`)}),t.rootPackage!=null&&l($,{label:n(`serverManagement.lastReportedVersion`,`Last reported version`),value:t.rootPackage.version})]}),t.isOnline&&p!=null&&r(`div`,{className:`space-y-1.5`,children:[l($,{label:n(`serverManagement.runningVersion`,`Running version`),value:p.runningVersion}),l($,{label:n(`serverManagement.latestVersion`,`Latest on registry`),value:p.latestVersion}),l($,{label:n(`serverManagement.imageSeedVersion`,`Image seed (baked)`),value:p.seedVersion}),l($,{label:n(`serverManagement.nodeRuntimeVersion`,`Node.js runtime`),value:p.nodeRuntimeVersion}),p.pendingVersion!=null&&l($,{label:n(`serverManagement.pendingVersion`,`Staged (applies on restart)`),value:p.pendingVersion})]}),t.isOnline&&p?.imageContract?.state===`behind-series`&&r(`div`,{className:`rounded-md border border-red-500/40 bg-red-500/10 px-3 py-2 text-xs leading-relaxed text-red-500`,children:[l(`p`,{className:`mb-0.5 font-semibold`,children:n(`serverManagement.imageContractDiverged`,`Container image diverges from the deployment contract`)}),l(`p`,{children:p.imageContract.message})]}),t.isOnline&&T&&l(`p`,{className:`text-xs text-foreground-subtle`,children:n(`serverManagement.workspaceNote`,`Development workspace detected — runtime updates are disabled (the dev loop resolves code from the workspace).`)}),o&&r(`div`,{className:`flex items-center gap-2 text-sm text-amber-500`,children:[l(A,{className:`h-4 w-4 animate-spin`}),n(`serverManagement.nodeRestarting`,`The node is restarting to apply the change…`)]}),t.isOnline&&r(`div`,{className:`flex flex-wrap items-center gap-2 pt-1`,children:[r(`button`,{type:`button`,className:`inline-flex items-center gap-1.5 rounded border border-border px-2.5 py-1.5 text-xs font-medium text-foreground hover:bg-surface-muted disabled:opacity-50`,disabled:_.isPending||p?.updateState===`staging`,onClick:()=>_.mutate(d),children:[_.isPending?l(A,{className:`h-3.5 w-3.5 animate-spin`}):l(de,{className:`h-3.5 w-3.5`}),n(`serverManagement.checkForUpdate`,`Check for update`)]}),te&&r(`button`,{type:`button`,className:`inline-flex items-center gap-1.5 rounded bg-primary px-2.5 py-1.5 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50`,onClick:()=>void S(),children:[v.isPending?l(A,{className:`h-3.5 w-3.5 animate-spin`}):l(I,{className:`h-3.5 w-3.5`}),n(`serverManagement.updateTo`,`Update to {{version}}`,{version:p?.latestVersion??``})]}),r(`button`,{type:`button`,className:`inline-flex items-center gap-1.5 rounded border border-border px-2.5 py-1.5 text-xs font-medium text-foreground hover:bg-surface-muted disabled:opacity-50`,disabled:!ne,onClick:()=>void C(),children:[y.isPending?l(A,{className:`h-3.5 w-3.5 animate-spin`}):l(k,{className:`h-3.5 w-3.5`}),n(`serverManagement.restart`,`Restart`)]})]}),t.isOnline&&p?.lastCheckedAtMs!=null&&r(`p`,{className:`flex items-center gap-1.5 text-[11px] text-foreground-subtle`,children:[l(Se,{className:`h-3 w-3`}),n(`serverManagement.lastChecked`,`Last checked: {{when}}`,{when:new Date(p.lastCheckedAtMs).toLocaleString()})]})]})}n();function Qt({node:e}){let t=d(),[n,i]=c(`info`),[a,o]=c(!1),s=e.processes??[],u=e.addons.map(e=>e.id),f=async()=>{if(!a){o(!0);try{await Promise.allSettled([t.trpcClient.pipelineOrchestrator.removeAgentSettings.mutate({agentNodeId:e.id}),t.trpcClient.clusterNodes.forgetNode.mutate({nodeId:e.id})])}finally{o(!1)}}};return e.isOnline?r(`div`,{className:`border border-border rounded-xl`,children:[r(`div`,{className:`flex items-center gap-3 px-4 py-3 bg-muted/30 border-b border-border rounded-t-xl`,children:[l(`div`,{className:`w-2.5 h-2.5 rounded-full bg-success`}),l(`span`,{className:`text-sm font-semibold`,children:e.name}),r(`span`,{className:`text-xs text-muted-foreground`,children:[e.cpuModel??e.platform,` ·`,` `,e.memoryMB>0?`${Math.round(e.memoryMB/1024)}GB`:``,` · `,e.arch]}),e.engines.map(e=>l(`span`,{className:`text-[9px] px-1.5 py-0.5 rounded-full bg-purple-500/10 text-purple-400 font-medium`,children:e},e)),r(`div`,{className:`ml-auto flex gap-3 text-xs text-muted-foreground`,children:[r(`span`,{className:`flex items-center gap-1`,children:[l(he,{className:`h-3 w-3`}),` `,e.cpuPercent,`%`]}),r(`span`,{className:`flex items-center gap-1`,children:[l(Ce,{className:`h-3 w-3`}),` `,e.memoryPercent,`%`]})]})]}),l(`div`,{className:`sticky top-0 z-20 flex border-b border-border px-4 bg-background overflow-x-auto`,children:[`info`,`forks`,`processes`,`load`,`settings`,`capabilities`].map(e=>l(`button`,{onClick:()=>i(e),className:`shrink-0 whitespace-nowrap px-3 py-2 text-xs font-medium border-b-2 transition-colors ${n===e?`border-primary text-primary`:`border-transparent text-muted-foreground hover:text-foreground`}`,children:e===`info`?`General`:e===`forks`?`Forks (${s.length})`:e===`processes`?`Processes`:e===`load`?`Load`:e===`settings`?`Settings`:`Capabilities`},e))}),r(`div`,{className:`px-4 py-2`,children:[n===`info`&&r(`div`,{className:`space-y-3`,children:[l(Jt,{nodeId:e.id,name:e.name,hostname:e.hostname,platform:e.platform,arch:e.arch,cpuModel:e.cpuModel,cpuCores:e.cpuCores,memoryMB:e.memoryMB,engines:e.engines,isHub:e.isHub,uptime:e.uptime,localIps:e.localIps,rootPackage:e.rootPackage}),l(`div`,{className:`border-t border-border pt-1`,children:l(Zt,{node:{id:e.id,name:e.name,isHub:e.isHub,isOnline:e.isOnline,rootPackage:e.rootPackage??null}})})]}),n===`forks`&&l(Oe,{nodeId:e.id,processes:s}),n===`processes`&&l(Me,{nodeId:e.id}),n===`load`&&l(Vt,{nodeId:e.id,isHub:e.isHub}),n===`settings`&&l(Gt,{nodeId:e.id,addonIds:u}),n===`capabilities`&&l(Kt,{nodeId:e.id,addons:e.addons})]})]}):r(`div`,{className:`border border-border rounded-xl overflow-hidden opacity-60`,children:[r(`div`,{className:`flex items-center gap-3 px-4 py-3 bg-muted/30`,children:[l(`div`,{className:`w-2.5 h-2.5 rounded-full bg-destructive`}),l(`span`,{className:`text-sm font-semibold`,children:e.name}),r(`span`,{className:`text-xs text-muted-foreground`,children:[e.cpuModel??e.platform,` ·`,` `,e.memoryMB>0?`${Math.round(e.memoryMB/1024)}GB`:``,` · `,e.arch]}),e.engines.map(e=>l(`span`,{className:`text-[9px] px-1.5 py-0.5 rounded-full bg-purple-500/10 text-purple-400 font-medium`,children:e},e)),e.rootPackage&&r(`span`,{className:`text-[10px] font-mono px-1.5 py-0.5 rounded bg-muted text-muted-foreground`,children:[e.rootPackage.name,`@`,e.rootPackage.version]}),l(`span`,{className:`ml-auto text-xs px-2 py-0.5 rounded-full bg-destructive/10 text-destructive`,children:`offline`})]}),r(`div`,{className:`flex items-center justify-between gap-3 px-4 py-3`,children:[r(`span`,{className:`text-xs text-muted-foreground`,children:[e.lastSeen?`Last seen ${new Date(e.lastSeen).toLocaleString()} — `:``,e.addons.length,` addon`,e.addons.length===1?``:`s`,` assigned; will resume when node reconnects`]}),r(`button`,{type:`button`,onClick:()=>void f(),disabled:a,title:`Forget this node — removes its saved history and pipeline assignments`,className:`flex items-center gap-1 text-xs px-2 py-1 rounded-md border border-border text-muted-foreground hover:text-destructive hover:border-destructive/50 disabled:opacity-50 transition-colors`,children:[l(ve,{className:`h-3 w-3`}),a?`Forgetting…`:`Forget node`]})]})]})}function $t({nodes:e}){return l(`div`,{className:`flex flex-col gap-4`,children:[...e].toSorted((e,t)=>e.isHub?-1:t.isHub?1:e.isOnline&&!t.isOnline?-1:!e.isOnline&&t.isOnline?1:e.name.localeCompare(t.name)).map(e=>l(`div`,{children:l(Qt,{node:e})},e.id))})}n();function en(){let{data:e,isLoading:t}=re(void 0,{staleTime:1/0}),[n,r]=c(null);return _(`cluster.topology-snapshot`,e=>{let t=e.data?.nodes;Array.isArray(t)&&r(t)}),{nodes:a(()=>n??e??[],[n,e]),isLoading:t&&!n}}function tn(){let{t}=P(),n=D(),{nodes:i,isLoading:o}=en(),s=a(()=>i.filter(e=>e.isOnline),[i]),u=s.length,d=i.filter(e=>!e.isOnline).length,f=x(),[p,m]=c(!1),h=e(async()=>{let e=[...s].sort((e,t)=>e.isHub===t.isHub?0:e.isHub?1:-1);if(await n({title:t(`serverManagement.confirmUpdateAllTitle`,`Update all nodes`),message:t(`serverManagement.confirmUpdateAllBody`,`Update and restart every online node, agents first, hub last. Up-to-date nodes are skipped; this page reconnects when the hub comes back.`),confirmLabel:t(`serverManagement.confirmUpdateAllAction`,`Update all & restart`),variant:`warning`})){m(!0);try{for(let t of e)try{await f.mutateAsync(t.isHub?{}:{nodeId:t.id})}catch{}}finally{m(!1)}}},[f,n,s,t]);return r(`div`,{className:`flex flex-col h-full gap-4 p-4`,children:[r(`div`,{className:`flex items-center justify-between gap-2 flex-wrap`,children:[r(`div`,{className:`flex items-center gap-2`,children:[u>0&&r(`span`,{className:`text-xs px-2 py-0.5 rounded-full bg-success/10 text-success`,children:[u,` online`]}),d>0&&r(`span`,{className:`text-xs px-2 py-0.5 rounded-full bg-destructive/10 text-destructive`,children:[d,` offline`]})]}),l(`div`,{className:`flex items-center gap-2`,children:u>1&&r(`button`,{type:`button`,className:`inline-flex items-center gap-1.5 rounded bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50`,disabled:p,title:t(`serverManagement.updateAllHint`,`Sequenced agents-first, hub-last — up-to-date nodes are skipped.`),onClick:()=>void h(),children:[p?l(A,{className:`h-3.5 w-3.5 animate-spin`}):l(I,{className:`h-3.5 w-3.5`}),t(`serverManagement.updateAll`,`Update all nodes`)]})})]}),o?l(`div`,{className:`flex items-center justify-center flex-1 text-muted-foreground`,children:`Loading cluster topology...`}):l(`div`,{className:`flex-1 overflow-auto`,children:l($t,{nodes:i})})]})}export{tn as ClusterPage};