@xenon-device-management/xenon 2.12.0 → 2.13.1

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 (150) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +223 -488
  3. package/lib/package.json +2 -2
  4. package/lib/public/assets/{AnnotationOverlay-Crkn72_3.js → AnnotationOverlay-Cvpkk4r9.js} +1 -1
  5. package/lib/public/assets/{ApiKeyGate-BThVlj_j.js → ApiKeyGate-Bhn5ZtVM.js} +1 -1
  6. package/lib/public/assets/{BugReportButton-Dwa0GnBh.js → BugReportButton-BA6aEjTC.js} +1 -1
  7. package/lib/public/assets/{DeviceMosaicView-Ccc-pOls.js → DeviceMosaicView-ChhHnpFQ.js} +1 -1
  8. package/lib/public/assets/{EmptyState-BeuTrlN_.js → EmptyState-CgzbuUvO.js} +1 -1
  9. package/lib/public/assets/{FieldGroup-DBIaNxl1.js → FieldGroup-BHc_5qNc.js} +1 -1
  10. package/lib/public/assets/{FilterMenu-BgSAS6iT.js → FilterMenu-D7OMGuRc.js} +1 -1
  11. package/lib/public/assets/{Menu-GCbP6gYN.js → Menu-C173JJ0l.js} +1 -1
  12. package/lib/public/assets/{Modal-ByESeBRC.js → Modal-ChvVUI3B.js} +1 -1
  13. package/lib/public/assets/{RecordingPage-Bwyf3WMl.js → RecordingPage-Dhw4d_O6.js} +1 -1
  14. package/lib/public/assets/{RecordingsPage-7kM1rB3i.js → RecordingsPage-z97ftGK-.js} +2 -2
  15. package/lib/public/assets/{SegmentedControl-B8tTU8kD.js → SegmentedControl-DbXhBGZC.js} +1 -1
  16. package/lib/public/assets/{SettingCard-Clg_Ijbc.js → SettingCard-C908GgiI.js} +1 -1
  17. package/lib/public/assets/{Table-CIaR0QWV.js → Table-B1DB_ZN-.js} +1 -1
  18. package/lib/public/assets/{activity-Bengh9SD.js → activity-Css0xNvp.js} +1 -1
  19. package/lib/public/assets/ai-settings-BMQK3-yN.js +21 -0
  20. package/lib/public/assets/{api-keys-CrGlbfn-.js → api-keys-CbJ42eDQ.js} +1 -1
  21. package/lib/public/assets/{apps-DIbcGVlU.js → apps-DW1pw2re.js} +2 -2
  22. package/lib/public/assets/{arrow-left-BtJIXXMY.js → arrow-left-Dpog9L0F.js} +1 -1
  23. package/lib/public/assets/{arrow-right-zm-NagQh.js → arrow-right-CsBIlSIw.js} +1 -1
  24. package/lib/public/assets/{arrow-up-right-DdU8YEYp.js → arrow-up-right-z1tRQE4R.js} +1 -1
  25. package/lib/public/assets/{auth-shell-CHmBFmOz.js → auth-shell-mXiX5vNk.js} +2 -2
  26. package/lib/public/assets/{builds-page-JkzWKRpU.js → builds-page-BcFn23bE.js} +1 -1
  27. package/lib/public/assets/{button-CqhPBGRj.js → button-Cq_zHETv.js} +1 -1
  28. package/lib/public/assets/{calendar-C7eYYJTo.js → calendar-CKxABX9q.js} +1 -1
  29. package/lib/public/assets/{check-DpbIM4E0.js → check-C7xu-hGU.js} +1 -1
  30. package/lib/public/assets/{chevron-right-Dl4X1PRz.js → chevron-right-BiqfdCGA.js} +1 -1
  31. package/lib/public/assets/{circle-check-Du3sjbfV.js → circle-check-CyBjjPxR.js} +1 -1
  32. package/lib/public/assets/{circle-x-BSSqhpXi.js → circle-x-6gMRcx6z.js} +1 -1
  33. package/lib/public/assets/{clock-DMT60v1C.js → clock-U1rDTT1h.js} +1 -1
  34. package/lib/public/assets/{copy-BKNyOehd.js → copy-BIM0ZX0L.js} +1 -1
  35. package/lib/public/assets/device-explorer-DSpAA_t9.js +279 -0
  36. package/lib/public/assets/{download-BM6Xn22t.js → download-DDBRXnAZ.js} +1 -1
  37. package/lib/public/assets/{forgot-password-B8WqMqBT.js → forgot-password-Dpg7yxM8.js} +1 -1
  38. package/lib/public/assets/{index-ClrpAMAT.js → index-BGBqohfQ.js} +43 -43
  39. package/lib/public/assets/{index-DauQh6ie.js → index-BQ2luT7L.js} +1 -1
  40. package/lib/public/assets/{input-CyKdLnEx.js → input-BvB1cjHE.js} +1 -1
  41. package/lib/public/assets/{line-chart-EIBXwYGo.js → line-chart-m78VggnW.js} +1 -1
  42. package/lib/public/assets/{list-checks-fWsgD9bI.js → list-checks-cY7o1QTL.js} +1 -1
  43. package/lib/public/assets/{lock-CVCe56TH.js → lock-loz8txQm.js} +1 -1
  44. package/lib/public/assets/{login-BJ8a7yVD.js → login-BYOvz4lx.js} +1 -1
  45. package/lib/public/assets/maintenance-settings-BxbA7SkB.js +6 -0
  46. package/lib/public/assets/{monitor-Bw1YQZnL.js → monitor-C4HPHb6s.js} +1 -1
  47. package/lib/public/assets/{mouse-pointer-2-C01jbqkO.js → mouse-pointer-2-BkdKtCzi.js} +1 -1
  48. package/lib/public/assets/{network-CBGUjJDJ.js → network-C5e0MIWo.js} +1 -1
  49. package/lib/public/assets/{overview-Cxe8aQ7C.js → overview-tlu8ijGd.js} +1 -1
  50. package/lib/public/assets/{page-header-B92DKLiq.js → page-header-FXPlo6az.js} +1 -1
  51. package/lib/public/assets/{play-Ck0L-0_m.js → play-CWdk0_Sy.js} +1 -1
  52. package/lib/public/assets/{plus-Dq3tCy2N.js → plus-B2xCnntV.js} +1 -1
  53. package/lib/public/assets/{profile-page--dkDiKbr.js → profile-page-D080qzr4.js} +1 -1
  54. package/lib/public/assets/{recording-group-store-5BYIFFN9.js → recording-group-store-B75435xR.js} +1 -1
  55. package/lib/public/assets/{reset-password-KYWlfjia.js → reset-password-D3u4Mbvn.js} +1 -1
  56. package/lib/public/assets/{runbook-page-mnsxgS1d.js → runbook-page-hPRZMy7s.js} +1 -1
  57. package/lib/public/assets/{select-BBOIZTYm.js → select-12RThAXc.js} +1 -1
  58. package/lib/public/assets/{selector-detail-redirect-CcI2j_Su.js → selector-detail-redirect-uQwvV7EQ.js} +1 -1
  59. package/lib/public/assets/{selector-health-page-BdJb_x5L.js → selector-health-page-CThoxDFO.js} +2 -2
  60. package/lib/public/assets/{session-detail-page-BjLaJy8z.js → session-detail-page-Bc02YjMm.js} +1 -1
  61. package/lib/public/assets/settings-C1VIluuT.js +6 -0
  62. package/lib/public/assets/{stat-tile-BU9e4s36.js → stat-tile-B02WEte8.js} +1 -1
  63. package/lib/public/assets/{tablet-hgbEwrWq.js → tablet-Dj30VGZl.js} +1 -1
  64. package/lib/public/assets/{teams-uiiG4hZM.js → teams-uL4fjTDR.js} +1 -1
  65. package/lib/public/assets/{trash-2-NK_Iazmg.js → trash-2-B54e-TfY.js} +1 -1
  66. package/lib/public/assets/{upload-BUX8TNFi.js → upload-B3k8WCSP.js} +1 -1
  67. package/lib/public/assets/{use-builds-data-DZszmFyl.js → use-builds-data-BFn90NH3.js} +1 -1
  68. package/lib/public/assets/{use-password-reset-mode-BK4B9kRC.js → use-password-reset-mode-DL2TmWZ0.js} +1 -1
  69. package/lib/public/assets/{users-BU4XBbMT.js → users-CqBuxMlX.js} +1 -1
  70. package/lib/public/assets/{users-BDh1xjad.js → users-DjQSucKb.js} +1 -1
  71. package/lib/public/assets/{video-off-BOjNwT4R.js → video-off-T0rlt0GA.js} +1 -1
  72. package/lib/public/assets/webhook-settings-DAHgnJyZ.js +1 -0
  73. package/lib/public/assets/{zap-DTWwVMUg.js → zap-gBHqutZV.js} +1 -1
  74. package/lib/public/index.html +1 -1
  75. package/lib/src/app/apiErrors.js +118 -0
  76. package/lib/src/app/index.js +6 -1
  77. package/lib/src/app/openapi/control.yaml +3129 -0
  78. package/lib/src/app/openapi/grid.yaml +2295 -0
  79. package/lib/src/app/openapi/identity.yaml +2168 -0
  80. package/lib/src/app/openapi/platform.yaml +2885 -0
  81. package/lib/src/app/openapi/sessions.yaml +3784 -0
  82. package/lib/src/app/routers/bug-report.js +4 -1
  83. package/lib/src/app/routers/config.js +6 -107
  84. package/lib/src/app/routers/control.js +117 -59
  85. package/lib/src/app/routers/dashboard.js +13 -7
  86. package/lib/src/app/routers/grid.js +54 -14
  87. package/lib/src/app/routers/profile.js +27 -13
  88. package/lib/src/app/routers/recordings.js +11 -5
  89. package/lib/src/app/routers/reservation.js +63 -15
  90. package/lib/src/app/routers/users.js +4 -0
  91. package/lib/src/app/routers/webhook.js +17 -8
  92. package/lib/src/app/swagger.js +259 -177
  93. package/lib/src/data-service/device-service.js +4 -1
  94. package/lib/src/data-service/deviceFieldOwners.js +1 -0
  95. package/lib/src/device-managers/AndroidDeviceManager.js +5 -2
  96. package/lib/src/device-managers/ios/WDAClient.js +153 -41
  97. package/lib/src/generated/client/edge.js +4 -3
  98. package/lib/src/generated/client/index-browser.js +1 -0
  99. package/lib/src/generated/client/index.d.ts +38 -0
  100. package/lib/src/generated/client/index.js +4 -3
  101. package/lib/src/generated/client/package.json +1 -1
  102. package/lib/src/generated/client/schema.prisma +2 -0
  103. package/lib/src/generated/client/wasm.js +1 -0
  104. package/lib/src/middleware/csrfMiddleware.js +13 -5
  105. package/lib/src/middleware/rateLimitMiddleware.js +19 -5
  106. package/lib/src/middleware/roleGuard.js +20 -0
  107. package/lib/src/services/AIService.js +13 -3
  108. package/lib/src/services/NotificationService.js +28 -28
  109. package/lib/src/services/bug-report/BugReportService.js +8 -2
  110. package/lib/src/services/lease/LeaseService.js +71 -20
  111. package/lib/src/services/omni-vision/OmniVisionService.js +14 -5
  112. package/lib/src/services/recording/RecordingOrchestrator.js +16 -2
  113. package/lib/test/helpers/expressRoutes.js +41 -0
  114. package/lib/test/integration/team-visibility-control.spec.js +2 -2
  115. package/lib/test/unit/access-scopes.spec.js +227 -0
  116. package/lib/test/unit/api-error-handling.spec.js +179 -0
  117. package/lib/test/unit/bug-report/route.spec.js +29 -0
  118. package/lib/test/unit/bug-report/service.spec.js +30 -0
  119. package/lib/test/unit/control-honest-answers.spec.js +134 -0
  120. package/lib/test/unit/device-allocation-routes.spec.js +232 -0
  121. package/lib/test/unit/healing-state-endpoints.spec.js +8 -5
  122. package/lib/test/unit/install-repository-app-team.spec.js +4 -1
  123. package/lib/test/unit/lease/LeaseService.spec.js +5 -4
  124. package/lib/test/unit/lease/lease-device-match.spec.js +161 -0
  125. package/lib/test/unit/lease/lease-session-ownership.spec.js +10 -9
  126. package/lib/test/unit/omni-vision-failures.spec.js +81 -0
  127. package/lib/test/unit/openapi-coverage.spec.js +114 -0
  128. package/lib/test/unit/profile-router.test.js +42 -0
  129. package/lib/test/unit/rateLimitMiddleware.test.js +49 -0
  130. package/lib/test/unit/recording-orchestrator.spec.js +80 -0
  131. package/lib/test/unit/recordings-library-routes.spec.js +37 -0
  132. package/lib/test/unit/reservation-team-visibility.spec.js +4 -3
  133. package/lib/test/unit/reset-link.test.js +2 -1
  134. package/lib/test/unit/stream-ticket-identity.spec.js +1 -1
  135. package/lib/test/unit/users-router.test.js +14 -1
  136. package/lib/test/unit/wda-client-failures.spec.js +90 -0
  137. package/lib/test/unit/wda-client-session.spec.js +128 -0
  138. package/lib/test/unit/wda-clipboard-write.spec.js +135 -0
  139. package/lib/test/unit/webhook-delivery.spec.js +142 -0
  140. package/lib/tsconfig.tsbuildinfo +1 -1
  141. package/package.json +2 -2
  142. package/prisma/migrations/20261004120000_reservation_holder/migration.sql +2 -0
  143. package/prisma/schema.prisma +2 -0
  144. package/scripts/dev/readme-screenshots.js +231 -0
  145. package/lib/public/assets/ai-settings-G9SymiPH.js +0 -21
  146. package/lib/public/assets/device-explorer-CoPR8DW3.js +0 -279
  147. package/lib/public/assets/maintenance-settings-DcRmTLS6.js +0 -6
  148. package/lib/public/assets/settings-B6cpMhsx.js +0 -6
  149. package/lib/public/assets/webhook-settings-BMzyUcx5.js +0 -1
  150. package/lib/src/app/swagger-docs.js +0 -1701
