@camstack/addon-admin-ui 1.2.58 → 1.2.60

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 (152) hide show
  1. package/dist/assets/AddLocationWizard-nmw7yXa5.js +1 -0
  2. package/dist/assets/AddonCollectionPage-L-_Ay0C2.js +1 -0
  3. package/dist/assets/Addons-Ce18i_2K.js +1 -0
  4. package/dist/assets/{AdminPage-Dq33XE9j.js → AdminPage-CCD07UMs.js} +1 -1
  5. package/dist/assets/{AdminTabs-SuhxFl_f.js → AdminTabs-VEJ9aAds.js} +1 -1
  6. package/dist/assets/AiProfiles-BmyaHJsG.js +3 -0
  7. package/dist/assets/BrokerForm-C_ydrEiE.js +1 -0
  8. package/dist/assets/Build-B0V3eQCD.js +32 -0
  9. package/dist/assets/Capabilities-mYaTP3zj.js +1 -0
  10. package/dist/assets/CapabilityBadges-CUuQq9C6.js +1 -0
  11. package/dist/assets/Cluster-CAPVntiH.js +1 -0
  12. package/dist/assets/Data-Cy6cxYnL.js +1 -0
  13. package/dist/assets/DetectionIntelligence-D_UfLFUs.js +1 -0
  14. package/dist/assets/{DeviceAggregateSection-CkqESYNX.js → DeviceAggregateSection-cezz4Q1I.js} +1 -1
  15. package/dist/assets/DeviceDetail-OZn62WTd.js +2 -0
  16. package/dist/assets/Devices-B7naJHzB.js +1 -0
  17. package/dist/assets/EmbedPlayerPage-CUJ-TF0I.js +1 -0
  18. package/dist/assets/Events-C54Nf_FA.js +1 -0
  19. package/dist/assets/FormBuilder-79Fgr735.js +1 -0
  20. package/dist/assets/Identity-C-vZI88k.js +1 -0
  21. package/dist/assets/IntegrationDetail-hg7RVODR.js +1 -0
  22. package/dist/assets/Integrations-BDhj7UlK.js +1 -0
  23. package/dist/assets/Integrations-CD7WfSkd.js +1 -0
  24. package/dist/assets/Logs-BlgZwoZu.js +1 -0
  25. package/dist/assets/{MaskShapeCanvas-pGn6MKOi.js → MaskShapeCanvas-Dcpby34M.js} +1 -1
  26. package/dist/assets/MotionZonesSettings-B-y6Y1Xg.js +1 -0
  27. package/dist/assets/MyAccess-D62ngaEJ.js +2 -0
  28. package/dist/assets/Network-DNg2yCKK.js +1 -0
  29. package/dist/assets/NodeAddonsSettingsPanel-B_e7Y4br.js +1 -0
  30. package/dist/assets/NodeInferenceDevices-DTEpjg6z.js +1 -0
  31. package/dist/assets/Notifications-DZywZPYK.js +1 -0
  32. package/dist/assets/Pipeline-BGd2zTfL.js +1 -0
  33. package/dist/assets/Plates-Dgn8ovPo.js +1 -0
  34. package/dist/assets/PrivacyMaskSettings-aqN_KJa8.js +1 -0
  35. package/dist/assets/{ProviderIcon-X2_0sDU9.js → ProviderIcon-CSVJE_O0.js} +1 -1
  36. package/dist/assets/Recordings-WBG1aCx6.js +1 -0
  37. package/dist/assets/ScanNetworkModal-BY3gf03t.js +1 -0
  38. package/dist/assets/SceneMonitorEditor-BZimh2ck.js +1 -0
  39. package/dist/assets/Scenes-DeCu0w94.js +1 -0
  40. package/dist/assets/{SchemaTabBar-BrF8ZUiR.js → SchemaTabBar-DC28nb9j.js} +1 -1
  41. package/dist/assets/Settings-BVd4lsaN.js +1 -0
  42. package/dist/assets/{Showroom-7m0amlh4.js → Showroom-DFG_5cnr.js} +1 -1
  43. package/dist/assets/{StableTabPanel-p16epZGa.js → StableTabPanel-Dc954AQl.js} +1 -1
  44. package/dist/assets/{StatusBadge-Dcr2bw58.js → StatusBadge-CeptlsPG.js} +1 -1
  45. package/dist/assets/{Terminal-BkKFvIvp.js → Terminal-QEGYVgmV.js} +2 -2
  46. package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-DbudtQGC.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-C6w2JXhl.js} +1 -1
  47. package/dist/assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js +1 -0
  48. package/dist/assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_client__loadShare__.js-8kv6pj9W.js +6 -0
  49. package/dist/assets/{_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.js-DkcC9IVF.js → _virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.js-DN3UpM8E.js} +1 -1
  50. package/dist/assets/{_virtual_mf___mfe_internal__admin_ui_host__loadShare__react_mf_2_dom__loadShare__.js-D9-wIasd.js → _virtual_mf___mfe_internal__admin_ui_host__loadShare__react_mf_2_dom__loadShare__.js-BsvPG3BX.js} +1 -1
  51. package/dist/assets/{_virtual_mf___mfe_internal__admin_ui_host__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-DstpX0yz.js → _virtual_mf___mfe_internal__admin_ui_host__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-DtoGixA1.js} +1 -1
  52. package/dist/assets/{_virtual_mf___mfe_internal__admin_ui_host__loadShare__react_mf_2_konva__loadShare__.js-BBBrLLEa.js → _virtual_mf___mfe_internal__admin_ui_host__loadShare__react_mf_2_konva__loadShare__.js-PmiLsYEK.js} +1 -1
  53. package/dist/assets/{authed-media-B8T1dsFh.js → authed-media-BPd_L9jr.js} +1 -1
  54. package/dist/assets/{bell-ring-CUDpUeWG.js → bell-ring-BdqqbAsS.js} +1 -1
  55. package/dist/assets/{boxes-pkqHQIKP.js → boxes-Di4kSEDf.js} +1 -1
  56. package/dist/assets/build-cluster-graph-BgVD-LXn.js +7 -0
  57. package/dist/assets/circle-check-big-C6H3pgU0.js +1 -0
  58. package/dist/assets/{circle-x-R5CNfb24.js → circle-x-BZtdhG1q.js} +1 -1
  59. package/dist/assets/clock-Dkewc-gM.js +1 -0
  60. package/dist/assets/{copy-DQHP39o_.js → copy-B3EObRTl.js} +1 -1
  61. package/dist/assets/{debounced-invalidate-BWBc5uaY.js → debounced-invalidate-kboD8U--.js} +1 -1
  62. package/dist/assets/dist-CZzC-g02.js +345 -0
  63. package/dist/assets/{dist-DqwPCDLL.js → dist-DG2WQw7F.js} +1 -1
  64. package/dist/assets/{download-D8qqR37g.js → download-zOlfMwmw.js} +1 -1
  65. package/dist/assets/{face-bbox-GlN6DJbE.js → face-bbox-Bdb4p_vZ.js} +1 -1
  66. package/dist/assets/{hard-drive-DDGRH4_v.js → hard-drive-CEELYlZc.js} +1 -1
  67. package/dist/assets/{hostInit-BYA5grd_.js → hostInit-DQGrrD-J.js} +1 -1
  68. package/dist/assets/index-BfeBVcjY.js +4 -0
  69. package/dist/assets/index-C_00GTmn.css +2 -0
  70. package/dist/assets/{key-round-DBnD5hXc.js → key-round-B4uUkd69.js} +1 -1
  71. package/dist/assets/{mf-entry-bootstrap-0-834af401.js → mf-entry-bootstrap-0-f9457985.js} +2 -2
  72. package/dist/assets/{pencil-hCEUn1IA.js → pencil-BFtrhTcL.js} +1 -1
  73. package/dist/assets/play-CAOvtlt9.js +1 -0
  74. package/dist/assets/player-overlays-CYHBR24b.js +1 -0
  75. package/dist/assets/plus-CT9BkTdL.js +1 -0
  76. package/dist/assets/power-C3GOj5RH.js +1 -0
  77. package/dist/assets/{radio-CPfefoam.js → radio-D6rodlbU.js} +1 -1
  78. package/dist/assets/{refresh-cw-C2McgRco.js → refresh-cw-BBMiW3q-.js} +1 -1
  79. package/dist/assets/remoteEntry-96xMdM9r.js +1 -0
  80. package/dist/assets/rotate-ccw-B3RVJZHZ.js +1 -0
  81. package/dist/assets/rotate-cw-Dt9c2DJW.js +1 -0
  82. package/dist/assets/search-DTWCy4IW.js +1 -0
  83. package/dist/assets/send-5Q9K_RF0.js +1 -0
  84. package/dist/assets/{server-CsRBtCL_.js → server-DVsXQl7s.js} +1 -1
  85. package/dist/assets/square-CEELVm2C.js +1 -0
  86. package/dist/assets/{square-check-big-BjxSMbRP.js → square-check-big-B39UzQnv.js} +1 -1
  87. package/dist/assets/src-gHOaseTq.js +67 -0
  88. package/dist/assets/src-k7u3Lt4U.js +345 -0
  89. package/dist/assets/{undo-2-CZfJ2e4-.js → undo-2-DgZBtWUR.js} +1 -1
  90. package/dist/assets/{upload-B5vgLYAF.js → upload-xzQ3maRZ.js} +1 -1
  91. package/dist/assets/{useBodyScrollLock-B_rtaBV1.js → useBodyScrollLock-BDocajQE.js} +1 -1
  92. package/dist/assets/{useEventInvalidation-Dx_csywq.js → useEventInvalidation-HvWohuH6.js} +1 -1
  93. package/dist/assets/{video-BjqUkGjh.js → video-CRvcPI-y.js} +1 -1
  94. package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-6SojcGBS.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-B_q_nV5G.js} +2 -2
  95. package/dist/assets/{wifi-Nvsx1sGJ.js → wifi-gnm9Xr91.js} +1 -1
  96. package/dist/assets/{zap-CnaB8TAK.js → zap-D_23PdBs.js} +1 -1
  97. package/dist/index.html +6 -6
  98. package/dist/server/addon.js +356 -40
  99. package/package.json +1 -1
  100. package/dist/assets/AddLocationWizard-Cmdtjb0W.js +0 -1
  101. package/dist/assets/AddonCollectionPage-y8thZKiD.js +0 -1
  102. package/dist/assets/Addons-B9wNlAOy.js +0 -1
  103. package/dist/assets/AiProfiles-XHdOJhGT.js +0 -1
  104. package/dist/assets/BrokerForm-BhfOMuh3.js +0 -1
  105. package/dist/assets/Build-CZbt5dnz.js +0 -32
  106. package/dist/assets/Capabilities-DW_cF8pr.js +0 -1
  107. package/dist/assets/CapabilityBadges-CLqqoyhl.js +0 -1
  108. package/dist/assets/Cluster-wbUSa2Re.js +0 -1
  109. package/dist/assets/Data-DanAozV1.js +0 -1
  110. package/dist/assets/DetectionIntelligence-CTmhHAmP.js +0 -1
  111. package/dist/assets/DeviceDetail-Ddl_t3v8.js +0 -2
  112. package/dist/assets/Devices-m7dylGih.js +0 -1
  113. package/dist/assets/EmbedPlayerPage-mrUvuhmS.js +0 -1
  114. package/dist/assets/Events-CabU88J8.js +0 -1
  115. package/dist/assets/FormBuilder-DaUdmatu.js +0 -1
  116. package/dist/assets/Identity-Ds8l4JNd.js +0 -1
  117. package/dist/assets/IntegrationDetail-DjUM9SnA.js +0 -1
  118. package/dist/assets/Integrations-C6MckNDg.js +0 -1
  119. package/dist/assets/Integrations-CVUNXry0.js +0 -1
  120. package/dist/assets/Logs-DU2VOlmq.js +0 -1
  121. package/dist/assets/MotionZonesSettings-DVkvgKnc.js +0 -1
  122. package/dist/assets/MyAccess-BhlABS_W.js +0 -2
  123. package/dist/assets/Network-D0gir4s-.js +0 -1
  124. package/dist/assets/NodeAddonsSettingsPanel-CtO75ohk.js +0 -1
  125. package/dist/assets/NodeInferenceDevices-qUEYtq1P.js +0 -1
  126. package/dist/assets/Notifications-CwdeYxsw.js +0 -1
  127. package/dist/assets/Pipeline-vgsETpZ3.js +0 -1
  128. package/dist/assets/Plates-B5Uqs4w7.js +0 -1
  129. package/dist/assets/PrivacyMaskSettings-fIxue0-D.js +0 -1
  130. package/dist/assets/Recordings-BPztmXFS.js +0 -1
  131. package/dist/assets/ScanNetworkModal-ByoxTvyg.js +0 -1
  132. package/dist/assets/Settings-Be5E8nSp.js +0 -1
  133. package/dist/assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DTufy7Ue.js +0 -1
  134. package/dist/assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_client__loadShare__.js-CSJxMxg6.js +0 -6
  135. package/dist/assets/build-cluster-graph-BWM8pGFr.js +0 -7
  136. package/dist/assets/circle-check-big-CobJ2qp-.js +0 -1
  137. package/dist/assets/clock-CUnxvjbi.js +0 -1
  138. package/dist/assets/dist-Bh4pP3Lk.js +0 -345
  139. package/dist/assets/index-BhFZLP1H.css +0 -2
  140. package/dist/assets/index-kOLbwjEX.js +0 -4
  141. package/dist/assets/play-N-aCIv2P.js +0 -1
  142. package/dist/assets/player-overlays-BfDIfiH7.js +0 -1
  143. package/dist/assets/plus-BwBDSSUm.js +0 -1
  144. package/dist/assets/power-BftD_p5X.js +0 -1
  145. package/dist/assets/remoteEntry-DFgCYg0q.js +0 -1
  146. package/dist/assets/rotate-ccw-xAFA8eXV.js +0 -1
  147. package/dist/assets/rotate-cw-9bzblRxY.js +0 -1
  148. package/dist/assets/search-B4tuDAaK.js +0 -1
  149. package/dist/assets/send-D_iwM8fS.js +0 -1
  150. package/dist/assets/square-9Bf62Szc.js +0 -1
  151. package/dist/assets/src-BbMtrcN-.js +0 -67
  152. package/dist/assets/src-BtBhNsTu.js +0 -345
