@xenon-device-management/xenon 2.13.0 → 2.13.2
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.
- package/LICENSE +15 -0
- package/README.md +223 -488
- package/lib/package.json +1 -1
- package/lib/public/assets/{AnnotationOverlay-CzbFYtPI.js → AnnotationOverlay-BXruClDk.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-CA5KFHHJ.js → ApiKeyGate-qTS4LPLC.js} +1 -1
- package/lib/public/assets/{BugReportButton-D-DSGg8C.js → BugReportButton-BoGhlI7F.js} +1 -1
- package/lib/public/assets/{DeviceMosaicView-paDEFU4B.js → DeviceMosaicView-8J0oYqMM.js} +1 -1
- package/lib/public/assets/{EmptyState-BE_5f8Vw.js → EmptyState-Cr17PdDv.js} +1 -1
- package/lib/public/assets/{FieldGroup-DqMf7Rf3.js → FieldGroup-Bvn08svv.js} +1 -1
- package/lib/public/assets/{FilterMenu-BQY75Su3.js → FilterMenu-DDSO8aDf.js} +1 -1
- package/lib/public/assets/{Menu-BiW3os9u.js → Menu-B-Qn2X3T.js} +1 -1
- package/lib/public/assets/{Modal-DfawTxih.js → Modal-Cg9V5JYU.js} +1 -1
- package/lib/public/assets/{RecordingPage-BvemyRiy.js → RecordingPage-BTUs9VTW.js} +1 -1
- package/lib/public/assets/{RecordingsPage-CXfw7wpd.js → RecordingsPage-DYcfxITK.js} +2 -2
- package/lib/public/assets/{SegmentedControl-B7m9kpom.js → SegmentedControl-Zy7suhT5.js} +1 -1
- package/lib/public/assets/{SettingCard-Cw6lDD0l.js → SettingCard-CWYmycoe.js} +1 -1
- package/lib/public/assets/{Table-DlEuFgv-.js → Table-io9fwa98.js} +1 -1
- package/lib/public/assets/{activity-CnLvNwae.js → activity-Bdf_uIWl.js} +1 -1
- package/lib/public/assets/ai-settings-Bce5SJob.js +21 -0
- package/lib/public/assets/{api-keys-DphSVzBr.js → api-keys-CPqtVpm3.js} +1 -1
- package/lib/public/assets/{apps-vUfu15lX.js → apps-BgZan0H9.js} +2 -2
- package/lib/public/assets/{arrow-left-CLQWAiCY.js → arrow-left-B27ckB31.js} +1 -1
- package/lib/public/assets/{arrow-right-K6A5e_Ee.js → arrow-right-DOF8ITCz.js} +1 -1
- package/lib/public/assets/{arrow-up-right-DUA_lq6f.js → arrow-up-right-eFG5KTFS.js} +1 -1
- package/lib/public/assets/{auth-shell-BYRHcZd6.js → auth-shell-DlC3_27j.js} +2 -2
- package/lib/public/assets/{builds-page-C0fTvIG8.js → builds-page-BuXpPfKo.js} +1 -1
- package/lib/public/assets/{button-BxnHCCsX.js → button-fIW8owMI.js} +1 -1
- package/lib/public/assets/{calendar-CqVsn28A.js → calendar-WNjmrF-a.js} +1 -1
- package/lib/public/assets/{check-R6V3bot0.js → check-BdTh1Sq7.js} +1 -1
- package/lib/public/assets/{chevron-right-BA2Fntu-.js → chevron-right-B4BKlsqk.js} +1 -1
- package/lib/public/assets/{circle-check-D4EzyGzs.js → circle-check-zJU80_EF.js} +1 -1
- package/lib/public/assets/{circle-x-Cf6AF2B2.js → circle-x-5Uwquo0h.js} +1 -1
- package/lib/public/assets/{clock-EZfet0sS.js → clock-BtUprDsZ.js} +1 -1
- package/lib/public/assets/{copy-Bc6P8v9V.js → copy-DGSbZI3u.js} +1 -1
- package/lib/public/assets/device-explorer-C-KnhMHI.js +279 -0
- package/lib/public/assets/{download-Bd8OGLzi.js → download-WG6l7BgD.js} +1 -1
- package/lib/public/assets/{forgot-password-CoE7Ugtx.js → forgot-password-DnudqFFo.js} +1 -1
- package/lib/public/assets/{index-BTYMAqNW.css → index-Bxoxt9sV.css} +1 -1
- package/lib/public/assets/{index-CHOr4JCs.js → index-DI1fGIyj.js} +43 -43
- package/lib/public/assets/{index-BiL0Enl_.js → index-HDQVe1gi.js} +1 -1
- package/lib/public/assets/{input-CJ8z3IO5.js → input-BjDRKCea.js} +1 -1
- package/lib/public/assets/{line-chart-CPK0ObjV.js → line-chart-C0zdgd0v.js} +1 -1
- package/lib/public/assets/{list-checks-mXGHUVbB.js → list-checks-BdW5CjM8.js} +1 -1
- package/lib/public/assets/{lock-BSXM1xvq.js → lock-Dy3L4I8E.js} +1 -1
- package/lib/public/assets/{login-Ct2iW066.js → login-BHbY9CLG.js} +1 -1
- package/lib/public/assets/maintenance-settings-zDVlOMVK.js +6 -0
- package/lib/public/assets/{monitor-D8WQK8md.js → monitor-8xjkeHHi.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-Cw2tWzDX.js → mouse-pointer-2-CXdKMXQ8.js} +1 -1
- package/lib/public/assets/{network-Ba_uFxWo.js → network-Cj8JOKON.js} +1 -1
- package/lib/public/assets/{overview-CxrZ7_6k.js → overview-CTg7F0im.js} +1 -1
- package/lib/public/assets/{page-header-Cm1XL49f.js → page-header--aTyJXq6.js} +1 -1
- package/lib/public/assets/{play-C0CuKLXj.js → play-B-FodgHV.js} +1 -1
- package/lib/public/assets/{plus-CIIvR_6Z.js → plus-CsIk10Cq.js} +1 -1
- package/lib/public/assets/{profile-page-BZ_e-Bgw.js → profile-page-DkFKkMfl.js} +1 -1
- package/lib/public/assets/{recording-group-store-BWeeNs2_.js → recording-group-store-WVM3hoXn.js} +1 -1
- package/lib/public/assets/{reset-password-lfNz8Rm1.js → reset-password-I2-zqLlI.js} +1 -1
- package/lib/public/assets/{runbook-page-Cnec3VSx.js → runbook-page-DJTVCJX7.js} +1 -1
- package/lib/public/assets/{select-CZuCnold.js → select-DvCSv25z.js} +1 -1
- package/lib/public/assets/{selector-detail-redirect-BG3pxFLw.js → selector-detail-redirect-BLVfMJs_.js} +1 -1
- package/lib/public/assets/{selector-health-page-DsyL58tb.js → selector-health-page-oqadRokn.js} +2 -2
- package/lib/public/assets/session-detail-page-CGn55Pvl.js +4 -0
- package/lib/public/assets/settings-MsVU2ukE.js +6 -0
- package/lib/public/assets/{stat-tile-DFhv8JoI.js → stat-tile-DV9Ix-4O.js} +1 -1
- package/lib/public/assets/{tablet-68J5f6zh.js → tablet-6b_SEN2X.js} +1 -1
- package/lib/public/assets/{teams-eHSyEpbD.js → teams-BjZyfuHh.js} +1 -1
- package/lib/public/assets/{trash-2-CcSsv4fS.js → trash-2-CTZpPSdd.js} +1 -1
- package/lib/public/assets/{upload-BYf26K71.js → upload-P3XzwQUb.js} +1 -1
- package/lib/public/assets/{use-builds-data-CHW-MhaP.js → use-builds-data-1cOCMf-z.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-DozShXkk.js → use-password-reset-mode-DgF9w088.js} +1 -1
- package/lib/public/assets/{users-CcSQRoK9.js → users-CiNTKUiz.js} +1 -1
- package/lib/public/assets/{users-BpYnpOl2.js → users-DMF9Mk5H.js} +1 -1
- package/lib/public/assets/{video-off-dFKgps73.js → video-off-CHp1KU_T.js} +1 -1
- package/lib/public/assets/webhook-settings-DiS8geIm.js +1 -0
- package/lib/public/assets/{zap-COC2tZaH.js → zap-DRz0tDq5.js} +1 -1
- package/lib/public/index.html +2 -2
- package/lib/schema.json +0 -25
- package/lib/src/app/routers/control.js +19 -4
- package/lib/src/app/routers/streamStartConflict.js +3 -0
- package/lib/src/data-service/device-service.js +40 -0
- package/lib/src/data-service/device-store.js +4 -1
- package/lib/src/data-service/prisma-store.js +2 -1
- package/lib/src/device-managers/ios/WDAClient.js +128 -16
- package/lib/src/device-utils.js +63 -13
- package/lib/src/middleware/deviceAccessGuard.js +63 -9
- package/lib/src/services/ServerManager.js +3 -0
- package/lib/src/services/SessionLifecycleService.js +16 -4
- package/lib/src/services/device-access/SessionOwnerResolver.js +26 -0
- package/lib/src/services/device-access/deviceAccessPolicy.js +38 -6
- package/lib/src/services/device-access/leaseHold.js +26 -0
- package/lib/src/services/device-access/ticketActorAccess.js +6 -0
- package/lib/src/services/lease/activeLeases.js +37 -0
- package/lib/src/services/recording/busy-precheck.js +24 -3
- package/lib/test/unit/device-access-lease.spec.js +270 -0
- package/lib/test/unit/lease/allocate-lease-branch.spec.js +22 -0
- package/lib/test/unit/lease/session-lease-proof.spec.js +59 -0
- package/lib/test/unit/lease-held-phone.spec.js +196 -0
- package/lib/test/unit/partial-config-file.spec.js +100 -0
- package/lib/test/unit/schema-required-args.spec.js +21 -16
- package/lib/test/unit/wda-client-session.spec.js +128 -0
- package/lib/test/unit/wda-clipboard-write.spec.js +135 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/schema.json +0 -25
- package/scripts/dev/readme-screenshots.js +231 -0
- package/scripts/generate-types-from-schema.js +6 -0
- package/scripts/lib/always-present-plugin-args.js +39 -0
- package/lib/public/assets/ai-settings-D7Te8o-a.js +0 -21
- package/lib/public/assets/device-explorer-BFXpRq6d.js +0 -279
- package/lib/public/assets/maintenance-settings-DaOs00P6.js +0 -6
- package/lib/public/assets/session-detail-page-ZZyGM413.js +0 -4
- package/lib/public/assets/settings-DGZ7B_AR.js +0 -6
- package/lib/public/assets/webhook-settings-DD1XjiOD.js +0 -1
|
@@ -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-CHOr4JCs.js";/* empty css */import{S as o,A as k}from"./SettingCard-Cw6lDD0l.js";import{P as B}from"./page-header-Cm1XL49f.js";import{T as z}from"./trash-2-CcSsv4fS.js";import{C as E}from"./check-R6V3bot0.js";import{C as R}from"./calendar-CqVsn28A.js";import"./button-BxnHCCsX.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,4 +0,0 @@
|
|
|
1
|
-
import{r as x,X as k,j as t,e as Q,f as Se,t as P,y as _e,R as Ce,p as Re,i as Te,u as $e}from"./index-CHOr4JCs.js";import{C as Ae,d as Me,p as ve,e as De,r as be,f as Ee,j as ge,k as V,c as Oe,h as Z,u as Le,b as Pe,q as Fe}from"./use-builds-data-CHW-MhaP.js";import{A as Be}from"./arrow-left-CLQWAiCY.js";import{C as I}from"./chevron-right-BA2Fntu-.js";import{s as He}from"./labels-C21SA4JG.js";import{B as Ue,S as je,P as _}from"./BugReportButton-D-DSGg8C.js";import{C as Ie}from"./circle-x-Cf6AF2B2.js";import{C as Je}from"./circle-check-D4EzyGzs.js";import{S as E}from"./stat-tile-DFhv8JoI.js";import{C as z}from"./copy-Bc6P8v9V.js";import{A as Ve}from"./arrow-up-right-DUA_lq6f.js";import{A as ze}from"./arrow-right-K6A5e_Ee.js";import{f as Ke,a as ee,C as Ne,b as We,s as Xe,L as Ye,n as te}from"./line-chart-CPK0ObjV.js";import{E as M}from"./EmptyState-BE_5f8Vw.js";import{A as qe}from"./activity-CnLvNwae.js";import{V as Ge}from"./video-off-dFKgps73.js";import{D as ye}from"./download-Bd8OGLzi.js";import{T as Qe,a as Ze,b as re,c as T,d as et,e as $}from"./Table-DlEuFgv-.js";import{M as tt}from"./Modal-DfawTxih.js";import{N as rt}from"./network-Ba_uFxWo.js";import"./time-D-gjmE1o.js";import"./index-BiL0Enl_.js";const st=4e3,se=e=>!!e&&typeof e=="object"&&typeof e.id=="string"&&typeof e.status=="string";function at(e){const[r,s]=x.useState(null),[a,n]=x.useState([]),[l,c]=x.useState([]),[i,d]=x.useState([]),[u,N]=x.useState([]),[p,v]=x.useState(!0),[g,j]=x.useState(null),[y,w]=x.useState(!1),[A,C]=x.useState(0),b=x.useRef(null);x.useEffect(()=>{if(!e){s(null),n([]),c([]),d([]),N([]),w(!1),v(!1);return}let m=!0;return b.current!==e&&v(!0),Promise.all([k.getSession(e),k.getSessionLogs(e),k.getDeviceLogs(e),k.getDebugLogs(e),k.getProfilingData(e).catch(()=>[])]).then(([f,h,R,D,q])=>{if(!m)return;const G=se(f);s(G?f:null),G?(b.current=e,w(!1),j(null)):(w(!!f&&typeof f=="object"&&(f.error===!0||typeof f.message=="string")),j(typeof(f==null?void 0:f.message)=="string"?f.message:"Session not found")),n(Array.isArray(h)?h:[]),c(Array.isArray(R)?R:[]),d(Array.isArray(D)?D:[]),N(Array.isArray(q)?q:[])}).catch(f=>{m&&j(f instanceof Error?f.message:String(f))}).finally(()=>{m&&v(!1)}),()=>{m=!1}},[e,A]);const o=(r==null?void 0:r.status)==="running";return x.useEffect(()=>{if(!e||!o)return;let m=!0;const f=setInterval(async()=>{try{const[h,R]=await Promise.all([k.getSession(e),k.getSessionLogs(e)]);if(!m||!se(h))return;s(h),Array.isArray(R)&&n(R),h.status!=="running"&&C(D=>D+1)}catch{}},st);return()=>{m=!1,clearInterval(f)}},[e,o]),{session:r,sessionLogs:a,deviceLogs:l,debugLogs:i,profiling:u,loading:p,error:g,notFound:y,refresh:()=>C(m=>m+1)}}const ae=({buildId:e,buildName:r,sessionId:s})=>t.jsxs("nav",{"aria-label":"Breadcrumb",className:"flex items-center gap-2 px-4 py-2 border-b border-[var(--border)] bg-[var(--surface)]",children:[t.jsxs(Q,{to:"/builds",className:"inline-flex items-center gap-1.5 text-xs text-[var(--text-muted)] hover:text-[var(--text)] transition-colors",children:[t.jsx(Be,{className:"h-3.5 w-3.5"}),"Sessions"]}),t.jsx(I,{className:"h-3 w-3 text-[var(--text-dim)]"}),r&&t.jsxs(t.Fragment,{children:[t.jsx(Q,{to:`/builds/${e}`,className:"text-[11px] text-[var(--color-accent)] hover:underline",children:r}),t.jsx(I,{className:"h-3 w-3 text-[var(--text-dim)]"})]}),t.jsxs("span",{className:"font-mono text-[11px] text-[var(--text-muted)]",title:s,children:["#",s.slice(0,8)]})]}),nt={passed:{label:"Passed",Icon:Je,cls:"text-[var(--color-success)]"},failed:{label:"Failed",Icon:Ie,cls:"text-[var(--color-danger)]"},running:{label:"Running",Icon:Se,cls:"text-[var(--color-warning)]"},other:{Icon:Ae,cls:"text-[var(--text-dim)]"}};function K(e){const r=Oe(e),s=nt[r];return{bucket:r,...s,label:s.label??He(e||"unknown")}}const it=({session:e})=>{var l,c;const r=K(e.status),s=Me(e),a=[ve(e),e.device_version].filter(i=>i&&i!=="—").join(" "),n=[[De(e),a].filter(Boolean).join(" · "),be(e.ranOn),(l=e.owner)==null?void 0:l.name,Ee(e.startTime),ge(V(e))].filter(i=>i&&i!=="—");return t.jsxs("header",{className:"flex items-start justify-between gap-4 px-4 py-4 border-b border-[var(--border)] bg-[var(--surface)]",children:[t.jsxs("div",{className:"min-w-0",children:[t.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[t.jsxs("span",{className:`inline-flex items-center gap-1.5 text-xs font-medium shrink-0 ${r.cls}`,children:[t.jsx(r.Icon,{className:`h-3.5 w-3.5 ${r.bucket==="running"?"animate-spin":""}`,"aria-hidden":"true"}),r.label]}),t.jsx("h1",{className:`text-lg font-semibold truncate ${s.source==="name"?"text-[var(--text)]":"text-[var(--text-muted)]"}`,title:s.source==="name"?s.text:`${s.text}
|
|
2
|
-
Unnamed session. Set xe:options.name in its capabilities to name it.`,children:s.text})]}),t.jsx("p",{"data-testid":"outcome-meta",className:"mt-1 text-xs text-[var(--text-muted)] tabular-nums truncate",title:((c=e.owner)==null?void 0:c.email)??void 0,children:n.join(" · ")})]}),t.jsx("div",{className:"shrink-0",children:t.jsx(Ue,{sessionId:e.id,mode:"full",variant:"inline"})})]})};function ot(e,r=8){if(!e)return[];const s=e.split(/\r?\n/),a=[];for(const n of s){const l=n.trim();if(/^at\s+/.test(l)&&(a.push(l),a.length>=r))break}return a}function W(e){return e?e.split(/[_\s]+/).filter(Boolean).map(r=>r.charAt(0).toUpperCase()+r.slice(1).toLowerCase()).join(" "):""}function ne(e){if(!e)return{};try{const r=JSON.parse(e);return r&&typeof r=="object"&&!Array.isArray(r)?r:{}}catch{return{}}}function ie(e,r=200){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean")return String(e);try{const s=JSON.stringify(e);return s.length>r?s.slice(0,r)+"…":s}catch{return String(e)}}const F=e=>e.command_name||e.title||"command",J=e=>{const r=e.createdAt?Date.parse(e.createdAt):NaN;return Number.isFinite(r)?r:0},X=e=>[...e].sort((r,s)=>J(r)-J(s));function B(e){return e.is_success===!1||e.is_error===!0}function we(e){const r=[];let s=null;for(const a of X(e))a.is_healed&&a.healing_tier&&!r.includes(a.healing_tier)&&r.push(a.healing_tier),typeof a.duration=="number"&&(!s||a.duration>s.ms)&&(s={name:F(a),ms:a.duration});return{total:e.length,failed:e.filter(B).length,healed:e.filter(a=>a.is_healed).length,healedTiers:r,slowest:s,screenshots:e.filter(a=>a.screenshot).length}}const oe=(e,r)=>r?e?`${e}=${r}`:r:null;function lt(e){return X(e.filter(r=>r.is_healed)).map(r=>({id:r.id,command:F(r),original:oe(r.original_strategy,r.original_selector),healed:oe(r.healed_strategy,r.healed_selector),tier:r.healing_tier??null,confidence:typeof r.healing_confidence=="number"?Math.round(r.healing_confidence*100):null,at:r.createdAt}))}function ct(e){var r;if(!e.response)return null;try{const s=(r=JSON.parse(e.response))==null?void 0:r.value;if(!s||typeof s!="object")return null;const a=typeof s.message=="string"?s.message.trim():"";return a||(typeof s.error=="string"&&s.error?s.error:null)}catch{return null}}function dt(e){if(e.length===0)return{spanMs:0,bars:[]};const r=e.map(n=>{const l=J(n),c=typeof n.duration=="number"&&n.duration>0?n.duration:0;return{l:n,start:l-c,end:l,durationMs:c}}),s=Math.min(...r.map(n=>n.start)),a=Math.max(...r.map(n=>n.end));return{spanMs:Math.max(1,a-s),bars:r.sort((n,l)=>n.start-l.start).map(({l:n,start:l,durationMs:c})=>({id:n.id,label:F(n),startMs:l-s,durationMs:c,failed:B(n),healed:!!n.is_healed}))}}function xt(e){return X(e.filter(r=>r.screenshot)).map(r=>({id:r.id,path:r.screenshot,command:F(r),at:r.createdAt,failed:B(r)}))}function L(e){if(e==null||!Number.isFinite(e)||e<0)return"—";if(e<1e3)return`${Math.round(e)}ms`;if(e<6e4)return`${(e/1e3).toFixed(1)}s`;const r=Math.round(e/1e3);return`${Math.floor(r/60)}m ${r%60}s`}const mt=({session:e,commands:r})=>{var c,i;const s=K(e.status),a=we(r),n=ge(V(e)),l=s.bucket==="failed"?W(e.failure_category)||"No category recorded":s.bucket==="running"?`Running for ${n}`:`Ran ${n}`;return t.jsxs("div",{className:"grid grid-cols-4 gap-3",children:[t.jsx(E,{label:"Result",value:s.label,valueCls:s.cls,note:l,noteTone:s.bucket==="failed"?"bad":"neutral"}),t.jsx(E,{label:"Commands",value:a.total,note:a.failed>0?`${a.failed} failed`:"None failed",noteTone:a.failed>0?"bad":"neutral"}),t.jsx(E,{label:"Self-healing",value:a.healed>0?`${a.healed} healed`:"None",note:a.healed>0?a.healedTiers.join(", ")||"Tier not recorded":"No selector needed healing"}),t.jsx(E,{label:"Slowest command",value:L((c=a.slowest)==null?void 0:c.ms),note:((i=a.slowest)==null?void 0:i.name)??"No timings recorded"})]})};function ut(e){const r=e.filter(B),s=r[r.length-1];return s?{command:s.command_name||s.title||"command",message:ct(s)}:null}function ht(e,r,s,a,n){var u;const l=W(e.failure_category),c=((u=e.device_name)==null?void 0:u.trim())||"Unknown Device",i=e.device_version?` ${e.device_version}`:"",d=n?`${n.command}${n.message?`: ${n.message}`:""}`:"(no failed command recorded)";return[`**Session:** ${e.id}`,`**Build:** ${r} (#${s})`,`**Device:** ${c} (${e.node_id||"unknown-node"}) · ${e.device_platform||"unknown"}${i}`,`**Duration:** ${a}`,`**Failure category:** ${l||"(uncategorized)"}`,`**Failure reason:** ${e.failure_reason||"(not set)"}`,`**First failed command:** ${d}`,...e.ai_analysis?[`**AI analysis:** ${e.ai_analysis}`]:[]].join(`
|
|
3
|
-
`)}function ft({text:e}){return t.jsx(t.Fragment,{children:e.split(/(\*\*[^*]+\*\*|`[^`]+`)/g).map((r,s)=>/^\*\*[^*]+\*\*$/.test(r)?t.jsx("strong",{children:r.slice(2,-2)},s):/^`[^`]+`$/.test(r)?t.jsx("code",{className:"font-mono text-[11px] px-1 rounded bg-[var(--surface-2)]",children:r.slice(1,-1)},s):t.jsx(Ce.Fragment,{children:r},s))})}const O=({children:e})=>t.jsx("div",{className:"text-[11px] font-semibold text-[var(--text-dim)] mb-1",children:e}),pt=({session:e,buildName:r,buildId:s,commands:a,durationText:n})=>{var y;const{toast:l}=P(),[c,i]=x.useState(!1),d=W(e.failure_category),u=ut(a),N=ot(e.failure_reason),p=(y=e.ai_analysis)==null?void 0:y.trim(),v=!!p&&p.length>480,g=async()=>{const w=ht(e,r,s,n,u);try{await navigator.clipboard.writeText(w),l("Failure report copied","success")}catch{l("Clipboard unavailable","error")}},j=e.failure_category?`/xenon/runbooks/${encodeURIComponent(e.failure_category.toLowerCase())}`:void 0;return t.jsxs("section",{className:"rounded-lg border border-[var(--red)]/30 bg-[var(--surface)] overflow-hidden",children:[t.jsxs("header",{className:"flex items-center justify-between gap-4 px-4 py-3 border-b border-[var(--border)] bg-[var(--red)]/5",children:[t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx(_e,{className:"h-4 w-4 text-[var(--red)]","aria-hidden":"true"}),t.jsx("h2",{className:"text-sm font-semibold text-[var(--red)]",children:"Why it failed"}),d&&t.jsx("span",{className:"text-[11px] text-[var(--text-dim)] ml-2",children:d})]}),t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsxs("button",{type:"button",onClick:g,className:"inline-flex items-center gap-1.5 h-7 px-2.5 rounded-md border border-[var(--border)] text-xs text-[var(--text)] hover:border-[var(--border-strong)] hover:bg-[var(--surface-2)] transition-colors",children:[t.jsx(z,{className:"h-3 w-3"}),"Copy"]}),t.jsxs("a",{...j?{href:j,target:"_blank",rel:"noreferrer"}:{"aria-disabled":!0,onClick:w=>w.preventDefault()},className:`inline-flex items-center gap-1.5 h-7 px-2.5 rounded-md border border-[var(--border)] text-xs text-[var(--text)] hover:border-[var(--border-strong)] hover:bg-[var(--surface-2)] transition-colors ${j?"":"opacity-50 cursor-not-allowed"}`,title:j?"Open runbook in new tab":"No runbook — failure category not set",children:[t.jsx(Ve,{className:"h-3 w-3"}),"Open runbook"]})]})]}),t.jsxs("div",{className:"px-4 py-4 space-y-4",children:[t.jsxs("div",{children:[t.jsx(O,{children:"Reason"}),t.jsx("div",{className:"text-sm text-[var(--text)] break-words",children:e.failure_reason||t.jsx("span",{className:"text-[var(--text-dim)]",children:"(not set)"})})]}),t.jsxs("div",{children:[t.jsx(O,{children:"First failed command"}),u?t.jsxs("div",{className:"flex items-baseline gap-2 min-w-0 text-sm",children:[t.jsx("span",{className:"font-mono text-xs text-[var(--color-danger)] shrink-0",children:u.command}),u.message&&t.jsx("span",{className:"text-[var(--text-muted)] break-words min-w-0",children:u.message})]}):t.jsx("span",{className:"text-sm text-[var(--text-dim)]",children:"No failed command recorded."})]}),p&&t.jsxs("div",{"data-testid":"ai-analysis",children:[t.jsx(O,{children:t.jsxs("span",{className:"inline-flex items-center gap-1.5",children:[t.jsx(je,{className:"h-3 w-3","aria-hidden":"true"}),"AI analysis"]})}),t.jsx("div",{className:`text-sm text-[var(--text)] whitespace-pre-wrap break-words ${v&&!c?"line-clamp-6":""}`,children:t.jsx(ft,{text:p})}),v&&t.jsx("button",{type:"button",onClick:()=>i(w=>!w),"aria-expanded":c,className:"mt-1 text-xs text-[var(--color-accent)] hover:underline",children:c?"Show less":"Show all"})]}),N.length>0&&t.jsxs("div",{children:[t.jsx(O,{children:"Stack trace"}),t.jsx("pre",{className:"rounded-md border border-[var(--border)] bg-[var(--bg)] p-3 font-mono text-[11px] leading-relaxed text-[var(--color-accent)] overflow-x-auto",children:N.join(`
|
|
4
|
-
`)})]})]})]})};function vt(e){return e>=1e3?`${(e/1e3).toFixed(1).replace(/\.0$/,"")}k`:String(e)}function H(e){const r=e.timestamp;if(!r){const c=e.createdAt;return c?H({timestamp:c}):"--:--:--"}const s=typeof r=="number"?new Date(r):new Date(String(r));if(Number.isNaN(s.getTime()))return"--:--:--";const a=String(s.getHours()).padStart(2,"0"),n=String(s.getMinutes()).padStart(2,"0"),l=String(s.getSeconds()).padStart(2,"0");return`${a}:${n}:${l}`}function bt(e){if(e.is_healed===!0)return{label:String(e.command_name||"healed"),tone:"amber"};if(e.is_success===!1||e.is_error===!0)return{label:String(e.command_name||e.title||"error"),tone:"red"};if(e.is_success===!0)return{label:String(e.command_name||e.title||"ok"),tone:"green"};const r=e.title||e.event||e.command_name;return typeof r=="string"?/stop|fail|error/i.test(r)?{label:r,tone:"red"}:/start|begin|init/i.test(r)?{label:r,tone:"green"}:{label:r,tone:"blue"}:{label:"log",tone:"neutral"}}function gt(e){if(e==null)return"";if(typeof e=="string")try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}try{return JSON.stringify(e,null,2)}catch{return String(e)}}function jt(e){if(!e||typeof e!="string")return[{text:String(e??""),kind:"plain"}];const r=[],s=/("(?:\\.|[^"\\])*")(\s*:)|("(?:\\.|[^"\\])*")|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|\b(true|false)\b|\b(null)\b|([{}[\],])/g;let a=0,n;for(;(n=s.exec(e))!==null;)n.index>a&&r.push({text:e.slice(a,n.index),kind:"plain"}),n[1]&&n[2]!==void 0?(r.push({text:n[1],kind:"key"}),r.push({text:n[2],kind:"punct"})):n[3]?r.push({text:n[3],kind:"string"}):n[4]?r.push({text:n[4],kind:"number"}):n[5]?r.push({text:n[5],kind:"boolean"}):n[6]?r.push({text:n[6],kind:"null"}):n[7]&&r.push({text:n[7],kind:"punct"}),a=s.lastIndex;return a<e.length&&r.push({text:e.slice(a),kind:"plain"}),r}function Nt(e){const r=e,s=typeof r.title=="string"&&r.title.trim()?r.title.trim():null,a=typeof r.command_name=="string"&&r.command_name.trim()?r.command_name.trim():null;return s??a??"Command"}function le(e){const r=e.subtitle;return typeof r=="string"&&r.trim()?r.trim():null}function yt(e,r){return r?e.filter(s=>s.is_success===!1||s.is_error===!0):e}const wt=({commands:e})=>{const r=lt(e);return r.length===0?null:t.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[t.jsxs("header",{className:"flex items-center gap-2 px-4 py-3 border-b border-[var(--border)]",children:[t.jsx(je,{className:"h-4 w-4 text-[var(--color-warning)]","aria-hidden":"true"}),t.jsx("h2",{className:"text-sm font-semibold text-[var(--text)]",children:"Self-healing"}),t.jsxs("span",{className:"text-[11px] text-[var(--text-muted)]",children:[r.length," selector",r.length===1?"":"s"," healed. Update the test so the next run doesn't need to."]})]}),t.jsxs("table",{className:"w-full table-fixed text-left",children:[t.jsx("thead",{children:t.jsxs("tr",{className:"border-b border-[var(--border)] text-[11px] text-[var(--text-dim)]",children:[t.jsx("th",{className:"px-4 py-2 font-medium w-[88px]",children:"Time"}),t.jsx("th",{className:"px-3 py-2 font-medium w-[128px]",children:"Command"}),t.jsx("th",{className:"px-3 py-2 font-medium",children:"Asked for"}),t.jsx("th",{className:"w-[28px]",children:t.jsx("span",{className:"sr-only",children:"healed to"})}),t.jsx("th",{className:"px-3 py-2 font-medium",children:"Healed to"}),t.jsx("th",{className:"px-3 py-2 font-medium w-[112px]",children:"Tier"}),t.jsx("th",{className:"px-4 py-2 font-medium w-[96px] text-right",children:"Confidence"})]})}),t.jsx("tbody",{children:r.map((s,a)=>t.jsxs("tr",{className:"border-b border-[var(--border)] last:border-b-0 align-top",children:[t.jsx("td",{className:"px-4 py-2 text-xs text-[var(--text-muted)] tabular-nums",children:H({timestamp:s.at??null})}),t.jsx("td",{className:"px-3 py-2 text-xs text-[var(--text)] truncate",children:s.command}),t.jsx("td",{className:"px-3 py-2 font-mono text-[11px] text-[var(--text-muted)] break-all",children:s.original??"—"}),t.jsx("td",{className:"py-2 text-[var(--text-dim)]",children:t.jsx(ze,{className:"h-3.5 w-3.5","aria-hidden":"true"})}),t.jsx("td",{className:"px-3 py-2 font-mono text-[11px] text-[var(--text)] break-all",children:s.healed??"—"}),t.jsx("td",{className:"px-3 py-2 text-xs text-[var(--text)]",children:s.tier??"—"}),t.jsx("td",{className:"px-4 py-2 text-xs text-[var(--text)] tabular-nums text-right",children:s.confidence===null?"—":`${s.confidence}%`})]},s.id??a))})]})]})},kt=1e4,ce="var(--color-info)",de="var(--color-accent)",St="var(--border-strong)";function xe(e){return e>=1024?te(e/1024)*1024:te(e)}const me=e=>{let r=0;for(const s of e)for(const a of s.values)a!==null&&a>r&&(r=a);return r},U=({title:e,series:r,times:s,yMax:a,format:n,ariaLabel:l})=>t.jsxs("section",{"aria-label":e,children:[t.jsxs("div",{className:"mb-2 flex flex-wrap items-baseline gap-x-4 gap-y-1",children:[t.jsx("h3",{className:"text-xs font-semibold text-[var(--text)]",children:e}),r.map(c=>{const i=Xe(c.values);return t.jsxs("span",{className:"flex min-w-0 items-center gap-1.5 text-[11px] tabular-nums text-[var(--text-muted)]",children:[t.jsx("span",{className:"inline-block h-0.5 w-3 shrink-0",style:{background:c.color}}),t.jsx("span",{className:"max-w-[16rem] truncate",title:c.label,children:c.label}),i&&(c.dashed?t.jsx("span",{children:n(i.peak)}):t.jsxs("span",{children:["peak ",n(i.peak)," · avg ",n(i.average)]}))]},c.key)})]}),t.jsx(Ye,{times:s,series:r,yMax:a,formatValue:n,ariaLabel:l})]}),_t=({sessionId:e,running:r,hasTrace:s})=>{const[a,n]=x.useState(null),[l,c]=x.useState(!1);x.useEffect(()=>{let o=!0;const m=()=>k.getSessionMetrics(e).then(h=>{o&&(n(We(h)),c(!0))}).catch(()=>{o&&c(!0)});if(m(),!r)return()=>{o=!1};const f=setInterval(()=>void m(),kt);return()=>{o=!1,clearInterval(f)}},[e,r]);const i=(a==null?void 0:a.samples)??[],d=x.useMemo(()=>i.map(o=>{var m;return o.t-(((m=i[0])==null?void 0:m.t)??0)}),[i]),u=new Set(i.map(o=>o.app).filter(o=>!!o)),N=u.size>1?"Foreground app":Array.from(u)[0]??(a==null?void 0:a.appId)??"App",p=i.map(o=>o.app??null),v=o=>o>0&&!!p[o]&&!!p[o-1]&&p[o]!==p[o-1],g=[],j=[],y=[];a&&(a.series.deviceCpu&&g.push({key:"deviceCpu",label:"Device",color:ce,values:i.map(o=>o.deviceCpu)}),a.series.appCpu&&g.push({key:"appCpu",label:N,color:de,values:i.map((o,m)=>v(m)?null:o.appCpu),pointLabels:p}),a.series.appMem&&j.push({key:"appMem",label:N,color:de,values:i.map((o,m)=>v(m)?null:o.appMemMb),pointLabels:p}),a.series.deviceMem&&(y.push({key:"deviceMem",label:"Device used",color:ce,values:i.map(o=>o.deviceMemMb)}),y.push({key:"deviceTotal",label:"Device total",color:St,values:i.map(o=>o.deviceMemTotalMb),dashed:!0})));const w=r&&(a==null?void 0:a.recording)==="off",A=(a==null?void 0:a.recording)==="stopped",C="Recording stopped: the device stopped responding.";let b;return l?w?b=t.jsx(M,{title:"Performance isn't recorded for this session",description:"CPU and memory are recorded for Android devices and iPhones. This session runs on an iOS simulator or on a machine that needs updating, or recording is turned off."}):i.length===0&&!r?b=t.jsx(M,{title:"No performance data for this session",description:"CPU and memory are recorded for Android devices and iPhones. This session ran on an iOS simulator or on a machine that needed updating, before recording was available, or with recording turned off."}):i.length<2?b=t.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:A?C:r?"Collecting… The first figures appear within 10 seconds.":"Too few figures were recorded to draw a chart."}):b=t.jsxs("div",{className:"space-y-4",children:[A&&t.jsxs("p",{className:"text-xs text-[var(--color-warning)]",children:[C," The data ends there."]}),(a==null?void 0:a.platform)==="ios"&&t.jsxs("p",{className:"text-xs text-[var(--text-dim)]",children:["On iPhones, only the device's overall CPU is recorded.",s?" For more detail, download the Performance trace under Details.":""]}),g.length>0&&t.jsx(U,{title:"CPU",series:g,times:d,yMax:100,format:Ke,ariaLabel:"CPU over the session"}),j.length>0&&t.jsx(U,{title:"App memory",series:j,times:d,yMax:xe(me(j)),format:ee,ariaLabel:"App memory over the session"}),y.length>0&&t.jsx(U,{title:"Device memory",series:y,times:d,yMax:xe(me(y)),format:ee,ariaLabel:"Device memory over the session"})]}):b=t.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:"Loading performance…"}),t.jsx(Ne,{header:t.jsxs("span",{className:"flex items-center gap-2",children:[t.jsx(qe,{size:14})," Performance"]}),children:b})},Ct=({session:e})=>{const r=e.status==="running",s=!!e.video_recording;return t.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[t.jsxs("header",{className:"px-3 py-2 border-b border-[var(--border)] flex items-center justify-between",children:[t.jsx("span",{className:"text-[11px] font-semibold text-[var(--text-dim)]",children:"Recording"}),r&&t.jsxs("span",{className:"inline-flex items-center gap-1.5 text-[11px] text-[var(--red)]",children:[t.jsx("span",{className:"h-1.5 w-1.5 rounded-full bg-[var(--red)] pulse-dot"}),"Live"]})]}),t.jsx("div",{className:"aspect-video bg-[var(--bg)] flex items-center justify-center",children:r&&e.has_live_video?t.jsx("img",{src:k.getSessionLiveVideoUrl(e.id),alt:`Live view of session ${e.id}`,className:"w-full h-full object-contain"}):s?t.jsx("video",{controls:!0,src:k.getAssetUrl(e.video_recording),className:"w-full h-full object-contain"}):t.jsxs("div",{className:"flex flex-col items-center text-center px-6 py-8 gap-2",children:[t.jsx("div",{className:"h-10 w-10 rounded-full bg-[var(--surface-2)] border border-[var(--border)] flex items-center justify-center",children:t.jsx(Ge,{className:"h-5 w-5 text-[var(--text-dim)]"})}),t.jsx("div",{className:"text-sm text-[var(--text)] font-medium",children:"No video available"}),t.jsx("div",{className:"text-xs text-[var(--text-dim)] max-w-xs",children:e.status==="failed"?"Session failed before recording started.":e.video_recording_enabled===!1?"Recording was disabled for this session.":"No recording was captured."})]})})]})},S=({label:e,children:r,title:s,mono:a})=>t.jsxs("div",{className:"flex items-baseline justify-between gap-3 px-3 py-2",children:[t.jsx("dt",{className:"text-xs text-[var(--text-muted)] shrink-0",children:e}),t.jsx("dd",{className:`text-xs text-[var(--text)] text-right min-w-0 break-all ${a?"font-mono text-[11px]":""}`,title:s,children:r})]}),Rt=({session:e,buildName:r})=>{var c,i;const{toast:s}=P(),a=be(e.ranOn),n=e.performance_trace?k.getAssetUrl(e.performance_trace):"",l=async()=>{try{await navigator.clipboard.writeText(e.id),s("Session ID copied","success")}catch{s("Clipboard unavailable","error")}};return t.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[t.jsx("header",{className:"px-3 py-2 border-b border-[var(--border)]",children:t.jsx("h2",{className:"text-[11px] font-semibold text-[var(--text-dim)]",children:"Details"})}),t.jsxs("dl",{className:"divide-y divide-[var(--border)]",children:[t.jsx(S,{label:"Session ID",mono:!0,children:t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{children:e.id}),t.jsx("button",{type:"button",onClick:l,"aria-label":"Copy session ID",className:"p-0.5 rounded text-[var(--text-dim)] hover:text-[var(--text)]",children:t.jsx(z,{className:"h-3 w-3"})})]})}),r&&t.jsx(S,{label:"Build",children:r}),t.jsx(S,{label:"Device UDID",mono:!0,children:e.device_udid||"—"}),t.jsx(S,{label:"Platform",children:[ve(e),e.device_version].filter(d=>d&&d!=="—").join(" ")||"—"}),t.jsx(S,{label:"Ran on",title:e.node_id?`Node ${e.node_id}`:void 0,children:a??"Not known"}),t.jsx(S,{label:"Run by",title:(c=e.owner)==null?void 0:c.email,children:((i=e.owner)==null?void 0:i.name)??"Not recorded"}),t.jsx(S,{label:"Started",children:Z(e.startTime)}),t.jsx(S,{label:"Ended",children:e.endTime?Z(e.endTime):"—"}),e.tags&&t.jsx(S,{label:"Tags",children:e.tags}),n&&t.jsx(S,{label:"iOS trace",children:t.jsxs("a",{href:n,className:"inline-flex items-center gap-1 text-[var(--color-accent)] hover:underline",children:[t.jsx(ye,{className:"h-3 w-3","aria-hidden":"true"}),"Performance trace"]})})]})]})},ue={desired:"Requested",session:"Actual"},Tt=({session:e})=>{const[r,s]=x.useState("desired"),a=ne(e.desired_capabilities),n=ne(e.session_capabilities),c=Object.entries(r==="desired"?a:n);return t.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[t.jsx("header",{className:"flex items-center border-b border-[var(--border)]",children:["desired","session"].map(i=>t.jsx("button",{type:"button",onClick:()=>s(i),"aria-pressed":r===i,className:`flex-1 h-9 text-[11px] font-semibold transition-colors border-b-2 ${r===i?"border-[var(--color-accent)] text-[var(--text)]":"border-transparent text-[var(--text-muted)] hover:text-[var(--text)]"}`,children:ue[i]},i))}),c.length===0?t.jsxs("div",{className:"px-3 py-6 text-center text-xs text-[var(--text-dim)]",children:["No ",ue[r].toLowerCase()," capabilities recorded for this session."]}):t.jsx("dl",{className:"divide-y divide-[var(--border)]",children:c.map(([i,d])=>t.jsxs("div",{className:"grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] items-baseline gap-3 px-3 py-2",children:[t.jsx("dt",{className:"text-xs text-[var(--text-muted)] break-all",children:i}),t.jsx("dd",{className:"font-mono text-xs text-[var(--text)] truncate text-right min-w-0",title:ie(d,500),children:ie(d)})]},i))})]})},$t=({tabs:e,active:r,onChange:s,errorsOnly:a,onErrorsOnlyChange:n,showErrorsOnly:l=!0})=>{const c=e.filter(i=>!(i.hideWhenEmpty&&i.count===0));return t.jsxs("div",{className:"flex items-center justify-between gap-4 px-3 border-b border-[var(--border)] bg-[var(--surface)]",children:[t.jsx("div",{className:"flex items-center gap-1 overflow-x-auto",children:c.map(i=>{const d=i.key===r;return t.jsxs("button",{type:"button",onClick:()=>s(i.key),"aria-pressed":d,"data-empty":i.count===0?"true":"false",className:`relative inline-flex items-center gap-1.5 h-9 px-3 text-xs whitespace-nowrap transition-colors ${d?"text-[var(--text)]":i.count===0?"text-[var(--text-dim)] hover:text-[var(--text-muted)]":"text-[var(--text-muted)] hover:text-[var(--text)]"}`,children:[t.jsx("span",{children:i.label}),i.count>0&&t.jsx("span",{className:`text-[10px] tabular-nums ${d?"text-[var(--text-muted)]":"text-[var(--text-dim)]"}`,children:vt(i.count)}),d&&t.jsx("span",{className:"absolute left-2 right-2 bottom-0 h-[2px] bg-[var(--color-accent)] rounded-t"})]},i.key)})}),l&&t.jsxs("label",{className:"inline-flex items-center gap-2 text-[11px] text-[var(--text-muted)] cursor-pointer select-none whitespace-nowrap",children:[t.jsx("input",{type:"checkbox",checked:a,onChange:i=>n(i.target.checked),"aria-label":"Show only error rows"}),"Errors only"]})]})},At={key:"text-[var(--blue)]",string:"text-[var(--color-accent)]",number:"text-[var(--amber)]",boolean:"text-[var(--amber)]",null:"text-[var(--text-dim)] italic",punct:"text-[var(--text-muted)]",plain:"text-[var(--text)]"},Mt=({value:e,maxHeightPx:r})=>{const s=gt(e);if(!s)return t.jsx("span",{className:"text-[var(--text-dim)] text-xs",children:"(empty)"});let a;try{a=jt(s)}catch{a=[{text:s,kind:"plain"}]}return t.jsx("pre",{className:"font-mono text-[11px] leading-relaxed bg-[var(--bg)] border border-[var(--border)] rounded-md p-3 overflow-x-auto whitespace-pre",style:r?{maxHeight:r,overflowY:"auto"}:void 0,children:a.map((n,l)=>t.jsx("span",{className:At[n.kind],children:n.text},l))})},Dt={green:"bg-[var(--color-success)]",red:"bg-[var(--red)]",amber:"bg-[var(--amber)]",blue:"bg-[var(--blue)]",neutral:"bg-[var(--text-dim)]"},Et={green:"text-[var(--color-success)]",red:"text-[var(--red)]",amber:"text-[var(--amber)]",blue:"text-[var(--blue)]",neutral:"text-[var(--text-muted)]"},Ot=({log:e})=>{const[r,s]=x.useState(!1),{toast:a}=P(),n=H(e),l=bt(e),c=async i=>{i.stopPropagation();try{const d=e.body??e,u=typeof d=="string"?d:JSON.stringify(d,null,2);await navigator.clipboard.writeText(u),a("Log entry copied","success")}catch{a("Clipboard unavailable","error")}};return t.jsxs("div",{className:"border-b border-[var(--border)] last:border-b-0",children:[t.jsxs("button",{type:"button",onClick:()=>s(i=>!i),"aria-expanded":r,className:"group w-full flex items-center gap-2 px-3 py-2 text-left hover:bg-[var(--surface-2)]/60 transition-colors",children:[t.jsx(I,{className:`h-3 w-3 text-[var(--text-dim)] transition-transform shrink-0 ${r?"rotate-90":""}`}),t.jsx("span",{className:"font-mono text-[11px] text-[var(--text-muted)] w-[64px] shrink-0",children:n}),t.jsx("span",{className:`h-1.5 w-1.5 rounded-full shrink-0 ${Dt[l.tone]}`}),t.jsx("span",{className:`font-mono text-[11px] font-medium shrink-0 ${Et[l.tone]}`,children:l.label}),t.jsxs("span",{className:"min-w-0 flex-1 flex items-baseline gap-2 truncate",children:[t.jsx("span",{className:"text-[12px] text-[var(--text)] shrink-0",children:Nt(e)}),le(e)&&t.jsx("span",{className:"font-mono text-[11px] text-[var(--text-dim)] truncate min-w-0",children:le(e)})]}),typeof e.duration=="number"&&t.jsx("span",{className:"text-[11px] tabular-nums text-[var(--text-muted)] shrink-0",children:L(e.duration)}),t.jsx("span",{role:"button",tabIndex:0,onClick:c,onKeyDown:i=>{(i.key==="Enter"||i.key===" ")&&c(i)},"aria-label":"Copy log entry",className:"opacity-0 group-hover:opacity-100 p-1 rounded text-[var(--text-dim)] hover:text-[var(--text)] transition-opacity shrink-0",children:t.jsx(z,{className:"h-3 w-3"})})]}),r&&t.jsx("div",{className:"px-3 pb-3 pl-9",children:t.jsx(Mt,{value:e.body??e,maxHeightPx:400})})]})},Lt=(e,r)=>e?"bg-[var(--color-danger)]":r?"bg-[var(--color-warning)]":"bg-[var(--color-success)]",Pt=({commands:e})=>{const{spanMs:r,bars:s}=dt(e);return s.length===0?t.jsx("div",{className:"px-4 py-10 text-center text-xs text-[var(--text-dim)]",children:"No commands recorded for this session."}):t.jsxs("div",{className:"px-3 py-3",children:[t.jsxs("div",{className:"flex items-center justify-between pb-2 text-[11px] text-[var(--text-dim)] tabular-nums",children:[t.jsxs("span",{children:[s.length," command",s.length===1?"":"s"," over ",L(r)]}),t.jsxs("span",{className:"inline-flex items-center gap-3",children:[t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{className:"h-2 w-2 rounded-sm bg-[var(--color-danger)]"})," Failed"]}),t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{className:"h-2 w-2 rounded-sm bg-[var(--color-warning)]"})," Healed"]})]})]}),t.jsx("ol",{className:"space-y-1",children:s.map((a,n)=>t.jsxs("li",{"data-testid":"timeline-bar","data-command":a.label,"data-failed":a.failed?"true":"false",className:"grid grid-cols-[128px_minmax(0,1fr)_64px] items-center gap-2",children:[t.jsx("span",{className:"truncate text-[11px] font-mono text-[var(--text-muted)]",title:a.label,children:a.label}),t.jsx("span",{className:"relative h-3 rounded-sm bg-[rgb(var(--rgb-fg)/0.05)]",children:t.jsx("span",{className:`absolute inset-y-0 rounded-sm ${Lt(a.failed,a.healed)}`,style:{left:`${a.startMs/r*100}%`,width:`max(2px, ${a.durationMs/r*100}%)`}})}),t.jsx("span",{className:"text-right text-[11px] tabular-nums text-[var(--text-muted)]",children:a.durationMs>0?L(a.durationMs):"—"})]},a.id??n))})]})},Ft=({commands:e})=>{const r=xt(e);return r.length===0?t.jsxs("div",{className:"px-4 py-10 text-center text-xs text-[var(--text-dim)]",children:["No screenshots were kept. A failed command keeps one unless"," ",t.jsx("code",{className:"font-mono",children:"xe:options.screenshotOnFailure"})," is false; set"," ",t.jsx("code",{className:"font-mono",children:"xe:options.screenshotOnEveryCommand"})," to keep one for every command."]}):t.jsx("ul",{className:"grid grid-cols-[repeat(auto-fill,minmax(140px,1fr))] gap-3 p-3",children:r.map((s,a)=>{const n=k.getAssetUrl(s.path),l=H({timestamp:s.at??null});return t.jsx("li",{children:t.jsxs("a",{href:n,target:"_blank",rel:"noreferrer",className:`block rounded-md border overflow-hidden hover:border-[var(--border-strong)] ${s.failed?"border-[var(--color-danger)]":"border-[var(--border)]"}`,children:[t.jsx("img",{src:n,alt:`${s.command} at ${l}`,loading:"lazy",className:"block w-full aspect-[9/16] object-contain bg-[rgb(var(--rgb-fg)/0.03)]"}),t.jsxs("span",{className:"flex items-center justify-between gap-2 px-2 py-1 text-[11px]",children:[t.jsx("span",{className:`truncate font-mono ${s.failed?"text-[var(--color-danger)]":"text-[var(--text)]"}`,children:s.command}),t.jsx("span",{className:"tabular-nums text-[var(--text-dim)] shrink-0",children:l})]})]})},s.id??a)})})},he=["commands","device","debug","profiling"],Bt=({sessionLogs:e,deviceLogs:r,debugLogs:s,profiling:a})=>{const[n,l]=x.useState("commands"),[c,i]=x.useState(!1),d=e,u=x.useMemo(()=>we(d).screenshots,[d]),N=x.useMemo(()=>[{key:"commands",label:"Commands",count:e.length},{key:"timeline",label:"Timeline",count:e.length},{key:"screenshots",label:"Screenshots",count:u},{key:"device",label:"Device logs",count:r.length},{key:"debug",label:"Debug logs",count:s.length},{key:"profiling",label:"System profiling",count:a.length,hideWhenEmpty:!0}],[e.length,u,r.length,s.length,a.length]),p=x.useMemo(()=>{let v=[];return n==="commands"?v=e:n==="device"?v=r:n==="debug"?v=s:n==="profiling"&&(v=a),yt(v,c)},[n,e,r,s,a,c]);return t.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] flex flex-col min-h-[280px] max-h-[640px]",children:[t.jsx($t,{tabs:N,active:n,onChange:l,errorsOnly:c,onErrorsOnlyChange:i,showErrorsOnly:he.includes(n)}),t.jsxs("div",{className:"flex-1 overflow-y-auto",children:[n==="timeline"&&t.jsx(Pt,{commands:d}),n==="screenshots"&&t.jsx(Ft,{commands:d}),he.includes(n)&&(p.length===0?t.jsx("div",{className:"px-4 py-10 text-center text-xs text-[var(--text-dim)]",children:c?"No error rows in this tab.":"No log entries in this tab."}):p.map((v,g)=>t.jsx(Ot,{log:v},v.id??g)))]})]})};function ke(e){if(!e)return"failed";switch(e){case"HTTPS_CLIENT_ERROR":case"HTTPS_SERVER_ERROR":case"OPEN_HTTPS_SERVER_ERROR":case"ON_CONNECT_ERROR":return"tls";case"PROXY_TO_SERVER_REQUEST_ERROR":return"net";default:return"failed"}}const Y="/xenon/api/interceptor";async function Ht(e){const r=await fetch(`${Y}/sessions/${encodeURIComponent(e)}/requests`);if(r.status===404){const a=await r.json().catch(()=>({}));return{active:!1,error:a==null?void 0:a.error}}return r.ok?{active:!0,requests:(await r.json()).requests||[]}:{active:!1,error:`request failed (${r.status})`}}async function Ut(e,r){const s=await fetch(`${Y}/sessions/${encodeURIComponent(e)}/requests/${encodeURIComponent(r)}`);return s.ok?s.json():null}function It(e){return`${Y}/sessions/${encodeURIComponent(e)}/har`}function Jt(e){return e>=200&&e<300?"ready":e>=300&&e<400?"neutral":e>=400&&e<500?"busy":e>=500?"error":"neutral"}const fe=({headers:e})=>{const r=Object.entries(e||{});return r.length===0?t.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:"No headers"}):t.jsx("div",{className:"space-y-0.5 font-mono text-xs",children:r.map(([s,a])=>t.jsxs("div",{className:"flex gap-2",children:[t.jsxs("span",{className:"text-[var(--text-dim)] shrink-0",children:[s,":"]}),t.jsx("span",{className:"break-all",children:a})]},s))})},pe=({body:e})=>{if(e==null||e==="")return t.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:"No body"});let r=e;try{r=JSON.stringify(JSON.parse(e),null,2)}catch{}return t.jsx("pre",{className:"font-mono text-xs whitespace-pre-wrap break-all bg-[var(--surface-2,var(--surface))] rounded p-2 max-h-64 overflow-auto",children:r})},Vt=({request:e,onClose:r})=>{if(!e)return null;const s=t.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[t.jsx(_,{tone:"accent",children:e.method}),e.failed?t.jsx(_,{tone:"error",children:ke(e.failureKind)}):t.jsx(_,{tone:Jt(e.resStatus),children:e.resStatus||"—"}),t.jsx("span",{className:"font-mono text-xs truncate",title:e.url,children:e.url})]});return t.jsx(tt,{open:!!e,title:s,onClose:r,width:720,children:t.jsxs("div",{className:"space-y-4 text-xs",children:[e.failed&&t.jsxs("div",{className:"border border-[var(--red)] rounded p-2 bg-[rgb(var(--rgb-red-600)/0.08)]",children:[t.jsx("div",{className:"text-[var(--red)] text-[11px] mb-1",children:"Failure"}),t.jsx("div",{className:"text-xs",children:e.failureReason||"Request failed"}),e.failureKind&&t.jsx("div",{className:"font-mono text-[10px] text-[var(--text-dim)] mt-1",children:e.failureKind})]}),!e.failed&&t.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Duration"}),t.jsxs("div",{className:"font-mono",children:[e.durationMs," ms"]})]}),t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Flags"}),t.jsxs("div",{className:"flex gap-1",children:[e.mocked&&t.jsx(_,{tone:"accent",children:"mocked"}),e.modified&&t.jsx(_,{tone:"busy",children:"modified"}),!e.mocked&&!e.modified&&t.jsx("span",{className:"text-[var(--text-dim)]",children:"passthrough"})]})]})]}),e.commandHint&&t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Triggered by"}),t.jsx("div",{className:"font-mono",children:e.commandHint.commandName})]}),!e.failed&&t.jsxs(t.Fragment,{children:[t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Request Headers"}),t.jsx(fe,{headers:e.reqHeaders})]}),t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Request Body"}),t.jsx(pe,{body:e.reqBody})]}),t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Response Headers"}),t.jsx(fe,{headers:e.resHeaders})]}),t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Response Body"}),t.jsx(pe,{body:e.resBody})]})]})]})})};function zt(e){return e?new Date(e).toLocaleTimeString(void 0,{hour12:!1}):"—"}function Kt(e){return e>=200&&e<300?"ready":e>=300&&e<400?"neutral":e>=400&&e<500?"busy":e>=500?"error":"neutral"}const Wt=({sessionId:e,sessionEnded:r})=>{const[s,a]=x.useState(null),[n,l]=x.useState(null),[c,i]=x.useState([]),[d,u]=x.useState(!0),[N,p]=x.useState(null),v=x.useRef(new Set),{on:g,isConnected:j}=Re();x.useEffect(()=>{let o=!0;return u(!0),Ht(e).then(m=>{if(!o)return;a(m.active),l(m.error||null);const f=m.requests||[];i(f),v.current=new Set(f.map(h=>h.id))}).finally(()=>{o&&u(!1)}),()=>{o=!1}},[e]),x.useEffect(()=>{if(r)return;const o=g("interceptor_request",h=>{!h||h.sessionId!==e||v.current.has(h.id)||(v.current.add(h.id),i(R=>[h,...R]),a(!0))}),m=g("interceptor_session_started",h=>{(h==null?void 0:h.sessionId)===e&&a(!0)}),f=g("interceptor_session_stopped",h=>{(h==null?void 0:h.sessionId)===e&&a(!1)});return()=>{o(),m(),f()}},[g,e,r,j]);const y=x.useMemo(()=>[...c].sort((o,m)=>m.ts-o.ts),[c]),w=async o=>{if(p(o),s){const m=await Ut(e,o.id);m&&p(m)}},A=s&&c.length>0?t.jsxs("a",{href:It(e),className:"inline-flex items-center gap-1 text-xs text-[var(--text-dim)] hover:text-[var(--text)]",title:"Download HAR",children:[t.jsx(ye,{className:"h-3 w-3"}),"HAR"]}):null,C=t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx(rt,{className:"h-3.5 w-3.5 text-[var(--text-dim)]"}),t.jsx("span",{children:"Network"}),s&&c.length>0&&t.jsxs("span",{className:"text-xs text-[var(--text-dim)]",children:["(",c.length,")"]})]});let b;return d?b=t.jsx("div",{className:"p-4 text-xs text-[var(--text-dim)]",children:"Loading…"}):s===!1&&r?b=t.jsx(M,{title:"No network capture",description:"Network interception was not enabled for this session."}):s===!1?b=t.jsx(M,{title:"Network interception disabled",description:"Enable interceptor.enabled in plugin config to capture requests for Android sessions."}):n?b=t.jsxs("div",{className:"p-4 text-xs text-[var(--red)]",children:["Failed to load requests: ",n]}):y.length===0?b=t.jsx(M,{title:"Waiting for requests",description:r?"No HTTP traffic was captured during this session.":"Captured requests will appear here as the device makes calls."}):b=t.jsx("div",{className:"overflow-auto max-h-96",children:t.jsxs(Qe,{children:[t.jsx(Ze,{children:t.jsxs(re,{children:[t.jsx(T,{style:{width:80},children:"Time"}),t.jsx(T,{style:{width:70},children:"Method"}),t.jsx(T,{style:{width:70},children:"Status"}),t.jsx(T,{children:"Host"}),t.jsx(T,{children:"Path"}),t.jsx(T,{style:{width:80},children:"Duration"}),t.jsx(T,{style:{width:90},children:"Flags"})]})}),t.jsx(et,{children:y.map(o=>t.jsxs(re,{onClick:()=>w(o),style:{cursor:"pointer"},title:o.failed?o.failureReason:void 0,children:[t.jsx($,{className:"font-mono text-[11px]",children:zt(o.ts)}),t.jsx($,{children:t.jsx(_,{tone:"accent",children:o.method})}),t.jsx($,{children:o.failed?t.jsx(_,{tone:"error",children:ke(o.failureKind)}):t.jsx(_,{tone:Kt(o.resStatus),children:o.resStatus||"—"})}),t.jsx($,{className:"font-mono text-[11px] truncate",title:o.host,children:o.host}),t.jsx($,{className:"font-mono text-[11px] truncate",title:o.path,children:o.failed?"—":o.path}),t.jsx($,{className:"font-mono text-[11px]",children:o.failed?"—":`${o.durationMs}ms`}),t.jsx($,{children:t.jsxs("div",{className:"flex gap-1",children:[o.mocked&&t.jsx(_,{tone:"accent",children:"mock"}),o.modified&&t.jsx(_,{tone:"busy",children:"mod"})]})})]},o.id))})]})}),t.jsxs(t.Fragment,{children:[t.jsx(Ne,{header:C,action:A,padded:!1,children:b}),t.jsx(Vt,{request:N,onClose:()=>p(null)})]})},pr=()=>{const{buildId:e="",sessionId:r=""}=Te(),{toast:s}=P(),a=$e(),n=Le(),l=at(r||null),c=x.useMemo(()=>n.builds.find(p=>p.id===e)??null,[n.builds,e]),i=c?Pe(c):"Build";if(x.useEffect(()=>{!l.loading&&l.notFound&&(s("Session not available — it may belong to a team you are not on.","info"),a("/builds",{replace:!0}))},[l.loading,l.notFound,a,s]),l.loading)return t.jsx("div",{className:"p-6 text-xs text-[var(--text-dim)]",children:"Loading session…"});if(l.error||!l.session)return t.jsxs("div",{className:"p-6 space-y-2",children:[t.jsx(ae,{buildId:e,buildName:c?i:null,sessionId:r}),t.jsx("div",{className:"mt-6 rounded-md border border-[var(--red)]/30 bg-[var(--surface)] p-4 text-xs text-[var(--red)]",children:l.error||"Session not found."})]});const d=l.session,u=l.sessionLogs,N=K(d.status).bucket==="failed";return t.jsxs("div",{className:"flex flex-col h-full min-h-0",children:[t.jsx(ae,{buildId:e,buildName:c?i:null,sessionId:d.id}),t.jsx(it,{session:d}),t.jsx("div",{className:"flex-1 overflow-y-auto overflow-x-hidden",children:t.jsxs("div",{className:"px-4 py-4 space-y-4",children:[t.jsx(mt,{session:d,commands:u}),N&&t.jsx(pt,{session:d,buildName:i,buildId:e,commands:u,durationText:Fe(V(d))}),t.jsx(wt,{commands:u}),t.jsx(_t,{sessionId:d.id,running:d.status==="running",hasTrace:!!d.performance_trace}),t.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-[minmax(0,1fr)_340px] gap-4",children:[t.jsx("div",{className:"min-w-0",children:t.jsx(Bt,{sessionLogs:l.sessionLogs,deviceLogs:l.deviceLogs,debugLogs:l.debugLogs,profiling:l.profiling})}),t.jsxs("div",{className:"flex flex-col gap-4 min-w-0",children:[t.jsx(Ct,{session:d}),t.jsx(Rt,{session:d,buildName:c?i:null}),t.jsx(Tt,{session:d})]})]}),t.jsx(Wt,{sessionId:d.id,sessionEnded:d.status!=="running"})]})})]})};export{pr as SessionDetailPage,pr as default};
|
|
@@ -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-CHOr4JCs.js";/* empty css */import{S as j,A as Q}from"./SettingCard-Cw6lDD0l.js";import{M as W}from"./Modal-DfawTxih.js";import{B as A}from"./button-BxnHCCsX.js";import{P as J}from"./page-header-Cm1XL49f.js";import{C as I}from"./circle-check-D4EzyGzs.js";import{C as Y}from"./clock-EZfet0sS.js";import{M as Z}from"./mouse-pointer-2-Cw2tWzDX.js";import{C as ee}from"./check-R6V3bot0.js";import{A as ae}from"./activity-CnLvNwae.js";import{C as se}from"./calendar-CqVsn28A.js";import{A as te}from"./arrow-up-right-DUA_lq6f.js";import"./index-BiL0Enl_.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-CHOr4JCs.js";/* empty css */import{F as S}from"./FieldGroup-DqMf7Rf3.js";import{P as D}from"./page-header-Cm1XL49f.js";import{T as V}from"./trash-2-CcSsv4fS.js";import{P as x}from"./plus-CIIvR_6Z.js";import{C as B}from"./circle-x-Cf6AF2B2.js";import{C as H}from"./circle-check-D4EzyGzs.js";import{Z as I}from"./zap-COC2tZaH.js";import{A as X}from"./activity-CnLvNwae.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};
|