@camstack/addon-admin-ui 1.2.62 → 1.2.64

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 (100) hide show
  1. package/dist/assets/{AddLocationWizard-I8kBbXKL.js → AddLocationWizard-Rtx2w8Ge.js} +1 -1
  2. package/dist/assets/{AddonCollectionPage-KrivzOtq.js → AddonCollectionPage-CRMi8Hpy.js} +1 -1
  3. package/dist/assets/{Addons-D_HB3Gz6.js → Addons-DrGoA3sf.js} +1 -1
  4. package/dist/assets/{AdminTabs-T4tXzr6m.js → AdminTabs-DPygIgVT.js} +1 -1
  5. package/dist/assets/AiProfiles-Cau0z0rv.js +3 -0
  6. package/dist/assets/{BrokerForm-B8GkNs2Z.js → BrokerForm-CNS8TLW1.js} +1 -1
  7. package/dist/assets/{Build-B-Jg80Db.js → Build-p2UrTXXB.js} +1 -1
  8. package/dist/assets/{Capabilities-KvgWF7xs.js → Capabilities-CBelMVi9.js} +1 -1
  9. package/dist/assets/{CapabilityBadges-CDULR6Ww.js → CapabilityBadges-CePOZ9pv.js} +1 -1
  10. package/dist/assets/{Cluster-BtFDl4d9.js → Cluster-CKJ4Di2k.js} +1 -1
  11. package/dist/assets/{Data-CxxsTdTH.js → Data-DQN08Osn.js} +1 -1
  12. package/dist/assets/{DetectionIntelligence-DFpsI2re.js → DetectionIntelligence-3W-QsAKt.js} +1 -1
  13. package/dist/assets/{DeviceAggregateSection-Bwo1fvlT.js → DeviceAggregateSection-CN7PswWV.js} +1 -1
  14. package/dist/assets/{DeviceDetail-BLZw49Iz.js → DeviceDetail-CvfaMldw.js} +1 -1
  15. package/dist/assets/{Devices-BQ-a1X4I.js → Devices-ekrIigG7.js} +1 -1
  16. package/dist/assets/{EmbedPlayerPage-D7ZQ6n8q.js → EmbedPlayerPage-BI2AGOPB.js} +1 -1
  17. package/dist/assets/{Events-CnrHHUoS.js → Events-dfb6f9u9.js} +1 -1
  18. package/dist/assets/{FormBuilder-BCPJkQ63.js → FormBuilder-DC09MD8z.js} +1 -1
  19. package/dist/assets/{Identity-Co5glfeQ.js → Identity-DMvDr3fz.js} +1 -1
  20. package/dist/assets/{IntegrationDetail-BxeX15Pm.js → IntegrationDetail-DmYaRumv.js} +1 -1
  21. package/dist/assets/{Integrations-CA8jB6CT.js → Integrations-BWV9LK2_.js} +1 -1
  22. package/dist/assets/{Integrations-BfKnikwe.js → Integrations-CntkGNRq.js} +1 -1
  23. package/dist/assets/{Logs-TioRBvTP.js → Logs-DmhrO61-.js} +1 -1
  24. package/dist/assets/{MotionZonesSettings-DXhni28t.js → MotionZonesSettings-Bh5Pjl_Y.js} +1 -1
  25. package/dist/assets/{MyAccess-BhxYSV_l.js → MyAccess-DeKpJLjj.js} +1 -1
  26. package/dist/assets/{Network-WkehLI9m.js → Network-nRbvH88f.js} +1 -1
  27. package/dist/assets/{NodeAddonsSettingsPanel-iAVvOCGv.js → NodeAddonsSettingsPanel-B9GdHeQO.js} +1 -1
  28. package/dist/assets/{NodeInferenceDevices-CwI9ek65.js → NodeInferenceDevices-DIbWSF6N.js} +1 -1
  29. package/dist/assets/{Notifications-DntoQaM6.js → Notifications-DPGgZRkO.js} +1 -1
  30. package/dist/assets/{Pipeline-CecRYpzk.js → Pipeline-CHfPE4zw.js} +1 -1
  31. package/dist/assets/{Plates-CG_WFsXV.js → Plates-DjHIRX11.js} +1 -1
  32. package/dist/assets/{PrivacyMaskSettings-DYJCefRc.js → PrivacyMaskSettings-BSFzMp1X.js} +1 -1
  33. package/dist/assets/{ProviderIcon-y1m8BuFZ.js → ProviderIcon-uiUuYEst.js} +1 -1
  34. package/dist/assets/{Recordings-Bd-VF9gP.js → Recordings-B8SloOkL.js} +1 -1
  35. package/dist/assets/{ScanNetworkModal-ByHfMRVr.js → ScanNetworkModal-DnvSvL3H.js} +1 -1
  36. package/dist/assets/{SceneMonitorEditor-B-vpudbK.js → SceneMonitorEditor-Ck1EyLTi.js} +1 -1
  37. package/dist/assets/{Scenes-C1eh3JuE.js → Scenes-CoFARtT2.js} +1 -1
  38. package/dist/assets/{Settings-BztTE2tp.js → Settings-srwQ29Zm.js} +1 -1
  39. package/dist/assets/{Showroom-POB0_SpW.js → Showroom-FdSkNLM7.js} +1 -1
  40. package/dist/assets/{Terminal-33TmgWoS.js → Terminal-lR1PtLfa.js} +1 -1
  41. package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-CwqT4DOs.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-Ceb4j9T3.js} +1 -1
  42. package/dist/assets/{bell-ring-DPD6sMve.js → bell-ring-2GuVPUMV.js} +1 -1
  43. package/dist/assets/{boxes-D4otfCdM.js → boxes-Cv1y4Bzc.js} +1 -1
  44. package/dist/assets/{build-cluster-graph-HhTcae8G.js → build-cluster-graph-BEIOSj-q.js} +1 -1
  45. package/dist/assets/circle-check-big-dTS9q1Zi.js +1 -0
  46. package/dist/assets/{circle-x-U6SWO2IP.js → circle-x-yDCV4dJs.js} +1 -1
  47. package/dist/assets/clock-BZHOxtKN.js +1 -0
  48. package/dist/assets/{copy-CMcitcxu.js → copy-BzvPXBQa.js} +1 -1
  49. package/dist/assets/{debounced-invalidate-CHwSuvzf.js → debounced-invalidate--vYgOSs8.js} +1 -1
  50. package/dist/assets/{dist-DaBaoyDk.js → dist-B3abLTAk.js} +1 -1
  51. package/dist/assets/dist-jUZC7DhT.js +345 -0
  52. package/dist/assets/{download-ge4FL0UH.js → download-ZEauntF-.js} +1 -1
  53. package/dist/assets/{face-bbox-Db31_MCB.js → face-bbox-Cvff56YN.js} +1 -1
  54. package/dist/assets/{hard-drive-BjTbYotH.js → hard-drive-vk5wyQE5.js} +1 -1
  55. package/dist/assets/{hostInit-COiRp72S.js → hostInit-B81OBEcL.js} +1 -1
  56. package/dist/assets/{index-B8gfFLO3.js → index-CbTjMrYx.js} +3 -3
  57. package/dist/assets/{key-round-DRN-KYrp.js → key-round-CW2EaBMX.js} +1 -1
  58. package/dist/assets/{mf-entry-bootstrap-0-cc2983c3.js → mf-entry-bootstrap-0-f99faee4.js} +2 -2
  59. package/dist/assets/{pencil-CCTI-4rI.js → pencil-XI_43st4.js} +1 -1
  60. package/dist/assets/play-DDV73FUi.js +1 -0
  61. package/dist/assets/{player-overlays-DTulF4Wm.js → player-overlays-C89ysycJ.js} +1 -1
  62. package/dist/assets/plus-CInUrJd7.js +1 -0
  63. package/dist/assets/power-2AvrvxPl.js +1 -0
  64. package/dist/assets/{radio-DxJgUT8d.js → radio-rZ8qayNX.js} +1 -1
  65. package/dist/assets/{refresh-cw-CIsX4AtN.js → refresh-cw-4m2P1KWX.js} +1 -1
  66. package/dist/assets/remoteEntry-Bz9gcjD-.js +1 -0
  67. package/dist/assets/rotate-ccw-BOvw_QMX.js +1 -0
  68. package/dist/assets/rotate-cw-D5AfagzL.js +1 -0
  69. package/dist/assets/search-Cn4Sp_ZB.js +1 -0
  70. package/dist/assets/{send-D-EllYB0.js → send-CqFJrG4o.js} +1 -1
  71. package/dist/assets/{server-BTc5FZH2.js → server-xGGeh4GQ.js} +1 -1
  72. package/dist/assets/{square-check-big-BR_fEref.js → square-check-big-ChrSTyBa.js} +1 -1
  73. package/dist/assets/square-gU59rjC6.js +1 -0
  74. package/dist/assets/src-ByBn7YGK.js +345 -0
  75. package/dist/assets/src-DJ5lhM9h.js +67 -0
  76. package/dist/assets/{undo-2-C3TKGI8Q.js → undo-2-CCsVR_hG.js} +1 -1
  77. package/dist/assets/{upload-BDphFIfP.js → upload-CqTYxUpd.js} +1 -1
  78. package/dist/assets/{use-scenes-feed-gNX_X7lL.js → use-scenes-feed-BwCEYdwU.js} +1 -1
  79. package/dist/assets/{useEventInvalidation-DxrL9t2b.js → useEventInvalidation-PP_z4Ptm.js} +1 -1
  80. package/dist/assets/{video-DIq9FtAl.js → video-oj6ReSiz.js} +1 -1
  81. package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-BiWwXNnq.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-BwjddgUO.js} +2 -2
  82. package/dist/assets/{wifi-CAt114K2.js → wifi-BEqdJPGy.js} +1 -1
  83. package/dist/assets/{zap-Css9POSH.js → zap-CPBaAYNY.js} +1 -1
  84. package/dist/index.html +5 -5
  85. package/dist/server/addon.js +163 -4
  86. package/package.json +1 -1
  87. package/dist/assets/AiProfiles-DWhKAjFC.js +0 -3
  88. package/dist/assets/circle-check-big-CQf8q4ZY.js +0 -1
  89. package/dist/assets/clock-BzxnvWco.js +0 -1
  90. package/dist/assets/dist-CqpztYBE.js +0 -345
  91. package/dist/assets/play-CO2t37JV.js +0 -1
  92. package/dist/assets/plus-el6mwbuy.js +0 -1
  93. package/dist/assets/power-DSbj5xjk.js +0 -1
  94. package/dist/assets/remoteEntry-CZM3JGNn.js +0 -1
  95. package/dist/assets/rotate-ccw-vrvftCHH.js +0 -1
  96. package/dist/assets/rotate-cw-DnaKkb_h.js +0 -1
  97. package/dist/assets/search-D_IrNll3.js +0 -1
  98. package/dist/assets/square-7qF8nU-R.js +0 -1
  99. package/dist/assets/src-ZxR8RVbo.js +0 -67
  100. package/dist/assets/src-_Z89-Z6-.js +0 -345