@@ -12185,6 +12185,17 @@ var LlmImageSchema = object({
12185
12185
  bytes: _instanceof(Uint8Array),
12186
12186
  mimeType: string()
12187
12187
  });
12188
+ /**
12189
+ * Retry policy. `enabled: false` is NOT the same as `maxAttempts: 1` in intent —
12190
+ * the flag is what a consumer table flips, the count is what the operator tunes.
12191
+ * A retry doubles the wall time of a call, so the two gates that run inside a
12192
+ * notification's budget keep it off (see `CONSUMER_RETRY_POLICY` in addon-ai).
12193
+ */
12194
+ var LlmRetryPolicySchema = object({
12195
+ enabled: boolean().default(false),
12196
+ /** Total attempts INCLUDING the first. 1 = no retry. */
12197
+ maxAttempts: number().int().min(1).max(5).default(1)
12198
+ });
12188
12199
  var LlmGenerateBaseInputSchema = object({
12189
12200
  /** Collection routing (the notification-output posture). */
12190
12201
  addonId: string().optional(),
@@ -12199,7 +12210,28 @@ var LlmGenerateBaseInputSchema = object({
12199
12210
  jsonSchema: record(string(), unknown()).optional(),
12200
12211
  /** Per-call override of the profile default. */
12201
12212
  maxTokens: number().int().positive().optional(),
12202
- temperature: number().optional()
12213
+ temperature: number().optional(),
12214
+ /** Per-call override of the profile default (nucleus sampling). */
12215
+ topP: number().min(0).max(1).optional(),
12216
+ /** Per-call override of the profile default (top-k sampling). */
12217
+ topK: number().int().positive().optional(),
12218
+ /** Per-call override of `profile.timeoutMs` — the total generation bound. */
12219
+ timeoutMs: number().int().positive().optional(),
12220
+ /** Per-call override; beats both the consumer table and the profile. */
12221
+ retry: LlmRetryPolicySchema.optional(),
12222
+ /**
12223
+ * Caller-minted id that makes this generation CANCELLABLE.
12224
+ *
12225
+ * Without it a caller that stops waiting cannot stop the work: the gates race
12226
+ * the call against 8 s and free their own slot when the timer wins, while the
12227
+ * generation upstream keeps running to `profile.timeoutMs` — 60 s by default,
12228
+ * on a single-threaded local model. The per-camera bound then counts WAITS,
12229
+ * not generations, and the real load is unbounded.
12230
+ *
12231
+ * `AbortSignal` cannot cross a process boundary; an id can. Pass one here and
12232
+ * `llm.cancel({ requestId })` tears the socket down.
12233
+ */
12234
+ requestId: string().optional()
12203
12235
  });
12204
12236
  /**
12205
12237
  * `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
@@ -12238,8 +12270,49 @@ var ManagedRuntimeConfigSchema = object({
12238
12270
  gpuLayers: number().int().default(0),
12239
12271
  /** Default: cpus-2, clamped ≥1 (resolved node-side). */
12240
12272
  threads: number().int().optional(),
12241
- /** Concurrent slots. */
12273
+ /** Concurrent slots (`--parallel`). */
12242
12274
  parallel: number().int().default(1),
12275
+ /** Logical batch size (`-b`). Larger = faster prompt ingest, more RAM. */
12276
+ batchSize: number().int().positive().optional(),
12277
+ /** Physical batch / micro-batch (`-ub`). */
12278
+ ubatchSize: number().int().positive().optional(),
12279
+ /**
12280
+ * `--flash-attn`. Cuts KV-cache memory on the backends that implement it and
12281
+ * is a no-op elsewhere, so it is offered rather than assumed.
12282
+ */
12283
+ flashAttention: boolean().default(false),
12284
+ /**
12285
+ * `--mlock`. Pins the weights in RAM so the OS cannot page them out mid
12286
+ * inference. Costs the full model size in resident memory — which is exactly
12287
+ * what the RAM budget is counting.
12288
+ */
12289
+ mlock: boolean().default(false),
12290
+ /**
12291
+ * `--no-mmap`. Reads the whole GGUF up front instead of mapping it. Slower to
12292
+ * start, but avoids the page-fault stalls a network or spinning-disk model
12293
+ * store produces on every first token.
12294
+ */
12295
+ noMmap: boolean().default(false),
12296
+ /** `--cache-type-k` / `--cache-type-v` — quantising the KV cache is the
12297
+ * cheapest way to fit a longer context in the same RAM. */
12298
+ cacheTypeK: _enum([
12299
+ "f32",
12300
+ "f16",
12301
+ "q8_0",
12302
+ "q5_1",
12303
+ "q5_0",
12304
+ "q4_1",
12305
+ "q4_0"
12306
+ ]).optional(),
12307
+ cacheTypeV: _enum([
12308
+ "f32",
12309
+ "f16",
12310
+ "q8_0",
12311
+ "q5_1",
12312
+ "q5_0",
12313
+ "q4_1",
12314
+ "q4_0"
12315
+ ]).optional(),
12243
12316
  /** Else lazy: first generate boots it. */
12244
12317
  autoStart: boolean().default(false),
12245
12318
  /** 0 = never; frees RAM after quiet periods. */
@@ -12327,10 +12400,44 @@ var LlmProfileSchema = object({
12327
12400
  baseUrl: string().optional(),
12328
12401
  /** ConfigUISchema type:'password' — never round-trips (spec §5). */
12329
12402
  apiKey: string().optional(),
12403
+ /** Vision on/off. A vision call against a `false` profile is REFUSED, never
12404
+ * degraded to text — that shipped once and produced a confident answer to a
12405
+ * question about a picture nobody sent. */
12330
12406
  supportsVision: boolean(),
12331
12407
  temperature: number().min(0).max(2).optional(),
12408
+ /** Nucleus sampling. Every wire we speak has it. */
12409
+ topP: number().min(0).max(1).optional(),
12410
+ /** Top-k sampling. Carried only by the wires that have it — NEITHER OpenAI
12411
+ * wire does, and the client drops it there (measured: the request body gets
12412
+ * `top_p` and no `top_k`). The profile editor hides the field wherever it
12413
+ * would change nothing; `KINDS_WITH_TOP_K` is the single owner of that list. */
12414
+ topK: number().int().positive().optional(),
12332
12415
  maxTokens: number().int().positive().optional(),
12416
+ /** Prompt context window. Advisory for cloud kinds (they enforce their own);
12417
+ * for `managed-local` it is the llama.cpp `--ctx-size` the runtime starts
12418
+ * the model with, so it is the one field that changes a PROCESS. */
12419
+ contextLength: number().int().positive().optional(),
12420
+ /** Default system prompt. A caller's `system` REPLACES it (never appends —
12421
+ * two system prompts fighting is worse than either alone). */
12422
+ systemPrompt: string().optional(),
12423
+ /** Total generation bound — the only one a unary call has. */
12333
12424
  timeoutMs: number().int().positive().default(6e4),
12425
+ /** Wait for response headers only. */
12426
+ connectTimeoutMs: number().int().positive().default(1e4),
12427
+ /** Accepted, but no output yet — a cold GPU load lives here. */
12428
+ firstTokenTimeoutMs: number().int().positive().default(12e4),
12429
+ /** Output started then stopped. */
12430
+ idleTimeoutMs: number().int().positive().default(6e4),
12431
+ /** Profile-level default. The per-consumer table and a per-call override
12432
+ * both beat it — see `resolveRetryPolicy`. */
12433
+ retry: LlmRetryPolicySchema.default({
12434
+ enabled: false,
12435
+ maxAttempts: 1
12436
+ }),
12437
+ /** Whether this profile may use tools. The tool-call plumbing rides the
12438
+ * library; the REGISTRY of callable tools is ours and is empty in v1, so a
12439
+ * `true` here buys the wiring, not behaviour, until tools are registered. */
12440
+ toolsEnabled: boolean().default(false),
12334
12441
  extraHeaders: record(string(), string()).optional(),
12335
12442
  /** kind === 'managed-local' only (spec §4). */
12336
12443
  runtime: ManagedRuntimeConfigSchema.optional()
@@ -12392,7 +12499,10 @@ var ProfileRefInputSchema = object({
12392
12499
  addonId: string(),
12393
12500
  profileId: string()
12394
12501
  });
12395
- method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
12502
+ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({
12503
+ addonId: string().optional(),
12504
+ requestId: string()
12505
+ }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
12396
12506
  kind: "mutation",
12397
12507
  auth: "admin"
12398
12508
  }), method(ProfileRefInputSchema, _void(), {
@@ -14128,28 +14238,36 @@ var NcOccupancyConditionSchema = object({
14128
14238
  /**
14129
14239
  * Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
14130
14240
  *
14131
- * Operator-approved vocabulary (2026-08-12, option A — the same one the
14132
- * reference notifier uses, so an operator moving between them re-uses what
14133
- * they already know): a rule matches when, over a sampling window of
14134
- * `samplingSeconds`, at least `hitPercent`% of the audio samples in that
14135
- * window are HITS. A sample is a hit when it satisfies BOTH present filters:
14136
- *
14137
- * - `dbThreshold` its level is at or above this many dBFS (see
14138
- * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale);
14139
- * - `labels` the classifier put at least one of these labels on it.
14140
- *
14141
- * Both are OPTIONAL and independent, which is the point of the shape: a
14142
- * loudness rule ("something loud at 3am") needs no model to be right, and a
14143
- * label rule ("a dog barked") needs no threshold. **Fail-closed when NEITHER
14144
- * is given** a window in which every sample is trivially a hit would fire on
14145
- * silence, so the engine refuses such a condition rather than notifying on
14146
- * nothing (the schema cannot express "at least one of" without becoming a
14147
- * ZodEffects the cap path would have to special-case).
14148
- *
14149
- * `hitPercent` is over the samples the window actually HOLDS, and the window
14150
- * must be FULL before it can match a window that has been open for two
14151
- * seconds of its ten is 100% of nothing, and firing on it would make
14152
- * `samplingSeconds` decorative.
14241
+ * **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
14242
+ * rule is in is not a stored field it is WHICH FILTER the rule carries, so
14243
+ * there is no second switch that can disagree with the first and every rule
14244
+ * authored before the decision migrates for free (`audioModeOf`):
14245
+ *
14246
+ * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14247
+ * classifier labels with one of them. No window, no percentage:
14248
+ * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14249
+ * `throttle` cooldown is the only brake. The per-label confidence floor is
14250
+ * the analyzer's (`classificationMinScore`, per device) — a label only
14251
+ * reaches this condition if the classifier was already confident enough.
14252
+ * - **LEVEL mode `dbThreshold` present, no labels.** The sampling window IS
14253
+ * the condition: at least `hitPercent`% of the samples over
14254
+ * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
14255
+ * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
14256
+ * must be FULL before it can match a window open for two of its ten
14257
+ * seconds is 100% of nothing.
14258
+ *
14259
+ * **Why label mode has no window.** It had one, and it never fired: the
14260
+ * analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
14261
+ * of them per episode, even through continuous crying. The measured maximum
14262
+ * `hitPercent` over the whole live history was 40 — under the shipped default
14263
+ * of 60, so a label rule could not fire at all, ever. A percentage of frames is
14264
+ * the wrong question to ask of a sparse classifier.
14265
+ *
14266
+ * **Fail-closed when NEITHER is given** — every sample would be a trivial hit
14267
+ * and the rule would fire on silence. The schema cannot express "exactly one
14268
+ * of" without becoming a ZodEffects the cap path would have to special-case, so
14269
+ * the exclusivity is enforced where every editor writes (`patchAudio`) and a
14270
+ * legacy rule carrying both resolves to LABEL (the mode that fires).
14153
14271
  *
14154
14272
  * Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
14155
14273
  * `audio-*` ids). Both spellings are accepted — the matcher normalizes the
@@ -14157,13 +14275,13 @@ var NcOccupancyConditionSchema = object({
14157
14275
  * an operator who typed `dog` mean the same thing.
14158
14276
  */
14159
14277
  var NcAudioConditionSchema = object({
14160
- /** Audio macro labels; absent = any sound (level-only rule). */
14278
+ /** LABEL MODE: audio macro labels. Present fires on the first labelled frame. */
14161
14279
  labels: array(string().min(1)).min(1).optional(),
14162
- /** Level floor in dBFS (negative-going, `0` = full scale); absent = any level. */
14280
+ /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14163
14281
  dbThreshold: number().min(-96).max(0).optional(),
14164
- /** Percentage of the window's samples that must be hits (1–100). */
14282
+ /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14165
14283
  hitPercent: number().int().min(1).max(100).default(60),
14166
- /** Length of the sampling window in seconds. */
14284
+ /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14167
14285
  samplingSeconds: number().int().min(1).max(300).default(10)
14168
14286
  });
14169
14287
  /**
@@ -24119,14 +24237,50 @@ method(object({
24119
24237
  * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
24120
24238
  * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
24121
24239
  *
24122
- * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) the framework
24123
- * derives the device-detail contribution; the provider carries NO hand-written
24124
- * settings-contribution methods. `status.kind:'push'` the engine pushes on
24125
- * every hysteresis flip / availability change; consumers never poll.
24126
- */
24127
- /** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
24128
- * be added without a wire break (matching falls back to any-condition refs). */
24240
+ * **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
24241
+ * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
24242
+ * for the thing: a scene is a standing question about the property ("is the bin
24243
+ * still out"), and the operator's question is "which of my scenes have tripped",
24244
+ * across every camera at once — not "what does camera 617 think". Buried one
24245
+ * camera deep it also could not be found. The surface is now a top-level admin
24246
+ * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
24247
+ * picks the camera inside the create flow, the same shape Events and Faces have.
24248
+ *
24249
+ * The consequence to keep in mind: `host/scene-monitor-editor` is gone from
24250
+ * `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
24251
+ * directions, so a registration nobody declares fails exactly as loudly as a
24252
+ * declaration nobody registers. The editor is imported directly by the page.
24253
+ *
24254
+ * `status.kind:'push'` — the engine pushes on every hysteresis flip /
24255
+ * availability change; consumers never poll.
24256
+ */
24257
+ /** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
24258
+ * captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
24259
+ * Open by design so more can be added without a wire break.
24260
+ *
24261
+ * Matching does NOT fall back across conditions: cross-condition cosines are
24262
+ * not comparable, so "I have never seen this scene in this light" is reported
24263
+ * as `unknown`, never guessed. A day reference scored against an IR frame
24264
+ * collapses the cosine and would latch a false alarm every single night. */
24129
24265
  var SceneConditionSchema = string();
24266
+ /** `matched` = the baseline is what we see; `diverged` = it demonstrably is not;
24267
+ * `unknown` = we cannot judge (no reference for this condition, encoder model
24268
+ * changed, view shifted, no snapshot). `unknown` is a real value, not a null,
24269
+ * and never counts toward hysteresis in either direction. */
24270
+ var SceneVerdictSchema = _enum([
24271
+ "matched",
24272
+ "diverged",
24273
+ "unknown"
24274
+ ]);
24275
+ /** Why a scene cannot judge. Named, because this feature's failure mode is
24276
+ * silence that reads as "nothing has happened". */
24277
+ var SceneUnavailableSchema = _enum([
24278
+ "no-reference-for-condition",
24279
+ "view-shifted",
24280
+ "no-vision-profile",
24281
+ "encoder-model-changed",
24282
+ "no-snapshot"
24283
+ ]);
24130
24284
  /** One captured reference — condition-tagged, model-version-gated. `embedding`
24131
24285
  * is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
24132
24286
  var SceneReferenceSchema = object({
@@ -24134,7 +24288,14 @@ var SceneReferenceSchema = object({
24134
24288
  modelId: string(),
24135
24289
  condition: SceneConditionSchema,
24136
24290
  capturedAt: number(),
24137
- thumbnailMediaId: string().optional()
24291
+ thumbnailMediaId: string().optional(),
24292
+ /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
24293
+ * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
24294
+ * normalized rect frame a different piece of world, and the scene would
24295
+ * diverge forever with a perfectly plausible cosine. Checked LAZILY, only
24296
+ * when hysteresis is about to flip — one extra encode per candidate
24297
+ * transition, not per poll. */
24298
+ anchorEmbedding: array(number()).optional()
24138
24299
  });
24139
24300
  var SceneMonitorStateSchema = object({
24140
24301
  id: string(),
@@ -24156,6 +24317,25 @@ var SceneCheckSchema = discriminatedUnion("mode", [object({
24156
24317
  profileId: string().optional(),
24157
24318
  hysteresisCount: number().int().positive()
24158
24319
  })]);
24320
+ var SCENE_DEFAULT_ANCHOR_THRESHOLD = .85;
24321
+ /**
24322
+ * Vision-model adjudication of a candidate flip. Field names deliberately
24323
+ * mirror `NcConfirmSchema` so an operator meets one vocabulary, not two.
24324
+ *
24325
+ * `onTimeout` defaults to **'hold'**, the OPPOSITE of `NcConfirmGate`'s
24326
+ * fail-open: a notification suppressed is the worse error there, but a vision
24327
+ * model that timed out has not told us the bin is gone, and a latch is a
24328
+ * stateful claim that costs the operator a trip to reset.
24329
+ */
24330
+ var SceneConfirmSchema = object({
24331
+ enabled: boolean().default(false),
24332
+ prompt: string().min(1).max(1e3),
24333
+ profileId: string().optional(),
24334
+ timeoutMs: number().int().min(1e3).max(2e4).default(8e3),
24335
+ maxImagePx: number().int().min(64).max(2048).default(448),
24336
+ /** What a timeout / unavailable model means for the PENDING flip. */
24337
+ onTimeout: _enum(["flip", "hold"]).default("hold")
24338
+ });
24159
24339
  var SceneMonitorSchema = object({
24160
24340
  id: string(),
24161
24341
  label: string(),
@@ -24174,7 +24354,41 @@ var SceneMonitorSchema = object({
24174
24354
  lastConfidence: number().nullable(),
24175
24355
  currentCondition: SceneConditionSchema.nullable(),
24176
24356
  availability: _enum(["ok", "unavailable"]),
24177
- unavailableReason: string().nullable()
24357
+ unavailableReason: string().nullable(),
24358
+ /** Which state is "the initial screen". `null` until the first capture. */
24359
+ baselineStateId: string().nullable(),
24360
+ /** Which boolean drives notification rules and any export. */
24361
+ emit: _enum(["latched", "live"]).default("latched"),
24362
+ /** Live: does the region match the baseline RIGHT NOW. */
24363
+ verdict: SceneVerdictSchema,
24364
+ /** Has it been `diverged` at least once since `armedAt` — the operator's boolean. */
24365
+ latched: boolean(),
24366
+ /** Last reset (or creation). */
24367
+ armedAt: number(),
24368
+ divergedAt: number().nullable(),
24369
+ restoredAt: number().nullable(),
24370
+ /** A check is only COUNTED when the device has been quiet this long. Motion
24371
+ * during the window DISCARDS the observation — a car pulling up in front of
24372
+ * the bin must not be able to spend hysteresis credit. */
24373
+ quietSeconds: number().int().min(0).max(3600).default(60),
24374
+ /** An observation only advances the pending count when it is at least this
24375
+ * far from the previously counted one, so N agreeing checks span real time
24376
+ * rather than N adjacent polls inside one occlusion. */
24377
+ minObservationSpacingSec: number().int().min(0).max(3600).default(120),
24378
+ /** Vision-model adjudication of a candidate flip. Similarity primary only. */
24379
+ confirm: SceneConfirmSchema.optional(),
24380
+ /** Whole-frame anchor cosine below which a flip is REFUSED as `view-shifted`. */
24381
+ anchorThreshold: number().min(0).max(1).default(SCENE_DEFAULT_ANCHOR_THRESHOLD),
24382
+ /** Clear the latch on its own when the scene matches again? Default false —
24383
+ * `restoredAt` and the `scene-restored` edge are recorded regardless, so an
24384
+ * automation can react to the bin coming back without the operator's own
24385
+ * alarm silently clearing itself. */
24386
+ autoRestore: boolean().default(false),
24387
+ /** Named cause when `verdict === 'unknown'`. */
24388
+ unavailable: SceneUnavailableSchema.nullable(),
24389
+ /** Conditions that have at least one comparable reference — the coverage line
24390
+ * ("day ✓ · ir ✓ · dusk ✗") that turns a silent fallback into a visible fact. */
24391
+ coveredConditions: array(SceneConditionSchema)
24178
24392
  });
24179
24393
  var SceneMonitorStatusSchema = object({
24180
24394
  monitors: array(SceneMonitorSchema),
@@ -24207,7 +24421,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
24207
24421
  "both"
24208
24422
  ]).optional(),
24209
24423
  checkIntervalSec: number().optional(),
24210
- check: SceneCheckSchema.optional()
24424
+ check: SceneCheckSchema.optional(),
24425
+ emit: _enum(["latched", "live"]).optional(),
24426
+ quietSeconds: number().int().min(0).max(3600).optional(),
24427
+ minObservationSpacingSec: number().int().min(0).max(3600).optional(),
24428
+ anchorThreshold: number().min(0).max(1).optional(),
24429
+ autoRestore: boolean().optional(),
24430
+ /** `null` clears the vision-model adjudicator. */
24431
+ confirm: SceneConfirmSchema.nullable().optional()
24211
24432
  })
24212
24433
  }), _void(), {
24213
24434
  kind: "mutation",
@@ -24244,6 +24465,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
24244
24465
  }), _void(), {
24245
24466
  kind: "mutation",
24246
24467
  auth: "admin"
24468
+ }), method(object({
24469
+ deviceId: number(),
24470
+ monitorId: string(),
24471
+ /** Defaults to TRUE at the provider seam — see `SCENE_RESET_RECAPTURES`. */
24472
+ recapture: boolean().optional()
24473
+ }), _void(), {
24474
+ kind: "mutation",
24475
+ auth: "admin"
24247
24476
  });
24248
24477
  /**
24249
24478
  * Per-stage gating mode applied to the zones a rule references.
@@ -24552,12 +24781,64 @@ var NetworkAddressSchema = object({
24552
24781
  family: string(),
24553
24782
  internal: boolean()
24554
24783
  });
24784
+ /**
24785
+ * Provenance of the site coordinates, and the whole reason this is not just two
24786
+ * numbers.
24787
+ *
24788
+ * - `operator-set` — a human typed it, or accepted a detection. Authoritative;
24789
+ * nothing overwrites it.
24790
+ * - `derived-from-ip` — the hub geolocated its own public IP once, because a
24791
+ * default that is right to a few kilometres beats the coarse UTC clock split
24792
+ * the sun-times consumers otherwise fall back to.
24793
+ *
24794
+ * The UI shows which one it is. An operator who cannot tell a guess from their
24795
+ * own input will eventually trust the guess.
24796
+ */
24797
+ var SiteLocationSourceSchema = _enum(["operator-set", "derived-from-ip"]);
24798
+ /**
24799
+ * The read shape: the location plus the honest state of the one-shot derivation.
24800
+ *
24801
+ * `derivationAttemptedAt` is what makes the "one call, ever" contract
24802
+ * inspectable. When it is set and `location` is null, the geo-IP lookup ran and
24803
+ * failed; the hub will NOT try again on its own — the fallback is declared
24804
+ * (consumers degrade to their own last resort) and the operator either types the
24805
+ * coordinates or presses detect.
24806
+ */
24807
+ var SiteLocationStatusSchema = object({
24808
+ location: object({
24809
+ /** WGS84 decimal degrees. */
24810
+ latitude: number().min(-90).max(90),
24811
+ longitude: number().min(-180).max(180),
24812
+ source: SiteLocationSourceSchema,
24813
+ /** Epoch ms the value was last written. */
24814
+ updatedAt: number(),
24815
+ /**
24816
+ * Human-readable place the geo-IP service reported ("Napoli, IT"). Display
24817
+ * only — never parsed, never matched on. Absent for an operator-typed value.
24818
+ */
24819
+ label: string().optional()
24820
+ }).nullable(),
24821
+ derivationAttemptedAt: number().nullable(),
24822
+ /** Why the last derivation failed, for the UI to show instead of a shrug. */
24823
+ derivationError: string().nullable()
24824
+ });
24825
+ /** `null` clears the location and re-arms nothing — the derivation stays spent. */
24826
+ var SetSiteLocationInputSchema = object({
24827
+ latitude: number().min(-90).max(90),
24828
+ longitude: number().min(-180).max(180)
24829
+ }).nullable();
24555
24830
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
24556
24831
  kind: "mutation",
24557
24832
  auth: "admin"
24558
24833
  }), method(_void(), _void(), {
24559
24834
  kind: "mutation",
24560
24835
  auth: "admin"
24836
+ }), method(_void(), SiteLocationStatusSchema), method(SetSiteLocationInputSchema, SiteLocationStatusSchema, {
24837
+ kind: "mutation",
24838
+ auth: "admin"
24839
+ }), method(_void(), SiteLocationStatusSchema, {
24840
+ kind: "mutation",
24841
+ auth: "admin"
24561
24842
  });
24562
24843
  object({
24563
24844
  /** True when the device's tamper switch / case-open contact is
@@ -27287,6 +27568,12 @@ Object.freeze({
27287
27568
  addonId: null,
27288
27569
  access: "create"
27289
27570
  },
27571
+ "llm.cancel": {
27572
+ capName: "llm",
27573
+ capScope: "system",
27574
+ addonId: null,
27575
+ access: "create"
27576
+ },
27290
27577
  "llm.deleteModel": {
27291
27578
  capName: "llm",
27292
27579
  capScope: "system",
@@ -29537,6 +29824,12 @@ Object.freeze({
29537
29824
  addonId: null,
29538
29825
  access: "create"
29539
29826
  },
29827
+ "sceneMonitor.resetScene": {
29828
+ capName: "scene-monitor",
29829
+ capScope: "device",
29830
+ addonId: null,
29831
+ access: "delete"
29832
+ },
29540
29833
  "sceneMonitor.updateScene": {
29541
29834
  capName: "scene-monitor",
29542
29835
  capScope: "device",
@@ -30215,6 +30508,12 @@ Object.freeze({
30215
30508
  addonId: null,
30216
30509
  access: "create"
30217
30510
  },
30511
+ "system.detectSiteLocation": {
30512
+ capName: "system",
30513
+ capScope: "system",
30514
+ addonId: null,
30515
+ access: "create"
30516
+ },
30218
30517
  "system.featureFlags": {
30219
30518
  capName: "system",
30220
30519
  capScope: "system",
@@ -30233,6 +30532,12 @@ Object.freeze({
30233
30532
  addonId: null,
30234
30533
  access: "view"
30235
30534
  },
30535
+ "system.getSiteLocation": {
30536
+ capName: "system",
30537
+ capScope: "system",
30538
+ addonId: null,
30539
+ access: "view"
30540
+ },
30236
30541
  "system.health": {
30237
30542
  capName: "system",
30238
30543
  capScope: "system",
@@ -30257,6 +30562,12 @@ Object.freeze({
30257
30562
  addonId: null,
30258
30563
  access: "create"
30259
30564
  },
30565
+ "system.setSiteLocation": {
30566
+ capName: "system",
30567
+ capScope: "system",
30568
+ addonId: null,
30569
+ access: "create"
30570
+ },
30260
30571
  "terminalSession.adoptLegacyMonitor": {
30261
30572
  capName: "terminal-session",
30262
30573
  capScope: "system",
@@ -32219,6 +32530,11 @@ Object.freeze({
32219
32530
  form: "single",
32220
32531
  optional: false
32221
32532
  }],
32533
+ "sceneMonitor.resetScene": [{
32534
+ name: "deviceId",
32535
+ form: "single",
32536
+ optional: false
32537
+ }],
32222
32538
  "sceneMonitor.updateScene": [{
32223
32539
  name: "deviceId",
32224
32540
  form: "single",
@@ -32897,7 +33213,7 @@ var AdminUIAddon = class extends BaseAddon {
32897
33213
  capability: adminUiCapability,
32898
33214
  provider: {
32899
33215
  getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
32900
- getVersion: async () => ({ version: "1.2.58" })
33216
+ getVersion: async () => ({ version: "1.2.60" })
32901
33217
  }
32902
33218
  }];
32903
33219
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-admin-ui",
3
- "version": "1.2.58",
3
+ "version": "1.2.60",
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{La as e,Ma as t,wo as n}from"./src-BbMtrcN-.js";import{I as r,Q as i,S as a,W as o,a as s,o as c,q as l,x as u,z as d}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DTufy7Ue.js";import{p as f}from"./player-overlays-BfDIfiH7.js";import{t as p}from"./FormBuilder-DaUdmatu.js";import{t as m}from"./pencil-hCEUn1IA.js";import{t as h}from"./plus-BwBDSSUm.js";import{t as g}from"./power-BftD_p5X.js";import{P as _,S as v,W as y,i as b,s as x}from"./index-kOLbwjEX.js";var S=y(`star`,[[`path`,{d:`M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z`,key:`r04s7s`}]]);i();function C(e){return e.enabled!==!1}function w(e){return e.isDefault}function T(e,t){let n={id:e.id,type:e.type,displayName:t.displayName??e.displayName,providerId:e.providerId,config:e.config,isDefault:t.isDefault??e.isDefault,isSystem:e.isSystem},r=e.nodeId===void 0?n:{...n,nodeId:e.nodeId},i=t.enabled??e.enabled;return i===void 0?r:{...r,enabled:i}}function E(e){return`${(e/1024**3).toFixed(e>=100*1024**3?0:1)} GB`}function D({location:e}){let t=e.capacity;return t==null?null:a(`span`,{className:`text-[10px] text-foreground-subtle tabular-nums`,children:[E(t.availableBytes),` free of `,E(t.totalBytes)]})}function O(e){let t=e.basePath;if(typeof t==`string`&&t.length>0)return t;let n=Object.keys(e);return n.length===0?``:n.join(`, `)}function k({location:e,cardinality:t,onEdit:n,onDelete:r,onSetDefault:i,onToggleEnabled:o}){let s=O(e.config),c=C(e),l=w(e);return a(`div`,{className:`flex flex-wrap items-center gap-x-3 gap-y-2 px-3 py-2 border-t border-border first:border-t-0 hover:bg-primary/5`,children:[a(`div`,{className:`flex flex-wrap items-center gap-2 min-w-0 flex-1 basis-64`,children:[u(`div`,{className:`w-[88px] shrink-0`,children:e.isDefault?a(`span`,{className:`inline-flex items-center gap-0.5 rounded bg-primary/10 border border-primary/20 px-1.5 py-0.5 text-[10px] font-semibold text-primary uppercase tracking-wide`,title:`Default location for this type`,children:[u(S,{className:`h-2.5 w-2.5 fill-primary`}),`Default`]}):a(`button`,{onClick:()=>i(e),className:`inline-flex items-center gap-0.5 rounded border border-border px-1.5 py-0.5 text-[10px] font-medium text-foreground-subtle hover:text-foreground hover:bg-primary/10 hover:border-primary/30 uppercase tracking-wide`,title:`Promote to default for this type`,children:[u(S,{className:`h-2.5 w-2.5`}),`Set default`]})}),u(`span`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle bg-background border border-border rounded px-1.5 py-0.5 font-mono`,children:e.providerId}),!c&&u(`span`,{className:`text-[10px] uppercase tracking-wide text-warning bg-warning/10 border border-warning/30 rounded px-1.5 py-0.5`,title:`Disabled — nothing new is written here. Existing data stays readable and is still pruned by age.`,children:`disabled`}),e.isSystem&&u(`span`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle bg-background border border-border rounded px-1.5 py-0.5`,title:`System-managed location — cannot be deleted; edit config to relocate`,children:`system`}),e.config.readOnly===!0&&u(`span`,{className:`text-[10px] uppercase tracking-wide text-warning bg-warning/10 border border-warning/30 rounded px-1.5 py-0.5`,title:`Drain mode — reads and age retention continue; nothing new is written here and disk-pressure eviction skips it`,children:`read-only`}),a(`div`,{className:`min-w-0`,children:[u(`div`,{className:`text-xs font-medium text-foreground truncate`,children:e.displayName}),a(`div`,{className:`flex flex-wrap items-center gap-x-2`,children:[s&&u(`span`,{className:`text-[10px] text-foreground-subtle truncate font-mono`,children:s}),u(D,{location:e})]})]})]}),a(`div`,{className:`flex flex-wrap items-center gap-1 shrink-0`,children:[a(`button`,{type:`button`,role:`switch`,"aria-label":`Writes enabled`,"aria-checked":c,disabled:l,onClick:()=>o(e,!c),title:l?`The default location of a type is always enabled — it is the fallback every balancing consumer falls back to. Promote another location to default first.`:c?`Enabled — balancing consumers may write new data here. Click to stop new writes.`:`Disabled — no new data is written here. Existing data stays readable. Click to enable.`,className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium border disabled:cursor-not-allowed ${c?`border-success/30 text-success hover:bg-success/10`:`border-warning/40 text-warning hover:bg-warning/10`}`,children:[u(g,{className:`h-3 w-3`}),c?`Enabled`:`Disabled`]}),!e.isDefault&&a(`button`,{onClick:()=>i(e),className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-foreground-subtle border border-border hover:text-foreground hover:bg-primary/10`,title:`Set as default for this type`,children:[u(S,{className:`h-3 w-3`}),`Set default`]}),a(`button`,{onClick:()=>n(e),className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-foreground-subtle border border-border hover:text-foreground hover:bg-primary/10`,children:[u(m,{className:`h-3 w-3`}),`Edit`]}),!e.isSystem&&t!==`single`&&u(`button`,{onClick:()=>r(e),className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-danger hover:bg-danger/10`,title:`Delete location`,children:u(x,{className:`h-3 w-3`})})]})]})}function A({type:e,displayName:t,description:n,cardinality:r,locations:i,onAdd:s,onEdit:c,onDelete:l,onSetDefault:d,onToggleEnabled:f}){let p=o(()=>{let e=[...i];return e.sort((e,t)=>e.isDefault===t.isDefault?e.displayName.localeCompare(t.displayName):e.isDefault?-1:1),e},[i]);return a(`div`,{className:`rounded-lg border border-border bg-surface`,children:[a(`div`,{className:`flex items-start justify-between border-b border-border px-3 py-2`,children:[a(`div`,{className:`space-y-0.5`,children:[a(`div`,{className:`flex items-center gap-2`,children:[u(`span`,{className:`text-sm font-semibold text-foreground capitalize`,children:t}),u(`span`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle bg-background border border-border rounded px-1.5 py-0.5 font-mono`,children:e})]}),n&&u(`p`,{className:`text-[11px] text-foreground-subtle`,children:n}),r===`single`&&u(`p`,{className:`text-[10px] text-foreground-subtle italic`,children:`Single — one location per type. Edit the existing entry; it can't be split.`})]}),r===`multi`&&a(`button`,{onClick:()=>s(e),className:`inline-flex items-center gap-1 rounded bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground hover:bg-primary/90`,children:[u(h,{className:`h-3 w-3`}),`Add`]})]}),p.length===0?u(`div`,{className:`px-3 py-4 text-center text-[11px] text-foreground-subtle italic`,children:r===`multi`?`No location registered. Click "Add" to register one.`:`No location registered yet — should be auto-seeded at next boot.`}):u(`div`,{children:p.map(e=>u(k,{location:e,cardinality:r,onEdit:c,onDelete:l,onSetDefault:d,onToggleEnabled:f},e.id))})]})}var j=new Set([`proc`,`sys`,`dev`,`run`,`boot`,`lost+found`,`snap`,`bin`,`sbin`,`lib`,`lib32`,`lib64`,`libx32`,`usr`,`etc`,`var`,`opt`,`tmp`,`srv`,`root`,`System`,`Library`,`Applications`,`cores`,`private`]),M=[`recordings`,`data`,`mnt`,`media`,`Volumes`];function N(e){let t=M.findIndex(t=>t===`recordings`?e.startsWith(t):e===t);return t===-1?M.length:t}function P(e){return e.filter(e=>!e.name.startsWith(`.`)&&!j.has(e.name)).toSorted((e,t)=>{let n=N(e.name)-N(t.name);return n===0?e.name.localeCompare(t.name):n}).map(e=>e.path)}i();function F({nodeId:e,value:t,onChange:n}){let i=f().trpcClient,[o,s]=l([]),[c,p]=l(t),[m,h]=l(null),[g,_]=l(null),[v,y]=l(``),b=r(async(t,r)=>{_(null);try{let a=await i.filesystemBrowse.browse.query({nodeId:e,path:t});h(a),p(a.path),r&&n(a.path)}catch(e){_(e instanceof Error?e.message:`browse failed`)}},[i,e,n]);d(()=>{p(t),(async()=>{let n=await i.filesystemBrowse.browse.query({nodeId:e,path:`/`}).then(e=>e).catch(()=>null);if(n){s(P(n.entries)),await b(t.startsWith(`/`)?t:`/`,!1);return}try{let n=await i.filesystemBrowse.listAllowedRoots.query({nodeId:e});s(n),await b(t&&n.some(e=>t.startsWith(e))?t:n[0]??``,!1)}catch(e){s([]),h(null),_(e instanceof Error&&/not available on node/i.test(e.message)?`This node can't be browsed remotely. Enter the path manually.`:e instanceof Error?e.message:`browse unavailable`)}})()},[e]);let x=async()=>{if(!(!v.trim()||!m)){_(null);try{let t=`${m.path.replace(/\/+$/,``)}/${v.trim()}`;await i.filesystemBrowse.createDir.mutate({nodeId:e,path:t}),y(``),await b(m.path,!0)}catch(e){_(e instanceof Error?e.message:`create failed`)}}},S=e=>`${(e/1e9).toFixed(1)} GB`;return a(`div`,{className:`space-y-2 rounded border border-border p-2`,children:[u(`div`,{className:`flex flex-wrap gap-1`,children:o.map(e=>u(`button`,{onClick:()=>void b(e,!0),className:`rounded border border-border px-2 py-0.5 text-[10px] font-mono hover:border-primary/40`,children:e},e))}),u(`input`,{value:c,onChange:e=>{p(e.target.value),n(e.target.value)},placeholder:`/absolute/path`,className:`w-full rounded border border-border bg-background px-2 py-1 text-[11px] font-mono`}),m&&a(`div`,{className:`text-[10px] text-foreground-subtle`,children:[S(m.freeBytes),` free / `,S(m.totalBytes)]}),u(`div`,{className:`max-h-40 space-y-0.5 overflow-y-auto`,children:m?.entries.map(e=>a(`button`,{onClick:()=>void b(e.path,!0),className:`block w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-primary/5`,children:[`📁 `,e.name]},e.path))}),a(`div`,{className:`flex gap-1`,children:[u(`input`,{value:v,onChange:e=>y(e.target.value),placeholder:`new folder`,className:`flex-1 rounded border border-border bg-background px-2 py-1 text-xs`}),u(`button`,{onClick:()=>void x(),className:`rounded border border-border px-2 py-1 text-xs hover:border-primary/40`,children:`Create`})]}),g&&u(`div`,{className:`text-[10px] text-red-400`,children:g})]})}i();function I({location:r,onClose:i,onSaved:d}){let m=c(),h=f().trpcClient,[g,_]=l({...r.config}),[y,x]=l(r.displayName),[S,C]=l(null),[w,T]=l(!1),E=t(),D=o(()=>{let e=E.data;return Array.isArray(e)?e.find(e=>e.providerId===r.providerId)??null:null},[E.data,r.providerId]),O=e({onSuccess:()=>{m.invalidateQueries({queryKey:[[`storage`,`listLocations`]]}),d()}}),k=D?.shouldSaveDiskSpace===!0,A=D?.nodeLocal===!0,[j,M]=l(typeof r.nodeId==`string`?r.nodeId:`hub`),N=typeof g.basePath==`string`?g.basePath:``,P=e=>_(t=>({...t,basePath:e})),I=g.readOnly===!0,L=s({queryKey:[`storage`,`listLocations`,`ro-guard`],queryFn:()=>h.storage.listLocations.query({})}),R=(L.data??[]).filter(e=>e.type===r.type&&e.id!==r.id&&e.config.readOnly!==!0).length,z=!I&&L.data!==void 0&&R===0,B=e=>_(t=>{let n={...t};return delete n.readOnly,e?{...n,readOnly:!0}:n}),V=typeof g.maxUsedGb==`number`?g.maxUsedGb:``,H=e=>{let t=e.trim(),n=t===``?void 0:Number(t);_(e=>{let t={...e};return n===void 0||!Number.isFinite(n)||n<=0?delete t.maxUsedGb:t.maxUsedGb=n,t})},U=typeof g.minFreePercent==`number`?g.minFreePercent:``,W=e=>{let t=e.trim(),n=t===``?void 0:Number(t);_(e=>{let t={...e};return n===void 0||!Number.isFinite(n)?delete t.minFreePercent:t.minFreePercent=Math.min(100,Math.max(0,n)),t})},G=async()=>{T(!0),C(null);try{C(await(e=>h.storage.testConfig.query(e))({providerId:r.providerId,config:g}))}catch(e){C({ok:!1,error:e instanceof Error?e.message:String(e)})}finally{T(!1)}};return u(`div`,{className:`fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm`,onClick:i,children:a(`div`,{className:`w-full max-w-md rounded-xl border border-border bg-surface shadow-2xl`,onClick:e=>e.stopPropagation(),children:[a(`div`,{className:`flex items-start justify-between border-b border-border px-4 py-3`,children:[a(`div`,{className:`space-y-0.5`,children:[a(`h2`,{className:`text-sm font-semibold text-foreground`,children:[`Edit `,r.displayName]}),a(`p`,{className:`text-xs text-foreground-subtle`,children:[u(`span`,{className:`font-mono`,children:r.id}),` ·`,` `,D?.displayName??r.providerId]})]}),u(`button`,{onClick:i,className:`text-foreground-subtle hover:text-foreground`,children:u(b,{className:`h-4 w-4`})})]}),a(`div`,{className:`p-4 space-y-3 max-h-[60vh] overflow-y-auto`,children:[a(`div`,{className:`space-y-1`,children:[u(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Display name`}),u(`input`,{type:`text`,value:y,onChange:e=>x(e.target.value),className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`})]}),D&&u(p,{schema:D.configSchema,values:g,onChange:_}),a(`div`,{className:`space-y-1`,children:[a(`label`,{className:`flex items-center gap-2 text-xs ${z?`text-foreground-subtle cursor-not-allowed`:`text-foreground cursor-pointer`}`,children:[u(`input`,{type:`checkbox`,checked:I,disabled:z,onChange:e=>B(e.target.checked),className:`accent-primary`}),`Read-only (drain mode)`]}),u(`p`,{className:`text-[10px] text-foreground-subtle`,children:z?`Blocked: this is the last writable location of its type — new footage would have nowhere to go.`:`Reads and age-based retention continue; nothing new is written here and disk-pressure eviction skips it. Use it to slowly decommission a disk.`})]}),A&&a(`div`,{className:`space-y-2`,children:[u(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Node`}),u(n,{selectedNodeId:j,onSelect:M,alwaysShow:!0}),u(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Path`}),u(F,{nodeId:j,value:N,onChange:P})]}),k&&a(`div`,{className:`space-y-1`,children:[u(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Min free disk %`}),u(`input`,{type:`number`,min:0,max:100,value:U,placeholder:D?.minFreePercent==null?``:String(D.minFreePercent),onChange:e=>W(e.target.value),className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`}),a(`p`,{className:`text-[10px] text-foreground-subtle`,children:[`When free space on this volume drops below this %, the oldest data stored here is evicted to reclaim space.`,D?.minFreePercent==null?` Empty = use the default.`:` Empty = use the default (${D.minFreePercent}%).`]}),u(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Max used (GB)`}),u(`input`,{type:`number`,min:1,value:V,placeholder:`unlimited`,onChange:e=>H(e.target.value),className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`}),u(`p`,{className:`text-[10px] text-foreground-subtle`,children:`Hard cap on the TOTAL bytes evictable data (recordings) may occupy on this location, independent of disk free space — oldest footage is evicted past it. Empty = unlimited.`})]}),!D&&!E.isLoading&&a(`div`,{className:`rounded border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-[11px] text-amber-700 dark:text-amber-300`,children:[`Provider `,u(`code`,{className:`font-mono`,children:r.providerId}),` is no longer registered. Field schema unavailable; raw config is preserved on save.`]}),S&&u(`div`,{className:`rounded border px-3 py-2 text-[11px] ${S.ok?`border-success/30 bg-success/5 text-success`:`border-danger/30 bg-danger/5 text-danger`}`,children:S.ok?`Connection OK.`:`Test failed: ${S.error??`unknown error`}`}),O.isError&&u(`div`,{className:`rounded border border-danger/30 bg-danger/5 text-danger px-3 py-2 text-[11px]`,children:O.error instanceof Error?O.error.message:String(O.error)})]}),a(`div`,{className:`flex items-center justify-between gap-2 border-t border-border px-4 py-3`,children:[u(`button`,{onClick:i,className:`rounded px-3 py-1.5 text-xs text-foreground-subtle border border-border hover:text-foreground`,children:`Cancel`}),a(`div`,{className:`flex items-center gap-2`,children:[u(`button`,{onClick:()=>{G()},disabled:w||!D,className:`rounded border border-border px-3 py-1.5 text-xs text-foreground hover:bg-primary/10 disabled:opacity-50`,children:w?`Testing…`:`Test connection`}),a(`button`,{onClick:()=>{O.mutate({id:r.id,type:r.type,providerId:r.providerId,isDefault:r.isDefault,displayName:y.trim(),config:g,nodeId:A?j:void 0})},disabled:O.isPending||y.trim().length===0,className:`inline-flex items-center gap-1.5 rounded bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50`,children:[O.isPending?u(v,{className:`h-3.5 w-3.5 animate-spin`}):null,O.isPending?`Saving…`:`Save`]})]})]})]})})}function L(e){let t=e.trim().toLowerCase();return t.length===0?``:t.replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)}function R(e,t){let n=L(t);return`${e}:${n.length>0?n:`default`}`}i();function z({initialType:r,onClose:i,onCreated:s}){let d=c(),m=f().trpcClient,[h,g]=l(`picker`),[y,x]=l(r),[S,C]=l(null),[w,T]=l({}),[E,D]=l(``),[O,k]=l(null),[A,j]=l(!1),[M,N]=l(!1),[P,I]=l(`hub`),[L,z]=l(null),[B,V]=l(!1),H=e=>T(t=>({...t,basePath:e})),U=typeof w.basePath==`string`?w.basePath:``,W=t(),G=o(()=>{let e=W.data;return Array.isArray(e)?e:[]},[W.data]),K=W.isLoading,q=e({onSuccess:()=>{d.invalidateQueries({queryKey:[[`storage`,`listLocations`]]}),s()}}),J=o(()=>O!==null&&O.length>0?O:R(y,E),[y,E,O]),Y=async()=>{if(S){V(!0),z(null);try{z(await(e=>m.storage.testConfig.query(e))({providerId:S.providerId,config:w}))}catch(e){z({ok:!1,error:e instanceof Error?e.message:String(e)})}finally{V(!1)}}};return u(`div`,{className:`fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm`,onClick:i,children:a(`div`,{className:`w-full max-w-md rounded-xl border border-border bg-surface shadow-2xl`,onClick:e=>e.stopPropagation(),children:[a(`div`,{className:`flex items-start justify-between border-b border-border px-4 py-3`,children:[a(`div`,{className:`space-y-0.5`,children:[u(`h2`,{className:`text-sm font-semibold text-foreground`,children:{picker:`Add Location — Pick provider`,config:`Add Location — ${S?.displayName??``}`,naming:`Add Location — Name & defaults`}[h]}),a(`p`,{className:`text-xs text-foreground-subtle`,children:[`Type: `,u(`span`,{className:`font-mono`,children:y})]})]}),u(`button`,{onClick:i,className:`text-foreground-subtle hover:text-foreground`,children:u(b,{className:`h-4 w-4`})})]}),h===`picker`&&a(`div`,{className:`p-4 space-y-2`,children:[K&&u(`div`,{className:`text-xs text-foreground-subtle animate-pulse`,children:`Loading providers…`}),!K&&G.length===0&&a(`div`,{className:`rounded border border-border bg-background px-3 py-4 text-center text-xs text-foreground-subtle`,children:[`No storage-provider supports type "`,y,`". Install a compatible addon (e.g.`,` `,u(`code`,{className:`font-mono`,children:`remote-storage`}),`) to enable it.`]}),G.map(e=>a(`button`,{onClick:()=>{C(e),g(`config`)},className:`flex w-full items-center gap-3 rounded-lg border border-border bg-background p-3 text-left hover:border-primary/40 hover:bg-primary/5 transition-colors`,children:[a(`div`,{className:`flex-1 min-w-0`,children:[u(`p`,{className:`text-xs font-semibold text-foreground`,children:e.displayName}),u(`p`,{className:`text-[10px] text-foreground-subtle font-mono mt-0.5 truncate`,children:e.providerId})]}),u(_,{className:`h-4 w-4 text-foreground-subtle flex-shrink-0`})]},e.providerId))]}),h===`config`&&S&&a(`div`,{className:`p-4 space-y-3 max-h-[60vh] overflow-y-auto`,children:[u(p,{schema:S.configSchema,values:w,onChange:T}),S.nodeLocal&&a(`div`,{className:`space-y-2`,children:[u(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Node`}),u(n,{selectedNodeId:P,onSelect:I,alwaysShow:!0}),u(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Path`}),u(F,{nodeId:P,value:U,onChange:H})]}),L&&u(`div`,{className:`rounded border px-3 py-2 text-[11px] ${L.ok?`border-success/30 bg-success/5 text-success`:`border-danger/30 bg-danger/5 text-danger`}`,children:L.ok?`Connection OK.`:`Test failed: ${L.error??`unknown error`}`}),a(`div`,{className:`flex items-center justify-between pt-1`,children:[u(`button`,{onClick:()=>g(`picker`),className:`rounded px-3 py-1.5 text-xs text-foreground-subtle border border-border hover:text-foreground`,children:`Back`}),a(`div`,{className:`flex items-center gap-2`,children:[u(`button`,{onClick:()=>{Y()},disabled:B,className:`rounded border border-border px-3 py-1.5 text-xs text-foreground hover:bg-primary/10 disabled:opacity-50`,children:B?`Testing…`:`Test connection`}),u(`button`,{onClick:()=>g(`naming`),className:`rounded bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90`,children:`Next`})]})]})]}),h===`naming`&&S&&a(`div`,{className:`p-4 space-y-3`,children:[a(`div`,{className:`space-y-1`,children:[u(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Display name`}),u(`input`,{type:`text`,value:E,onChange:e=>D(e.target.value),placeholder:`${y} on ${S.displayName}`,className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`})]}),a(`div`,{className:`space-y-1`,children:[a(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:[`Id`,` `,u(`span`,{className:`lowercase tracking-normal text-foreground-subtle`,children:`— auto-derived; override if needed`})]}),u(`input`,{type:`text`,value:J,onChange:e=>k(e.target.value),placeholder:R(y,E),className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground font-mono focus:outline-none focus:ring-1 focus:ring-primary`}),a(`p`,{className:`text-[10px] text-foreground-subtle`,children:[`Format: `,u(`code`,{className:`font-mono`,children:`<type>:<slug>`}),`. Lowercase letters, digits, and hyphens only.`]})]}),a(`label`,{className:`flex items-start gap-2 rounded border border-border bg-background px-3 py-2 cursor-pointer hover:bg-primary/5`,children:[u(`input`,{type:`checkbox`,checked:A,onChange:e=>j(e.target.checked),className:`accent-primary mt-0.5`}),a(`div`,{className:`space-y-0.5`,children:[a(`div`,{className:`text-xs font-medium text-foreground`,children:[`Mark as default for type "`,y,`"`]}),a(`div`,{className:`text-[10px] text-foreground-subtle`,children:[`Setting this as default will demote the current default for this type. References like `,a(`code`,{className:`font-mono`,children:[`"`,y,`"`]}),` resolve to whichever location is the default.`]})]})]}),a(`label`,{className:`flex items-start gap-2 rounded border border-border bg-background px-3 py-2 cursor-pointer hover:bg-primary/5`,children:[u(`input`,{type:`checkbox`,checked:M,onChange:e=>N(e.target.checked),className:`accent-primary mt-0.5`}),a(`div`,{className:`space-y-0.5`,children:[u(`div`,{className:`text-xs font-medium text-foreground`,children:`Enable immediately`}),u(`div`,{className:`text-[10px] text-foreground-subtle`,children:`A disabled location receives no writes until enabled — reads and age-based retention still work, nothing new lands here. Leave this off to register the disk now and start using it later.`})]})]}),q.isError&&u(`div`,{className:`rounded border border-danger/30 bg-danger/5 text-danger px-3 py-2 text-[11px]`,children:q.error instanceof Error?q.error.message:String(q.error)}),a(`div`,{className:`flex items-center justify-between pt-1`,children:[u(`button`,{onClick:()=>g(`config`),className:`rounded px-3 py-1.5 text-xs text-foreground-subtle border border-border hover:text-foreground`,children:`Back`}),a(`div`,{className:`flex items-center gap-2`,children:[u(`button`,{onClick:()=>{x(r),C(null),T({}),D(``),k(null),j(!1),N(!1),z(null),g(`picker`)},className:`rounded px-3 py-1.5 text-xs text-foreground-subtle border border-border hover:text-foreground`,children:`Reset`}),a(`button`,{onClick:()=>{S&&q.mutate({id:J,type:y,displayName:E.trim(),providerId:S.providerId,config:w,isDefault:A,enabled:M,nodeId:S.nodeLocal?P:void 0})},disabled:q.isPending||E.trim().length===0,className:`inline-flex items-center gap-1.5 rounded bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50`,children:[q.isPending?u(v,{className:`h-3.5 w-3.5 animate-spin`}):null,q.isPending?`Saving…`:`Save`]})]})]})]})]})})}export{T as i,I as n,A as r,z as t};
@@ -1 +0,0 @@
1
- import{Co as e,Et as t,So as n,fs as r,g as i,jt as a,ms as o,p as s,v as c}from"./src-BbMtrcN-.js";import{Q as l,S as u,o as d,q as f,x as p,z as m}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DTufy7Ue.js";import{t as h}from"./play-N-aCIv2P.js";import{t as g}from"./plus-BwBDSSUm.js";import{t as _}from"./refresh-cw-C2McgRco.js";import{t as v}from"./square-9Bf62Szc.js";import{G as y,I as b,S as x,V as S,m as C}from"./index-kOLbwjEX.js";import{t as w}from"./AdminPage-Dq33XE9j.js";import{t as T}from"./NodeAddonsSettingsPanel-CtO75ohk.js";l();function E({addonId:e,fallback:t,tileClassName:n,iconPath:r=`assets/icon.svg`}){let[i,a]=f(!1),o=`/api/addon-assets/${e}/${r}`;return p(`div`,{className:`h-10 w-10 rounded-lg flex items-center justify-center shrink-0 ${n}`,children:i?p(t,{className:`h-5 w-5`}):p(`img`,{src:o,alt:e,className:`h-7 w-7 rounded-md bg-white p-1 ring-1 ring-black/5`,onError:()=>a(!0)})})}l();function D({addonId:e,defaultActivity:t,showActivity:n=!0,maxHeight:r=`max-h-64`,defaultTab:a,capability:o}){let[s,l]=f(t??(a===`events`?`events`:`logs`));return u(`div`,{className:`flex flex-col gap-4`,children:[p(T,{nodeId:`hub`,addonIds:[e],level:`global`,capability:o}),n&&u(`div`,{className:`rounded-lg border border-border bg-surface overflow-hidden`,children:[u(`div`,{className:`flex items-center justify-between px-3 py-2 border-b border-border bg-surface-hover/20`,children:[p(`span`,{className:`text-[11px] font-semibold text-foreground-subtle uppercase tracking-wide`,children:`Activity`}),u(`div`,{className:`flex items-center gap-1`,children:[p(O,{kind:`logs`,active:s===`logs`,onClick:()=>l(`logs`)}),p(O,{kind:`events`,active:s===`events`,onClick:()=>l(`events`)})]})]}),s===`logs`?p(c,{addonId:e,maxHeight:r,showFilters:!1}):p(i,{addonId:e,category:`addon.*`,maxHeight:r})]})]})}function O({kind:e,active:t,onClick:n}){return u(`button`,{onClick:n,className:`inline-flex items-center gap-1 rounded px-2 py-0.5 text-[11px] font-medium transition-colors ${t?`bg-primary/10 text-primary`:`text-foreground-subtle hover:text-foreground hover:bg-surface-hover`}`,children:[p(e===`logs`?C:S,{className:`h-3 w-3`}),e===`logs`?`Logs`:`Events`]})}l();function k(t){let{title:i,subtitle:a,pageIcon:s,itemIcon:c,capability:l,installButtonLabel:d=`Add addon`,items:h,isLoading:_,emptyTitle:v=`No addons installed for this capability`,emptyDescription:x,itemIconColor:S,renderStatusBadges:C,renderPrimaryAction:T,itemError:O,renderExpandedPanel:k,workspaceMaxHeight:N=`max-h-72`,trailing:P,showChrome:F=!1}=t,I=h.length>1,[L,R]=f(()=>h[0]?.addonId??null);m(()=>{L&&(h.some(e=>e.addonId===L)||R(h[0]?.addonId??null))},[h,L]);let z=e=>R(t=>t===e?null:e),B=`/system/addons?capability=${encodeURIComponent(l)}`;return p(w,{icon:F?s:void 0,title:F?i:void 0,subtitle:F?a:void 0,actions:F?p(y,{to:B,children:u(o,{variant:`primary`,size:`sm`,children:[p(g,{className:`h-3.5 w-3.5 mr-1`}),d]})}):void 0,children:_?p(r,{children:p(`div`,{className:`p-6 text-sm text-foreground-subtle`,children:`Loading…`})}):h.length===0?p(r,{children:p(e,{title:v,description:x??`Install an addon that declares the "${l}" capability to get started.`,action:F?p(y,{to:B,children:u(o,{variant:`primary`,size:`sm`,children:[p(g,{className:`h-3.5 w-3.5 mr-1`}),d]})}):void 0})}):u(`div`,{className:`flex flex-col gap-4`,children:[h.map(e=>{let t=!I||L===e.addonId,i=S?.(e)??(e.isActive?`bg-emerald-500/15 text-emerald-700 dark:text-emerald-300`:`bg-foreground-subtle/15 text-foreground-subtle`),a=O?.(e)??null;return u(r,{children:[u(`div`,{className:`flex flex-col gap-3 p-4 sm:flex-row sm:items-center ${t?`border-b border-border`:``}`,children:[u(`div`,{className:`flex items-center gap-3 flex-1 min-w-0`,children:[I&&p(`button`,{type:`button`,onClick:()=>z(e.addonId),"aria-label":t?`Collapse`:`Expand`,className:`shrink-0 p-1 rounded hover:bg-foreground-subtle/10 transition-colors`,children:p(b,{className:`h-4 w-4 text-foreground-subtle transition-transform ${t?`rotate-180`:``}`})}),u(`button`,{type:`button`,onClick:()=>I&&z(e.addonId),className:`flex items-center gap-4 flex-1 min-w-0 text-left ${I?`hover:opacity-80 transition-opacity`:``}`,disabled:!I,children:[p(E,{addonId:e.addonId,fallback:c,tileClassName:i}),u(`div`,{className:`flex-1 min-w-0`,children:[u(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[p(`span`,{className:`font-semibold text-foreground`,children:e.displayName}),p(A,{isActive:e.isActive}),C?.(e)]}),p(`div`,{className:`text-xs text-foreground-subtle mt-1 font-mono break-all`,children:e.addonId})]})]})]}),u(`div`,{className:`flex items-center gap-1 shrink-0 flex-wrap justify-end`,children:[T?.(e),p(j,{capName:e.capName??l,addonId:e.addonId,isActive:e.isActive}),p(M,{addonId:e.addonId,displayName:e.displayName})]})]}),t&&u(`div`,{className:`p-4 flex flex-col gap-3`,children:[a!==null&&p(n,{message:a}),k?.(e),p(D,{addonId:e.addonId,maxHeight:N,capability:e.capName??l})]})]},e.addonId)}),P]})})}function A({isActive:e}){return p(`span`,{className:`text-[10px] rounded-full px-2 py-0.5 font-medium ${e?`bg-emerald-500/15 text-emerald-700 dark:text-emerald-300`:`bg-foreground-subtle/15 text-foreground-subtle`}`,children:e?`Enabled`:`Disabled`})}function j({capName:e,addonId:t,isActive:n}){let r=d(),i=a({onSuccess:()=>{r.invalidateQueries({queryKey:[[`addons`,`listCapabilityProviders`]]})}}),s=i.isPending;return u(o,{size:`sm`,variant:n?`secondary`:`primary`,disabled:s,onClick:()=>i.mutate({capName:e,addonId:t,enabled:!n}),children:[s?p(x,{className:`h-3 w-3 animate-spin`}):p(n?v:h,{className:`h-3 w-3`}),n?`Disable`:`Enable`]})}function M({addonId:e,displayName:n}){let r=d(),i=t({onSuccess:()=>{r.invalidateQueries({queryKey:[[`addons`,`listCapabilityProviders`]]})}});return p(s,{label:`Restart`,icon:_,size:`sm`,title:`Restart "${n}"?`,description:`Restarting the addon bounces its subprocess — the provider will briefly drop and reconnect automatically.`,confirmLabel:`Restart`,triggerVariant:`ghost`,confirmVariant:`danger`,disabled:i.isPending,action:()=>i.mutateAsync({addonId:e})})}export{k as t};