@@ -1,6 +0,0 @@
1
- import{c as N,t as M,r as d,X as C,j as e,A as D,S as y,I as w,V as A}from"./index-ClrpAMAT.js";/* empty css */import{S as o,A as k}from"./SettingCard-Clg_Ijbc.js";import{P as B}from"./page-header-B92DKLiq.js";import{T as z}from"./trash-2-NK_Iazmg.js";import{C as E}from"./check-DpbIM4E0.js";import{C as R}from"./calendar-C7eYYJTo.js";import"./button-CqhPBGRj.js";import"./utils-CDN07tui.js";/**
2
- * @license lucide-react v0.555.0 - ISC
3
- *
4
- * This source code is licensed under the ISC license.
5
- * See the LICENSE file in the root directory of this source tree.
6
- */const I=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]],L=N("history",I),c={buildCleanupDays:30,buildCleanupMaxCount:100,buildCleanupSchedule:"0 0 * * *",deleteBuildAssets:!0},T=[{label:"Daily (midnight)",value:"0 0 * * *"},{label:"Weekly (Sunday)",value:"0 0 * * 0"},{label:"Every 12 hours",value:"0 */12 * * *"}],P=(n,s)=>n.buildCleanupDays===s.buildCleanupDays&&n.buildCleanupMaxCount===s.buildCleanupMaxCount&&n.buildCleanupSchedule===s.buildCleanupSchedule&&n.deleteBuildAssets===s.deleteBuildAssets,$=()=>{const{toast:n}=M(),[s,i]=d.useState(c),[l,u]=d.useState(c),[f,p]=d.useState(!0),[h,m]=d.useState(!1);d.useEffect(()=>{j()},[]);const j=async()=>{p(!0);try{const a=await C.getGlobalConfig(),t={buildCleanupDays:a.buildCleanupDays||30,buildCleanupMaxCount:a.buildCleanupMaxCount||100,buildCleanupSchedule:a.buildCleanupSchedule||"0 0 * * *",deleteBuildAssets:a.deleteBuildAssets!==void 0?a.deleteBuildAssets:!0};i(t),u(t)}catch(a){console.error("Failed to load maintenance settings",a),n("Failed to access maintenance parameters.","error")}finally{p(!1)}},g=async a=>{const t=a??s;m(!0);try{await C.updateGlobalConfig(t),u(t),i(t),n("Maintenance parameters synchronized across fleet.","success")}catch(b){console.error("Failed to save maintenance settings",b),n(A(b),"error")}finally{m(!1)}},v=async()=>{i(c),await g(c)},S=()=>{i(l)},x=!P(s,l),r={days:s.buildCleanupDays!==l.buildCleanupDays,max:s.buildCleanupMaxCount!==l.buildCleanupMaxCount,purge:s.deleteBuildAssets!==l.deleteBuildAssets,schedule:s.buildCleanupSchedule!==l.buildCleanupSchedule};return f?e.jsxs("div",{className:"settings-loading",children:[e.jsx(D,{className:"animate-spin",size:32}),e.jsx("span",{children:"Synchronizing Maintenance Parameters..."})]}):e.jsxs("div",{className:"settings-container",children:[e.jsx(B,{icon:y,title:"Maintenance",subtitle:"Manage the lifecycle of test artifacts, automated purging schedules, and storage optimization across the global registry."}),e.jsxs("div",{className:"settings-content",children:[e.jsxs("div",{className:"settings-grid settings-grid--two-equal",children:[e.jsx(o,{icon:e.jsx(L,{size:16}),title:e.jsxs("span",{className:"card-title-row",children:["Retention window",r.days&&e.jsx("span",{className:"modified-dot","aria-label":"Modified"})]}),description:"Number of days to preserve builds and sessions before automatic purging from the system.",hint:"Standard enterprise retention is typically 30-90 days.",children:e.jsxs("div",{className:"input-group",children:[e.jsx("input",{type:"number",value:s.buildCleanupDays,onChange:a=>i({...s,buildCleanupDays:parseInt(a.target.value,10)}),min:1}),e.jsx("span",{className:"code-font",children:"days"})]})}),e.jsx(o,{icon:e.jsx(z,{size:16}),title:e.jsxs("span",{className:"card-title-row",children:["Max build capacity",r.max&&e.jsx("span",{className:"modified-dot","aria-label":"Modified"})]}),description:"Cap the maximum number of historical builds stored in the primary database.",hint:"Protects against database bloat during high-frequency CI bursts.",children:e.jsxs("div",{className:"input-group",children:[e.jsx("input",{type:"number",value:s.buildCleanupMaxCount,onChange:a=>i({...s,buildCleanupMaxCount:parseInt(a.target.value,10)}),min:1}),e.jsx("span",{className:"code-font",children:"builds"})]})}),e.jsx(o,{icon:e.jsx(y,{size:16}),title:e.jsxs("span",{className:"card-title-row",children:["Asset purge strategy",r.purge&&e.jsx("span",{className:"modified-dot","aria-label":"Modified"})]}),description:"Automatically remove binary artifacts (videos, screenshots) when build records are purged.",hint:"Disabling this will leave orphaned files on disk—use with caution.",children:e.jsxs("div",{className:"toggle-group",children:[e.jsxs("label",{className:"switch",children:[e.jsx("input",{type:"checkbox",checked:s.deleteBuildAssets,onChange:a=>i({...s,deleteBuildAssets:a.target.checked})}),e.jsx("span",{className:"slider round"})]}),e.jsx("span",{className:"toggle-label",children:s.deleteBuildAssets?"Enabled":"Disabled"})]})}),e.jsxs(o,{icon:e.jsx(R,{size:16}),title:e.jsxs("span",{className:"card-title-row",children:["Cleanup orchestration",r.schedule&&e.jsx("span",{className:"modified-dot","aria-label":"Modified"})]}),description:"Standardized Cron syntax for scheduling the automated cleanup engine.",children:[e.jsx("div",{className:"setting-input-wrapper",children:e.jsx("input",{type:"text",placeholder:"e.g. 0 0 * * * (Midnight)",value:s.buildCleanupSchedule,onChange:a=>i({...s,buildCleanupSchedule:a.target.value})})}),e.jsx("div",{className:"cron-presets",children:e.jsx("div",{className:"presets-grid",children:T.map(a=>{const t=s.buildCleanupSchedule===a.value;return e.jsxs("button",{type:"button",className:`preset-chip ${t?"active":""}`,onClick:()=>i({...s,buildCleanupSchedule:a.value}),"aria-pressed":t,children:[t&&e.jsx(E,{size:11,className:"preset-chip__check"}),e.jsx("span",{children:a.label})]},a.label)})})})]})]}),e.jsxs("div",{className:"health-monitor-alert maintenance-notice",children:[e.jsx(w,{size:18}),e.jsxs("span",{children:[e.jsx("strong",{children:"Resource Notice:"})," Bulk purging operations are non-blocking and execute at low priority to ensure zero interference with active test execution."]})]})]}),(x||h)&&e.jsx(k,{onSave:()=>g(),onDiscard:S,onRestoreDefaults:v,isSaving:h,isDirty:x,saveLabel:"Save configuration"})]})};export{$ as MaintenanceSettings};
@@ -1,6 +0,0 @@
1
- import{c as q,t as B,r,p as P,X as g,j as e,A as V,P as X,C as G,e as O,Q as U,V as K}from"./index-ClrpAMAT.js";/* empty css */import{S as j,A as Q}from"./SettingCard-Clg_Ijbc.js";import{M as W}from"./Modal-ByESeBRC.js";import{B as A}from"./button-CqhPBGRj.js";import{P as J}from"./page-header-B92DKLiq.js";import{C as I}from"./circle-check-Du3sjbfV.js";import{C as Y}from"./clock-DMT60v1C.js";import{M as Z}from"./mouse-pointer-2-C01jbqkO.js";import{C as ee}from"./check-DpbIM4E0.js";import{A as ae}from"./activity-Bengh9SD.js";import{C as se}from"./calendar-C7eYYJTo.js";import{A as te}from"./arrow-up-right-DdU8YEYp.js";import"./index-DauQh6ie.js";import"./utils-CDN07tui.js";/**
2
- * @license lucide-react v0.555.0 - ISC
3
- *
4
- * This source code is licensed under the ISC license.
5
- * See the LICENSE file in the root directory of this source tree.
6
- */const ne=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",key:"1oefj6"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5",key:"wfsgrz"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]],ie=q("file-text",ne),m={healthCheckIntervalMs:3e4,healthCheckSchedule:"",enableSelfHealing:!0},S=5e3,le=[{label:"Battery saver (2 AM)",value:"0 2 * * *"},{label:"Standard (hourly)",value:"0 * * * *"},{label:"Operational coverage (30m)",value:"*/30 * * * *"},{label:"High performance (10m)",value:"*/10 * * * *"}],N=5;function re(t){const s=Date.parse(t);if(!Number.isFinite(s))return"";const n=Math.max(0,Date.now()-s),l=Math.floor(n/1e3);if(l<60)return`${l}s ago`;const h=Math.floor(l/60);if(h<60)return`${h}m ago`;const d=Math.floor(h/60);return d<24?`${d}h ago`:`${Math.floor(d/24)}d ago`}function E(t){var d,u;const s=(d=t.originalSelector)==null?void 0:d.trim(),n=(u=t.healedSelector)==null?void 0:u.trim(),l=t.tier&&t.commandName?`${t.commandName}: `:"";if(s&&n&&s!==n)return`${l}${s} → ${n}`;if(n)return`${l}Recovered locator ${n}`;if(s)return`${l}Recovered locator ${s}`;const h=typeof t.confidence=="number"?` (${Math.round(t.confidence*100)}%)`:"";return`${l||""}Self-healed${h}`}function ce(t){return t.tier?t.tier:t.commandName?t.commandName:t.deviceName?t.deviceName:"Self-heal"}const oe=(t,s)=>t.healthCheckIntervalMs===s.healthCheckIntervalMs&&t.healthCheckSchedule===s.healthCheckSchedule&&t.enableSelfHealing===s.enableSelfHealing,ke=()=>{const{toast:t}=B(),[s,n]=r.useState(m),[l,h]=r.useState(m),[d,u]=r.useState(!0),[y,b]=r.useState(!1),[H,f]=r.useState(!1),[C,k]=r.useState([]),[w,z]=r.useState(!1),{on:M}=P();r.useEffect(()=>{$()},[]),r.useEffect(()=>{let a=!1;return g.getRecentHealingEvents(N).then(i=>{if(a||!i)return;const o=Array.isArray(i.events)?i.events.slice(0,N):[];k(o)}).catch(()=>{}).finally(()=>{a||z(!0)}),()=>{a=!0}},[]),r.useEffect(()=>{const a=M("healing_event",i=>{const o=i;!o||!o.id||k(x=>x.some(F=>F.id===o.id)?x:[o,...x].slice(0,N))});return()=>{a&&a()}},[M]);const $=async()=>{u(!0);try{const a=await g.getGlobalConfig(),i={healthCheckIntervalMs:a.healthCheckIntervalMs||3e4,healthCheckSchedule:a.healthCheckSchedule||"",enableSelfHealing:a.enableSelfHealing!==void 0?a.enableSelfHealing:!0};n(i),h(i)}catch(a){console.error("Failed to load settings",a),t("Failed to access infrastructure parameters.","error")}finally{u(!1)}},v=!oe(s,l),c=Number.isNaN(s.healthCheckIntervalMs)||s.healthCheckIntervalMs<S?`Below minimum safe value of ${S}ms.`:null,R=v&&!c,p={interval:s.healthCheckIntervalMs!==l.healthCheckIntervalMs,schedule:s.healthCheckSchedule!==l.healthCheckSchedule,healing:s.enableSelfHealing!==l.enableSelfHealing},_=async a=>{const i=a??s;if(!a&&c){t(c,"error");return}b(!0);try{await g.updateGlobalConfig(i),h(i),n(i),t("Infrastructure parameters synchronized across fleet.","success")}catch(o){console.error("Failed to save settings",o),t(K(o),"error")}finally{b(!1)}},L=async()=>{n(m),await _(m);try{await g.resetMetrics()}catch(a){console.error("Failed to reset metrics",a)}},D=()=>{n(l)},T=s.healthCheckSchedule!=="";return d?e.jsxs("div",{className:"settings-loading",children:[e.jsx(V,{className:"animate-spin",size:32}),e.jsx("span",{children:"Synchronizing Global Infrastructure..."})]}):e.jsxs("div",{className:"settings-container",children:[e.jsx(J,{icon:X,title:"Settings",subtitle:"Manage core farm parameters, heartbeat frequency, and maintenance orchestrations across the global registry."}),e.jsx("div",{className:"settings-content",children:e.jsxs("div",{className:"settings-grid settings-grid--three",children:[e.jsxs(j,{icon:e.jsx(Y,{size:16}),title:e.jsxs("span",{className:"card-title-row",children:["Idle health frequency",p.interval&&e.jsx("span",{className:"modified-dot","aria-label":"Modified"})]}),description:"Frequency of passive health pings when the system is in idle state.",hint:"Minimum safe value: 5000ms. Note: This frequency is overridden when a schedule is active.",children:[e.jsx("div",{className:"card-spacer"}),e.jsxs("div",{className:`input-group ${c?"has-error":""}`,children:[e.jsx("input",{type:"number",value:s.healthCheckIntervalMs,onChange:a=>n({...s,healthCheckIntervalMs:parseInt(a.target.value,10)}),min:S,step:5e3,"aria-invalid":!!c,"aria-describedby":c?"interval-error":"interval-status"}),e.jsx("span",{className:"code-font",children:"MS"})]}),c?e.jsxs("div",{id:"interval-error",className:"input-status input-status--error",role:"alert",children:[e.jsx(G,{size:12}),e.jsx("span",{children:c})]}):e.jsxs("div",{id:"interval-status",className:"input-status input-status--ok",children:[e.jsx(I,{size:12}),e.jsx("span",{children:"Valid frequency"})]})]}),e.jsxs(j,{icon:e.jsx(se,{size:16}),title:e.jsxs("span",{className:"card-title-row",children:["Deep diagnostic schedule",p.schedule&&e.jsx("span",{className:"modified-dot","aria-label":"Modified"})]}),titleExtra:T?e.jsxs("span",{className:"active-pill",children:[e.jsx(ae,{size:11}),e.jsx("span",{children:"Active"})]}):null,description:"Execute intensive reliability bursts (WDA restarts, Cache purges) using standardized Cron syntax.",children:[e.jsx("div",{className:"setting-input-wrapper",children:e.jsx("input",{type:"text",placeholder:"e.g. 0 * * * * (hourly, at minute 0)",value:s.healthCheckSchedule,onChange:a=>n({...s,healthCheckSchedule:a.target.value})})}),e.jsxs("div",{className:"cron-presets",children:[e.jsxs("div",{className:"presets-label",children:[e.jsx(Z,{size:12}),e.jsx("span",{children:"Intent-based presets"})]}),e.jsx("div",{className:"presets-grid presets-grid--stacked",children:le.map(a=>{const i=s.healthCheckSchedule===a.value;return e.jsxs("button",{type:"button",className:`preset-chip ${i?"active":""}`,onClick:()=>n({...s,healthCheckSchedule:a.value}),"aria-pressed":i,children:[i&&e.jsx(ee,{size:11,className:"preset-chip__check"}),e.jsx("span",{children:a.label})]},a.label)})})]})]}),e.jsxs(j,{icon:e.jsx(U,{size:16}),title:e.jsxs("span",{className:"card-title-row",children:["AI self-healing",p.healing&&e.jsx("span",{className:"modified-dot","aria-label":"Modified"})]}),titleExtra:e.jsxs("label",{className:"switch switch--header",children:[e.jsx("input",{type:"checkbox",checked:s.enableSelfHealing,onChange:a=>n({...s,enableSelfHealing:a.target.checked}),"aria-label":"Toggle AI self-healing"}),e.jsx("span",{className:"slider round"})]}),children:[e.jsx("div",{className:`healing-state ${s.enableSelfHealing?"is-on":"is-off"}`,children:s.enableSelfHealing?"Enabled":"Disabled"}),e.jsx("p",{className:"setting-card-description",children:"Automatically intercept and recover from failing locators using Xenon's 6-tier strategy: etalon recovery, native retry, fuzzy XML, OCR, visual AI, and LLM — before failing a test."}),s.enableSelfHealing&&e.jsxs("div",{className:"healing-events",children:[e.jsxs("div",{className:"healing-events__header",children:[e.jsx(ie,{size:11}),e.jsx("span",{children:"Recent healing events"}),e.jsxs(O,{to:"/selector-health",className:"healing-events__link",children:["Selector health ",e.jsx(te,{size:10})]})]}),C.length>0?e.jsx("ul",{className:"healing-events__list",children:C.map(a=>e.jsxs("li",{className:"healing-event",children:[e.jsx(I,{size:14,className:"healing-event__icon"}),e.jsxs("div",{className:"healing-event__body",children:[e.jsxs("div",{className:"healing-event__row",children:[e.jsx("span",{className:"healing-event__kind",children:ce(a)}),e.jsx("span",{className:"healing-event__when",children:re(a.createdAt)})]}),e.jsx("div",{className:"healing-event__message",title:E(a),children:E(a)})]})]},a.id))}):e.jsx("div",{className:"healing-events__empty",children:w?"No healing events recorded yet — the dashboard will populate as locators are recovered.":"Loading recent events…"})]})]})]})}),(v||y)&&e.jsx(Q,{onSave:()=>_(),onDiscard:D,onRestoreDefaults:()=>f(!0),isSaving:y,isValidating:!!c,isDirty:v,saveLabel:R?"Save Configuration":"Resolve errors to save"}),e.jsxs(W,{open:H,onClose:()=>f(!1),title:"Restore default settings?",footer:e.jsxs(e.Fragment,{children:[e.jsx(A,{variant:"secondary",onClick:()=>f(!1),children:"Cancel"}),e.jsx(A,{variant:"danger",onClick:async()=>{f(!1),await L()},children:"Restore defaults"})]}),children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"This saves immediately and can't be undone:"}),e.jsxs("ul",{className:"mt-2 list-disc pl-5 text-sm text-[var(--text)] space-y-1",children:[e.jsxs("li",{children:["Idle health frequency goes back to ",m.healthCheckIntervalMs.toLocaleString()," ms"]}),e.jsx("li",{children:"The diagnostic schedule is cleared"}),e.jsx("li",{children:"AI self-healing is turned on"}),e.jsx("li",{children:"Every device's healed-selector count is reset to zero"})]})]})]})};export{ke as Settings};
@@ -1 +0,0 @@
1
- import{t as P,r,X as c,j as e,K as u,A as b,C as U,O}from"./index-ClrpAMAT.js";/* empty css */import{F as S}from"./FieldGroup-DBIaNxl1.js";import{P as D}from"./page-header-B92DKLiq.js";import{T as V}from"./trash-2-NK_Iazmg.js";import{P as x}from"./plus-Dq3tCy2N.js";import{C as B}from"./circle-x-BSSqhpXi.js";import{C as H}from"./circle-check-Du3sjbfV.js";import{Z as I}from"./zap-DTWwVMUg.js";import{A as X}from"./activity-Bengh9SD.js";const C=[{id:"device_offline",label:"Device offline",icon:U,tone:"red"},{id:"device_new",label:"New device",icon:x,tone:"green"},{id:"session_failed",label:"Session failed",icon:B,tone:"amber"},{id:"selector_health_digest",label:"Selector health digest",icon:O,tone:"green"}],J=["udid","host","name","sessionId","failureReason","eventType","platform"],ae=()=>{const{toast:o}=P(),[g,T]=r.useState([]),[A,w]=r.useState(!0),[l,j]=r.useState(""),[d,k]=r.useState(["device_offline","session_failed"]),[h,f]=r.useState(!1),[v,N]=r.useState(!1),[n,_]=r.useState(!1),[y,m]=r.useState("");r.useEffect(()=>{p()},[]);const p=async()=>{w(!0);try{const s=await c.getWebhookConfigs();T(s||[])}catch(s){console.error("Failed to load webhook configs",s),o("Failed to load webhooks.","error")}finally{w(!1)}},z=async()=>{if(!l.trim()){o("Webhook URL is required.","error");return}if(d.length===0){o("Select at least one trigger event.","error");return}f(!0);try{await c.addWebhookConfig(l.trim(),d,"slack",y||void 0),j(""),m(""),_(!1),k(["device_offline","session_failed"]),await p(),o("Webhook saved.","success")}catch(s){console.error("Failed to add webhook",s),o(`Failed to add webhook: ${s.message||"unknown error"}`,"error")}finally{f(!1)}},E=async(s,t)=>{if(window.confirm(`Remove webhook for "${t}"?`))try{await c.deleteWebhookConfig(s),await p(),o("Webhook removed.","success")}catch(a){console.error("Failed to delete webhook",a),o(`Failed to delete: ${a.message||"unknown error"}`,"error")}},L=async()=>{if(l.trim()){N(!0);try{await c.testWebhook(l.trim(),"slack"),o("Test payload delivered.","success")}catch(s){o(`Test failed: ${s.message||"check the URL"}`,"error")}finally{N(!1)}}},$=s=>{k(t=>t.includes(s)?t.filter(a=>a!==s):[...t,s])},R=s=>{m(t=>t+`{{${s}}} `)};return e.jsxs("div",{className:"settings-container",children:[e.jsx(D,{icon:u,title:"Notifications",subtitle:"Configure Slack or generic webhooks to receive alerts for critical infrastructure events."}),e.jsxs("div",{className:"settings-content",children:[A?e.jsxs("div",{className:"settings-loading",style:{height:200},children:[e.jsx(b,{className:"animate-spin",size:28}),e.jsx("span",{children:"Loading webhooks…"})]}):g.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-state__icon",children:e.jsx(u,{size:28})}),e.jsx("h3",{className:"empty-state__title",children:"No webhooks configured"}),e.jsx("p",{className:"empty-state__copy",children:"Wire up a Slack incoming webhook or any HTTPS endpoint to receive alerts when devices go offline, new devices register, or sessions fail. Configure your first webhook below."})]}):e.jsx("div",{className:"webhook-list-grid",children:g.map(s=>{const t=(()=>{try{return JSON.parse(s.events)}catch{return[]}})();return e.jsxs("div",{className:"webhook-row-card",children:[e.jsxs("div",{className:"webhook-row-card__header",children:[e.jsx("span",{className:`pill-chip ${s.payloadTemplate?"pill-chip--admin":"pill-chip--scope"}`,children:s.payloadTemplate?"CUSTOM":"SLACK"}),e.jsx("span",{className:"webhook-row-card__url",title:s.url,children:s.url}),e.jsxs("button",{type:"button",className:"row-action row-action--danger",onClick:()=>E(s.id,s.url),"aria-label":`Remove webhook for ${s.url}`,children:[e.jsx(V,{size:13}),e.jsx("span",{children:"Remove"})]})]}),e.jsx("div",{className:"webhook-row-card__events",children:t.length===0?e.jsx("span",{className:"webhook-row-card__no-events",children:"No triggers"}):t.map(a=>{const i=C.find(F=>F.id===a),W=(i==null?void 0:i.icon)??u;return e.jsxs("span",{className:`event-pill event-pill--${(i==null?void 0:i.tone)||"gray"}`,children:[e.jsx(W,{size:11}),(i==null?void 0:i.label)||a]},a)})})]},s.id)})}),e.jsxs("div",{className:"add-webhook-card",children:[e.jsxs("div",{className:"add-webhook-card__header",children:[e.jsx(x,{size:14}),e.jsx("span",{children:"Add a new webhook"})]}),e.jsxs("div",{className:"add-webhook-card__body",children:[e.jsx(S,{label:"Webhook URL",description:"The endpoint we POST webhook events to.",htmlFor:"webhook-url",children:e.jsx("div",{className:"setting-input-wrapper",children:e.jsx("input",{id:"webhook-url",type:"url",placeholder:"https://hooks.slack.com/services/...",value:l,onChange:s=>j(s.target.value)})})}),e.jsx(S,{label:"Trigger events",children:e.jsx("div",{className:"event-toggle-grid",children:C.map(s=>{const t=d.includes(s.id),a=s.icon;return e.jsxs("button",{type:"button",role:"checkbox","aria-checked":t,className:`event-toggle event-toggle--${s.tone} ${t?"is-selected":""}`,onClick:()=>$(s.id),children:[e.jsx(a,{size:14,className:"event-toggle__lead"}),e.jsx("span",{children:s.label}),t&&e.jsx(H,{size:14,className:"event-toggle__check"})]},s.id)})})}),e.jsxs("div",{className:"template-section",children:[e.jsxs("button",{type:"button",className:"template-section__toggle",onClick:()=>_(!n),"aria-expanded":n,children:[e.jsx(I,{size:14,className:n?"tone-amber":"tone-dim"}),e.jsx("span",{children:"Use custom payload (optional)"}),e.jsx("span",{className:"template-section__indicator",children:n?"−":"+"})]}),n&&e.jsxs("div",{className:"template-editor",children:[e.jsx("p",{className:"template-editor__hint",children:"Define a JSON or text template. Click a variable below to insert it."}),e.jsx("div",{className:"template-editor__chips",children:J.map(s=>e.jsx("button",{type:"button",className:"template-editor__chip",onClick:()=>R(s),children:`{{${s}}}`},s))}),e.jsx("textarea",{className:"template-editor__textarea",placeholder:'Example: { "text": "Alert: Device {{udid}} is offline!" }',value:y,onChange:s=>m(s.target.value),rows:4})]})]})]}),e.jsxs("div",{className:"add-webhook-card__footer",children:[e.jsxs("button",{type:"button",className:"page-header-action page-header-action--ghost",onClick:L,disabled:!l.trim()||v,children:[v?e.jsx(b,{size:14,className:"animate-spin"}):e.jsx(X,{size:14}),e.jsx("span",{children:"Test payload"})]}),e.jsxs("button",{type:"button",className:"page-header-action",onClick:z,disabled:h||!l.trim(),children:[h?e.jsx(b,{size:14,className:"animate-spin"}):e.jsx(x,{size:14}),e.jsx("span",{children:h?"Saving…":"Save webhook"})]})]})]})]})]})};export{ae as WebhookSettings};