@@ -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(),
@@ -12490,6 +12543,36 @@ var ManagedModelCatalogEntrySchema = object({
12490
12543
  /** Vision models: companion projector file. */
12491
12544
  mmprojUrl: string().optional()
12492
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
+ })]);
12493
12576
  var LlmRuntimeNodeSchema = object({
12494
12577
  nodeId: string(),
12495
12578
  reachable: boolean(),
@@ -12526,6 +12609,15 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
12526
12609
  consumer: string().optional(),
12527
12610
  profileId: string().optional()
12528
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({
12529
12621
  nodeId: string(),
12530
12622
  model: ManagedModelRefSchema
12531
12623
  }), _void(), {
@@ -17658,6 +17750,17 @@ var maxSessionHoldMsField = {
17658
17750
  default: 12e4,
17659
17751
  step: 5e3
17660
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
+ };
17661
17764
  var motionFpsField = {
17662
17765
  min: 1,
17663
17766
  max: 30,
@@ -17834,6 +17937,27 @@ var RunnerCameraConfigSchema = object({
17834
17937
  * resolved `CameraDetectionConfig`.
17835
17938
  */
17836
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(),
17837
17961
  motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
17838
17962
  detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
17839
17963
  motionStreamId: string(),
@@ -17929,7 +18053,7 @@ var RunnerCameraConfigSchema = object({
17929
18053
  */
17930
18054
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
17931
18055
  });
17932
- 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;
17933
18057
  /**
17934
18058
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
17935
18059
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -20467,6 +20591,25 @@ var BatteryStatusSchema = object({
20467
20591
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
20468
20592
  lastUpdated: number(),
20469
20593
  /**
20594
+ * Ms epoch of the last time the device PROVED it was reachable — a
20595
+ * completed firmware round-trip, an observed wake, or an inbound push
20596
+ * (firmware event, email). `0`/absent = never since this slice was born.
20597
+ *
20598
+ * This is the ONLY input that separates "asleep" from "gone", and it is
20599
+ * fed exclusively by PASSIVE signals: nothing may write it by reaching
20600
+ * for the radio, because a poll that confirms reachability is the same
20601
+ * poll that drains the battery. See {@link deriveBatteryPresence} — the
20602
+ * single derivation every consumer must use; no surface computes its own.
20603
+ *
20604
+ * It is deliberately NOT a clock in the
20605
+ * `scripts/check-runtime-state-durability.ts` sense: it is the
20606
+ * observation itself, and it is the only thing a 30-hour silence is
20607
+ * visible in. Writers quantise it (see `CONTACT_WRITE_QUANTUM_MS` in the
20608
+ * Reolink provider) so a value that means "recently" cannot cost a
20609
+ * SQLite commit per round-trip.
20610
+ */
20611
+ lastContactAt: number().optional(),
20612
+ /**
20470
20613
  * True when the source is a BINARY low-battery indicator (HA
20471
20614
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
20472
20615
  * charge level — `percentage` is then a coarse stand-in (100 = normal,
@@ -24744,6 +24887,16 @@ var CamStreamDescriptorSchema = object({
24744
24887
  /** Transport-specific opaque metadata (e.g. rfc4571 SDP). */
24745
24888
  metadata: record(string(), unknown()).optional()
24746
24889
  });
24890
+ object({
24891
+ /** The descriptors as last built from a real camera response. Never a guess:
24892
+ * a failed or refused build writes NOTHING, so a restored catalog is always
24893
+ * one the camera itself once produced. */
24894
+ descriptors: array(CamStreamDescriptorSchema),
24895
+ /** Ms epoch of the build that produced {@link descriptors}. Lets the wake
24896
+ * path decide whether the camera's own awake window is worth spending on a
24897
+ * re-read. */
24898
+ lastFetchedAt: number()
24899
+ });
24747
24900
  DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(CamStreamDescriptorSchema).readonly());
24748
24901
  /** One of the camera's stream profiles. */
24749
24902
  var StreamProfileSchema = _enum([
@@ -27776,6 +27929,12 @@ Object.freeze({
27776
27929
  addonId: null,
27777
27930
  access: "view"
27778
27931
  },
27932
+ "llm.resolveModelRef": {
27933
+ capName: "llm",
27934
+ capScope: "system",
27935
+ addonId: null,
27936
+ access: "create"
27937
+ },
27779
27938
  "llm.setDefault": {
27780
27939
  capName: "llm",
27781
27940
  capScope: "system",
@@ -33331,7 +33490,7 @@ var AdminUIAddon = class extends BaseAddon {
33331
33490
  capability: adminUiCapability,
33332
33491
  provider: {
33333
33492
  getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
33334
- getVersion: async () => ({ version: "1.2.62" })
33493
+ getVersion: async () => ({ version: "1.2.64" })
33335
33494
  }
33336
33495
  }];
33337
33496
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-admin-ui",
3
- "version": "1.2.62",
3
+ "version": "1.2.64",
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-DTulF4Wm.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-ZxR8RVbo.js";import{K as A}from"./src-_Z89-Z6-.js";import{t as j}from"./boxes-D4otfCdM.js";import{n as M,t as N}from"./send-D-EllYB0.js";import{t as P}from"./pencil-CCTI-4rI.js";import{t as F}from"./plus-el6mwbuy.js";import{t as I}from"./server-BTc5FZH2.js";import{t as ee}from"./square-7qF8nU-R.js";import{C as L,D as R,H as z,K as B,N as V,V as H,i as te,l as ne,s as re,t as U,v as ie}from"./index-B8gfFLO3.js";import{t as W}from"./AdminPage-CCD07UMs.js";import{t as ae}from"./AdminTabs-T4tXzr6m.js";var G=B(`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 K=[`llm`,`listProfiles`],oe=[`llm`,`listProfileKinds`],se=[`llm`,`getDefaults`],ce=[`llm`,`listRuntimeNodes`],le=[`llm`,`listModelCatalog`],ue=[`llm`,`getUsage`],de=[{purpose:`vision`,visionOnly:!0},{purpose:`text`,visionOnly:!1}],fe=new Map(Object.entries({openai:G,anthropic:G,google:G,"openai-compatible":ie,"managed-local":V}));function pe(e){return fe.get(e)??null}function q(e){return typeof e==`string`&&e.length>0?e:void 0}function J(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 Y(e){return e===!0}function me(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 he=[`f32`,`f16`,`q8_0`,`q5_1`,`q5_0`,`q4_1`,`q4_0`];function ge(e){return typeof e==`string`&&he.includes(e)?e:void 0}function _e(e,t,n){let r=J(e.threads),i=J(e.batchSize),a=J(e.ubatchSize),o=ge(e.cacheTypeK),s=ge(e.cacheTypeV);return{nodeId:t,engine:`llama-cpp`,model:{kind:`catalog`,catalogId:n},contextSize:J(e.contextSize)??4096,gpuLayers:J(e.gpuLayers)??0,...r===void 0?{}:{threads:r},parallel:J(e.parallel)??1,...i===void 0?{}:{batchSize:i},...a===void 0?{}:{ubatchSize:a},flashAttention:Y(e.flashAttention),mlock:Y(e.mlock),noMmap:Y(e.noMmap),...o===void 0?{}:{cacheTypeK:o},...s===void 0?{}:{cacheTypeV:s},autoStart:Y(e.autoStart),idleStopMinutes:J(e.idleStopMinutes)??30}}function ve(e,t,n,r,i){let a={id:n?.id??crypto.randomUUID(),name:t,kind:e,addonId:`ai`,enabled:n?.enabled??!0,model:q(r.model)??n?.model??``,supportsVision:i?.supportsVision??Y(r.supportsVision),timeoutMs:J(r.timeoutMs)??A.totalMs,connectTimeoutMs:J(r.connectTimeoutMs)??A.connectMs,firstTokenTimeoutMs:J(r.firstTokenTimeoutMs)??A.firstTokenMs,idleTimeoutMs:J(r.idleTimeoutMs)??A.idleMs,retry:{enabled:Y(r.retryEnabled),maxAttempts:J(r.retryMaxAttempts)??1},toolsEnabled:Y(r.toolsEnabled)},o=q(r.baseUrl),s=q(r.apiKey),c=J(r.temperature),l=J(r.topP),u=J(r.topK),d=J(r.maxTokens),f=J(r.contextLength),p=q(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:_e(r,i.nodeId,i.catalogId)}}}function ye(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 X({kind:e,className:t}){let n=pe(e.kind);return n===null?l(`span`,{className:t,children:e.icon}):l(n,{className:t})}function be({kinds:e,onPick:t,onCancel:n}){let{t:a}=U(),o=i(()=>ye(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(X,{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 xe({kind:e,existing:t,onSave:n,onCancel:c,saving:u,error:f}){let{t:_}=U(),b=d(),w=e.kind===`managed-local`,[O,k]=s(t?.name??e.label),[A,j]=s(()=>t===null?{}:me(t)),[M,N]=s(t?.runtime?.nodeId??`hub`),[P,F]=s(t?.runtime?.model.kind===`catalog`?t.runtime.model.catalogId:``),I=a({queryKey:le,enabled:w,queryFn:()=>b.trpcClient.llm.listModelCatalog.query({})}),ee=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,_]),R=()=>{if(!w){n(ve(e.kind,O,t,A));return}let r=I.data?.find(e=>e.id===P);n(ve(e.kind,O,t,A,{nodeId:M,catalogId:P,supportsVision:r?.purpose===`vision`}))},z=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(X,{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:ee,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:z,onClick:R,children:[u&&l(L,{className:`h-3.5 w-3.5 animate-spin`}),t===null?_(`ai.createProfile`,`Create profile`):_(`ai.saveChanges`,`Save changes`)]})]})]})})}n();function Se(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(X,{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(L,{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(re,{className:`h-3.5 w-3.5 text-danger`})})]})}]}function Ce(){let{t:e}=U(),t=d(),[n,o]=s({mode:`closed`}),[u,f]=s(null),[p,m]=s(null),h=a({queryKey:K,queryFn:()=>t.trpcClient.llm.listProfiles.query({})}),g=a({queryKey:oe,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(W,{icon:ne,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(we,{profiles:C}),h.isLoading&&l(`div`,{className:`flex items-center justify-center py-16`,children:l(L,{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:Se({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(be,{kinds:T,onCancel:()=>o({mode:`closed`}),onPick:e=>o({mode:`form`,kind:e,profile:null})}),n.mode===`form`&&l(xe,{kind:n.kind,existing:n.profile,saving:y.isPending,error:p,onSave:e=>y.mutate(e),onCancel:()=>o({mode:`closed`})})]})}function we({profiles:e}){let{t}=U(),n=d(),i=a({queryKey:se,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:de.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 Te=5e3;function Ee(e){return`${(e/1e9).toFixed(1)} GB`}function De(){let{t:e}=U(),t=d(),n=a({queryKey:ce,refetchInterval:Te,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?`—`:Ee(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(W,{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 Oe=`/addon/ai/test-chat`,ke=6e5;function Ae(e){let t=e.firstTokenTimeoutMs;if(!(typeof t!=`number`||!Number.isFinite(t)||t<=0))return Math.min(Math.round(t),ke)}function je(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Me(e){if(!je(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 Ne=[`resolving-image`,`connecting`,`first-token-wait`,`streaming`];function Pe(e){let t;try{t=JSON.parse(e)}catch{return null}if(!je(t))return null;switch(t.kind){case`status`:{let e=Ne.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:Me(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*Fe(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=Pe(e);t!==null&&(yield t)}a=r.indexOf(`
3
- `)}}let e=r.trim();if(e.length>0){let t=Pe(e);t!==null&&(yield t)}}finally{t.releaseLock()}}function Ie(e){return e.filter(e=>e.failed!==!0&&e.content.length>0).map(e=>({role:e.role,content:e.content}))}function Le(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 Re(e,t){switch(e){case`resolving-image`:return`fetching the picture…`;case`connecting`:return`connecting to the provider…`;case`first-token-wait`:return t>0?`the model is loading / thinking — a cold load can take minutes (up to ${String(Math.round(t/1e3))}s)`:`the model is loading / thinking — a cold load can take minutes`;case`streaming`:return`answering…`}}n();var ze=[`deviceManager`,`listAll`,`cameras`],Be=[`pipelineAnalytics`,`listRecentTracks`,`ai-test`],Ve=360*60*1e3,He=40,Ue=160,Z={phase:`connecting`,firstTokenTimeoutMs:0,text:``,mediaSent:null};function We(){let{t:n}=U(),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:K,queryFn:()=>o.trpcClient.llm.listProfiles.query({})}),I=i(()=>(F.data??[]).filter(e=>e.enabled),[F.data]),L=I.find(e=>e.id===c)??null,R=A!==null,B=e(async(e,t)=>{let r=new AbortController;P.current=r,k(null);let i=Ae(L??{});j({...Z,firstTokenTimeoutMs:i??0});let a=``,o=null,s,l=null,u=null;try{let n=await fetch(Oe,{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},...i===void 0?{}:{firstTokenTimeoutMs:i}})});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 Fe(n.body))switch(e.kind){case`status`:j(t=>({...t??Z,phase:e.phase}));break;case`meta`:o=e.mediaSent,s=e.model,j(t=>({...t??Z,firstTokenTimeoutMs:e.firstTokenTimeoutMs,mediaSent:e.mediaSent}));break;case`token`:a+=e.text,j(e=>({...e??Z,text:a}));break;case`done`:u={outputTokens:e.outputTokens,latencyMs:e.latencyMs};break;case`error`:l=`${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 d=r.signal.aborted&&a.length>0;l===null&&a.length===0&&!d||b(e=>[...e,{id:`a-${String(Date.now())}`,role:`assistant`,content:l??a,...l===null?{}:{failed:!0},...o===null?{}:{attachment:o},...s!==void 0&&l===null?{model:s}:{},...u===null?{}:u}])},[L,c,p,n]),V=e(()=>{let e=h.trim();if(e===``||c===``||R)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`),B(Ie(n),r)},[h,C,c,B,R,y]),ne=()=>P.current?.abort();return r(W,{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(re,{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.`)}),L!==null&&!L.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(Ge,{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(H,{className:`h-3.5 w-3.5 text-foreground-subtle`}):l(z,{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(te,{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(H,{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(z,{className:`h-3.5 w-3.5`}),n(`ai.attachTrack`,`Recent track`)]})]}),E===`snapshot`&&l(Ke,{onPick:e=>{T({ref:{kind:`snapshot`,deviceId:e.id},label:n(`ai.snapshotOf`,`Snapshot of {{name}}`,{name:e.name})}),D(`none`)}}),E===`track`&&l(Je,{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(),V())},placeholder:n(`ai.messagePlaceholder`,`Ask the model something…`),"aria-label":n(`ai.message`,`Message`)}),R?r(S,{size:`sm`,variant:`secondary`,type:`button`,onClick:ne,children:[l(ee,{className:`h-3.5 w-3.5`}),n(`ai.stop`,`Stop`)]}):r(S,{size:`sm`,type:`button`,disabled:h.trim()===``||c===``,onClick:V,children:[l(N,{className:`h-3.5 w-3.5`}),n(`ai.send`,`Send`)]})]})]})]})}function Ge({turns:e,live:t}){let{t:n}=U();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(Q,{turn:e},e.id)),t!==null&&r(`div`,{className:`space-y-1`,children:[t.text.length>0&&l(Q,{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(L,{className:`h-3.5 w-3.5 animate-spin`}),Re(t.phase,t.firstTokenTimeoutMs)]})]})]})})}function Q({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(z,{className:`h-3 w-3`}):l(H,{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(H,{className:`h-3 w-3`}):l(z,{className:`h-3 w-3`}),Le(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 Ke({onPick:e}){let{t}=U(),n=d(),r=a({queryKey:ze,queryFn:()=>n.trpcClient.deviceManager.listAll.query({isCamera:!0,projection:`slim`})}),i=(r.data??[]).filter(e=>!e.disabled);return r.isLoading?l(L,{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(qe,{device:t,onPick:e},t.id))})}function qe({device:e,onPick:t}){let{src:n}=f(e.id,{width:Ue});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 Je({onPick:e}){let{t}=U(),n=d(),o=a({queryKey:ze,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:[...Be,s.length],enabled:s.length>0,queryFn:()=>n.trpcClient.pipelineAnalytics.listRecentTracks.query({deviceIds:[...s],since:Date.now()-Ve,limit:He,projection:`slim`})});if(o.isLoading||u.isLoading)return l(L,{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(z,{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 Ye=1440*60*1e3,Xe=[1,7,30,90];function Ze(){let{t:e}=U(),t=d(),[n,o]=s(7),c=i(()=>{let e=new Date;return e.setHours(0,0,0,0),e.getTime()-(n-1)*Ye},[n]),u=a({queryKey:K,queryFn:()=>t.trpcClient.llm.listProfiles.query({})}),f=a({queryKey:[...ue,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(W,{icon:R,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:Xe.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 $(){let{t:e}=U();return l(`div`,{className:`flex flex-col p-4`,children:l(ae,{tabs:[{id:`profiles`,label:e(`ai.tabProfiles`,`Profiles`),icon:j,content:l(Ce,{})},{id:`runtimes`,label:e(`ai.tabRuntimes`,`Runtimes`),icon:I,content:l(De,{})},{id:`usage`,label:e(`ai.tabUsage`,`Usage`),icon:R,content:l(Ze,{})},{id:`test`,label:e(`ai.tabTest`,`Test`),icon:M,content:l(We,{})}]})})}export{$ as AiProfilesPage,$ as default};
@@ -1 +0,0 @@
1
- import{K as e}from"./index-B8gfFLO3.js";var t=e(`circle-check-big`,[[`path`,{d:`M21.801 10A10 10 0 1 1 17 3.335`,key:`yps3ct`}],[`path`,{d:`m9 11 3 3L22 4`,key:`1pflzl`}]]);export{t};
@@ -1 +0,0 @@
1
- import{K as e}from"./index-B8gfFLO3.js";var t=e(`clock`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`polyline`,{points:`12 6 12 12 16 14`,key:`68esgv`}]]);export{t};