@camstack/addon-admin-ui 1.2.61 → 1.2.63

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 (103) hide show
  1. package/dist/assets/{AddLocationWizard-BVrPN8yV.js → AddLocationWizard-DL-p2pfI.js} +1 -1
  2. package/dist/assets/{AddonCollectionPage-Cluggqrl.js → AddonCollectionPage-GmBzeWfb.js} +1 -1
  3. package/dist/assets/{Addons-pf4_6Np6.js → Addons-DJZ0fLKa.js} +1 -1
  4. package/dist/assets/{AdminTabs-D4DXIa-o.js → AdminTabs-BJAp46Yc.js} +1 -1
  5. package/dist/assets/AiProfiles-Dr3P3tTd.js +3 -0
  6. package/dist/assets/{BrokerForm-DpZEb2oo.js → BrokerForm-B5lFCkpx.js} +1 -1
  7. package/dist/assets/{Build-DFf7JJQN.js → Build-BF5TjhPZ.js} +1 -1
  8. package/dist/assets/{Capabilities-fyyhemKk.js → Capabilities-dM4kxoPi.js} +1 -1
  9. package/dist/assets/{CapabilityBadges-CFAqIVEN.js → CapabilityBadges-BXTtPMj5.js} +1 -1
  10. package/dist/assets/{Cluster-BI79yXHi.js → Cluster-Bc9KL5vo.js} +1 -1
  11. package/dist/assets/{Data-BnH1FBcv.js → Data-Ck3PTxjk.js} +1 -1
  12. package/dist/assets/{DetectionIntelligence-pZ34_QKw.js → DetectionIntelligence-CQAuuHZJ.js} +1 -1
  13. package/dist/assets/{DeviceAggregateSection-DcU4HtPo.js → DeviceAggregateSection-BUdjIJ3K.js} +1 -1
  14. package/dist/assets/{DeviceDetail-D4IEMIPA.js → DeviceDetail-BcCMnGp5.js} +1 -1
  15. package/dist/assets/{Devices-Qvmjz6OV.js → Devices-BcsFIXeR.js} +1 -1
  16. package/dist/assets/{EmbedPlayerPage-BiOYECZo.js → EmbedPlayerPage-BvDdFRwh.js} +1 -1
  17. package/dist/assets/{Events-BkGGzjc6.js → Events-BPJx8pMN.js} +1 -1
  18. package/dist/assets/{FormBuilder-Ce9uWxtp.js → FormBuilder-eO_2opNc.js} +1 -1
  19. package/dist/assets/{Identity-Dz2q6O35.js → Identity-cScC97Mq.js} +1 -1
  20. package/dist/assets/{IntegrationDetail-BgYnCOqc.js → IntegrationDetail-BrI5QbcU.js} +1 -1
  21. package/dist/assets/{Integrations-JZkpFkbO.js → Integrations-BDRRlSaJ.js} +1 -1
  22. package/dist/assets/{Integrations-CGDgpbY3.js → Integrations-DJuPZXOa.js} +1 -1
  23. package/dist/assets/{Logs-Dc8myuGB.js → Logs-DgjSPchK.js} +1 -1
  24. package/dist/assets/{MotionZonesSettings-CNAneuwi.js → MotionZonesSettings-B2pHjyow.js} +1 -1
  25. package/dist/assets/{MyAccess-BplkKorv.js → MyAccess-Cb_qTJZZ.js} +1 -1
  26. package/dist/assets/{Network-DkXgbHRL.js → Network-Bc4x3mCD.js} +1 -1
  27. package/dist/assets/{NodeAddonsSettingsPanel-qSFnzXqZ.js → NodeAddonsSettingsPanel-S9qh9oix.js} +1 -1
  28. package/dist/assets/{NodeInferenceDevices-B7RYmrXb.js → NodeInferenceDevices-egUQGuFl.js} +1 -1
  29. package/dist/assets/Notifications-1ARXHzBI.js +1 -0
  30. package/dist/assets/{Pipeline-BVix_5wm.js → Pipeline-D1NEy1ZN.js} +1 -1
  31. package/dist/assets/{Plates-Cn1Al2WA.js → Plates-WUDggEuV.js} +1 -1
  32. package/dist/assets/{PrivacyMaskSettings-GAkxAsA3.js → PrivacyMaskSettings-x5jpJN6L.js} +1 -1
  33. package/dist/assets/{ProviderIcon-CQo4yDQS.js → ProviderIcon-C5_0vsMH.js} +1 -1
  34. package/dist/assets/{Recordings-DZMmDA_4.js → Recordings-CiwNtOmP.js} +1 -1
  35. package/dist/assets/{ScanNetworkModal-TrU5QhWB.js → ScanNetworkModal-CuyJeqBI.js} +1 -1
  36. package/dist/assets/SceneMonitorEditor-lhBYjABG.js +1 -0
  37. package/dist/assets/{Scenes-bMn8sgm0.js → Scenes-DGv23Apk.js} +1 -1
  38. package/dist/assets/{Settings-B1YsrVcn.js → Settings-DG4iqZOC.js} +1 -1
  39. package/dist/assets/{Showroom-Cik2ivrC.js → Showroom-BdcMmdVm.js} +1 -1
  40. package/dist/assets/{Terminal-BWct2hhL.js → Terminal-Cll24Ext.js} +1 -1
  41. package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-B59PxHv9.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-CWCjzW96.js} +1 -1
  42. package/dist/assets/{bell-ring-CtdRZtjG.js → bell-ring-_rBBDOOp.js} +1 -1
  43. package/dist/assets/{boxes-BN_wTQoj.js → boxes-D21_Gtau.js} +1 -1
  44. package/dist/assets/{build-cluster-graph-JVj1fl5o.js → build-cluster-graph-B2YakfxI.js} +1 -1
  45. package/dist/assets/circle-check-big-CdgHxf2K.js +1 -0
  46. package/dist/assets/{circle-x-DKEv2fk-.js → circle-x-Dme7swuV.js} +1 -1
  47. package/dist/assets/clock-LRrp-sNT.js +1 -0
  48. package/dist/assets/{copy-C0Y9pMFE.js → copy-D-RYlseV.js} +1 -1
  49. package/dist/assets/{debounced-invalidate-CYoigLZW.js → debounced-invalidate-DUItRp3z.js} +1 -1
  50. package/dist/assets/dist-B2dk3x-1.js +345 -0
  51. package/dist/assets/{dist-DVOPUibu.js → dist-DM5W_-lf.js} +1 -1
  52. package/dist/assets/{download-B538th-h.js → download-2daKodbc.js} +1 -1
  53. package/dist/assets/{face-bbox-0omHJ2v9.js → face-bbox-C1EmZtcs.js} +1 -1
  54. package/dist/assets/{hard-drive-DFBgqBxK.js → hard-drive-CN2O-rzf.js} +1 -1
  55. package/dist/assets/{hostInit-CiIhfzzr.js → hostInit-CIIBSAj3.js} +1 -1
  56. package/dist/assets/{index-Dr-ap6SH.js → index-D9F3sdd-.js} +3 -3
  57. package/dist/assets/{key-round-D1XSxvLS.js → key-round-Bq2pdPu0.js} +1 -1
  58. package/dist/assets/{mf-entry-bootstrap-0-82869dcc.js → mf-entry-bootstrap-0-e5e0dfd2.js} +2 -2
  59. package/dist/assets/{pencil-CZ2gMyTB.js → pencil-DA48cMNw.js} +1 -1
  60. package/dist/assets/play-Bg5-lqI9.js +1 -0
  61. package/dist/assets/player-overlays-DG8Qq2SN.js +1 -0
  62. package/dist/assets/plus-B17_qT4q.js +1 -0
  63. package/dist/assets/power-B3ls9EjL.js +1 -0
  64. package/dist/assets/{radio-C0T0_Ki5.js → radio-D5YXD5Mk.js} +1 -1
  65. package/dist/assets/{refresh-cw-DuE4ONv-.js → refresh-cw-CeN01FBy.js} +1 -1
  66. package/dist/assets/remoteEntry-CmQ-DXMH.js +1 -0
  67. package/dist/assets/rotate-ccw-DdaLkN93.js +1 -0
  68. package/dist/assets/rotate-cw-DiDpzlTp.js +1 -0
  69. package/dist/assets/search-5IKob3Pf.js +1 -0
  70. package/dist/assets/{send-CiGeFhM_.js → send-DtoGJcOc.js} +1 -1
  71. package/dist/assets/{server-BBJTl0ih.js → server-DVYSamal.js} +1 -1
  72. package/dist/assets/square-BSJyk4ja.js +1 -0
  73. package/dist/assets/{square-check-big-DEpi1DZf.js → square-check-big-BVgrKc-i.js} +1 -1
  74. package/dist/assets/src-B2p9OhlL.js +345 -0
  75. package/dist/assets/src-DpB-8ADl.js +67 -0
  76. package/dist/assets/{undo-2-BEASNScj.js → undo-2-RQcgGfE7.js} +1 -1
  77. package/dist/assets/{upload-CCWw0hX4.js → upload-CIlYkZ_R.js} +1 -1
  78. package/dist/assets/{use-scenes-feed-BrAVTu20.js → use-scenes-feed-asIA0HHK.js} +1 -1
  79. package/dist/assets/{useEventInvalidation-CnCqhaHZ.js → useEventInvalidation-C4hcZ266.js} +1 -1
  80. package/dist/assets/{video-BEAVV4bf.js → video-C0rp1BOP.js} +1 -1
  81. package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-D8I8UoDY.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-8FeeO_xT.js} +2 -2
  82. package/dist/assets/{wifi-sgX03Yna.js → wifi-CEXNRrIb.js} +1 -1
  83. package/dist/assets/{zap-DVVII2tr.js → zap-DZBO_poe.js} +1 -1
  84. package/dist/index.html +5 -5
  85. package/dist/server/addon.js +185 -6
  86. package/package.json +1 -1
  87. package/dist/assets/AiProfiles-Dc-3w9Z5.js +0 -3
  88. package/dist/assets/Notifications-CsUJcPdF.js +0 -1
  89. package/dist/assets/SceneMonitorEditor-DcTfkrW3.js +0 -1
  90. package/dist/assets/circle-check-big-KvCernzh.js +0 -1
  91. package/dist/assets/clock-Ddu_eThB.js +0 -1
  92. package/dist/assets/dist-Bz6O5BsH.js +0 -345
  93. package/dist/assets/play-BrdA8mXB.js +0 -1
  94. package/dist/assets/player-overlays-7AKPeTm-.js +0 -1
  95. package/dist/assets/plus-ChsVL8uT.js +0 -1
  96. package/dist/assets/power-Dz2il9ai.js +0 -1
  97. package/dist/assets/remoteEntry-CgpjK5QJ.js +0 -1
  98. package/dist/assets/rotate-ccw-DS5e2J2f.js +0 -1
  99. package/dist/assets/rotate-cw-Bb9OhnRT.js +0 -1
  100. package/dist/assets/search-BunRfhTv.js +0 -1
  101. package/dist/assets/square-BlgtgcV9.js +0 -1
  102. package/dist/assets/src-BaVKQIP9.js +0 -345
  103. package/dist/assets/src-CgOOvCVA.js +0 -67
@@ -12244,6 +12244,18 @@ var LlmGenerateBaseInputSchema = object({
12244
12244
  * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
12245
12245
  * watchdog — operator decision #3).
12246
12246
  */
12247
+ /**
12248
+ * A companion artifact that MUST land beside the main GGUF: the `mmproj`
12249
+ * projector of a vision model, or shards 2..N of a split GGUF. Carried on the
12250
+ * REF rather than looked up at install time, so what the operator approved in
12251
+ * the preview is exactly what the node downloads.
12252
+ */
12253
+ var ManagedModelExtraFileSchema = object({
12254
+ url: string(),
12255
+ filename: string(),
12256
+ sizeBytes: number(),
12257
+ sha256: string().optional()
12258
+ });
12247
12259
  var ManagedModelRefSchema = discriminatedUnion("kind", [
12248
12260
  object({
12249
12261
  kind: literal("catalog"),
@@ -12252,7 +12264,11 @@ var ManagedModelRefSchema = discriminatedUnion("kind", [
12252
12264
  object({
12253
12265
  kind: literal("url"),
12254
12266
  url: string(),
12255
- sha256: string().optional()
12267
+ sha256: string().optional(),
12268
+ /** Picker/status label; the file basename when absent. */
12269
+ label: string().optional(),
12270
+ sizeBytes: number().optional(),
12271
+ extraFiles: array(ManagedModelExtraFileSchema).optional()
12256
12272
  }),
12257
12273
  object({
12258
12274
  kind: literal("path"),
@@ -12313,11 +12329,39 @@ var ManagedRuntimeConfigSchema = object({
12313
12329
  "q4_1",
12314
12330
  "q4_0"
12315
12331
  ]).optional(),
12332
+ /**
12333
+ * Escape hatch for llama-server flags this schema does NOT model — `--jinja`
12334
+ * (which most vision chat templates need and some language-only models
12335
+ * dislike), `--cont-batching`, `--rope-scaling`, …
12336
+ *
12337
+ * It is NOT a second place to set the flags above. A token that collides
12338
+ * with a typed field is REJECTED at start, naming the field that owns it
12339
+ * (`assertNoOwnedFlags`), because two knobs writing the same argv is exactly
12340
+ * the "two switches that disagree" failure this repo has already shipped
12341
+ * twice (D62).
12342
+ */
12343
+ extraArgs: array(string()).default([]),
12316
12344
  /** Else lazy: first generate boots it. */
12317
12345
  autoStart: boolean().default(false),
12318
12346
  /** 0 = never; frees RAM after quiet periods. */
12319
12347
  idleStopMinutes: number().int().default(30)
12320
12348
  });
12349
+ /**
12350
+ * Where a multi-GB install currently is. A single 0..1 fraction cannot answer
12351
+ * "is it stuck?" for an install that is three files (shards + mmproj) followed
12352
+ * by a sha256 pass over 22 GB — during which the fraction sat at 1.0 and the
12353
+ * node looked hung. Phase + file + bytes is the smallest shape that does.
12354
+ */
12355
+ var LlmDownloadProgressSchema = object({
12356
+ phase: _enum(["downloading", "verifying"]),
12357
+ /** The artifact currently moving, e.g. `mmproj-F16.gguf`. */
12358
+ file: string(),
12359
+ fileIndex: number().int(),
12360
+ fileCount: number().int(),
12361
+ /** Across the WHOLE install, not the current file. */
12362
+ downloadedBytes: number(),
12363
+ totalBytes: number().optional()
12364
+ });
12321
12365
  var LlmRuntimeStatusSchema = object({
12322
12366
  /** Status is ALWAYS node-qualified. */
12323
12367
  nodeId: string(),
@@ -12334,6 +12378,8 @@ var LlmRuntimeStatusSchema = object({
12334
12378
  modelPath: string().optional(),
12335
12379
  modelId: string().optional(),
12336
12380
  downloadProgress: number().min(0).max(1).optional(),
12381
+ /** Detail behind `downloadProgress`; present for the same lifetime. */
12382
+ download: LlmDownloadProgressSchema.optional(),
12337
12383
  lastError: string().optional(),
12338
12384
  crashesInWindow: number(),
12339
12385
  /** Child RSS (sampled best-effort). */
@@ -12344,7 +12390,14 @@ var LlmNodeModelSchema = object({
12344
12390
  file: string(),
12345
12391
  sizeBytes: number(),
12346
12392
  catalogId: string().optional(),
12347
- installedAt: number().optional()
12393
+ installedAt: number().optional(),
12394
+ /**
12395
+ * Absolute path on the node. Present so a file that is on disk but matches
12396
+ * no catalog entry — a custom Hugging Face install, or a GGUF the operator
12397
+ * copied in by hand — is still SELECTABLE, as a `{kind:'path'}` ref. Without
12398
+ * it the picker could list such a file and do nothing with it.
12399
+ */
12400
+ path: string().optional()
12348
12401
  });
12349
12402
  var LlmRuntimeDiskUsageSchema = object({
12350
12403
  nodeId: string(),
@@ -12422,9 +12475,12 @@ var LlmProfileSchema = object({
12422
12475
  systemPrompt: string().optional(),
12423
12476
  /** Total generation bound — the only one a unary call has. */
12424
12477
  timeoutMs: number().int().positive().default(6e4),
12425
- /** Wait for response headers only. */
12478
+ /** The TCP handshake only — "is the port even open". NOT the wait for
12479
+ * response headers: on the LM Studio / llama-server wire those are written
12480
+ * once the model has finished loading, so they belong to the bound below. */
12426
12481
  connectTimeoutMs: number().int().positive().default(1e4),
12427
- /** Accepted, but no output yet — a cold GPU load lives here. */
12482
+ /** Accepted, but no output yet — response headers included, because a cold
12483
+ * GPU load is exactly what happens before them. */
12428
12484
  firstTokenTimeoutMs: number().int().positive().default(12e4),
12429
12485
  /** Output started then stopped. */
12430
12486
  idleTimeoutMs: number().int().positive().default(6e4),
@@ -12487,6 +12543,36 @@ var ManagedModelCatalogEntrySchema = object({
12487
12543
  /** Vision models: companion projector file. */
12488
12544
  mmprojUrl: string().optional()
12489
12545
  });
12546
+ /**
12547
+ * The outcome of turning one operator-typed Hugging Face reference into a
12548
+ * download plan. A RESULT, never a throw: "this repo has 24 quantizations and
12549
+ * I will not pick for you" is a normal answer the UI has to render, not an
12550
+ * exception.
12551
+ *
12552
+ * `candidates` is the whole reason the refusal is usable — every string in it
12553
+ * is a tag that resolves when pasted back as `<org>/<repo>:<TAG>`.
12554
+ */
12555
+ var HfModelResolutionSchema = discriminatedUnion("ok", [object({
12556
+ ok: literal(true),
12557
+ /** Ready to hand to `installModel` unchanged. */
12558
+ model: ManagedModelRefSchema,
12559
+ label: string(),
12560
+ repo: string(),
12561
+ quantization: string(),
12562
+ purpose: _enum(["text", "vision"]),
12563
+ totalBytes: number(),
12564
+ /** mmproj + shards, for the preview: an operator approving 23 GB should
12565
+ * see that 0.9 GB of it is a projector they did not name. */
12566
+ extraFilenames: array(string())
12567
+ }), object({
12568
+ ok: literal(false),
12569
+ code: string(),
12570
+ message: string(),
12571
+ candidates: array(string()).optional(),
12572
+ /** Set when the refusal was only the ceiling: re-calling with
12573
+ * `maxBytes: requiredBytes` is the operator's explicit override. */
12574
+ requiredBytes: number().optional()
12575
+ })]);
12490
12576
  var LlmRuntimeNodeSchema = object({
12491
12577
  nodeId: string(),
12492
12578
  reachable: boolean(),
@@ -12523,6 +12609,15 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
12523
12609
  consumer: string().optional(),
12524
12610
  profileId: string().optional()
12525
12611
  }), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
12612
+ /** `https://huggingface.co/<org>/<repo>/resolve/main/<f>.gguf`,
12613
+ * `<org>/<repo>/<f>.gguf`, `<org>/<repo>` or `<org>/<repo>:<QUANT>`. */
12614
+ ref: string(),
12615
+ /** Explicit ceiling override, in bytes. Absent = the built-in ceiling. */
12616
+ maxBytes: number().positive().optional()
12617
+ }), HfModelResolutionSchema, {
12618
+ kind: "mutation",
12619
+ auth: "admin"
12620
+ }), method(object({
12526
12621
  nodeId: string(),
12527
12622
  model: ManagedModelRefSchema
12528
12623
  }), _void(), {
@@ -17655,6 +17750,17 @@ var maxSessionHoldMsField = {
17655
17750
  default: 12e4,
17656
17751
  step: 5e3
17657
17752
  };
17753
+ /**
17754
+ * Quiet period that closes an `audioMode: 'on-motion'` audio window. Floor of
17755
+ * 5s so a rearm can never degenerate into per-event stream churn; default 90s
17756
+ * comfortably outlives the gap between two PIR wakes on a battery camera.
17757
+ */
17758
+ var audioMotionWindowMsField = {
17759
+ min: 5e3,
17760
+ max: 6e5,
17761
+ default: 9e4,
17762
+ step: 5e3
17763
+ };
17658
17764
  var motionFpsField = {
17659
17765
  min: 1,
17660
17766
  max: 30,
@@ -17831,6 +17937,27 @@ var RunnerCameraConfigSchema = object({
17831
17937
  * resolved `CameraDetectionConfig`.
17832
17938
  */
17833
17939
  maxSessionHoldMs: number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
17940
+ /**
17941
+ * Orchestrator-side quiet period (ms) that closes an `audioMode:
17942
+ * 'on-motion'` audio window, measured from the LAST motion event.
17943
+ *
17944
+ * This exists because the falling edge cannot be relied on. Camera-native
17945
+ * providers emit motion as a RISING EDGE ONLY (Reolink's Baichuan push and
17946
+ * its email-push SMTP path both emit `detected: true` and never the
17947
+ * counterpart); only the frame-diff analyzer emits falls. So on an
17948
+ * onboard-only camera a window that closed only on `detected: false` never
17949
+ * closed at all, and `on-motion` silently behaved as `always-on` — on a
17950
+ * battery camera, the one failure mode the mode exists to prevent.
17951
+ *
17952
+ * Every motion event rearms this timer WITHOUT restarting the stream, so a
17953
+ * burst of re-fires costs nothing. A falling edge, when one does arrive,
17954
+ * still closes earlier via `motionCooldownMs` — whichever comes first wins.
17955
+ *
17956
+ * Not consumed by the runner: carried here so it shares the per-camera
17957
+ * device-settings surface with `motionCooldownMs`, exactly like
17958
+ * `maxSessionHoldMs`.
17959
+ */
17960
+ audioMotionWindowMs: number().min(audioMotionWindowMsField.min).max(audioMotionWindowMsField.max).optional(),
17834
17961
  motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
17835
17962
  detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
17836
17963
  motionStreamId: string(),
@@ -17926,7 +18053,7 @@ var RunnerCameraConfigSchema = object({
17926
18053
  */
17927
18054
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
17928
18055
  });
17929
- motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
18056
+ motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
17930
18057
  /**
17931
18058
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
17932
18059
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -24332,6 +24459,33 @@ method(object({
24332
24459
  * as `unknown`, never guessed. A day reference scored against an IR frame
24333
24460
  * collapses the cosine and would latch a false alarm every single night. */
24334
24461
  var SceneConditionSchema = string();
24462
+ /**
24463
+ * What a scene does when the CURRENT light has no reference of its own.
24464
+ *
24465
+ * The lighting variants are not equally likely to exist. Almost every operator
24466
+ * captures daylight and then never stands outside at 22:00 to capture IR, and a
24467
+ * scene that is only ever going to be asked about a daytime question ("is the
24468
+ * bin still on the kerb at 08:00") does not need a night reference at all. The
24469
+ * night half must therefore be OPTIONAL, and optional means the scene keeps
24470
+ * working without it rather than degrading into a permanent complaint.
24471
+ *
24472
+ * - `skip` (default) — the check in that light is not made. Not a verdict, not
24473
+ * an alarm, not even an `unknown`: the live state simply stays whatever the
24474
+ * last covered light left it at, the latch is untouched, and the hysteresis
24475
+ * run is neither spent nor cleared. The scene resumes by itself at first
24476
+ * light. This is the only behaviour under which "I never captured IR" is a
24477
+ * configuration choice instead of a nightly fault.
24478
+ * - `judge-anyway` — score against the OTHER conditions' references. Available
24479
+ * for cameras whose IR frame is close enough to daylight (a floodlit
24480
+ * driveway, an always-white-light doorbell), and wrong for everything else:
24481
+ * cross-condition cosines are not comparable, so a day reference against a
24482
+ * true IR frame collapses and the scene reports a theft at 21:40.
24483
+ *
24484
+ * Never applies when the scene has NO comparable reference at all — that is
24485
+ * "not armed yet", it is reported as `no-reference-for-condition`, and silence
24486
+ * there would hide a scene the operator never finished setting up.
24487
+ */
24488
+ var SceneUncoveredPolicySchema = _enum(["skip", "judge-anyway"]);
24335
24489
  /** `matched` = the baseline is what we see; `diverged` = it demonstrably is not;
24336
24490
  * `unknown` = we cannot judge (no reference for this condition, encoder model
24337
24491
  * changed, view shifted, no snapshot). `unknown` is a real value, not a null,
@@ -24387,6 +24541,9 @@ var SceneCheckSchema = discriminatedUnion("mode", [object({
24387
24541
  hysteresisCount: number().int().positive()
24388
24542
  })]);
24389
24543
  var SCENE_DEFAULT_ANCHOR_THRESHOLD = .85;
24544
+ /** Night is OPTIONAL. A scene with only a daylight reference sits the IR hours
24545
+ * out in silence rather than reporting a fault every night. */
24546
+ var SCENE_DEFAULT_UNCOVERED_POLICY = "skip";
24390
24547
  /**
24391
24548
  * Vision-model adjudication of a candidate flip. Field names deliberately
24392
24549
  * mirror `NcConfirmSchema` so an operator meets one vocabulary, not two.
@@ -24453,6 +24610,21 @@ var SceneMonitorSchema = object({
24453
24610
  * automation can react to the bin coming back without the operator's own
24454
24611
  * alarm silently clearing itself. */
24455
24612
  autoRestore: boolean().default(false),
24613
+ /** What to do when the current light has no reference of its own. See
24614
+ * {@link SceneUncoveredPolicySchema} — the default makes night OPTIONAL. */
24615
+ onUncoveredCondition: SceneUncoveredPolicySchema.default(SCENE_DEFAULT_UNCOVERED_POLICY),
24616
+ /**
24617
+ * The light whose checks are currently being SAT OUT under
24618
+ * `onUncoveredCondition: 'skip'` — `null` when the scene is checking normally.
24619
+ *
24620
+ * Engine-reported and advisory only: it moves no verdict, no latch and no
24621
+ * hysteresis. It exists so the card can say *"night (IR) — checks paused,
24622
+ * nothing captured in this light"* in the same calm voice as the coverage
24623
+ * line, because the alternative is a scene that silently stops answering
24624
+ * after sunset with nothing anywhere saying why. A skipped check must never
24625
+ * read as a broken one.
24626
+ */
24627
+ suspendedCondition: SceneConditionSchema.nullable().default(null),
24456
24628
  /** Named cause when `verdict === 'unknown'`. */
24457
24629
  unavailable: SceneUnavailableSchema.nullable(),
24458
24630
  /** Conditions that have at least one comparable reference — the coverage line
@@ -24496,6 +24668,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
24496
24668
  minObservationSpacingSec: number().int().min(0).max(3600).optional(),
24497
24669
  anchorThreshold: number().min(0).max(1).optional(),
24498
24670
  autoRestore: boolean().optional(),
24671
+ onUncoveredCondition: SceneUncoveredPolicySchema.optional(),
24499
24672
  /** `null` clears the vision-model adjudicator. */
24500
24673
  confirm: SceneConfirmSchema.nullable().optional()
24501
24674
  })
@@ -27727,6 +27900,12 @@ Object.freeze({
27727
27900
  addonId: null,
27728
27901
  access: "view"
27729
27902
  },
27903
+ "llm.resolveModelRef": {
27904
+ capName: "llm",
27905
+ capScope: "system",
27906
+ addonId: null,
27907
+ access: "create"
27908
+ },
27730
27909
  "llm.setDefault": {
27731
27910
  capName: "llm",
27732
27911
  capScope: "system",
@@ -33282,7 +33461,7 @@ var AdminUIAddon = class extends BaseAddon {
33282
33461
  capability: adminUiCapability,
33283
33462
  provider: {
33284
33463
  getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
33285
- getVersion: async () => ({ version: "1.2.61" })
33464
+ getVersion: async () => ({ version: "1.2.63" })
33286
33465
  }
33287
33466
  }];
33288
33467
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-admin-ui",
3
- "version": "1.2.61",
3
+ "version": "1.2.63",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Admin UI — Vite frontend build and server-side addon",
@@ -1,3 +0,0 @@
1
- import{I as e,K as t,Q as n,S as r,W as i,a,b as o,q as s,t 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,o as f}from"./player-overlays-7AKPeTm-.js";import{At as p,Dt as m,Et as h,Ft as g,It as _,Kt as v,Lt as y,Mt as b,Pt as x,Rt as S,Tt as C,Xt as w,h as T,jt as E,kt as D,nt as O,tt as k}from"./src-CgOOvCVA.js";import{K as A}from"./src-BaVKQIP9.js";import{t as j}from"./boxes-BN_wTQoj.js";import{n as M,t as N}from"./send-CiGeFhM_.js";import{t as P}from"./pencil-CZ2gMyTB.js";import{t as F}from"./plus-ChsVL8uT.js";import{t as I}from"./server-BBJTl0ih.js";import{t as L}from"./square-BlgtgcV9.js";import{C as R,D as z,H as B,K as V,N as H,V as U,i as ee,l as W,s as te,t as G,v as ne}from"./index-Dr-ap6SH.js";import{t as K}from"./AdminPage-CCD07UMs.js";import{t as re}from"./AdminTabs-D4DXIa-o.js";var q=V(`cloud`,[[`path`,{d:`M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z`,key:`p7xjir`}]]);n();var J=[`llm`,`listProfiles`],ie=[`llm`,`listProfileKinds`],ae=[`llm`,`getDefaults`],oe=[`llm`,`listRuntimeNodes`],se=[`llm`,`listModelCatalog`],ce=[`llm`,`getUsage`],le=[{purpose:`vision`,visionOnly:!0},{purpose:`text`,visionOnly:!1}],ue=new Map(Object.entries({openai:q,anthropic:q,google:q,"openai-compatible":ne,"managed-local":H}));function de(e){return ue.get(e)??null}function Y(e){return typeof e==`string`&&e.length>0?e:void 0}function X(e){if(typeof e==`number`&&Number.isFinite(e))return e;if(typeof e==`string`&&e.trim()!==``){let t=Number(e);return Number.isFinite(t)?t:void 0}}function Z(e){return e===!0}function fe(e){return{model:e.model,baseUrl:e.baseUrl,apiKey:e.apiKey,supportsVision:e.supportsVision,temperature:e.temperature,topP:e.topP,topK:e.topK,maxTokens:e.maxTokens,contextLength:e.contextLength,systemPrompt:e.systemPrompt,timeoutMs:e.timeoutMs,connectTimeoutMs:e.connectTimeoutMs,firstTokenTimeoutMs:e.firstTokenTimeoutMs,idleTimeoutMs:e.idleTimeoutMs,retryEnabled:e.retry.enabled,retryMaxAttempts:e.retry.maxAttempts,toolsEnabled:e.toolsEnabled,contextSize:e.runtime?.contextSize,gpuLayers:e.runtime?.gpuLayers,threads:e.runtime?.threads,parallel:e.runtime?.parallel,idleStopMinutes:e.runtime?.idleStopMinutes,batchSize:e.runtime?.batchSize,ubatchSize:e.runtime?.ubatchSize,flashAttention:e.runtime?.flashAttention,mlock:e.runtime?.mlock,noMmap:e.runtime?.noMmap,cacheTypeK:e.runtime?.cacheTypeK,cacheTypeV:e.runtime?.cacheTypeV}}var pe=[`f32`,`f16`,`q8_0`,`q5_1`,`q5_0`,`q4_1`,`q4_0`];function me(e){return typeof e==`string`&&pe.includes(e)?e:void 0}function he(e,t,n){let r=X(e.threads),i=X(e.batchSize),a=X(e.ubatchSize),o=me(e.cacheTypeK),s=me(e.cacheTypeV);return{nodeId:t,engine:`llama-cpp`,model:{kind:`catalog`,catalogId:n},contextSize:X(e.contextSize)??4096,gpuLayers:X(e.gpuLayers)??0,...r===void 0?{}:{threads:r},parallel:X(e.parallel)??1,...i===void 0?{}:{batchSize:i},...a===void 0?{}:{ubatchSize:a},flashAttention:Z(e.flashAttention),mlock:Z(e.mlock),noMmap:Z(e.noMmap),...o===void 0?{}:{cacheTypeK:o},...s===void 0?{}:{cacheTypeV:s},autoStart:Z(e.autoStart),idleStopMinutes:X(e.idleStopMinutes)??30}}function ge(e,t,n,r,i){let a={id:n?.id??crypto.randomUUID(),name:t,kind:e,addonId:`ai`,enabled:n?.enabled??!0,model:Y(r.model)??n?.model??``,supportsVision:i?.supportsVision??Z(r.supportsVision),timeoutMs:X(r.timeoutMs)??A.totalMs,connectTimeoutMs:X(r.connectTimeoutMs)??A.connectMs,firstTokenTimeoutMs:X(r.firstTokenTimeoutMs)??A.firstTokenMs,idleTimeoutMs:X(r.idleTimeoutMs)??A.idleMs,retry:{enabled:Z(r.retryEnabled),maxAttempts:X(r.retryMaxAttempts)??1},toolsEnabled:Z(r.toolsEnabled)},o=Y(r.baseUrl),s=Y(r.apiKey),c=X(r.temperature),l=X(r.topP),u=X(r.topK),d=X(r.maxTokens),f=X(r.contextLength),p=Y(r.systemPrompt);return{...a,...o===void 0?{}:{baseUrl:o},...s===void 0?{}:{apiKey:s},...c===void 0?{}:{temperature:c},...l===void 0?{}:{topP:l},...u===void 0?{}:{topK:u},...d===void 0?{}:{maxTokens:d},...f===void 0?{}:{contextLength:f},...p===void 0?{}:{systemPrompt:p},...i===void 0?{}:{model:i.catalogId,runtime:he(r,i.nodeId,i.catalogId)}}}function _e(e){let t=new Map;for(let n of e){let e=t.get(n.addonId)??[];e.push(n),t.set(n.addonId,e)}return[...t.entries()].map(([e,t])=>({addonId:e,kinds:t}))}function Q({kind:e,className:t}){let n=de(e.kind);return n===null?l(`span`,{className:t,children:e.icon}):l(n,{className:t})}function ve({kinds:e,onPick:t,onCancel:n}){let{t:a}=G(),o=i(()=>_e(e),[e]);return l(C,{open:!0,onOpenChange:e=>!e&&n(),children:r(m,{width:`lg`,placement:`sheet-on-mobile`,scroll:`body`,children:[l(p,{children:l(E,{children:a(`ai.chooseKind`,`Choose a profile type`)})}),r(h,{className:`space-y-4 pr-1`,children:[o.length===0&&l(`p`,{className:`text-xs text-foreground-subtle`,children:a(`ai.noKinds`,`No AI provider is installed.`)}),o.map(e=>r(`div`,{children:[l(`div`,{className:`mb-1.5 text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle`,children:e.addonId}),l(`div`,{className:`grid grid-cols-2 gap-2 sm:grid-cols-3`,children:e.kinds.map(n=>r(`button`,{type:`button`,onClick:()=>t(n),className:`flex flex-col items-center gap-1.5 rounded-lg border border-border bg-surface px-3 py-3 text-xs transition-colors hover:border-primary/50 hover:bg-surface-hover`,children:[l(Q,{kind:n,className:`h-5 w-5 text-foreground-subtle`}),l(`span`,{className:`font-medium text-foreground`,children:n.label})]},`${e.addonId}:${n.kind}`))})]},e.addonId))]}),l(D,{children:l(S,{size:`sm`,variant:`secondary`,type:`button`,onClick:n,children:a(`common.cancel`,`Cancel`)})})]})})}function ye({kind:e,existing:t,onSave:n,onCancel:c,saving:u,error:f}){let{t:_}=G(),b=d(),w=e.kind===`managed-local`,[O,k]=s(t?.name??e.label),[A,j]=s(()=>t===null?{}:fe(t)),[M,N]=s(t?.runtime?.nodeId??`hub`),[P,F]=s(t?.runtime?.model.kind===`catalog`?t.runtime.model.catalogId:``),I=a({queryKey:se,enabled:w,queryFn:()=>b.trpcClient.llm.listModelCatalog.query({})}),L=i(()=>[{value:``,label:_(`ai.selectModel`,`Select a model…`)},...(I.data??[]).map(e=>({value:e.id,label:`${e.label} · ${e.quantization} · ${(e.sizeBytes/1e9).toFixed(1)} GB`}))],[I.data,_]),z=()=>{if(!w){n(ge(e.kind,O,t,A));return}let r=I.data?.find(e=>e.id===P);n(ge(e.kind,O,t,A,{nodeId:M,catalogId:P,supportsVision:r?.purpose===`vision`}))},B=u||O.trim()===``||w&&P===``;return l(C,{open:!0,onOpenChange:e=>!e&&c(),children:r(m,{width:`lg`,placement:`sheet-on-mobile`,scroll:`body`,children:[l(p,{children:r(E,{className:`flex items-center gap-2`,children:[l(Q,{kind:e,className:`h-4 w-4 text-foreground-subtle`}),t===null?_(`ai.newProfile`,`New {{kind}} profile`,{kind:e.label}):_(`ai.editProfile`,`Edit {{name}}`,{name:t.name})]})}),r(h,{className:`space-y-3 pr-1`,children:[f!==null&&l(`div`,{className:`rounded-md border border-danger/40 bg-danger/10 px-3 py-2 text-xs text-danger`,children:f}),r(`div`,{children:[l(`label`,{className:`mb-1 block text-[11px] font-medium text-foreground`,htmlFor:`ai-profile-name`,children:_(`ai.name`,`Name`)}),l(g,{id:`ai-profile-name`,value:O,onChange:e=>k(e.target.value),placeholder:e.label})]}),w&&r(o,{children:[l(T,{selectedNodeId:M,onSelect:N,label:_(`ai.runtimeNode`,`Runtime node`),alwaysShow:!0}),r(`div`,{children:[l(`label`,{className:`mb-1 block text-[11px] font-medium text-foreground`,htmlFor:`ai-profile-catalog`,children:_(`ai.model`,`Model`)}),l(x,{id:`ai-profile-catalog`,value:P,options:L,onChange:e=>F(e.target.value)})]})]}),l(v,{schema:e.configSchema,values:A,onChange:j}),t!==null&&l(`p`,{className:`text-[10px] text-foreground-subtle`,children:_(`ai.secretHint`,`A stored API key is never shown. Leave it as-is to keep it; type a new value to replace it.`)})]}),r(D,{children:[w&&P===``&&l(y,{variant:`warning`,children:_(`ai.pickModelFirst`,`pick a model`)}),l(S,{size:`sm`,variant:`secondary`,type:`button`,onClick:c,children:_(`common.cancel`,`Cancel`)}),r(S,{size:`sm`,type:`button`,disabled:B,onClick:z,children:[u&&l(R,{className:`h-3.5 w-3.5 animate-spin`}),t===null?_(`ai.createProfile`,`Create profile`):_(`ai.saveChanges`,`Save changes`)]})]})]})})}n();function be(e){return[{key:`name`,header:e.t(`ai.colProfile`,`Profile`),render:t=>{let n=e.kindByName.get(t.kind);return r(`span`,{className:`flex items-center gap-2`,children:[l(`span`,{className:`flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-surface`,title:n?.label??t.kind,children:n===void 0?l(`span`,{className:`text-xs text-foreground-subtle`,children:`?`}):l(Q,{kind:n,className:`h-4 w-4 text-foreground-subtle`})}),r(`span`,{className:`min-w-0`,children:[l(`span`,{className:`block truncate text-sm font-medium text-foreground`,children:t.name}),e.testResult?.id===t.id&&l(`span`,{className:`block truncate text-[10px] ${e.testResult.ok?`text-success`:`text-danger`}`,children:e.testResult.text})]})]})}},{key:`kind`,header:e.t(`ai.colKind`,`Kind`),nowrap:!0,render:t=>l(`span`,{className:`text-[11px] text-foreground-subtle`,children:e.kindByName.get(t.kind)?.label??t.kind})},{key:`model`,header:e.t(`ai.colModel`,`Model`),render:e=>l(`span`,{className:`text-[11px] text-foreground`,children:e.model})},{key:`vision`,header:e.t(`ai.colVision`,`Vision`),align:`center`,nowrap:!0,render:t=>t.supportsVision?l(y,{variant:`info`,style:`outline`,children:e.t(`ai.vision`,`vision`)}):l(`span`,{className:`text-[11px] text-foreground-subtle`,children:`—`})},{key:`node`,header:e.t(`ai.colNode`,`Node`),nowrap:!0,render:e=>e.runtime===void 0?l(`span`,{className:`text-[11px] text-foreground-subtle`,children:`—`}):l(y,{variant:`default`,children:e.runtime.nodeId})},{key:`enabled`,header:e.t(`ai.colOn`,`On`),align:`center`,nowrap:!0,render:t=>l(b,{checked:t.enabled,onCheckedChange:()=>e.onToggle(t),"aria-label":`toggle ${t.name}`})},{key:`actions`,header:``,align:`right`,nowrap:!0,render:t=>r(`span`,{className:`flex items-center justify-end gap-1`,children:[l(S,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>e.onTest(t),title:e.t(`ai.test`,`Test`),children:e.testingId===t.id?l(R,{className:`h-3.5 w-3.5 animate-spin`}):l(N,{className:`h-3.5 w-3.5`})}),l(S,{size:`sm`,variant:`ghost`,type:`button`,disabled:!e.kindByName.has(t.kind),onClick:()=>e.onEdit(t),title:e.t(`common.edit`,`Edit`),children:l(P,{className:`h-3.5 w-3.5`})}),l(S,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>e.onDelete(t),title:e.t(`common.delete`,`Delete`),children:l(te,{className:`h-3.5 w-3.5 text-danger`})})]})}]}function xe(){let{t:e}=G(),t=d(),[n,o]=s({mode:`closed`}),[u,f]=s(null),[p,m]=s(null),h=a({queryKey:J,queryFn:()=>t.trpcClient.llm.listProfiles.query({})}),g=a({queryKey:ie,queryFn:async()=>(await t.trpcClient.llm.listProfileKinds.query({})).map(e=>({...e,configSchema:e.configSchema}))}),v=()=>{h.refetch()},y=c({mutationFn:e=>t.trpcClient.llm.upsertProfile.mutate({profile:e}),onSuccess:()=>{o({mode:`closed`}),m(null),v()},onError:t=>m(t instanceof Error?t.message:e(`ai.saveFailed`,`Save failed`))}),b=c({mutationFn:e=>t.trpcClient.llm.deleteProfile.mutate({addonId:e.addonId,profileId:e.id}),onSuccess:v}),x=c({mutationFn:e=>t.trpcClient.llm.testProfile.mutate({addonId:e.addonId,profileId:e.id}),onSuccess:(e,t)=>f({id:t.id,ok:e.ok,text:e.ok?`${e.text} (${String(e.usage.outputTokens)} tok, ${String(e.latencyMs)}ms)`:`${e.code}: ${e.message}`})}),C=h.data??[],T=i(()=>g.data??[],[g.data]),E=i(()=>{let e=new Map;for(let t of T)e.set(t.kind,t);return e},[T]);return r(K,{icon:W,title:e(`ai.profilesTitle`,`AI profiles`),subtitle:e(`ai.profilesSubtitle`,`One row = one usable model endpoint. The defaults below decide who answers when a feature names no profile.`),actions:r(S,{size:`sm`,type:`button`,onClick:()=>{m(null),o({mode:`pick-kind`})},children:[l(F,{className:`h-3.5 w-3.5`}),e(`ai.addProfile`,`Add profile`)]}),children:[h.isError&&l(w,{title:e(`ai.loadFailed`,`Failed to load profiles`),message:h.error instanceof Error?h.error.message:e(`common.error`,`Error`),onRetry:()=>void h.refetch()}),l(Se,{profiles:C}),h.isLoading&&l(`div`,{className:`flex items-center justify-center py-16`,children:l(R,{className:`h-6 w-6 animate-spin text-primary`})}),!h.isLoading&&C.length===0&&l(`div`,{className:`py-16 text-center text-xs text-foreground-subtle`,children:e(`ai.noProfiles`,`No AI profiles yet. Click "Add profile" to configure one.`)}),C.length>0&&l(_,{children:l(k,{columns:be({kindByName:E,testResult:u,testingId:x.isPending?x.variables?.id??null:null,t:e,onToggle:e=>y.mutate({...e,enabled:!e.enabled,apiKey:`__redacted__`}),onTest:e=>x.mutate(e),onEdit:e=>{let t=E.get(e.kind);t!==void 0&&(m(null),o({mode:`form`,kind:t,profile:e}))},onDelete:e=>b.mutate(e)}),rows:C,rowKey:e=>e.id,minWidthPx:760,bordered:!1,mobileMode:`scroll`,maxBodyHeight:`max-h-[60vh]`})}),n.mode===`pick-kind`&&l(ve,{kinds:T,onCancel:()=>o({mode:`closed`}),onPick:e=>o({mode:`form`,kind:e,profile:null})}),n.mode===`form`&&l(ye,{kind:n.kind,existing:n.profile,saving:y.isPending,error:p,onSave:e=>y.mutate(e),onCancel:()=>o({mode:`closed`})})]})}function Se({profiles:e}){let{t}=G(),n=d(),i=a({queryKey:ae,queryFn:()=>n.trpcClient.llm.getDefaults.query({})}),o=c({mutationFn:e=>n.trpcClient.llm.setDefault.mutate({selector:{purpose:e.purpose},profileId:e.profileId}),onSuccess:()=>{i.refetch()}}),s=e=>(i.data??[]).find(t=>`purpose`in t.selector&&t.selector.purpose===e)?.profileId??``,u=e=>e===`vision`?t(`ai.defaultVision`,`Default vision profile`):t(`ai.defaultText`,`Default text profile`);return l(_,{children:l(`div`,{className:`grid gap-4 p-4 sm:grid-cols-2`,children:le.map(({purpose:n,visionOnly:i})=>{let a=i?e.filter(e=>e.supportsVision):e,c=s(n),d=e.find(e=>e.id===c);return r(`div`,{children:[l(`label`,{className:`mb-1 block text-[11px] font-medium text-foreground`,htmlFor:`llm-default-${n}`,children:u(n)}),r(`select`,{id:`llm-default-${n}`,"aria-label":u(n),className:`h-7 w-full rounded-md border border-border bg-surface px-2.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`,value:c,onChange:e=>o.mutate({purpose:n,profileId:e.target.value===``?null:e.target.value}),children:[l(`option`,{value:``,children:t(`ai.none`,`None`)}),c!==``&&!a.some(e=>e.id===c)&&l(`option`,{value:c,children:d?.name??c}),a.map(e=>r(`option`,{value:e.id,children:[e.name,` · `,e.model]},e.id))]}),c!==``&&d===void 0&&l(`p`,{className:`mt-1 text-[10px] text-danger`,children:t(`ai.defaultMissing`,`This default names a profile that no longer exists.`)}),d!==void 0&&!d.enabled&&l(`p`,{className:`mt-1 text-[10px] text-warning`,children:t(`ai.defaultDisabled`,`This default profile is disabled — callers resolve to no profile.`)}),o.error!=null&&l(`p`,{className:`mt-1 text-[10px] text-danger`,children:String(o.error)})]},n)})})})}n();var Ce=5e3;function we(e){return`${(e/1e9).toFixed(1)} GB`}function Te(){let{t:e}=G(),t=d(),n=a({queryKey:oe,refetchInterval:Ce,queryFn:()=>t.trpcClient.llm.listRuntimeNodes.query({})}),o=i(()=>[{key:`node`,header:e(`ai.colNode`,`Node`),render:e=>l(`span`,{className:`text-sm font-medium text-foreground`,children:e.nodeId})},{key:`state`,header:e(`ai.colState`,`State`),nowrap:!0,render:t=>r(`span`,{className:`flex items-center gap-1.5`,children:[l(y,{variant:t.reachable?`success`:`danger`,children:t.reachable?t.status?.state??e(`ai.ready`,`ready`):e(`ai.unreachable`,`unreachable`)}),t.status?.downloadProgress!==void 0&&l(y,{variant:`info`,children:`${String(Math.round(t.status.downloadProgress*100))}%`})]})},{key:`model`,header:e(`ai.colModel`,`Model`),render:e=>l(`span`,{className:`text-[11px] text-foreground`,children:e.status?.modelId??`—`})},{key:`disk`,header:e(`ai.colDisk`,`On disk`),align:`right`,nowrap:!0,render:e=>l(`span`,{className:`text-[11px] text-foreground-subtle`,children:e.disk===void 0?`—`:we(e.disk.modelsBytes)})},{key:`error`,header:e(`ai.colError`,`Error`),render:e=>e.error===void 0?l(`span`,{className:`text-[11px] text-foreground-subtle`,children:`—`}):l(`span`,{className:`text-[11px] text-danger`,children:e.error})}],[e]),s=n.data??[];return r(K,{icon:I,title:e(`ai.runtimesTitle`,`Managed runtimes`),subtitle:e(`ai.runtimesSubtitle`,`Nodes advertising the llm-runtime capability: what each has loaded, what it is downloading, and how much model data sits on its disk.`),children:[n.isError&&l(w,{title:e(`ai.runtimesFailed`,`Failed to read runtime nodes`),message:n.error instanceof Error?n.error.message:e(`common.error`,`Error`),onRetry:()=>void n.refetch()}),s.length===0?l(`div`,{className:`py-16 text-center text-xs text-foreground-subtle`,children:e(`ai.noRuntimes`,`No node advertises the llm-runtime capability.`)}):l(_,{children:l(k,{columns:o,rows:s,rowKey:e=>e.nodeId,minWidthPx:640,bordered:!1,mobileMode:`scroll`,maxBodyHeight:O})})]})}var Ee=`/addon/ai/test-chat`;function De(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Oe(e){if(!De(e))return null;let t=e.kind;return t!==`snapshot`&&t!==`track`||typeof e.mimeType!=`string`||typeof e.sizeBytes!=`number`?null:{kind:t,mimeType:e.mimeType,sizeBytes:e.sizeBytes,...typeof e.mediaKind==`string`?{mediaKind:e.mediaKind}:{},...typeof e.capturedAt==`number`?{capturedAt:e.capturedAt}:{}}}var ke=[`resolving-image`,`connecting`,`first-token-wait`,`streaming`];function Ae(e){let t;try{t=JSON.parse(e)}catch{return null}if(!De(t))return null;switch(t.kind){case`status`:{let e=ke.find(e=>e===t.phase);return e===void 0?null:{kind:`status`,phase:e}}case`meta`:return typeof t.profileName!=`string`||typeof t.model!=`string`?null:{kind:`meta`,profileName:t.profileName,model:t.model,mediaSent:Oe(t.mediaSent),streamed:t.streamed===!0,firstTokenTimeoutMs:typeof t.firstTokenTimeoutMs==`number`?t.firstTokenTimeoutMs:0};case`token`:return typeof t.text==`string`?{kind:`token`,text:t.text}:null;case`done`:return{kind:`done`,inputTokens:typeof t.inputTokens==`number`?t.inputTokens:0,outputTokens:typeof t.outputTokens==`number`?t.outputTokens:0,latencyMs:typeof t.latencyMs==`number`?t.latencyMs:0,truncated:t.truncated===!0};case`error`:return{kind:`error`,code:typeof t.code==`string`?t.code:`adapter-error`,message:typeof t.message==`string`?t.message:`the provider returned an error`};default:return null}}async function*je(e){let t=e.getReader(),n=new TextDecoder,r=``;try{for(;;){let{done:e,value:i}=await t.read();if(e)break;r+=n.decode(i,{stream:!0});let a=r.indexOf(`
2
- `);for(;a>=0;){let e=r.slice(0,a).trim();if(r=r.slice(a+1),e.length>0){let t=Ae(e);t!==null&&(yield t)}a=r.indexOf(`
3
- `)}}let e=r.trim();if(e.length>0){let t=Ae(e);t!==null&&(yield t)}}finally{t.releaseLock()}}function Me(e){return e.filter(e=>e.failed!==!0&&e.content.length>0).map(e=>({role:e.role,content:e.content}))}function Ne(e){let t=e.sizeBytes<1024*1024?`${(e.sizeBytes/1024).toFixed(0)} KB`:`${(e.sizeBytes/(1024*1024)).toFixed(1)} MB`,n=[e.kind===`snapshot`?`camera snapshot`:`track best-shot`,t];return e.mediaKind!==void 0&&n.push(e.mediaKind),e.capturedAt!==void 0&&n.push(new Date(e.capturedAt).toLocaleTimeString()),n.join(` · `)}function Pe(e,t){switch(e){case`resolving-image`:return`fetching the picture…`;case`connecting`:return`connecting to the provider…`;case`first-token-wait`:return`waiting for the model — a cold load can take a while (up to ${String(Math.round(t/1e3))}s)`;case`streaming`:return`answering…`}}n();var Fe=[`deviceManager`,`listAll`,`cameras`],Ie=[`pipelineAnalytics`,`listRecentTracks`,`ai-test`],Le=360*60*1e3,Re=40,ze=160,$={phase:`connecting`,firstTokenTimeoutMs:0,text:``,mediaSent:null};function Be(){let{t:n}=G(),o=d(),[c,f]=s(``),[p,m]=s(``),[h,v]=s(``),[y,b]=s([]),[C,T]=s(null),[E,D]=s(`none`),[O,k]=s(null),[A,j]=s(null),P=t(null);u(()=>()=>P.current?.abort(),[]);let F=a({queryKey:J,queryFn:()=>o.trpcClient.llm.listProfiles.query({})}),I=i(()=>(F.data??[]).filter(e=>e.enabled),[F.data]),R=I.find(e=>e.id===c)??null,z=A!==null,V=e(async(e,t)=>{let r=new AbortController;P.current=r,k(null),j($);let i=``,a=null,o,s=null,l=null;try{let n=await fetch(Ee,{method:`POST`,headers:{"content-type":`application/json`},credentials:`same-origin`,signal:r.signal,body:JSON.stringify({profileId:c,messages:e,...p.trim()===``?{}:{system:p.trim()},...t===void 0?{}:{attachment:t}})});if(!n.ok||n.body===null){let e=await n.text().catch(()=>``);throw Error(e.length>0?e:`HTTP ${String(n.status)}`)}for await(let e of je(n.body))switch(e.kind){case`status`:j(t=>({...t??$,phase:e.phase}));break;case`meta`:a=e.mediaSent,o=e.model,j(t=>({...t??$,firstTokenTimeoutMs:e.firstTokenTimeoutMs,mediaSent:e.mediaSent}));break;case`token`:i+=e.text,j(e=>({...e??$,text:i}));break;case`done`:l={outputTokens:e.outputTokens,latencyMs:e.latencyMs};break;case`error`:s=`${e.code}: ${e.message}`;break}}catch(e){r.signal.aborted||k(e instanceof Error?e.message:n(`common.error`,`Error`))}finally{P.current=null,j(null)}let u=r.signal.aborted&&i.length>0;s===null&&i.length===0&&!u||b(e=>[...e,{id:`a-${String(Date.now())}`,role:`assistant`,content:s??i,...s===null?{}:{failed:!0},...a===null?{}:{attachment:a},...o!==void 0&&s===null?{model:o}:{},...l===null?{}:l}])},[c,p,n]),H=e(()=>{let e=h.trim();if(e===``||c===``||z)return;let t={id:`u-${String(Date.now())}`,role:`user`,content:e,...C===null?{}:{attachmentLabel:C.label,attachmentKind:C.ref.kind}},n=[...y,t];b(n),v(``);let r=C?.ref;T(null),D(`none`),V(Me(n),r)},[h,C,c,V,z,y]),W=()=>P.current?.abort();return r(K,{icon:M,title:n(`ai.testTitle`,`Test chat`),subtitle:n(`ai.testSubtitle`,`A throwaway conversation with one profile. Attach a live camera snapshot or a recent track to check the model can actually read a picture. Nothing is saved — only the usage row.`),actions:r(S,{size:`sm`,variant:`secondary`,type:`button`,disabled:y.length===0&&C===null,onClick:()=>{P.current?.abort(),b([]),T(null),v(``),k(null),j(null)},children:[l(te,{className:`h-3.5 w-3.5`}),n(`ai.clearChat`,`Clear`)]}),children:[r(_,{className:`space-y-3 p-4`,children:[r(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[r(`div`,{children:[l(`label`,{className:`mb-1 block text-[11px] font-medium text-foreground`,htmlFor:`ai-test-profile`,children:n(`ai.profile`,`Profile`)}),l(x,{id:`ai-test-profile`,value:c,onChange:e=>f(e.target.value),options:[{value:``,label:n(`ai.selectProfile`,`Select a profile…`)},...I.map(e=>({value:e.id,label:`${e.name} · ${e.model}${e.supportsVision?` · vision`:``}`}))]}),I.length===0&&!F.isLoading&&l(`p`,{className:`mt-1 text-[10px] text-foreground-subtle`,children:n(`ai.noEnabledProfiles`,`No enabled profile. Add one on the Profiles tab.`)})]}),r(`div`,{children:[l(`label`,{className:`mb-1 block text-[11px] font-medium text-foreground`,htmlFor:`ai-test-system`,children:n(`ai.systemPrompt`,`System prompt (optional)`)}),l(g,{id:`ai-test-system`,value:p,onChange:e=>m(e.target.value),placeholder:n(`ai.systemPlaceholder`,`Answer in one short sentence.`)})]})]}),l(`p`,{className:`text-[10px] text-foreground-subtle`,children:n(`ai.streamNote`,`The answer streams as the model produces it. A cold model can take a while to load — the status line says which phase it is in.`)}),R!==null&&!R.supportsVision&&l(`p`,{className:`text-[10px] text-warning`,children:n(`ai.notVision`,`This profile is not vision-capable — an attached image will be refused before anything is read.`)})]}),O!==null&&l(w,{title:n(`ai.chatFailed`,`The test call failed`),message:O}),l(Ve,{turns:y,live:A}),r(_,{className:`space-y-2 p-3`,children:[C!==null&&r(`div`,{className:`flex items-center gap-2 rounded-md border border-border bg-surface px-2.5 py-1.5`,children:[C.ref.kind===`snapshot`?l(U,{className:`h-3.5 w-3.5 text-foreground-subtle`}):l(B,{className:`h-3.5 w-3.5 text-foreground-subtle`}),l(`span`,{className:`min-w-0 flex-1 truncate text-[11px] text-foreground`,children:C.label}),l(S,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>T(null),children:l(ee,{className:`h-3.5 w-3.5`})})]}),r(`div`,{className:`flex flex-wrap items-center gap-2`,children:[r(S,{size:`sm`,variant:E===`snapshot`?`primary`:`outline`,type:`button`,onClick:()=>D(E===`snapshot`?`none`:`snapshot`),children:[l(U,{className:`h-3.5 w-3.5`}),n(`ai.attachSnapshot`,`Camera snapshot`)]}),r(S,{size:`sm`,variant:E===`track`?`primary`:`outline`,type:`button`,onClick:()=>D(E===`track`?`none`:`track`),children:[l(B,{className:`h-3.5 w-3.5`}),n(`ai.attachTrack`,`Recent track`)]})]}),E===`snapshot`&&l(Ue,{onPick:e=>{T({ref:{kind:`snapshot`,deviceId:e.id},label:n(`ai.snapshotOf`,`Snapshot of {{name}}`,{name:e.name})}),D(`none`)}}),E===`track`&&l(Ge,{onPick:(e,t)=>{T({ref:{kind:`track`,trackId:e.trackId},label:n(`ai.trackOf`,`{{class}} on {{name}}`,{class:e.label??e.className,name:t})}),D(`none`)}}),r(`div`,{className:`flex items-center gap-2`,children:[l(g,{value:h,onChange:e=>v(e.target.value),onKeyDown:e=>{e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),H())},placeholder:n(`ai.messagePlaceholder`,`Ask the model something…`),"aria-label":n(`ai.message`,`Message`)}),z?r(S,{size:`sm`,variant:`secondary`,type:`button`,onClick:W,children:[l(L,{className:`h-3.5 w-3.5`}),n(`ai.stop`,`Stop`)]}):r(S,{size:`sm`,type:`button`,disabled:h.trim()===``||c===``,onClick:H,children:[l(N,{className:`h-3.5 w-3.5`}),n(`ai.send`,`Send`)]})]})]})]})}function Ve({turns:e,live:t}){let{t:n}=G();return e.length===0&&t===null?l(`div`,{className:`py-12 text-center text-xs text-foreground-subtle`,children:n(`ai.chatEmpty`,`Pick a profile, optionally attach a camera snapshot or a recent track, and send a message. Nothing here is saved.`)}):l(_,{children:r(`div`,{className:`max-h-[50vh] space-y-2 overflow-y-auto p-3`,children:[e.map(e=>l(He,{turn:e},e.id)),t!==null&&r(`div`,{className:`space-y-1`,children:[t.text.length>0&&l(He,{turn:{id:`live`,role:`assistant`,content:t.text},streaming:!0}),r(`div`,{className:`flex items-center gap-2 text-[11px] text-foreground-subtle`,children:[l(R,{className:`h-3.5 w-3.5 animate-spin`}),Pe(t.phase,t.firstTokenTimeoutMs)]})]})]})})}function He({turn:e,streaming:t=!1}){let n=e.role===`user`,i=e.failed===!0?`border-danger/40 bg-danger/10`:`border-border bg-surface`;return l(`div`,{className:`flex ${n?`justify-end`:`justify-start`}`,children:r(`div`,{className:`max-w-[85%] rounded-lg border px-3 py-2 ${n?`border-primary/40 bg-primary/10`:i}`,children:[r(`p`,{className:`whitespace-pre-wrap break-words text-xs text-foreground`,children:[e.content,t&&l(`span`,{className:`ml-0.5 animate-pulse text-primary`,children:`▌`})]}),e.attachmentLabel!==void 0&&r(`p`,{className:`mt-1 flex items-center gap-1 text-[10px] text-foreground-subtle`,children:[e.attachmentKind===`track`?l(B,{className:`h-3 w-3`}):l(U,{className:`h-3 w-3`}),e.attachmentLabel]}),e.attachment!==void 0&&r(`p`,{className:`mt-1 flex items-center gap-1 text-[10px] text-foreground-subtle`,children:[e.attachment.kind===`snapshot`?l(U,{className:`h-3 w-3`}):l(B,{className:`h-3 w-3`}),Ne(e.attachment)]}),e.model!==void 0&&l(`p`,{className:`mt-1 text-[10px] text-foreground-subtle`,children:`${e.model} · ${String(e.outputTokens??0)} tok · ${String(e.latencyMs??0)} ms`})]})})}function Ue({onPick:e}){let{t}=G(),n=d(),r=a({queryKey:Fe,queryFn:()=>n.trpcClient.deviceManager.listAll.query({isCamera:!0,projection:`slim`})}),i=(r.data??[]).filter(e=>!e.disabled);return r.isLoading?l(R,{className:`h-4 w-4 animate-spin text-foreground-subtle`}):i.length===0?l(`p`,{className:`text-[11px] text-foreground-subtle`,children:t(`ai.noCameras`,`No camera found.`)}):l(`div`,{className:`grid max-h-56 grid-cols-2 gap-2 overflow-y-auto sm:grid-cols-4`,children:i.map(t=>l(We,{device:t,onPick:e},t.id))})}function We({device:e,onPick:t}){let{src:n}=f(e.id,{width:ze});return r(`button`,{type:`button`,onClick:()=>t(e),className:`overflow-hidden rounded-md border border-border bg-surface text-left transition-colors hover:border-primary/50`,children:[l(`span`,{className:`block aspect-video w-full bg-background-elevated`,children:n!==null&&l(`img`,{src:n,alt:``,className:`h-full w-full object-cover`,loading:`lazy`})}),r(`span`,{className:`flex items-center gap-1 px-2 py-1`,children:[!e.online&&l(y,{variant:`warning`,children:`off`}),l(`span`,{className:`truncate text-[10px] text-foreground`,children:e.name})]})]})}function Ge({onPick:e}){let{t}=G(),n=d(),o=a({queryKey:Fe,queryFn:()=>n.trpcClient.deviceManager.listAll.query({isCamera:!0,projection:`slim`})}),s=i(()=>(o.data??[]).map(e=>e.id),[o.data]),c=i(()=>{let e=new Map;for(let t of o.data??[])e.set(t.id,t.name);return e},[o.data]),u=a({queryKey:[...Ie,s.length],enabled:s.length>0,queryFn:()=>n.trpcClient.pipelineAnalytics.listRecentTracks.query({deviceIds:[...s],since:Date.now()-Le,limit:Re,projection:`slim`})});if(o.isLoading||u.isLoading)return l(R,{className:`h-4 w-4 animate-spin text-foreground-subtle`});let f=u.data?.tracks??[];return f.length===0?l(`p`,{className:`text-[11px] text-foreground-subtle`,children:t(`ai.noTracks`,`No track in the last 6 hours.`)}):l(`div`,{className:`max-h-56 divide-y divide-border overflow-y-auto rounded-md border border-border`,children:f.map(t=>r(`button`,{type:`button`,onClick:()=>e(t,c.get(t.deviceId)??String(t.deviceId)),className:`flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-[11px] hover:bg-surface-hover`,children:[l(B,{className:`h-3.5 w-3.5 shrink-0 text-foreground-subtle`}),l(`span`,{className:`min-w-0 flex-1 truncate text-foreground`,children:t.label??t.className}),l(`span`,{className:`shrink-0 text-foreground-subtle`,children:c.get(t.deviceId)??t.deviceId}),l(`span`,{className:`shrink-0 text-foreground-subtle`,children:new Date(t.lastSeen).toLocaleTimeString()})]},t.trackId))})}n();var Ke=1440*60*1e3,qe=[1,7,30,90];function Je(){let{t:e}=G(),t=d(),[n,o]=s(7),c=i(()=>{let e=new Date;return e.setHours(0,0,0,0),e.getTime()-(n-1)*Ke},[n]),u=a({queryKey:J,queryFn:()=>t.trpcClient.llm.listProfiles.query({})}),f=a({queryKey:[...ce,n],queryFn:()=>t.trpcClient.llm.getUsage.query({since:c})}),p=i(()=>{let e=new Map;for(let t of u.data??[])e.set(t.id,t.name);return e},[u.data]),m=i(()=>[{key:`day`,header:e(`ai.colDay`,`Day`),nowrap:!0,render:e=>e.day},{key:`consumer`,header:e(`ai.colConsumer`,`Consumer`),nowrap:!0,render:e=>l(`span`,{className:`text-[11px] text-foreground`,children:e.consumer})},{key:`profile`,header:e(`ai.colProfile`,`Profile`),render:e=>l(`span`,{className:`text-[11px] text-foreground`,children:p.get(e.profileId)??e.profileId})},{key:`calls`,header:e(`ai.colCalls`,`Calls`),align:`right`,nowrap:!0,render:e=>String(e.calls)},{key:`okErr`,header:e(`ai.colOkErr`,`OK / err`),align:`right`,nowrap:!0,render:e=>l(`span`,{className:e.errorCalls>0?`text-danger`:void 0,children:`${String(e.okCalls)} / ${String(e.errorCalls)}`})},{key:`tokens`,header:e(`ai.colTokens`,`In / out tok`),align:`right`,nowrap:!0,render:e=>`${String(e.inputTokens)} / ${String(e.outputTokens)}`},{key:`latency`,header:e(`ai.colAvgMs`,`Avg ms`),align:`right`,nowrap:!0,render:e=>String(Math.round(e.avgLatencyMs))}],[p,e]),h=f.data??[];return r(K,{icon:z,title:e(`ai.usageTitle`,`Usage`),subtitle:e(`ai.usageSubtitle`,`Server-side rollup per day, consumer and profile — never raw call rows. Errors are counted, so a profile that always fails is visible here before a feature reports it.`),actions:l(`div`,{className:`w-40`,children:l(x,{"aria-label":e(`ai.window`,`Window`),value:String(n),options:qe.map(t=>({value:String(t),label:e(`ai.lastDays`,`Last {{days}} days`,{days:t})})),onChange:e=>o(Number(e.target.value))})}),children:[f.isError&&l(w,{title:e(`ai.usageFailed`,`Failed to read usage`),message:f.error instanceof Error?f.error.message:e(`common.error`,`Error`),onRetry:()=>void f.refetch()}),h.length===0?l(`div`,{className:`py-16 text-center text-xs text-foreground-subtle`,children:e(`ai.noUsage`,`No AI calls in this window.`)}):l(_,{children:l(k,{columns:m,rows:h,rowKey:e=>`${e.day}|${e.consumer}|${e.profileId}`,minWidthPx:720,bordered:!1,mobileMode:`scroll`,maxBodyHeight:O})})]})}function Ye(){let{t:e}=G();return l(`div`,{className:`flex flex-col p-4`,children:l(re,{tabs:[{id:`profiles`,label:e(`ai.tabProfiles`,`Profiles`),icon:j,content:l(xe,{})},{id:`runtimes`,label:e(`ai.tabRuntimes`,`Runtimes`),icon:I,content:l(Te,{})},{id:`usage`,label:e(`ai.tabUsage`,`Usage`),icon:z,content:l(Je,{})},{id:`test`,label:e(`ai.tabTest`,`Test`),icon:M,content:l(Be,{})}]})})}export{Ye as AiProfilesPage,Ye as default};