@xenon-device-management/xenon 2.16.0 → 2.17.0
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/README.md +2 -2
- package/lib/package.json +1 -1
- package/lib/public/assets/{AnnotationOverlay-2W4JvEyZ.js → AnnotationOverlay-CjmMCS0S.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-GmckX_7r.js → ApiKeyGate-B4Ob0v3C.js} +1 -1
- package/lib/public/assets/{DeviceMosaicView-DqODzkyo.js → DeviceMosaicView-DTVOB_NO.js} +1 -1
- package/lib/public/assets/{EmptyState-7hETKEWb.js → EmptyState-DcEG-oAi.js} +1 -1
- package/lib/public/assets/{FieldGroup-GOgssFEP.js → FieldGroup-CtnCDshE.js} +1 -1
- package/lib/public/assets/{FilterMenu-Yvf6RGMd.js → FilterMenu-BK0dE9cc.js} +1 -1
- package/lib/public/assets/{Menu-KrPnfdsf.js → Menu-eKsmLpKS.js} +1 -1
- package/lib/public/assets/{Modal-C6WjZFeI.js → Modal-DPwop3Lz.js} +1 -1
- package/lib/public/assets/{RecordingPage-CSRIhZEh.js → RecordingPage-CfgSkZkB.js} +1 -1
- package/lib/public/assets/{RecordingsPage-CB8ST3nE.js → RecordingsPage-Bzl9pTUY.js} +1 -1
- package/lib/public/assets/{SegmentedControl-C9o90KId.js → SegmentedControl-B_TZTrPm.js} +1 -1
- package/lib/public/assets/{SettingCard-CPm3w9l2.js → SettingCard-DlmvZJPM.js} +1 -1
- package/lib/public/assets/{Table-DwIgERRt.js → Table-BigPWKXi.js} +1 -1
- package/lib/public/assets/{activity-DrLbHifn.js → activity-CvuZdQYu.js} +1 -1
- package/lib/public/assets/{ai-settings-DU_Ls06P.js → ai-settings-lI2UJ2n7.js} +1 -1
- package/lib/public/assets/{api-keys-CWYdEzJ_.js → api-keys-CPgf9XJM.js} +1 -1
- package/lib/public/assets/{apps-BqNVdln7.js → apps-DIzOvtUp.js} +1 -1
- package/lib/public/assets/{arrow-left-BtB0_Jg_.js → arrow-left-DiP5ywBT.js} +1 -1
- package/lib/public/assets/{arrow-right-C8NhdtyN.js → arrow-right-DtW_8hm5.js} +1 -1
- package/lib/public/assets/{arrow-up-right-gMg0ffvv.js → arrow-up-right-C89FbNU0.js} +1 -1
- package/lib/public/assets/{auth-shell-CiYZ3N4M.js → auth-shell-B7sUDb--.js} +2 -2
- package/lib/public/assets/{builds-page-EaaqDmLN.js → builds-page-BBhvrMfA.js} +1 -1
- package/lib/public/assets/{button-DDau4Axv.js → button-DugsdUw1.js} +1 -1
- package/lib/public/assets/{calendar-DQ83B1ed.js → calendar-grKd08ZH.js} +1 -1
- package/lib/public/assets/{check-K1ovTQam.js → check-ORjyNfnX.js} +1 -1
- package/lib/public/assets/{chevron-right-Biq00pMt.js → chevron-right-Ei2FxUXq.js} +1 -1
- package/lib/public/assets/{circle-check-CBv88cH7.js → circle-check-Day0HjW4.js} +1 -1
- package/lib/public/assets/{circle-x-Bmltailn.js → circle-x-CHxSRLHp.js} +1 -1
- package/lib/public/assets/{clock-BPrld-3h.js → clock-BDHewWTx.js} +1 -1
- package/lib/public/assets/{copy-t0QB14JI.js → copy-CTwT4I3D.js} +1 -1
- package/lib/public/assets/{device-explorer-BgGZ8AvV.js → device-explorer-DlhrKJ-g.js} +1 -1
- package/lib/public/assets/{download-BroHxNOh.js → download-D3bjz_vr.js} +1 -1
- package/lib/public/assets/{forgot-password-Bjmf49i6.js → forgot-password-BNBLi023.js} +1 -1
- package/lib/public/assets/{h264Stream-DeD__6wa.js → h264Stream-C51Au2i_.js} +1 -1
- package/lib/public/assets/{index-B0gQkEg0.js → index-Dk5J6HB1.js} +2 -2
- package/lib/public/assets/{index-B0JeuZbg.js → index-DncSz5LG.js} +1 -1
- package/lib/public/assets/{index-72oF89LJ.js → index-Z7_BY3pJ.js} +1 -1
- package/lib/public/assets/{input-DIc4MSC7.js → input-bTXM4l09.js} +1 -1
- package/lib/public/assets/{line-chart-BcWm2vW8.js → line-chart-B3YmVYyr.js} +1 -1
- package/lib/public/assets/{list-checks-B06IJJad.js → list-checks-BClKEj1-.js} +1 -1
- package/lib/public/assets/{lock-pVLvnN30.js → lock-CZsenX3D.js} +1 -1
- package/lib/public/assets/{login-CN77vy--.js → login-B6A6tOp9.js} +1 -1
- package/lib/public/assets/{maintenance-settings-CSdzQSkY.js → maintenance-settings-CmCJGy7O.js} +1 -1
- package/lib/public/assets/{monitor-BtrN5IkI.js → monitor-BdTNEglU.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-CNsQfo6w.js → mouse-pointer-2-BM8CaLtt.js} +1 -1
- package/lib/public/assets/{network-BSfA8ZxG.js → network-BS6vUc1x.js} +1 -1
- package/lib/public/assets/{overview-BfTebCK5.js → overview-Yfk_Y7g3.js} +1 -1
- package/lib/public/assets/{page-header-D8KUX6Qg.js → page-header-DhXCwpml.js} +1 -1
- package/lib/public/assets/{play-Cvc8xzic.js → play-D08gP1td.js} +1 -1
- package/lib/public/assets/{plus-C9Zj84UF.js → plus-BKboorfa.js} +1 -1
- package/lib/public/assets/{profile-page-Bi27STgs.js → profile-page-Cd9NodrA.js} +1 -1
- package/lib/public/assets/{reset-password-CPsOuwSu.js → reset-password-acLDyyDW.js} +1 -1
- package/lib/public/assets/{runbook-page-DEQ-DBOX.js → runbook-page-CWLkC1Fd.js} +1 -1
- package/lib/public/assets/{select-BWpnhWXj.js → select-DZoRKNti.js} +1 -1
- package/lib/public/assets/{selector-detail-redirect-BefSCSpe.js → selector-detail-redirect-DS4qrt84.js} +1 -1
- package/lib/public/assets/{selector-health-page-MSk96Zz7.js → selector-health-page-TpmN61G3.js} +1 -1
- package/lib/public/assets/{session-detail-page-DPyBNef_.js → session-detail-page-C0wYaXqy.js} +1 -1
- package/lib/public/assets/{settings-V6x-cXwy.js → settings-CxPGEY2j.js} +1 -1
- package/lib/public/assets/{stat-tile-Duex-GZD.js → stat-tile-DUpyYgl-.js} +1 -1
- package/lib/public/assets/{tablet-BBcoqBmL.js → tablet-SA02BJNQ.js} +1 -1
- package/lib/public/assets/{teams-ts8p5rEh.js → teams-jamD0Kxr.js} +1 -1
- package/lib/public/assets/{trash-2-CZ2ew6AZ.js → trash-2-DO8fv6w4.js} +1 -1
- package/lib/public/assets/{upload-Dv99y_le.js → upload-CaiGCStb.js} +1 -1
- package/lib/public/assets/{use-builds-data-DrYC8JCF.js → use-builds-data-jS-fcvVf.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-C7QkBNkP.js → use-password-reset-mode-BzoenegM.js} +1 -1
- package/lib/public/assets/{users-gJ5Y01yo.js → users-Bnw7Vs_B.js} +1 -1
- package/lib/public/assets/{users-D7tKMESH.js → users-DHvDrriG.js} +1 -1
- package/lib/public/assets/{video-off-CqZVuZGs.js → video-off-FEvy0w5U.js} +1 -1
- package/lib/public/assets/{webhook-settings-DI5DSW99.js → webhook-settings-CBVB5lxi.js} +1 -1
- package/lib/public/assets/{zap-ClcHFxfo.js → zap-BMrTJO6M.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/schema.json +1 -1
- package/lib/src/app/index.js +2 -0
- package/lib/src/app/openapi/grid.yaml +102 -0
- package/lib/src/dashboard/event-manager.js +5 -8
- package/lib/src/device-utils.js +11 -2
- package/lib/src/gateway/nodeAsk.js +57 -0
- package/lib/src/gateway/nodeSessionDeviceLogs.js +35 -0
- package/lib/src/plugin.js +4 -1
- package/lib/src/scripts/initialize-database.js +0 -6
- package/lib/src/scripts/run-migrations.js +123 -880
- package/lib/src/services/SessionLifecycleService.js +20 -5
- package/lib/src/services/interceptor/CertManager.js +50 -16
- package/lib/src/services/logcat/NodeDeviceLogStore.js +99 -0
- package/lib/src/services/logcat/NodeDeviceLogsCollector.js +164 -0
- package/lib/src/services/logcat/SessionDeviceLogs.js +199 -44
- package/lib/src/services/logcat/nodeDeviceLogs.js +91 -0
- package/lib/src/services/metrics/nodeMetrics.js +20 -49
- package/lib/src/sessions/RemoteSession.js +11 -2
- package/lib/src/sessions/SessionManager.js +28 -1
- package/lib/test/helpers/fake-logcat.js +80 -0
- package/lib/test/helpers/hub-node-pair.js +227 -0
- package/lib/test/unit/dashboard-events-device-logs.spec.js +15 -0
- package/lib/test/unit/hub-node-iphone-readiness.spec.js +134 -0
- package/lib/test/unit/hub-node-session-device-logs.spec.js +194 -0
- package/lib/test/unit/hub-node-session-metrics.spec.js +24 -192
- package/lib/test/unit/hub-remote-session-heartbeat.spec.js +8 -1
- package/lib/test/unit/interceptor/CertManager.spec.js +89 -0
- package/lib/test/unit/node-device-log-store.spec.js +102 -0
- package/lib/test/unit/node-device-logs-collector.spec.js +343 -0
- package/lib/test/unit/node-gateway-create-appium-server.spec.js +8 -1
- package/lib/test/unit/node-hub-session-owner.spec.js +8 -1
- package/lib/test/unit/node-session-device-logs-route.spec.js +103 -0
- package/lib/test/unit/run-migrations-database.spec.js +57 -424
- package/lib/test/unit/run-migrations.test.js +74 -674
- package/lib/test/unit/session-device-logs-hub-node.spec.js +340 -0
- package/lib/test/unit/session-device-logs-lifecycle.spec.js +137 -0
- package/lib/test/unit/session-device-logs.spec.js +32 -94
- package/lib/test/unit/session-log-indexes.spec.js +15 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/schema.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as a,r as g}from"./index-
|
|
1
|
+
import{j as a,r as g}from"./index-Dk5J6HB1.js";const I=({header:e,footer:t,action:n,padded:o=!0,className:s,children:x})=>a.jsxs("div",{className:`card${s?" "+s:""}`,children:[(e||n)&&a.jsxs("div",{className:"card-header",children:[a.jsx("div",{className:"card-header-title",children:e}),n&&a.jsx("div",{className:"card-header-action",children:n})]}),a.jsx("div",{className:o?"card-body":"card-body-flush",children:x}),t&&a.jsx("div",{className:"card-footer",children:t})]});function E(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const t=e;return!Array.isArray(t.samples)||!t.series||typeof t.series!="object"?null:{platform:String(t.platform??""),intervalMs:Number(t.intervalMs)||2e3,appId:typeof t.appId=="string"?t.appId:null,series:{deviceCpu:!!t.series.deviceCpu,deviceMem:!!t.series.deviceMem,appCpu:!!t.series.appCpu,appMem:!!t.series.appMem},recording:t.recording==="sampling"||t.recording==="stopped"||t.recording==="off"?t.recording:null,samples:t.samples}}function L(e){let t=-1/0,n=0,o=0;for(const s of e)s===null||!Number.isFinite(s)||(t=Math.max(t,s),n+=s,o+=1);return o===0?null:{peak:t,average:n/o}}function k(e){const t=Math.max(0,Math.round(e/1e3)),n=Math.floor(t/3600),o=Math.floor(t%3600/60),s=String(t%60).padStart(2,"0");return n>0?`${n}:${String(o).padStart(2,"0")}:${s}`:`${o}:${s}`}function R(e){return e===0?"0%":`${e<10?e.toFixed(1):Math.round(e)}%`}function W(e){return e>=1024?`${(e/1024).toFixed(1)} GB`:`${Math.round(e)} MB`}const M=1e3,N=600;function $(e){if(!(e>0))return 1;const t=10**Math.floor(Math.log10(e));for(const n of[1,1.5,2,2.5,3,4,5,6,8,10])if(n*t>=e)return n*t;return 10*t}function w(e,t){if(e.length===0)return-1;let n=0,o=e.length-1;for(;n<o;){const s=n+o>>1;e[s]<t?n=s+1:o=s}return n>0&&t-e[n-1]<=e[n]-t?n-1:n}function S(e,t=N){const n=e.length;if(n<=t)return Array.from({length:n},(l,u)=>u);const o=Math.floor((t-2)/3),s=n/o,x=new Set([0,n-1]);for(let l=0;l<o;l+=1){const u=Math.min(n,Math.floor((l+1)*s));let i=-1,p=-1,f=-1;for(let c=Math.floor(l*s);c<u;c+=1){const h=e[c];if(h==null){f<0&&(f=c);continue}(i<0||h<e[i])&&(i=c),(p<0||h>e[p])&&(p=c)}for(const c of[i,p,f])c>=0&&x.add(c)}return Array.from(x).sort((l,u)=>l-u)}const b=e=>Math.round(e*100)/100;function A(e,t,n,o,s,x,l){const u=[];let i=!1;for(const p of n){const f=e[p];if(f==null){i=!1;continue}const c=t[p]/o*x,h=l-Math.min(f,s)/s*l;u.push(`${i?"L":"M"}${b(c)} ${b(h)}`),i=!0}return u.join(" ")}const B=({times:e,series:t,yMax:n,formatValue:o,formatTime:s,ariaLabel:x,height:l=140})=>{const u=g.useRef(null),[i,p]=g.useState(null),f=Math.max(e.length?e[e.length-1]:0,1),c=g.useMemo(()=>{if(n!==void 0)return n;let r=0;for(const d of t)for(const v of d.values)v!==null&&v>r&&(r=v);return $(r)},[t,n]),h=g.useMemo(()=>t.map(r=>S(r.values)),[t]),y=r=>{var j;const d=(j=u.current)==null?void 0:j.getBoundingClientRect();if(!d||d.width<=0||e.length===0)return;const v=Math.min(1,Math.max(0,(r.clientX-d.left)/d.width));p(w(e,v*f))},m=i!==null?e[i]/f*100:0;return a.jsxs("div",{className:"flex gap-2",children:[a.jsxs("div",{className:"flex w-14 flex-col justify-between text-right text-[10px] tabular-nums text-[var(--text-dim)]",style:{height:l},children:[a.jsx("span",{children:o(c)}),a.jsx("span",{children:o(0)})]}),a.jsxs("div",{ref:u,"data-testid":"line-chart",className:"relative min-w-0 flex-1",style:{height:l},onMouseMove:y,onMouseLeave:()=>p(null),children:[a.jsxs("svg",{role:"img","aria-label":x,viewBox:`0 0 ${M} ${l}`,preserveAspectRatio:"none",className:"absolute inset-0 h-full w-full overflow-visible",children:[[0,.5,1].map(r=>a.jsx("line",{x1:0,x2:M,y1:r*l,y2:r*l,stroke:"var(--border)",strokeWidth:1,vectorEffect:"non-scaling-stroke"},r)),t.map((r,d)=>a.jsx("path",{"data-series":r.key,d:A(r.values,e,h[d],f,c,M,l),fill:"none",stroke:r.color,strokeWidth:1.5,strokeLinejoin:"round",strokeDasharray:r.dashed?"4 3":void 0,vectorEffect:"non-scaling-stroke"},r.key)),i!==null&&a.jsx("line",{x1:m/100*M,x2:m/100*M,y1:0,y2:l,stroke:"var(--text-muted)",strokeWidth:1,vectorEffect:"non-scaling-stroke"})]}),i!==null&&a.jsxs("div",{role:"tooltip",className:"pointer-events-none absolute top-1 z-10 whitespace-nowrap rounded-md border border-[var(--border-strong)] bg-[var(--surface)] px-2 py-1 text-[11px] text-[var(--text)]",style:m>60?{right:`${100-m}%`,marginRight:8}:{left:`${m}%`,marginLeft:8},children:[a.jsx("div",{className:"tabular-nums text-[var(--text-dim)]",children:(s??k)(e[i])}),t.map(r=>{var v;const d=r.values[i];return a.jsxs("div",{className:"flex items-center gap-1.5 tabular-nums",children:[a.jsx("span",{className:"inline-block h-0.5 w-3",style:{background:r.color}}),a.jsx("span",{className:"text-[var(--text-muted)]",children:((v=r.pointLabels)==null?void 0:v[i])??r.label}),a.jsx("span",{className:"ml-auto pl-3",children:d==null?"—":o(d)})]},r.key)})]})]})]})};export{I as C,B as L,W as a,E as b,R as f,$ as n,L as s};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as g,L as X,u as z,a as H,b as K,r as a,d as O,j as e,N as B,I as W,e as Y,C as _,f as G,l as J}from"./index-
|
|
1
|
+
import{c as g,L as X,u as z,a as H,b as K,r as a,d as O,j as e,N as B,I as W,e as Y,C as _,f as G,l as J}from"./index-Dk5J6HB1.js";import{c as Q}from"./utils-CDN07tui.js";import{u as V}from"./use-password-reset-mode-BzoenegM.js";import{A as Z}from"./auth-shell-B7sUDb--.js";import{L as ee}from"./lock-CZsenX3D.js";import{C as te}from"./clock-BDHewWTx.js";import{A as se}from"./arrow-right-DtW_8hm5.js";import"./network-BS6vUc1x.js";/**
|
|
2
2
|
* @license lucide-react v0.555.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
package/lib/public/assets/{maintenance-settings-CSdzQSkY.js → maintenance-settings-CmCJGy7O.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as B,t as k,r as d,X as f,j as e,A as z,C as E,S as v,I as S,Y as R}from"./index-
|
|
1
|
+
import{c as B,t as k,r as d,X as f,j as e,A as z,C as E,S as v,I as S,Y as R}from"./index-Dk5J6HB1.js";/* empty css */import{S as c,A as F}from"./SettingCard-DlmvZJPM.js";import{B as I}from"./button-DugsdUw1.js";import{P as L}from"./page-header-DhXCwpml.js";import{T}from"./trash-2-DO8fv6w4.js";import{C as P}from"./check-ORjyNfnX.js";import{C as _}from"./calendar-grKd08ZH.js";import"./utils-CDN07tui.js";/**
|
|
2
2
|
* @license lucide-react v0.555.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as y,r as h,p as E,X as p,j as e,u as R,T as H,q as L,s as M}from"./index-
|
|
1
|
+
import{c as y,r as h,p as E,X as p,j as e,u as R,T as H,q as L,s as M}from"./index-Dk5J6HB1.js";import{p as T}from"./labels-C21SA4JG.js";import{A as O}from"./arrow-right-DtW_8hm5.js";import{T as B}from"./tablet-SA02BJNQ.js";import{M as P}from"./monitor-BdTNEglU.js";import{A as U}from"./activity-CvuZdQYu.js";import{P as X}from"./page-header-DhXCwpml.js";/**
|
|
2
2
|
* @license lucide-react v0.555.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e}from"./index-
|
|
1
|
+
import{j as e}from"./index-Dk5J6HB1.js";const i=({icon:s,children:t})=>e.jsxs("h1",{className:"flex items-center gap-2 text-xl font-semibold tracking-tight text-[var(--text)]",children:[s&&e.jsx(s,{className:"h-5 w-5 text-[var(--color-accent)] shrink-0","aria-hidden":"true"}),e.jsx("span",{className:"truncate",children:t})]}),l=({icon:s,eyebrow:t,title:x,subtitle:a,action:r})=>e.jsx("header",{className:"px-6 pt-6 pb-4 border-b border-[var(--border)]",children:e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{className:"min-w-0",children:[t&&e.jsx("div",{className:"text-[11px] font-semibold text-[var(--text-dim)] mb-1.5",children:t}),e.jsx(i,{icon:s,children:x}),a&&e.jsx("p",{className:"mt-1.5 text-sm text-[var(--text-muted)] max-w-3xl",children:a})]}),r&&e.jsx("div",{className:"flex items-center gap-2 shrink-0",children:r})]})});export{l as P,i as a};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as S,b as N,r as n,j as e,a1 as E,A as T}from"./index-
|
|
1
|
+
import{c as S,b as N,r as n,j as e,a1 as E,A as T}from"./index-Dk5J6HB1.js";import{P as A}from"./page-header-DhXCwpml.js";import{a as y,f as w}from"./time-D-gjmE1o.js";import{S as C}from"./select-DZoRKNti.js";import{C as $}from"./copy-CTwT4I3D.js";import{T as D}from"./trash-2-DO8fv6w4.js";import"./utils-CDN07tui.js";/**
|
|
2
2
|
* @license lucide-react v0.555.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i as f,r as s,u as w,k as v,j as e,e as g,m as j}from"./index-
|
|
1
|
+
import{i as f,r as s,u as w,k as v,j as e,e as g,m as j}from"./index-Dk5J6HB1.js";import{A as d}from"./auth-shell-B7sUDb--.js";import"./network-BS6vUc1x.js";function k(i){return window.location.hash.replace(/^#/,"")||i||void 0}function S(){const i=f(),[r]=s.useState(()=>k(i.token)),u=w(),[a,o]=s.useState("checking"),[n,x]=s.useState(""),[c,h]=s.useState(""),[m,l]=s.useState(null);s.useEffect(()=>{(window.location.hash||i.token)&&window.history.replaceState(null,"","/xenon/reset-password")},[]),s.useEffect(()=>{if(!r){o("invalid");return}v(r).then(t=>o(t?"ready":"invalid"))},[r]);async function p(t){if(t.preventDefault(),l(null),n!==c){l("Passwords do not match.");return}if(n.length<8){l("Password must be at least 8 characters.");return}o("submitting");try{await j(r,n),o("done"),setTimeout(()=>u("/login",{replace:!0}),1500)}catch(b){l(b.message||"Reset failed"),o("ready")}}return a==="checking"?e.jsx(d,{children:e.jsx("div",{className:"text-sm text-[var(--text-muted)] text-center",children:"Checking link…"})}):a==="invalid"?e.jsx(d,{children:e.jsxs("div",{className:"text-center",children:[e.jsx("h1",{className:"text-xl font-semibold mb-3",children:"This link is invalid or expired"}),e.jsx("p",{className:"text-sm text-[var(--text-muted)] mb-6",children:"Reset links are good for 1 hour and single-use. Request a new one."}),e.jsx(g,{to:"/forgot-password",className:"inline-block h-10 px-4 rounded-md bg-[var(--color-accent)] text-[var(--color-on-accent)] text-sm font-medium leading-10",children:"Request a new link"})]})}):a==="done"?e.jsx(d,{children:e.jsxs("div",{className:"text-center",children:[e.jsx("h1",{className:"text-xl font-semibold mb-3",children:"Password updated"}),e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Redirecting to sign in…"})]})}):e.jsx(d,{children:e.jsxs("form",{onSubmit:p,children:[e.jsx("h1",{className:"text-2xl font-semibold mb-1",children:"Reset your password"}),e.jsx("p",{className:"text-sm text-[var(--text-muted)] mb-6",children:"Choose a new password to sign in with."}),e.jsx("label",{htmlFor:"reset-password",className:"block text-sm font-medium text-[var(--text)] mb-1.5",children:"New password"}),e.jsx("input",{id:"reset-password",type:"password",autoComplete:"new-password",value:n,onChange:t=>x(t.target.value),autoFocus:!0,required:!0,className:"w-full mb-3 h-10 px-3 rounded-lg bg-[var(--bg)] border border-[var(--border-strong)] text-sm text-[var(--text)] outline-none focus:border-[var(--color-accent)] focus:shadow-[0_0_0_3px_var(--accent-subtle)]"}),e.jsx("label",{htmlFor:"reset-confirm",className:"block text-sm font-medium text-[var(--text)] mb-1.5",children:"Confirm new password"}),e.jsx("input",{id:"reset-confirm",type:"password",autoComplete:"new-password",value:c,onChange:t=>h(t.target.value),required:!0,className:"w-full mb-3 h-10 px-3 rounded-lg bg-[var(--bg)] border border-[var(--border-strong)] text-sm text-[var(--text)] outline-none focus:border-[var(--color-accent)] focus:shadow-[0_0_0_3px_var(--accent-subtle)]"}),m&&e.jsx("div",{role:"alert",className:"text-sm text-[var(--red)] mb-3",children:m}),e.jsx("button",{type:"submit",disabled:a==="submitting"||n.length<8,className:"mt-5 w-full h-10 rounded-md bg-[var(--color-accent)] text-[var(--color-on-accent)] font-medium text-sm disabled:opacity-50",children:a==="submitting"?"Updating…":"Update password"})]})})}export{S as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{J as _e,R as Ie,i as Ir,u as Fr,j as ne,K as vr}from"./index-
|
|
1
|
+
import{J as _e,R as Ie,i as Ir,u as Fr,j as ne,K as vr}from"./index-Dk5J6HB1.js";import{h as Or}from"./derive-UnLRp4r3.js";import{A as Lr}from"./arrow-left-DiP5ywBT.js";const $n=["http","https","mailto","tel"];function Dr(e){const n=(e||"").trim(),t=n.charAt(0);if(t==="#"||t==="/")return n;const r=n.indexOf(":");if(r===-1)return n;let i=-1;for(;++i<$n.length;){const l=$n[i];if(r===l.length&&n.slice(0,l.length).toLowerCase()===l)return n}return i=n.indexOf("?"),i!==-1&&r>i||(i=n.indexOf("#"),i!==-1&&r>i)?n:"javascript:void(0)"}/*!
|
|
2
2
|
* Determine if an object is a Buffer
|
|
3
3
|
*
|
|
4
4
|
* @author Feross Aboukhadijeh <https://feross.org>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as c,j as m}from"./index-
|
|
1
|
+
import{r as c,j as m}from"./index-Dk5J6HB1.js";import{c as o}from"./utils-CDN07tui.js";const l=c.forwardRef(({className:s,selectSize:e="md",children:t,...a},r)=>m.jsx("select",{ref:r,className:o("select-base",e==="sm"&&"select-sm",s),...a,children:t}));l.displayName="Select";export{l as S};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{H as e,j as t,N as r}from"./index-
|
|
1
|
+
import{H as e,j as t,N as r}from"./index-Dk5J6HB1.js";import{l as s}from"./view-state-83Z1gFME.js";const i=()=>{const[a]=e();return t.jsx(r,{to:s(a),replace:!0})};export{i as SelectorDetailRedirect,i as default};
|
package/lib/public/assets/{selector-health-page-MSk96Zz7.js → selector-health-page-TpmN61G3.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as ae,p as ne,r as d,j as t,A as fe,v as re,z as ve,w as ge,t as le,X as I,e as be,H as ye,b as je,Q as Ne}from"./index-
|
|
1
|
+
import{c as ae,p as ne,r as d,j as t,A as fe,v as re,z as ve,w as ge,t as le,X as I,e as be,H as ye,b as je,Q as Ne}from"./index-Dk5J6HB1.js";import{P as Se}from"./page-header-DhXCwpml.js";import{a as ke}from"./SegmentedControl-B_TZTrPm.js";import{B as N}from"./button-DugsdUw1.js";import{S as we}from"./stat-tile-DUpyYgl-.js";import{L as ie,n as Ce,C as Ae}from"./line-chart-B3YmVYyr.js";import{E as $e}from"./EmptyState-DcEG-oAi.js";import{I as Me}from"./input-bTXM4l09.js";import{S as H}from"./select-DZoRKNti.js";import{s as _,r as Y,w as Ee,P as Te,b as _e}from"./view-state-83Z1gFME.js";import{p as Le}from"./labels-C21SA4JG.js";import{M as R}from"./Modal-DPwop3Lz.js";import{C as Ie}from"./check-ORjyNfnX.js";import{C as oe}from"./copy-CTwT4I3D.js";import"./utils-CDN07tui.js";import"./index-Z7_BY3pJ.js";/**
|
|
2
2
|
* @license lucide-react v0.555.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
package/lib/public/assets/{session-detail-page-DPyBNef_.js → session-detail-page-C0wYaXqy.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as x,X as S,j as e,e as te,f as Ae,t as H,y as Ee,R as De,p as Le,i as Me,u as Oe}from"./index-
|
|
1
|
+
import{r as x,X as S,j as e,e as te,f as Ae,t as H,y as Ee,R as De,p as Le,i as Me,u as Oe}from"./index-Dk5J6HB1.js";import{C as Fe,d as Pe,p as Ne,e as Ie,r as ye,f as He,j as we,k as X,c as Be,h as re,u as Ue,b as Ve,q as ze}from"./use-builds-data-jS-fcvVf.js";import{A as Je}from"./arrow-left-DiP5ywBT.js";import{C as K}from"./chevron-right-Ei2FxUXq.js";import{s as We}from"./labels-C21SA4JG.js";import{B as Ke,S as ke,u as qe,P as $}from"./index-DncSz5LG.js";import{C as Xe}from"./circle-x-CHxSRLHp.js";import{C as Ge}from"./circle-check-Day0HjW4.js";import{S as F}from"./stat-tile-DUpyYgl-.js";import{h as G,p as Ye,a as se,b as ae}from"./derive-UnLRp4r3.js";import{C as Y}from"./copy-CTwT4I3D.js";import{A as Qe}from"./arrow-up-right-C89FbNU0.js";import{A as Ze}from"./arrow-right-DtW_8hm5.js";import{f as et,a as ne,C as Se,b as tt,s as rt,L as st,n as ie}from"./line-chart-B3YmVYyr.js";import{E as M}from"./EmptyState-DcEG-oAi.js";import{A as at}from"./activity-CvuZdQYu.js";import{V as nt}from"./video-off-FEvy0w5U.js";import{D as _e}from"./download-D3bjz_vr.js";import{T as it,a as ot,b as oe,c as E,d as lt,e as D}from"./Table-BigPWKXi.js";import{M as ct}from"./Modal-DPwop3Lz.js";import{N as dt}from"./network-BS6vUc1x.js";import"./time-D-gjmE1o.js";import"./index-Z7_BY3pJ.js";const xt=4e3,le=t=>!!t&&typeof t=="object"&&typeof t.id=="string"&&typeof t.status=="string";function mt(t){const[r,s]=x.useState(null),[a,n]=x.useState([]),[l,o]=x.useState([]),[i,c]=x.useState([]),[m,b]=x.useState([]),[f,j]=x.useState(!0),[p,g]=x.useState(null),[y,w]=x.useState(!1),[R,A]=x.useState(0),_=x.useRef(null);x.useEffect(()=>{if(!t){s(null),n([]),o([]),c([]),b([]),w(!1),j(!1);return}let v=!0;return _.current!==t&&j(!0),Promise.all([S.getSession(t),S.getSessionLogs(t),S.getDeviceLogs(t),S.getDebugLogs(t),S.getProfilingData(t).catch(()=>[])]).then(([h,C,d,N,L])=>{if(!v)return;const O=le(h);s(O?h:null),O?(_.current=t,w(!1),g(null)):(w(!!h&&typeof h=="object"&&(h.error===!0||typeof h.message=="string")),g(typeof(h==null?void 0:h.message)=="string"?h.message:"Session not found")),n(Array.isArray(C)?C:[]),o(Array.isArray(d)?d:[]),c(Array.isArray(N)?N:[]),b(Array.isArray(L)?L:[])}).catch(h=>{v&&g(h instanceof Error?h.message:String(h))}).finally(()=>{v&&j(!1)}),()=>{v=!1}},[t,R]);const u=(r==null?void 0:r.status)==="running";return x.useEffect(()=>{if(!t||!u)return;let v=!0;const h=setInterval(async()=>{try{const[C,d]=await Promise.all([S.getSession(t),S.getSessionLogs(t)]);if(!v||!le(C))return;s(C),Array.isArray(d)&&n(d),C.status!=="running"&&A(N=>N+1)}catch{}},xt);return()=>{v=!1,clearInterval(h)}},[t,u]),{session:r,sessionLogs:a,deviceLogs:l,debugLogs:i,profiling:m,loading:f,error:p,notFound:y,refresh:()=>A(v=>v+1)}}const ce=({buildId:t,buildName:r,sessionId:s})=>e.jsxs("nav",{"aria-label":"Breadcrumb",className:"flex items-center gap-2 px-4 py-2 border-b border-[var(--border)] bg-[var(--surface)]",children:[e.jsxs(te,{to:"/builds",className:"inline-flex items-center gap-1.5 text-xs text-[var(--text-muted)] hover:text-[var(--text)] transition-colors",children:[e.jsx(Je,{className:"h-3.5 w-3.5"}),"Sessions"]}),e.jsx(K,{className:"h-3 w-3 text-[var(--text-dim)]"}),r&&e.jsxs(e.Fragment,{children:[e.jsx(te,{to:`/builds/${t}`,className:"text-[11px] text-[var(--color-accent)] hover:underline",children:r}),e.jsx(K,{className:"h-3 w-3 text-[var(--text-dim)]"})]}),e.jsxs("span",{className:"font-mono text-[11px] text-[var(--text-muted)]",title:s,children:["#",s.slice(0,8)]})]}),ut={passed:{label:"Passed",Icon:Ge,cls:"text-[var(--color-success)]"},failed:{label:"Failed",Icon:Xe,cls:"text-[var(--color-danger)]"},running:{label:"Running",Icon:Ae,cls:"text-[var(--color-warning)]"},other:{Icon:Fe,cls:"text-[var(--text-dim)]"}};function Q(t){const r=Be(t),s=ut[r];return{bucket:r,...s,label:s.label??We(t||"unknown")}}const ht=({session:t})=>{var l,o;const r=Q(t.status),s=Pe(t),a=[Ne(t),t.device_version].filter(i=>i&&i!=="—").join(" "),n=[[Ie(t),a].filter(Boolean).join(" · "),ye(t.ranOn),(l=t.owner)==null?void 0:l.name,He(t.startTime),we(X(t))].filter(i=>i&&i!=="—");return e.jsxs("header",{className:"flex items-start justify-between gap-4 px-4 py-4 border-b border-[var(--border)] bg-[var(--surface)]",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsxs("span",{className:`inline-flex items-center gap-1.5 text-xs font-medium shrink-0 ${r.cls}`,children:[e.jsx(r.Icon,{className:`h-3.5 w-3.5 ${r.bucket==="running"?"animate-spin":""}`,"aria-hidden":"true"}),r.label]}),e.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
2
|
Unnamed session. Set xe:options.name in its capabilities to name it.`,children:s.text})]}),e.jsx("p",{"data-testid":"outcome-meta",className:"mt-1 text-xs text-[var(--text-muted)] tabular-nums truncate",title:((o=t.owner)==null?void 0:o.email)??void 0,children:n.join(" · ")})]}),e.jsx("div",{className:"shrink-0",children:e.jsx(Ke,{sessionId:t.id,mode:"full",variant:"inline"})})]})},B=t=>t.command_name||t.title||"command",q=t=>{const r=t.createdAt?Date.parse(t.createdAt):NaN;return Number.isFinite(r)?r:0},Z=t=>[...t].sort((r,s)=>q(r)-q(s));function U(t){return t.is_success===!1||t.is_error===!0}function Ce(t){const r=[];let s=null;for(const a of Z(t))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:B(a),ms:a.duration});return{total:t.length,failed:t.filter(U).length,healed:t.filter(a=>a.is_healed).length,healedTiers:r,slowest:s,screenshots:t.filter(a=>a.screenshot).length}}const de=(t,r)=>r?t?`${t}=${r}`:r:null;function ft(t){return Z(t.filter(r=>r.is_healed)).map(r=>({id:r.id,command:B(r),original:de(r.original_strategy,r.original_selector),healed:de(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 pt(t){var r;if(!t.response)return null;try{const s=(r=JSON.parse(t.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 vt(t){if(t.length===0)return{spanMs:0,bars:[]};const r=t.map(n=>{const l=q(n),o=typeof n.duration=="number"&&n.duration>0?n.duration:0;return{l:n,start:l-o,end:l,durationMs:o}}),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:o})=>({id:n.id,label:B(n),startMs:l-s,durationMs:o,failed:U(n),healed:!!n.is_healed}))}}function bt(t){return Z(t.filter(r=>r.screenshot)).map(r=>({id:r.id,path:r.screenshot,command:B(r),at:r.createdAt,failed:U(r)}))}function I(t){if(t==null||!Number.isFinite(t)||t<0)return"—";if(t<1e3)return`${Math.round(t)}ms`;if(t<6e4)return`${(t/1e3).toFixed(1)}s`;const r=Math.round(t/1e3);return`${Math.floor(r/60)}m ${r%60}s`}const gt=({session:t,commands:r})=>{var o,i;const s=Q(t.status),a=Ce(r),n=we(X(t)),l=s.bucket==="failed"?G(t.failure_category)||"No category recorded":s.bucket==="running"?`Running for ${n}`:`Ran ${n}`;return e.jsxs("div",{className:"grid grid-cols-4 gap-3",children:[e.jsx(F,{label:"Result",value:s.label,valueCls:s.cls,note:l,noteTone:s.bucket==="failed"?"bad":"neutral"}),e.jsx(F,{label:"Commands",value:a.total,note:a.failed>0?`${a.failed} failed`:"None failed",noteTone:a.failed>0?"bad":"neutral"}),e.jsx(F,{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"}),e.jsx(F,{label:"Slowest command",value:I((o=a.slowest)==null?void 0:o.ms),note:((i=a.slowest)==null?void 0:i.name)??"No timings recorded"})]})};function jt(t){const r=t.filter(U),s=r[r.length-1];return s?{command:s.command_name||s.title||"command",message:pt(s)}:null}function Nt(t,r,s,a,n){var m;const l=G(t.failure_category),o=((m=t.device_name)==null?void 0:m.trim())||"Unknown Device",i=t.device_version?` ${t.device_version}`:"",c=n?`${n.command}${n.message?`: ${n.message}`:""}`:"(no failed command recorded)";return[`**Session:** ${t.id}`,`**Build:** ${r} (#${s})`,`**Device:** ${o} (${t.node_id||"unknown-node"}) · ${t.device_platform||"unknown"}${i}`,`**Duration:** ${a}`,`**Failure category:** ${l||"(uncategorized)"}`,`**Failure reason:** ${t.failure_reason||"(not set)"}`,`**First failed command:** ${c}`,...t.ai_analysis?[`**AI analysis:** ${t.ai_analysis}`]:[]].join(`
|
|
3
3
|
`)}function yt({text:t}){return e.jsx(e.Fragment,{children:t.split(/(\*\*[^*]+\*\*|`[^`]+`)/g).map((r,s)=>/^\*\*[^*]+\*\*$/.test(r)?e.jsx("strong",{children:r.slice(2,-2)},s):/^`[^`]+`$/.test(r)?e.jsx("code",{className:"font-mono text-[11px] px-1 rounded bg-[var(--surface-2)]",children:r.slice(1,-1)},s):e.jsx(De.Fragment,{children:r},s))})}const P=({children:t})=>e.jsx("div",{className:"text-[11px] font-semibold text-[var(--text-dim)] mb-1",children:t}),wt=({session:t,buildName:r,buildId:s,commands:a,durationText:n})=>{var y;const{toast:l}=H(),[o,i]=x.useState(!1),c=G(t.failure_category),m=jt(a),b=Ye(t.failure_reason),f=(y=t.ai_analysis)==null?void 0:y.trim(),j=!!f&&f.length>480,p=async()=>{const w=Nt(t,r,s,n,m);try{await navigator.clipboard.writeText(w),l("Failure report copied","success")}catch{l("Clipboard unavailable","error")}},g=t.failure_category?`/xenon/runbooks/${encodeURIComponent(t.failure_category.toLowerCase())}`:void 0;return e.jsxs("section",{className:"rounded-lg border border-[var(--red)]/30 bg-[var(--surface)] overflow-hidden",children:[e.jsxs("header",{className:"flex items-center justify-between gap-4 px-4 py-3 border-b border-[var(--border)] bg-[var(--red)]/5",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Ee,{className:"h-4 w-4 text-[var(--red)]","aria-hidden":"true"}),e.jsx("h2",{className:"text-sm font-semibold text-[var(--red)]",children:"Why it failed"}),c&&e.jsx("span",{className:"text-[11px] text-[var(--text-dim)] ml-2",children:c})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("button",{type:"button",onClick:p,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:[e.jsx(Y,{className:"h-3 w-3"}),"Copy"]}),e.jsxs("a",{...g?{href:g,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 ${g?"":"opacity-50 cursor-not-allowed"}`,title:g?"Open runbook in new tab":"No runbook — failure category not set",children:[e.jsx(Qe,{className:"h-3 w-3"}),"Open runbook"]})]})]}),e.jsxs("div",{className:"px-4 py-4 space-y-4",children:[e.jsxs("div",{children:[e.jsx(P,{children:"Reason"}),e.jsx("div",{className:"text-sm text-[var(--text)] break-words",children:t.failure_reason||e.jsx("span",{className:"text-[var(--text-dim)]",children:"(not set)"})})]}),e.jsxs("div",{children:[e.jsx(P,{children:"First failed command"}),m?e.jsxs("div",{className:"flex items-baseline gap-2 min-w-0 text-sm",children:[e.jsx("span",{className:"font-mono text-xs text-[var(--color-danger)] shrink-0",children:m.command}),m.message&&e.jsx("span",{className:"text-[var(--text-muted)] break-words min-w-0",children:m.message})]}):e.jsx("span",{className:"text-sm text-[var(--text-dim)]",children:"No failed command recorded."})]}),f&&e.jsxs("div",{"data-testid":"ai-analysis",children:[e.jsx(P,{children:e.jsxs("span",{className:"inline-flex items-center gap-1.5",children:[e.jsx(ke,{className:"h-3 w-3","aria-hidden":"true"}),"AI analysis"]})}),e.jsx("div",{className:`text-sm text-[var(--text)] whitespace-pre-wrap break-words ${j&&!o?"line-clamp-6":""}`,children:e.jsx(yt,{text:f})}),j&&e.jsx("button",{type:"button",onClick:()=>i(w=>!w),"aria-expanded":o,className:"mt-1 text-xs text-[var(--color-accent)] hover:underline",children:o?"Show less":"Show all"})]}),b.length>0&&e.jsxs("div",{children:[e.jsx(P,{children:"Stack trace"}),e.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:b.join(`
|
|
4
4
|
`)})]})]})]})};function kt(t){return t>=1e3?`${(t/1e3).toFixed(1).replace(/\.0$/,"")}k`:String(t)}function V(t){const r=t;return typeof r.message=="string"&&!r.command_name&&!r.title}const St=/^\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d+\s+\d+\s+\d+\s+([VDIWEF])\s/,_t=/^([VDIWEF])\/[^(:]*[(:]/,Ct=/<(Error|Fault|Warning)>/,Rt=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+\s+(Df|Db|E|F|I|A)\s/;function Re(t){var n,l,o,i;const r=((n=St.exec(t))==null?void 0:n[1])??((l=_t.exec(t))==null?void 0:l[1]);if(r)return r==="E"||r==="F"?"error":r==="W"?"warn":null;const s=(o=Ct.exec(t))==null?void 0:o[1];if(s)return s==="Warning"?"warn":"error";const a=(i=Rt.exec(t))==null?void 0:i[1];return a==="E"||a==="F"?"error":null}function z(t){const r=t.timestamp;if(!r){const o=t.createdAt;return o?z({timestamp:o}):"--:--:--"}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 Tt(t){if(V(t)){const s=Re(t.message);return s==="error"?{label:"error",tone:"red"}:s==="warn"?{label:"warn",tone:"amber"}:{label:"",tone:"neutral"}}if(t.is_healed===!0)return{label:String(t.command_name||"healed"),tone:"amber"};if(t.is_success===!1||t.is_error===!0)return{label:String(t.command_name||t.title||"error"),tone:"red"};if(t.is_success===!0)return{label:String(t.command_name||t.title||"ok"),tone:"green"};const r=t.title||t.event||t.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 $t(t){if(t==null)return"";if(typeof t=="string")try{return JSON.stringify(JSON.parse(t),null,2)}catch{return t}try{return JSON.stringify(t,null,2)}catch{return String(t)}}function At(t){if(!t||typeof t!="string")return[{text:String(t??""),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(t))!==null;)n.index>a&&r.push({text:t.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<t.length&&r.push({text:t.slice(a),kind:"plain"}),r}function Et(t){if(V(t))return t.message.split(/\r?\n/,1)[0].trimEnd();const r=t,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 xe(t){const r=t.subtitle;return typeof r=="string"&&r.trim()?r.trim():null}function Dt(t,r){return r?t.filter(s=>s.is_success===!1||s.is_error===!0||V(s)&&Re(s.message)==="error"):t}const Lt=({commands:t})=>{const r=ft(t);return r.length===0?null:e.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[e.jsxs("header",{className:"flex items-center gap-2 px-4 py-3 border-b border-[var(--border)]",children:[e.jsx(ke,{className:"h-4 w-4 text-[var(--color-warning)]","aria-hidden":"true"}),e.jsx("h2",{className:"text-sm font-semibold text-[var(--text)]",children:"Self-healing"}),e.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."]})]}),e.jsxs("table",{className:"w-full table-fixed text-left",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-[var(--border)] text-[11px] text-[var(--text-dim)]",children:[e.jsx("th",{className:"px-4 py-2 font-medium w-[88px]",children:"Time"}),e.jsx("th",{className:"px-3 py-2 font-medium w-[128px]",children:"Command"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Asked for"}),e.jsx("th",{className:"w-[28px]",children:e.jsx("span",{className:"sr-only",children:"healed to"})}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Healed to"}),e.jsx("th",{className:"px-3 py-2 font-medium w-[112px]",children:"Tier"}),e.jsx("th",{className:"px-4 py-2 font-medium w-[96px] text-right",children:"Confidence"})]})}),e.jsx("tbody",{children:r.map((s,a)=>e.jsxs("tr",{className:"border-b border-[var(--border)] last:border-b-0 align-top",children:[e.jsx("td",{className:"px-4 py-2 text-xs text-[var(--text-muted)] tabular-nums",children:z({timestamp:s.at??null})}),e.jsx("td",{className:"px-3 py-2 text-xs text-[var(--text)] truncate",children:s.command}),e.jsx("td",{className:"px-3 py-2 font-mono text-[11px] text-[var(--text-muted)] break-all",children:s.original??"—"}),e.jsx("td",{className:"py-2 text-[var(--text-dim)]",children:e.jsx(Ze,{className:"h-3.5 w-3.5","aria-hidden":"true"})}),e.jsx("td",{className:"px-3 py-2 font-mono text-[11px] text-[var(--text)] break-all",children:s.healed??"—"}),e.jsx("td",{className:"px-3 py-2 text-xs text-[var(--text)]",children:s.tier??"—"}),e.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))})]})]})},Mt=1e4,me="var(--color-info)",ue="var(--color-accent)",Ot="var(--border-strong)";function he(t){return t>=1024?ie(t/1024)*1024:ie(t)}const fe=t=>{let r=0;for(const s of t)for(const a of s.values)a!==null&&a>r&&(r=a);return r},W=({title:t,series:r,times:s,yMax:a,format:n,ariaLabel:l})=>e.jsxs("section",{"aria-label":t,children:[e.jsxs("div",{className:"mb-2 flex flex-wrap items-baseline gap-x-4 gap-y-1",children:[e.jsx("h3",{className:"text-xs font-semibold text-[var(--text)]",children:t}),r.map(o=>{const i=rt(o.values);return e.jsxs("span",{className:"flex min-w-0 items-center gap-1.5 text-[11px] tabular-nums text-[var(--text-muted)]",children:[e.jsx("span",{className:"inline-block h-0.5 w-3 shrink-0",style:{background:o.color}}),e.jsx("span",{className:"max-w-[16rem] truncate",title:o.label,children:o.label}),i&&(o.dashed?e.jsx("span",{children:n(i.peak)}):e.jsxs("span",{children:["peak ",n(i.peak)," · avg ",n(i.average)]}))]},o.key)})]}),e.jsx(st,{times:s,series:r,yMax:a,formatValue:n,ariaLabel:l})]}),Ft=({sessionId:t,running:r,hasTrace:s})=>{const[a,n]=x.useState(null),[l,o]=x.useState(!1);x.useEffect(()=>{let u=!0;const v=()=>S.getSessionMetrics(t).then(C=>{u&&(n(tt(C)),o(!0))}).catch(()=>{u&&o(!0)});if(v(),!r)return()=>{u=!1};const h=setInterval(()=>void v(),Mt);return()=>{u=!1,clearInterval(h)}},[t,r]);const i=(a==null?void 0:a.samples)??[],c=x.useMemo(()=>i.map(u=>{var v;return u.t-(((v=i[0])==null?void 0:v.t)??0)}),[i]),m=new Set(i.map(u=>u.app).filter(u=>!!u)),b=m.size>1?"Foreground app":Array.from(m)[0]??(a==null?void 0:a.appId)??"App",f=i.map(u=>u.app??null),j=u=>u>0&&!!f[u]&&!!f[u-1]&&f[u]!==f[u-1],p=[],g=[],y=[];a&&(a.series.deviceCpu&&p.push({key:"deviceCpu",label:"Device",color:me,values:i.map(u=>u.deviceCpu)}),a.series.appCpu&&p.push({key:"appCpu",label:b,color:ue,values:i.map((u,v)=>j(v)?null:u.appCpu),pointLabels:f}),a.series.appMem&&g.push({key:"appMem",label:b,color:ue,values:i.map((u,v)=>j(v)?null:u.appMemMb),pointLabels:f}),a.series.deviceMem&&(y.push({key:"deviceMem",label:"Device used",color:me,values:i.map(u=>u.deviceMemMb)}),y.push({key:"deviceTotal",label:"Device total",color:Ot,values:i.map(u=>u.deviceMemTotalMb),dashed:!0})));const w=r&&(a==null?void 0:a.recording)==="off",R=(a==null?void 0:a.recording)==="stopped",A="Recording stopped: the device stopped responding.";let _;return l?w?_=e.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?_=e.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?_=e.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:R?A:r?"Collecting… The first figures appear within 10 seconds.":"Too few figures were recorded to draw a chart."}):_=e.jsxs("div",{className:"space-y-4",children:[R&&e.jsxs("p",{className:"text-xs text-[var(--color-warning)]",children:[A," The data ends there."]}),(a==null?void 0:a.platform)==="ios"&&e.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.":""]}),p.length>0&&e.jsx(W,{title:"CPU",series:p,times:c,yMax:100,format:et,ariaLabel:"CPU over the session"}),g.length>0&&e.jsx(W,{title:"App memory",series:g,times:c,yMax:he(fe(g)),format:ne,ariaLabel:"App memory over the session"}),y.length>0&&e.jsx(W,{title:"Device memory",series:y,times:c,yMax:he(fe(y)),format:ne,ariaLabel:"Device memory over the session"})]}):_=e.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:"Loading performance…"}),e.jsx(Se,{header:e.jsxs("span",{className:"flex items-center gap-2",children:[e.jsx(at,{size:14})," Performance"]}),children:_})};function Pt(t){const r=t.status==="running";return t.video_recording_enabled===!1?r?"Recording is off for this session.":"Recording was off for this session.":r?"Any video appears here after the session ends.":"No video was saved for this session."}const It=({session:t})=>{const r=t.status==="running",s=!!t.video_recording;return e.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[e.jsxs("header",{className:"px-3 py-2 border-b border-[var(--border)] flex items-center justify-between",children:[e.jsx("span",{className:"text-[11px] font-semibold text-[var(--text-dim)]",children:"Recording"}),r&&e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-[11px] text-[var(--red)]",children:[e.jsx("span",{className:"h-1.5 w-1.5 rounded-full bg-[var(--red)] pulse-dot"}),"Live"]})]}),e.jsx("div",{className:"aspect-video bg-[var(--bg)] flex items-center justify-center",children:r&&t.has_live_video?e.jsx("img",{src:S.getSessionLiveVideoUrl(t.id),alt:`Live view of session ${t.id}`,className:"w-full h-full object-contain"}):s?e.jsx("video",{controls:!0,src:S.getAssetUrl(t.video_recording),className:"w-full h-full object-contain"}):e.jsxs("div",{className:"flex flex-col items-center text-center px-6 py-8 gap-2",children:[e.jsx("div",{className:"h-10 w-10 rounded-full bg-[var(--surface-2)] border border-[var(--border)] flex items-center justify-center",children:e.jsx(nt,{className:"h-5 w-5 text-[var(--text-dim)]"})}),e.jsx("div",{className:"text-sm text-[var(--text)] font-medium",children:"No video available"}),e.jsx("div",{className:"text-xs text-[var(--text-dim)] max-w-xs",children:Pt(t)})]})})]})},T=({label:t,children:r,title:s,mono:a})=>e.jsxs("div",{className:"flex items-baseline justify-between gap-3 px-3 py-2",children:[e.jsx("dt",{className:"text-xs text-[var(--text-muted)] shrink-0",children:t}),e.jsx("dd",{className:`text-xs text-[var(--text)] text-right min-w-0 break-all ${a?"font-mono text-[11px]":""}`,title:s,children:r})]}),Ht=({session:t,buildName:r})=>{var o,i;const{toast:s}=H(),a=ye(t.ranOn),n=t.performance_trace?S.getAssetUrl(t.performance_trace):"",l=async()=>{try{await navigator.clipboard.writeText(t.id),s("Session ID copied","success")}catch{s("Clipboard unavailable","error")}};return e.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[e.jsx("header",{className:"px-3 py-2 border-b border-[var(--border)]",children:e.jsx("h2",{className:"text-[11px] font-semibold text-[var(--text-dim)]",children:"Details"})}),e.jsxs("dl",{className:"divide-y divide-[var(--border)]",children:[e.jsx(T,{label:"Session ID",mono:!0,children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx("span",{children:t.id}),e.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:e.jsx(Y,{className:"h-3 w-3"})})]})}),r&&e.jsx(T,{label:"Build",children:r}),e.jsx(T,{label:"Device UDID",mono:!0,children:t.device_udid||"—"}),e.jsx(T,{label:"Platform",children:[Ne(t),t.device_version].filter(c=>c&&c!=="—").join(" ")||"—"}),e.jsx(T,{label:"Ran on",title:t.node_id?`Node ${t.node_id}`:void 0,children:a??"Not known"}),e.jsx(T,{label:"Run by",title:(o=t.owner)==null?void 0:o.email,children:((i=t.owner)==null?void 0:i.name)??"Not recorded"}),e.jsx(T,{label:"Started",children:re(t.startTime)}),e.jsx(T,{label:"Ended",children:t.endTime?re(t.endTime):"—"}),t.tags&&e.jsx(T,{label:"Tags",children:t.tags}),n&&e.jsx(T,{label:"iOS trace",children:e.jsxs("a",{href:n,className:"inline-flex items-center gap-1 text-[var(--color-accent)] hover:underline",children:[e.jsx(_e,{className:"h-3 w-3","aria-hidden":"true"}),"Performance trace"]})})]})]})},pe={desired:"Requested",session:"Actual"},Bt=({session:t})=>{const[r,s]=x.useState("desired"),a=se(t.desired_capabilities),n=se(t.session_capabilities),o=Object.entries(r==="desired"?a:n);return e.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[e.jsx("header",{className:"flex items-center border-b border-[var(--border)]",children:["desired","session"].map(i=>e.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:pe[i]},i))}),o.length===0?e.jsxs("div",{className:"px-3 py-6 text-center text-xs text-[var(--text-dim)]",children:["No ",pe[r].toLowerCase()," capabilities recorded for this session."]}):e.jsx("dl",{className:"divide-y divide-[var(--border)]",children:o.map(([i,c])=>e.jsxs("div",{className:"grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] items-baseline gap-3 px-3 py-2",children:[e.jsx("dt",{className:"text-xs text-[var(--text-muted)] break-all",children:i}),e.jsx("dd",{className:"font-mono text-xs text-[var(--text)] truncate text-right min-w-0",title:ae(c,500),children:ae(c)})]},i))})]})},Ut=({tabs:t,active:r,onChange:s,errorsOnly:a,onErrorsOnlyChange:n,showErrorsOnly:l=!0})=>{const o=t.filter(i=>!(i.hideWhenEmpty&&i.count===0));return e.jsxs("div",{className:"flex items-center justify-between gap-4 px-3 border-b border-[var(--border)] bg-[var(--surface)]",children:[e.jsx("div",{className:"flex items-center gap-1 overflow-x-auto",children:o.map(i=>{const c=i.key===r;return e.jsxs("button",{type:"button",onClick:()=>s(i.key),"aria-pressed":c,"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 ${c?"text-[var(--text)]":i.count===0?"text-[var(--text-dim)] hover:text-[var(--text-muted)]":"text-[var(--text-muted)] hover:text-[var(--text)]"}`,children:[e.jsx("span",{children:i.label}),i.count>0&&e.jsx("span",{className:`text-[10px] tabular-nums ${c?"text-[var(--text-muted)]":"text-[var(--text-dim)]"}`,children:kt(i.count)}),c&&e.jsx("span",{className:"absolute left-2 right-2 bottom-0 h-[2px] bg-[var(--color-accent)] rounded-t"})]},i.key)})}),l&&e.jsxs("label",{className:"inline-flex items-center gap-2 text-[11px] text-[var(--text-muted)] cursor-pointer select-none whitespace-nowrap",children:[e.jsx("input",{type:"checkbox",checked:a,onChange:i=>n(i.target.checked),"aria-label":"Show only error rows"}),"Errors only"]})]})},Vt={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)]"},zt=({value:t,maxHeightPx:r})=>{const s=$t(t);if(!s)return e.jsx("span",{className:"text-[var(--text-dim)] text-xs",children:"(empty)"});let a;try{a=At(s)}catch{a=[{text:s,kind:"plain"}]}return e.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)=>e.jsx("span",{className:Vt[n.kind],children:n.text},l))})},Jt={green:"bg-[var(--color-success)]",red:"bg-[var(--red)]",amber:"bg-[var(--amber)]",blue:"bg-[var(--blue)]",neutral:"bg-[var(--text-dim)]"},Wt={green:"text-[var(--color-success)]",red:"text-[var(--red)]",amber:"text-[var(--amber)]",blue:"text-[var(--blue)]",neutral:"text-[var(--text-muted)]"},Te=({log:t})=>{const[r,s]=x.useState(!1),{toast:a}=H(),n=z(t),l=Tt(t),o=V(t),i=o?String(t.message):"",c=async m=>{m.stopPropagation();try{const b=o?i:t.body??t,f=typeof b=="string"?b:JSON.stringify(b,null,2);await navigator.clipboard.writeText(f),a("Log entry copied","success")}catch{a("Clipboard unavailable","error")}};return e.jsxs("div",{className:"border-b border-[var(--border)] last:border-b-0",children:[e.jsxs("button",{type:"button",onClick:()=>s(m=>!m),"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:[e.jsx(K,{className:`h-3 w-3 text-[var(--text-dim)] transition-transform shrink-0 ${r?"rotate-90":""}`}),e.jsx("span",{className:"font-mono text-[11px] text-[var(--text-muted)] w-[64px] shrink-0",children:n}),e.jsx("span",{className:`h-1.5 w-1.5 rounded-full shrink-0 ${Jt[l.tone]}`}),l.label&&e.jsx("span",{className:`font-mono text-[11px] font-medium shrink-0 ${Wt[l.tone]}`,children:l.label}),e.jsxs("span",{className:"min-w-0 flex-1 flex items-baseline gap-2 truncate",children:[e.jsx("span",{className:o?"font-mono text-[11px] text-[var(--text)] truncate min-w-0":"text-[12px] text-[var(--text)] shrink-0",children:Et(t)}),xe(t)&&e.jsx("span",{className:"font-mono text-[11px] text-[var(--text-dim)] truncate min-w-0",children:xe(t)})]}),typeof t.duration=="number"&&e.jsx("span",{className:"text-[11px] tabular-nums text-[var(--text-muted)] shrink-0",children:I(t.duration)}),e.jsx("span",{role:"button",tabIndex:0,onClick:c,onKeyDown:m=>{(m.key==="Enter"||m.key===" ")&&c(m)},"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:e.jsx(Y,{className:"h-3 w-3"})})]}),r&&e.jsx("div",{className:"px-3 pb-3 pl-9",children:o?e.jsx("pre",{className:"font-mono text-[11px] leading-relaxed text-[var(--text)] bg-[var(--bg)] border border-[var(--border)] rounded-md p-3 max-h-[400px] overflow-auto whitespace-pre-wrap break-words",children:i}):e.jsx(zt,{value:t.body??t,maxHeightPx:400})})]})},Kt=(t,r)=>t?"bg-[var(--color-danger)]":r?"bg-[var(--color-warning)]":"bg-[var(--color-success)]",qt=({commands:t})=>{const{spanMs:r,bars:s}=vt(t);return s.length===0?e.jsx("div",{className:"px-4 py-10 text-center text-xs text-[var(--text-dim)]",children:"No commands recorded for this session."}):e.jsxs("div",{className:"px-3 py-3",children:[e.jsxs("div",{className:"flex items-center justify-between pb-2 text-[11px] text-[var(--text-dim)] tabular-nums",children:[e.jsxs("span",{children:[s.length," command",s.length===1?"":"s"," over ",I(r)]}),e.jsxs("span",{className:"inline-flex items-center gap-3",children:[e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx("span",{className:"h-2 w-2 rounded-sm bg-[var(--color-danger)]"})," Failed"]}),e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx("span",{className:"h-2 w-2 rounded-sm bg-[var(--color-warning)]"})," Healed"]})]})]}),e.jsx("ol",{className:"space-y-1",children:s.map((a,n)=>e.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:[e.jsx("span",{className:"truncate text-[11px] font-mono text-[var(--text-muted)]",title:a.label,children:a.label}),e.jsx("span",{className:"relative h-3 rounded-sm bg-[rgb(var(--rgb-fg)/0.05)]",children:e.jsx("span",{className:`absolute inset-y-0 rounded-sm ${Kt(a.failed,a.healed)}`,style:{left:`${a.startMs/r*100}%`,width:`max(2px, ${a.durationMs/r*100}%)`}})}),e.jsx("span",{className:"text-right text-[11px] tabular-nums text-[var(--text-muted)]",children:a.durationMs>0?I(a.durationMs):"—"})]},a.id??n))})]})},Xt=({commands:t})=>{const r=bt(t);return r.length===0?e.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"," ",e.jsx("code",{className:"font-mono",children:"xe:options.screenshotOnFailure"})," is false; set"," ",e.jsx("code",{className:"font-mono",children:"xe:options.screenshotOnEveryCommand"})," to keep one for every command."]}):e.jsx("ul",{className:"grid grid-cols-[repeat(auto-fill,minmax(140px,1fr))] gap-3 p-3",children:r.map((s,a)=>{const n=S.getAssetUrl(s.path),l=z({timestamp:s.at??null});return e.jsx("li",{children:e.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:[e.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)]"}),e.jsxs("span",{className:"flex items-center justify-between gap-2 px-2 py-1 text-[11px]",children:[e.jsx("span",{className:`truncate font-mono ${s.failed?"text-[var(--color-danger)]":"text-[var(--text)]"}`,children:s.command}),e.jsx("span",{className:"tabular-nums text-[var(--text-dim)] shrink-0",children:l})]})]})},s.id??a)})})},ve=["commands","device","debug","profiling"],Gt=500,Yt=33,Qt=({rows:t,scroller:r})=>{const s=qe({count:t.length,getScrollElement:()=>r.current,estimateSize:()=>Yt,getItemKey:a=>t[a].id??a,overscan:20});return e.jsx("div",{className:"relative",style:{height:s.getTotalSize()},children:s.getVirtualItems().map(a=>e.jsx("div",{"data-index":a.index,ref:s.measureElement,className:`absolute left-0 top-0 w-full${a.index<t.length-1?" border-b border-[var(--border)]":""}`,style:{transform:`translateY(${a.start}px)`},children:e.jsx(Te,{log:t[a.index]})},String(a.key)))})},Zt=({sessionLogs:t,deviceLogs:r,debugLogs:s,profiling:a})=>{const[n,l]=x.useState("commands"),[o,i]=x.useState(!1),c=x.useRef(null),m=t,b=x.useMemo(()=>Ce(m).screenshots,[m]),f=x.useMemo(()=>[{key:"commands",label:"Commands",count:t.length},{key:"timeline",label:"Timeline",count:t.length},{key:"screenshots",label:"Screenshots",count:b},{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}],[t.length,b,r.length,s.length,a.length]),j=x.useMemo(()=>{let p=[];return n==="commands"?p=t:n==="device"?p=r:n==="debug"?p=s:n==="profiling"&&(p=a),Dt(p,o)},[n,t,r,s,a,o]);return e.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] flex flex-col min-h-[280px] max-h-[640px]",children:[e.jsx(Ut,{tabs:f,active:n,onChange:l,errorsOnly:o,onErrorsOnlyChange:i,showErrorsOnly:ve.includes(n)}),e.jsxs("div",{ref:c,className:"flex-1 overflow-y-auto","data-log-scroller":"",children:[n==="timeline"&&e.jsx(qt,{commands:m}),n==="screenshots"&&e.jsx(Xt,{commands:m}),ve.includes(n)&&(j.length===0?e.jsx("div",{className:"px-4 py-10 text-center text-xs text-[var(--text-dim)]",children:o?"No error rows in this tab.":"No log entries in this tab."}):j.length>=Gt?e.jsx(Qt,{rows:j,scroller:c},n):j.map((p,g)=>e.jsx(Te,{log:p},p.id??g)))]})]})};function $e(t){if(!t)return"failed";switch(t){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 ee="/xenon/api/interceptor";async function be(t){const r=await fetch(`${ee}/sessions/${encodeURIComponent(t)}/requests`);if(r.status===403)return{active:!1,forbidden:!0};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 er(t,r){const s=await fetch(`${ee}/sessions/${encodeURIComponent(t)}/requests/${encodeURIComponent(r)}`);return s.ok?s.json():null}function tr(t){return`${ee}/sessions/${encodeURIComponent(t)}/har`}function rr(t){return t>=200&&t<300?"ready":t>=300&&t<400?"neutral":t>=400&&t<500?"busy":t>=500?"error":"neutral"}const ge=({headers:t})=>{const r=Object.entries(t||{});return r.length===0?e.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:"No headers"}):e.jsx("div",{className:"space-y-0.5 font-mono text-xs",children:r.map(([s,a])=>e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("span",{className:"text-[var(--text-dim)] shrink-0",children:[s,":"]}),e.jsx("span",{className:"break-all",children:a})]},s))})},je=({body:t})=>{if(t==null||t==="")return e.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:"No body"});let r=t;try{r=JSON.stringify(JSON.parse(t),null,2)}catch{}return e.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})},sr=({request:t,onClose:r})=>{if(!t)return null;const s=e.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[e.jsx($,{tone:"accent",children:t.method}),t.failed?e.jsx($,{tone:"error",children:$e(t.failureKind)}):e.jsx($,{tone:rr(t.resStatus),children:t.resStatus||"—"}),e.jsx("span",{className:"font-mono text-xs truncate",title:t.url,children:t.url})]});return e.jsx(ct,{open:!!t,title:s,onClose:r,width:720,children:e.jsxs("div",{className:"space-y-4 text-xs",children:[t.failed&&e.jsxs("div",{className:"border border-[var(--red)] rounded p-2 bg-[rgb(var(--rgb-red-600)/0.08)]",children:[e.jsx("div",{className:"text-[var(--red)] text-[11px] mb-1",children:"Failure"}),e.jsx("div",{className:"text-xs",children:t.failureReason||"Request failed"}),t.failureKind&&e.jsx("div",{className:"font-mono text-[10px] text-[var(--text-dim)] mt-1",children:t.failureKind})]}),!t.failed&&e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Duration"}),e.jsxs("div",{className:"font-mono",children:[t.durationMs," ms"]})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Flags"}),e.jsxs("div",{className:"flex gap-1",children:[t.mocked&&e.jsx($,{tone:"accent",children:"mocked"}),t.modified&&e.jsx($,{tone:"busy",children:"modified"}),!t.mocked&&!t.modified&&e.jsx("span",{className:"text-[var(--text-dim)]",children:"passthrough"})]})]})]}),t.commandHint&&e.jsxs("div",{children:[e.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Triggered by"}),e.jsx("div",{className:"font-mono",children:t.commandHint.commandName})]}),!t.failed&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Request Headers"}),e.jsx(ge,{headers:t.reqHeaders})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Request Body"}),e.jsx(je,{body:t.reqBody})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Response Headers"}),e.jsx(ge,{headers:t.resHeaders})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Response Body"}),e.jsx(je,{body:t.resBody})]})]})]})})};function ar(t){return t?new Date(t).toLocaleTimeString(void 0,{hour12:!1}):"—"}function nr(t){return t>=200&&t<300?"ready":t>=300&&t<400?"neutral":t>=400&&t<500?"busy":t>=500?"error":"neutral"}const ir=({sessionId:t,sessionEnded:r})=>{const[s,a]=x.useState(null),[n,l]=x.useState(!1),[o,i]=x.useState(null),[c,m]=x.useState([]),[b,f]=x.useState(!0),[j,p]=x.useState(null),g=x.useRef(new Set),{on:y,isConnected:w}=Le(),R=x.useCallback(d=>{l(!!d.forbidden),a(d.active),i(d.error||null);const N=d.requests||[];m(N),g.current=new Set(N.map(L=>L.id))},[]);x.useEffect(()=>{let d=!0;return f(!0),be(t).then(N=>{d&&R(N)}).catch(()=>{d&&R({active:!1,error:"the server could not be reached"})}).finally(()=>{d&&f(!1)}),()=>{d=!1}},[t,R]),x.useEffect(()=>{if(r)return;let d=!0;const N=y("interceptor_request",k=>{!k||k.sessionId!==t||g.current.has(k.id)||(g.current.add(k.id),m(J=>[k,...J]),a(!0))}),L=y("interceptor_session_started",k=>{(k==null?void 0:k.sessionId)===t&&a(!0)}),O=y("interceptor_session_stopped",k=>{(k==null?void 0:k.sessionId)===t&&be(t).then(J=>{d&&R(J)}).catch(()=>{})});return()=>{d=!1,N(),L(),O()}},[y,t,r,w,R]);const A=x.useMemo(()=>[...c].sort((d,N)=>N.ts-d.ts),[c]),_=async d=>{if(p(d),s){const N=await er(t,d.id);N&&p(N)}},u=s&&c.length>0?e.jsxs("a",{href:tr(t),className:"inline-flex items-center gap-1 text-xs text-[var(--text-dim)] hover:text-[var(--text)]",title:"Download HAR",children:[e.jsx(_e,{className:"h-3 w-3"}),"HAR"]}):null,v=e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(dt,{className:"h-3.5 w-3.5 text-[var(--text-dim)]"}),e.jsx("span",{children:"Network"}),s&&c.length>0&&e.jsxs("span",{className:"text-xs text-[var(--text-dim)]",children:["(",c.length,")"]})]});let h;const C=s===!1&&(!o||o==="interceptor inactive");return b?h=e.jsx("div",{className:"p-4 text-xs text-[var(--text-dim)]",children:"Loading…"}):n?h=e.jsx(M,{title:"Only admins can see network requests",description:"Captured requests can carry sign-in details and personal data, so only admins can open them. Ask an admin to look at this session's requests."}):C&&r?h=e.jsx(M,{title:"No network capture",description:"This session didn't capture its network requests."}):C?h=e.jsx(M,{title:"Network capture is off",description:"This session isn't capturing its network requests. Android sessions capture them when the test turns network capture on in its capabilities, or when it is on for every session in the server settings."}):o?h=e.jsxs("div",{className:"p-4 text-xs text-[var(--red)]",children:["Failed to load requests: ",o]}):A.length===0?h=e.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."}):h=e.jsx("div",{className:"overflow-auto max-h-96",children:e.jsxs(it,{children:[e.jsx(ot,{children:e.jsxs(oe,{children:[e.jsx(E,{style:{width:80},children:"Time"}),e.jsx(E,{style:{width:70},children:"Method"}),e.jsx(E,{style:{width:70},children:"Status"}),e.jsx(E,{children:"Host"}),e.jsx(E,{children:"Path"}),e.jsx(E,{style:{width:80},children:"Duration"}),e.jsx(E,{style:{width:90},children:"Flags"})]})}),e.jsx(lt,{children:A.map(d=>e.jsxs(oe,{onClick:()=>_(d),style:{cursor:"pointer"},title:d.failed?d.failureReason:void 0,children:[e.jsx(D,{className:"font-mono text-[11px]",children:ar(d.ts)}),e.jsx(D,{children:e.jsx($,{tone:"accent",children:d.method})}),e.jsx(D,{children:d.failed?e.jsx($,{tone:"error",children:$e(d.failureKind)}):e.jsx($,{tone:nr(d.resStatus),children:d.resStatus||"—"})}),e.jsx(D,{className:"font-mono text-[11px] truncate",title:d.host,children:d.host}),e.jsx(D,{className:"font-mono text-[11px] truncate",title:d.path,children:d.failed?"—":d.path}),e.jsx(D,{className:"font-mono text-[11px]",children:d.failed?"—":`${d.durationMs}ms`}),e.jsx(D,{children:e.jsxs("div",{className:"flex gap-1",children:[d.mocked&&e.jsx($,{tone:"accent",children:"mock"}),d.modified&&e.jsx($,{tone:"busy",children:"mod"})]})})]},d.id))})]})}),e.jsxs(e.Fragment,{children:[e.jsx(Se,{header:v,action:u,padded:!1,children:h}),e.jsx(sr,{request:j,onClose:()=>p(null)})]})},$r=()=>{const{buildId:t="",sessionId:r=""}=Me(),{toast:s}=H(),a=Oe(),n=Ue(),l=mt(r||null),o=x.useMemo(()=>n.builds.find(f=>f.id===t)??null,[n.builds,t]),i=o?Ve(o):"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 e.jsx("div",{className:"p-6 text-xs text-[var(--text-dim)]",children:"Loading session…"});if(l.error||!l.session)return e.jsxs("div",{className:"p-6 space-y-2",children:[e.jsx(ce,{buildId:t,buildName:o?i:null,sessionId:r}),e.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 c=l.session,m=l.sessionLogs,b=Q(c.status).bucket==="failed";return e.jsxs("div",{className:"flex flex-col h-full min-h-0",children:[e.jsx(ce,{buildId:t,buildName:o?i:null,sessionId:c.id}),e.jsx(ht,{session:c}),e.jsx("div",{className:"flex-1 overflow-y-auto overflow-x-hidden",children:e.jsxs("div",{className:"px-4 py-4 space-y-4",children:[e.jsx(gt,{session:c,commands:m}),b&&e.jsx(wt,{session:c,buildName:i,buildId:t,commands:m,durationText:ze(X(c))}),e.jsx(Lt,{commands:m}),e.jsx(Ft,{sessionId:c.id,running:c.status==="running",hasTrace:!!c.performance_trace}),e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-[minmax(0,1fr)_340px] gap-4",children:[e.jsx("div",{className:"min-w-0",children:e.jsx(Zt,{sessionLogs:l.sessionLogs,deviceLogs:l.deviceLogs,debugLogs:l.debugLogs,profiling:l.profiling})}),e.jsxs("div",{className:"flex flex-col gap-4 min-w-0",children:[e.jsx(It,{session:c}),e.jsx(Ht,{session:c,buildName:o?i:null}),e.jsx(Bt,{session:c})]})]}),e.jsx(ir,{sessionId:c.id,sessionEnded:c.status!=="running"})]})})]})};export{$r as SessionDetailPage,$r as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as X,t as G,r as h,p as O,X as g,j as e,A as W,C as E,V as K,e as U,W as Y,Y as J}from"./index-
|
|
1
|
+
import{c as X,t as G,r as h,p as O,X as g,j as e,A as W,C as E,V as K,e as U,W as Y,Y as J}from"./index-Dk5J6HB1.js";/* empty css */import{S as x,A as Q}from"./SettingCard-DlmvZJPM.js";import{M as Z}from"./Modal-DPwop3Lz.js";import{B as j}from"./button-DugsdUw1.js";import{P as ee}from"./page-header-DhXCwpml.js";import{C as w}from"./circle-check-Day0HjW4.js";import{C as ae}from"./clock-BDHewWTx.js";import{M as se}from"./mouse-pointer-2-BM8CaLtt.js";import{C as te}from"./check-ORjyNfnX.js";import{A as le}from"./activity-CvuZdQYu.js";import{C as ne}from"./calendar-grKd08ZH.js";import{A as re}from"./arrow-up-right-C89FbNU0.js";import"./index-Z7_BY3pJ.js";import"./utils-CDN07tui.js";/**
|
|
2
2
|
* @license lucide-react v0.555.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as t}from"./index-
|
|
1
|
+
import{j as t}from"./index-Dk5J6HB1.js";const d={neutral:"text-[var(--text-muted)]",good:"text-[var(--color-success)]",bad:"text-[var(--color-danger)]"},o=({label:e,value:r,valueCls:a="text-[var(--text)]",note:s,noteTone:x="neutral"})=>t.jsxs("div",{className:"min-w-0 rounded-lg border border-[var(--border)] bg-[var(--surface)] px-4 py-3",children:[t.jsx("div",{className:"text-xs text-[var(--text-muted)]",children:e}),t.jsx("div",{className:`mt-1 text-2xl font-semibold tabular-nums leading-tight truncate ${a}`,children:r}),t.jsx("div",{className:`mt-0.5 text-[11px] truncate ${d[x]}`,children:s??" "})]});export{o as S};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as D,r as n,j as e,A as I,_ as f,q as $}from"./index-B0gQkEg0.js";/* empty css */import{a as F}from"./time-D-gjmE1o.js";import{T as E,a as M,b as T,c as u,d as P,e as p}from"./Table-DwIgERRt.js";import{F as K}from"./FieldGroup-GOgssFEP.js";import{P as R}from"./page-header-D8KUX6Qg.js";import{M as Q}from"./Modal-C6WjZFeI.js";import{B as A}from"./button-DDau4Axv.js";import{I as V}from"./input-DIc4MSC7.js";import{S as U}from"./select-BWpnhWXj.js";import{l as W}from"./users-D7tKMESH.js";import{P as N}from"./plus-C9Zj84UF.js";import{A as X}from"./arrow-left-BtB0_Jg_.js";import{T as L}from"./trash-2-CZ2ew6AZ.js";import"./index-72oF89LJ.js";import"./utils-CDN07tui.js";const B={SUPER_ADMIN:"Super admin",ADMIN:"Admin",MEMBER:"Member"};async function j(i,r){const t=await fetch(i,{credentials:"include",...r});if(!t.ok){const c=await t.json().catch(()=>({}));throw Object.assign(new Error(c.error||`HTTP ${t.status}`),{status:t.status})}return t.json()}const ve=()=>{const{toast:i}=D(),[r,t]=n.useState([]),[c,m]=n.useState(!0),[l,h]=n.useState(!1),[x,d]=n.useState(!1),[_,C]=n.useState(null),v=async()=>{m(!0);try{const a=await j("/xenon/api/teams");t(a),h(!1)}catch(a){a.status===403?h(!0):i(`Failed to load teams: ${a.message}`,"error")}finally{m(!1)}};if(n.useEffect(()=>{v()},[]),c)return e.jsxs("div",{className:"settings-loading",children:[e.jsx(I,{className:"animate-spin",size:32}),e.jsx("span",{children:"Loading teams…"})]});if(l)return e.jsx("div",{className:"settings-container",children:e.jsx(R,{icon:f,title:"Teams",subtitle:e.jsxs(e.Fragment,{children:["Your key lacks the ",e.jsx("code",{children:"admin"})," scope. Ask an administrator to promote your key before you can manage teams."]})})});if(_)return e.jsx(se,{team:_,onBack:()=>{C(null),v()}});const S=r.reduce((a,o)=>a+o.deviceCount,0),y=r.reduce((a,o)=>a+o.memberCount,0);return e.jsxs("div",{className:"settings-container",children:[e.jsx(R,{icon:f,title:"Teams",subtitle:"Group devices and members by team. Devices with no team assignment stay in the shared pool — visible to every authenticated user.",action:r.length>0?e.jsxs("button",{type:"button",className:"page-header-action",onClick:()=>d(!0),children:[e.jsx(N,{size:16}),e.jsx("span",{children:"New team"})]}):void 0}),e.jsxs("div",{className:"settings-content",children:[r.length>0&&e.jsxs("div",{className:"stat-strip",children:[e.jsxs("div",{className:"stat-strip__cell",children:[e.jsx("div",{className:"stat-strip__value stat-strip__value--accent",children:r.length}),e.jsx("div",{className:"stat-strip__label",children:"Teams"})]}),e.jsxs("div",{className:"stat-strip__cell",children:[e.jsx("div",{className:"stat-strip__value",children:y}),e.jsx("div",{className:"stat-strip__label",children:"Members"})]}),e.jsxs("div",{className:"stat-strip__cell",children:[e.jsx("div",{className:"stat-strip__value",children:S}),e.jsx("div",{className:"stat-strip__label",children:"Devices assigned"})]})]}),r.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-state__icon",children:e.jsx(f,{size:28})}),e.jsx("h3",{className:"empty-state__title",children:"No teams yet"}),e.jsx("p",{className:"empty-state__copy",children:"Every device is in the shared pool until you create a team and assign it. Teams let you scope an API key to a subset of devices — useful for separating CI runners, staging fleets, or per-team device pools."}),e.jsxs("button",{type:"button",className:"page-header-action empty-state__action",onClick:()=>d(!0),children:[e.jsx(N,{size:16}),e.jsx("span",{children:"Create your first team"})]})]}):e.jsx("div",{className:"team-grid",children:r.map(a=>e.jsxs("button",{type:"button",className:"team-card",onClick:()=>C(a),children:[e.jsxs("div",{className:"team-card__header",children:[e.jsx("div",{className:"team-card__icon",children:e.jsx(f,{size:16})}),e.jsxs("div",{className:"team-card__title",children:[e.jsx("div",{className:"team-card__name",children:a.name}),e.jsx("div",{className:"team-card__id",children:a.id.slice(0,8)})]}),e.jsx("span",{className:"team-card__delete",role:"button",tabIndex:0,onClick:o=>o.stopPropagation(),children:e.jsx(ee,{team:a,onDeleted:v})})]}),e.jsxs("div",{className:"team-card__metrics",children:[e.jsxs("div",{className:"team-card__metric",children:[e.jsx($,{size:13}),e.jsx("span",{className:"team-card__metric-value",children:a.deviceCount}),e.jsx("span",{className:"team-card__metric-label",children:"devices"})]}),e.jsxs("div",{className:"team-card__metric",children:[e.jsx(f,{size:13}),e.jsx("span",{className:"team-card__metric-value",children:a.memberCount}),e.jsx("span",{className:"team-card__metric-label",children:"members"})]})]}),e.jsxs("div",{className:"team-card__footer",children:["Created ",F(a.createdAt)]})]},a.id))})]}),x&&e.jsx(Z,{onClose:()=>d(!1),onCreated:()=>{d(!1),v()}})]})},Z=({onClose:i,onCreated:r})=>{const{toast:t}=D(),[c,m]=n.useState(""),[l,h]=n.useState(!1),x=async()=>{if(!c.trim())return t("Name required","error");h(!0);try{await j("/xenon/api/teams",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:c.trim()})}),t("Team created","success"),r()}catch(d){t(d.message,"error")}finally{h(!1)}};return e.jsx(Q,{open:!0,onClose:i,title:"New team",footer:e.jsxs(e.Fragment,{children:[e.jsx(A,{variant:"secondary",onClick:i,disabled:l,children:"Cancel"}),e.jsxs(A,{variant:"primary",onClick:x,disabled:l,children:[l?e.jsx(I,{className:"animate-spin",size:18}):e.jsx(N,{size:18}),l?"Creating…":"Create"]})]}),children:e.jsx("div",{style:{display:"flex",flexDirection:"column"},children:e.jsx(K,{label:"Name",htmlFor:"team-name",children:e.jsx(V,{id:"team-name",type:"text",value:c,onChange:d=>m(d.target.value),placeholder:"android-team, qa-ios, …",className:"w-full",autoFocus:!0})})})})},ee=({team:i,onDeleted:r})=>{const{toast:t}=D(),[c,m]=n.useState(!1),l=async h=>{if(h.stopPropagation(),!!window.confirm(`Delete team "${i.name}"? It must have no devices, members or apps.`)){m(!0);try{await j(`/xenon/api/teams/${i.id}`,{method:"DELETE"}),t("Team deleted","success"),r()}catch(x){t(x.message,"error")}finally{m(!1)}}};return e.jsxs("button",{className:"reset-btn",disabled:c,onClick:l,style:{color:"var(--status-error-fg)"},children:[e.jsx(L,{size:14}),c?"Deleting…":"Delete"]})},se=({team:i,onBack:r})=>{const{toast:t}=D(),[c,m]=n.useState([]),[l,h]=n.useState([]),[x,d]=n.useState([]),[_,C]=n.useState([]),[v,S]=n.useState(!0),[y,a]=n.useState(""),[o,k]=n.useState(""),b=async()=>{S(!0);try{const[s,g,Y]=await Promise.all([j(`/xenon/api/teams/${i.id}/members`),W(),j("/xenon/api/device")]);m(s),h(g);const z=Y;d(z.filter(w=>w.teamId===i.id)),C(z.filter(w=>!w.teamId))}catch(s){t(`Failed to load team detail: ${s.message}`,"error")}finally{S(!1)}};n.useEffect(()=>{b()},[i.id]);const O=async()=>{if(y)try{await j(`/xenon/api/teams/${i.id}/members`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userId:y})}),t("Member added","success"),a(""),b()}catch(s){t(s.message,"error")}},H=async s=>{if(window.confirm("Remove this member from the team?"))try{await j(`/xenon/api/teams/${i.id}/members/${encodeURIComponent(s)}`,{method:"DELETE"}),t("Member removed","success"),b()}catch(g){t(g.message,"error")}},J=async()=>{if(o)try{await j(`/xenon/api/device/${encodeURIComponent(o)}/team`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({teamId:i.id})}),t("Device assigned","success"),k(""),b()}catch(s){t(s.message,"error")}},q=async s=>{if(window.confirm("Return this device to the shared pool?"))try{await j(`/xenon/api/device/${encodeURIComponent(s)}/team`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({teamId:null})}),t("Device unassigned","success"),b()}catch(g){t(g.message,"error")}},G=l.filter(s=>!c.some(g=>g.userId===s.id));return e.jsxs("div",{className:"settings-container mesh-gradient-infra",children:[e.jsxs("div",{className:"settings-header",children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12},children:[e.jsx("button",{onClick:r,className:"reset-btn",style:{padding:"4px 8px"},"aria-label":"Back",children:e.jsx(X,{size:18})}),e.jsxs("div",{className:"settings-title-group",children:[e.jsx(f,{className:"settings-icon infra-icon",size:28}),e.jsx("h2",{children:i.name})]})]}),e.jsx("p",{className:"settings-subtitle",children:`Manage members and devices assigned to this team. Add a shared device below, or choose "Assign team…" in a device's ⋯ menu on the Devices page.`})]}),v?e.jsx("div",{className:"settings-loading",children:e.jsx(I,{className:"animate-spin",size:32})}):e.jsxs("div",{className:"settings-content",style:{display:"flex",flexDirection:"column",gap:24},children:[e.jsxs("section",{children:[e.jsxs("h3",{style:{marginBottom:12},children:["Members (",c.length,")"]}),e.jsxs("div",{className:"surface-card",style:{padding:12},children:[e.jsxs("div",{style:{display:"flex",gap:8,marginBottom:12},children:[e.jsxs(U,{value:y,onChange:s=>a(s.target.value),className:"flex-1",children:[e.jsx("option",{value:"",children:"Select a user to add…"}),G.map(s=>e.jsxs("option",{value:s.id,children:[s.name," (",s.email,") — ",B[s.role]]},s.id))]}),e.jsxs(A,{variant:"primary",onClick:O,disabled:!y,children:[e.jsx(N,{size:16})," Add"]})]}),c.length===0?e.jsx("p",{style:{opacity:.6,textAlign:"center",padding:24},children:"No members yet. Add a user above."}):e.jsxs(E,{children:[e.jsx(M,{children:e.jsxs(T,{children:[e.jsx(u,{children:"Name"}),e.jsx(u,{children:"Email"}),e.jsx(u,{children:"Role"}),e.jsx(u,{children:"Added"}),e.jsx(u,{style:{textAlign:"right"},children:"Action"})]})}),e.jsx(P,{children:c.map(s=>e.jsxs(T,{children:[e.jsx(p,{children:s.name}),e.jsx(p,{children:e.jsx("code",{style:{fontSize:"11px",color:"var(--text-muted)"},children:s.email})}),e.jsx(p,{children:B[s.role]}),e.jsx(p,{children:F(s.addedAt)}),e.jsx(p,{style:{textAlign:"right"},children:e.jsx("button",{className:"reset-btn",onClick:()=>H(s.userId),style:{color:"var(--status-error-fg)"},"aria-label":"Remove member",title:"Remove member",children:e.jsx(L,{size:14})})})]},s.userId))})]})]})]}),e.jsxs("section",{children:[e.jsxs("h3",{style:{marginBottom:12},children:["Devices (",x.length,")"]}),e.jsxs("div",{className:"surface-card",style:{padding:12},children:[e.jsxs("div",{style:{display:"flex",gap:8,marginBottom:12},children:[e.jsxs(U,{value:o,onChange:s=>k(s.target.value),className:"flex-1",children:[e.jsx("option",{value:"",children:"Select a shared-pool device to assign…"}),_.map(s=>e.jsxs("option",{value:s.udid,children:[s.name," (",s.platform,") — ",s.udid.slice(0,12),"…"]},s.udid))]}),e.jsxs(A,{variant:"primary",onClick:J,disabled:!o,children:[e.jsx(N,{size:16})," Assign"]})]}),x.length===0?e.jsx("p",{style:{opacity:.6,textAlign:"center",padding:24},children:"No devices assigned. Pick a shared device above to add one."}):e.jsxs(E,{children:[e.jsx(M,{children:e.jsxs(T,{children:[e.jsx(u,{children:"Name"}),e.jsx(u,{children:"Platform"}),e.jsx(u,{children:"UDID"}),e.jsx(u,{style:{textAlign:"right"},children:"Actions"})]})}),e.jsx(P,{children:x.map(s=>e.jsxs(T,{children:[e.jsxs(p,{children:[e.jsx($,{size:14,style:{marginRight:6,verticalAlign:"middle"}}),s.name]}),e.jsx(p,{children:s.platform}),e.jsx(p,{children:e.jsxs("code",{style:{fontSize:"11px"},children:[s.udid.slice(0,14),"…"]})}),e.jsx(p,{style:{textAlign:"right"},children:e.jsx("button",{className:"reset-btn",onClick:()=>q(s.udid),children:"Return to shared pool"})})]},s.udid))})]})]})]})]})]})};export{ve as Teams};
|
|
1
|
+
import{t as D,r as n,j as e,A as I,_ as f,q as $}from"./index-Dk5J6HB1.js";/* empty css */import{a as F}from"./time-D-gjmE1o.js";import{T as E,a as M,b as T,c as u,d as P,e as p}from"./Table-BigPWKXi.js";import{F as K}from"./FieldGroup-CtnCDshE.js";import{P as R}from"./page-header-DhXCwpml.js";import{M as Q}from"./Modal-DPwop3Lz.js";import{B as A}from"./button-DugsdUw1.js";import{I as V}from"./input-bTXM4l09.js";import{S as U}from"./select-DZoRKNti.js";import{l as W}from"./users-DHvDrriG.js";import{P as N}from"./plus-BKboorfa.js";import{A as X}from"./arrow-left-DiP5ywBT.js";import{T as L}from"./trash-2-DO8fv6w4.js";import"./index-Z7_BY3pJ.js";import"./utils-CDN07tui.js";const B={SUPER_ADMIN:"Super admin",ADMIN:"Admin",MEMBER:"Member"};async function j(i,r){const t=await fetch(i,{credentials:"include",...r});if(!t.ok){const c=await t.json().catch(()=>({}));throw Object.assign(new Error(c.error||`HTTP ${t.status}`),{status:t.status})}return t.json()}const ve=()=>{const{toast:i}=D(),[r,t]=n.useState([]),[c,m]=n.useState(!0),[l,h]=n.useState(!1),[x,d]=n.useState(!1),[_,C]=n.useState(null),v=async()=>{m(!0);try{const a=await j("/xenon/api/teams");t(a),h(!1)}catch(a){a.status===403?h(!0):i(`Failed to load teams: ${a.message}`,"error")}finally{m(!1)}};if(n.useEffect(()=>{v()},[]),c)return e.jsxs("div",{className:"settings-loading",children:[e.jsx(I,{className:"animate-spin",size:32}),e.jsx("span",{children:"Loading teams…"})]});if(l)return e.jsx("div",{className:"settings-container",children:e.jsx(R,{icon:f,title:"Teams",subtitle:e.jsxs(e.Fragment,{children:["Your key lacks the ",e.jsx("code",{children:"admin"})," scope. Ask an administrator to promote your key before you can manage teams."]})})});if(_)return e.jsx(se,{team:_,onBack:()=>{C(null),v()}});const S=r.reduce((a,o)=>a+o.deviceCount,0),y=r.reduce((a,o)=>a+o.memberCount,0);return e.jsxs("div",{className:"settings-container",children:[e.jsx(R,{icon:f,title:"Teams",subtitle:"Group devices and members by team. Devices with no team assignment stay in the shared pool — visible to every authenticated user.",action:r.length>0?e.jsxs("button",{type:"button",className:"page-header-action",onClick:()=>d(!0),children:[e.jsx(N,{size:16}),e.jsx("span",{children:"New team"})]}):void 0}),e.jsxs("div",{className:"settings-content",children:[r.length>0&&e.jsxs("div",{className:"stat-strip",children:[e.jsxs("div",{className:"stat-strip__cell",children:[e.jsx("div",{className:"stat-strip__value stat-strip__value--accent",children:r.length}),e.jsx("div",{className:"stat-strip__label",children:"Teams"})]}),e.jsxs("div",{className:"stat-strip__cell",children:[e.jsx("div",{className:"stat-strip__value",children:y}),e.jsx("div",{className:"stat-strip__label",children:"Members"})]}),e.jsxs("div",{className:"stat-strip__cell",children:[e.jsx("div",{className:"stat-strip__value",children:S}),e.jsx("div",{className:"stat-strip__label",children:"Devices assigned"})]})]}),r.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-state__icon",children:e.jsx(f,{size:28})}),e.jsx("h3",{className:"empty-state__title",children:"No teams yet"}),e.jsx("p",{className:"empty-state__copy",children:"Every device is in the shared pool until you create a team and assign it. Teams let you scope an API key to a subset of devices — useful for separating CI runners, staging fleets, or per-team device pools."}),e.jsxs("button",{type:"button",className:"page-header-action empty-state__action",onClick:()=>d(!0),children:[e.jsx(N,{size:16}),e.jsx("span",{children:"Create your first team"})]})]}):e.jsx("div",{className:"team-grid",children:r.map(a=>e.jsxs("button",{type:"button",className:"team-card",onClick:()=>C(a),children:[e.jsxs("div",{className:"team-card__header",children:[e.jsx("div",{className:"team-card__icon",children:e.jsx(f,{size:16})}),e.jsxs("div",{className:"team-card__title",children:[e.jsx("div",{className:"team-card__name",children:a.name}),e.jsx("div",{className:"team-card__id",children:a.id.slice(0,8)})]}),e.jsx("span",{className:"team-card__delete",role:"button",tabIndex:0,onClick:o=>o.stopPropagation(),children:e.jsx(ee,{team:a,onDeleted:v})})]}),e.jsxs("div",{className:"team-card__metrics",children:[e.jsxs("div",{className:"team-card__metric",children:[e.jsx($,{size:13}),e.jsx("span",{className:"team-card__metric-value",children:a.deviceCount}),e.jsx("span",{className:"team-card__metric-label",children:"devices"})]}),e.jsxs("div",{className:"team-card__metric",children:[e.jsx(f,{size:13}),e.jsx("span",{className:"team-card__metric-value",children:a.memberCount}),e.jsx("span",{className:"team-card__metric-label",children:"members"})]})]}),e.jsxs("div",{className:"team-card__footer",children:["Created ",F(a.createdAt)]})]},a.id))})]}),x&&e.jsx(Z,{onClose:()=>d(!1),onCreated:()=>{d(!1),v()}})]})},Z=({onClose:i,onCreated:r})=>{const{toast:t}=D(),[c,m]=n.useState(""),[l,h]=n.useState(!1),x=async()=>{if(!c.trim())return t("Name required","error");h(!0);try{await j("/xenon/api/teams",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:c.trim()})}),t("Team created","success"),r()}catch(d){t(d.message,"error")}finally{h(!1)}};return e.jsx(Q,{open:!0,onClose:i,title:"New team",footer:e.jsxs(e.Fragment,{children:[e.jsx(A,{variant:"secondary",onClick:i,disabled:l,children:"Cancel"}),e.jsxs(A,{variant:"primary",onClick:x,disabled:l,children:[l?e.jsx(I,{className:"animate-spin",size:18}):e.jsx(N,{size:18}),l?"Creating…":"Create"]})]}),children:e.jsx("div",{style:{display:"flex",flexDirection:"column"},children:e.jsx(K,{label:"Name",htmlFor:"team-name",children:e.jsx(V,{id:"team-name",type:"text",value:c,onChange:d=>m(d.target.value),placeholder:"android-team, qa-ios, …",className:"w-full",autoFocus:!0})})})})},ee=({team:i,onDeleted:r})=>{const{toast:t}=D(),[c,m]=n.useState(!1),l=async h=>{if(h.stopPropagation(),!!window.confirm(`Delete team "${i.name}"? It must have no devices, members or apps.`)){m(!0);try{await j(`/xenon/api/teams/${i.id}`,{method:"DELETE"}),t("Team deleted","success"),r()}catch(x){t(x.message,"error")}finally{m(!1)}}};return e.jsxs("button",{className:"reset-btn",disabled:c,onClick:l,style:{color:"var(--status-error-fg)"},children:[e.jsx(L,{size:14}),c?"Deleting…":"Delete"]})},se=({team:i,onBack:r})=>{const{toast:t}=D(),[c,m]=n.useState([]),[l,h]=n.useState([]),[x,d]=n.useState([]),[_,C]=n.useState([]),[v,S]=n.useState(!0),[y,a]=n.useState(""),[o,k]=n.useState(""),b=async()=>{S(!0);try{const[s,g,Y]=await Promise.all([j(`/xenon/api/teams/${i.id}/members`),W(),j("/xenon/api/device")]);m(s),h(g);const z=Y;d(z.filter(w=>w.teamId===i.id)),C(z.filter(w=>!w.teamId))}catch(s){t(`Failed to load team detail: ${s.message}`,"error")}finally{S(!1)}};n.useEffect(()=>{b()},[i.id]);const O=async()=>{if(y)try{await j(`/xenon/api/teams/${i.id}/members`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userId:y})}),t("Member added","success"),a(""),b()}catch(s){t(s.message,"error")}},H=async s=>{if(window.confirm("Remove this member from the team?"))try{await j(`/xenon/api/teams/${i.id}/members/${encodeURIComponent(s)}`,{method:"DELETE"}),t("Member removed","success"),b()}catch(g){t(g.message,"error")}},J=async()=>{if(o)try{await j(`/xenon/api/device/${encodeURIComponent(o)}/team`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({teamId:i.id})}),t("Device assigned","success"),k(""),b()}catch(s){t(s.message,"error")}},q=async s=>{if(window.confirm("Return this device to the shared pool?"))try{await j(`/xenon/api/device/${encodeURIComponent(s)}/team`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({teamId:null})}),t("Device unassigned","success"),b()}catch(g){t(g.message,"error")}},G=l.filter(s=>!c.some(g=>g.userId===s.id));return e.jsxs("div",{className:"settings-container mesh-gradient-infra",children:[e.jsxs("div",{className:"settings-header",children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12},children:[e.jsx("button",{onClick:r,className:"reset-btn",style:{padding:"4px 8px"},"aria-label":"Back",children:e.jsx(X,{size:18})}),e.jsxs("div",{className:"settings-title-group",children:[e.jsx(f,{className:"settings-icon infra-icon",size:28}),e.jsx("h2",{children:i.name})]})]}),e.jsx("p",{className:"settings-subtitle",children:`Manage members and devices assigned to this team. Add a shared device below, or choose "Assign team…" in a device's ⋯ menu on the Devices page.`})]}),v?e.jsx("div",{className:"settings-loading",children:e.jsx(I,{className:"animate-spin",size:32})}):e.jsxs("div",{className:"settings-content",style:{display:"flex",flexDirection:"column",gap:24},children:[e.jsxs("section",{children:[e.jsxs("h3",{style:{marginBottom:12},children:["Members (",c.length,")"]}),e.jsxs("div",{className:"surface-card",style:{padding:12},children:[e.jsxs("div",{style:{display:"flex",gap:8,marginBottom:12},children:[e.jsxs(U,{value:y,onChange:s=>a(s.target.value),className:"flex-1",children:[e.jsx("option",{value:"",children:"Select a user to add…"}),G.map(s=>e.jsxs("option",{value:s.id,children:[s.name," (",s.email,") — ",B[s.role]]},s.id))]}),e.jsxs(A,{variant:"primary",onClick:O,disabled:!y,children:[e.jsx(N,{size:16})," Add"]})]}),c.length===0?e.jsx("p",{style:{opacity:.6,textAlign:"center",padding:24},children:"No members yet. Add a user above."}):e.jsxs(E,{children:[e.jsx(M,{children:e.jsxs(T,{children:[e.jsx(u,{children:"Name"}),e.jsx(u,{children:"Email"}),e.jsx(u,{children:"Role"}),e.jsx(u,{children:"Added"}),e.jsx(u,{style:{textAlign:"right"},children:"Action"})]})}),e.jsx(P,{children:c.map(s=>e.jsxs(T,{children:[e.jsx(p,{children:s.name}),e.jsx(p,{children:e.jsx("code",{style:{fontSize:"11px",color:"var(--text-muted)"},children:s.email})}),e.jsx(p,{children:B[s.role]}),e.jsx(p,{children:F(s.addedAt)}),e.jsx(p,{style:{textAlign:"right"},children:e.jsx("button",{className:"reset-btn",onClick:()=>H(s.userId),style:{color:"var(--status-error-fg)"},"aria-label":"Remove member",title:"Remove member",children:e.jsx(L,{size:14})})})]},s.userId))})]})]})]}),e.jsxs("section",{children:[e.jsxs("h3",{style:{marginBottom:12},children:["Devices (",x.length,")"]}),e.jsxs("div",{className:"surface-card",style:{padding:12},children:[e.jsxs("div",{style:{display:"flex",gap:8,marginBottom:12},children:[e.jsxs(U,{value:o,onChange:s=>k(s.target.value),className:"flex-1",children:[e.jsx("option",{value:"",children:"Select a shared-pool device to assign…"}),_.map(s=>e.jsxs("option",{value:s.udid,children:[s.name," (",s.platform,") — ",s.udid.slice(0,12),"…"]},s.udid))]}),e.jsxs(A,{variant:"primary",onClick:J,disabled:!o,children:[e.jsx(N,{size:16})," Assign"]})]}),x.length===0?e.jsx("p",{style:{opacity:.6,textAlign:"center",padding:24},children:"No devices assigned. Pick a shared device above to add one."}):e.jsxs(E,{children:[e.jsx(M,{children:e.jsxs(T,{children:[e.jsx(u,{children:"Name"}),e.jsx(u,{children:"Platform"}),e.jsx(u,{children:"UDID"}),e.jsx(u,{style:{textAlign:"right"},children:"Actions"})]})}),e.jsx(P,{children:x.map(s=>e.jsxs(T,{children:[e.jsxs(p,{children:[e.jsx($,{size:14,style:{marginRight:6,verticalAlign:"middle"}}),s.name]}),e.jsx(p,{children:s.platform}),e.jsx(p,{children:e.jsxs("code",{style:{fontSize:"11px"},children:[s.udid.slice(0,14),"…"]})}),e.jsx(p,{style:{textAlign:"right"},children:e.jsx("button",{className:"reset-btn",onClick:()=>q(s.udid),children:"Return to shared pool"})})]},s.udid))})]})]})]})]})]})};export{ve as Teams};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as G,r as o,p as V,X as v}from"./index-
|
|
1
|
+
import{c as G,r as o,p as V,X as v}from"./index-Dk5J6HB1.js";import{f as Z}from"./time-D-gjmE1o.js";/**
|
|
2
2
|
* @license lucide-react v0.555.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as t,h as u}from"./index-
|
|
1
|
+
import{r as t,h as u}from"./index-Dk5J6HB1.js";function n(){const[s,r]=t.useState(null);return t.useEffect(()=>{let e=!0;return u().then(o=>e&&r(o.passwordReset)),()=>{e=!1}},[]),s}export{n as u};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as w,b as k,r as a,j as e,a2 as C}from"./index-
|
|
1
|
+
import{c as w,b as k,r as a,j as e,a2 as C}from"./index-Dk5J6HB1.js";import{l as M,d as R,c as I,a as A,u as D}from"./users-DHvDrriG.js";import{f as U}from"./time-D-gjmE1o.js";import{P}from"./page-header-DhXCwpml.js";import{B as y}from"./button-DugsdUw1.js";import{I as N}from"./input-bTXM4l09.js";import{S as g}from"./select-DZoRKNti.js";import{P as B}from"./plus-BKboorfa.js";import{T as L}from"./trash-2-DO8fv6w4.js";import"./utils-CDN07tui.js";/**
|
|
2
2
|
* @license lucide-react v0.555.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as s}from"./index-
|
|
1
|
+
import{o as s}from"./index-Dk5J6HB1.js";const o="/xenon/api/users";async function c(){const t=await fetch(`${o}/`,{credentials:"include"});if(!t.ok)throw new Error(`listUsers failed (${t.status})`);return t.json()}async function i(t){const e=await fetch(`${o}/`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",...s},body:JSON.stringify(t)});if(!e.ok){const n=await e.json().catch(()=>({}));throw new Error(n.error||`createUser failed (${e.status})`)}return e.json()}async function d(t,e){const n=await fetch(`${o}/${encodeURIComponent(t)}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.error||`updateUser failed (${n.status})`)}return n.json()}async function l(t){const e=await fetch(`${o}/${encodeURIComponent(t)}`,{method:"DELETE",credentials:"include"});if(!e.ok&&e.status!==204){const n=await e.json().catch(()=>({}));throw new Error(n.error||`deleteUser failed (${e.status})`)}}async function u(t){const e=await fetch(`${o}/${encodeURIComponent(t)}/reset-link`,{method:"POST",credentials:"include"}),n=await e.json().catch(()=>({}));if(!e.ok)throw new Error(n.error||`Reset link failed (${e.status})`);return n}export{i as a,u as c,l as d,c as l,d as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as P,r as l,X as p,j as e,P as w,A as x,C as V,Q as I}from"./index-
|
|
1
|
+
import{t as P,r as l,X as p,j as e,P as w,A as x,C as V,Q as I}from"./index-Dk5J6HB1.js";/* empty css */import{F as k}from"./FieldGroup-CtnCDshE.js";import{S as U}from"./select-DZoRKNti.js";import{P as B}from"./page-header-DhXCwpml.js";import{T as D}from"./trash-2-DO8fv6w4.js";import{P as _}from"./plus-BKboorfa.js";import{C as J}from"./circle-x-CHxSRLHp.js";import{C as K}from"./circle-check-Day0HjW4.js";import{Z as M}from"./zap-BMrTJO6M.js";import{A as X}from"./activity-CvuZdQYu.js";import"./utils-CDN07tui.js";const Z=["udid","name","host","platform"],q=["udid","name","host","platform"],G=["sessionId","sessionName","failureReason","udid","deviceName","platform","osVersion","startTime","endTime"],Q=["windowDays","totalHeals","distinctSelectors"],Y={device_offline:Z,device_new:q,session_failed:G,selector_health_digest:Q},ee=Y,se={eventType:"The event: device_offline, session_failed, ...",udid:"The device's id",name:"The device's name",host:"The machine it is plugged into",platform:"android or ios",sessionId:"The session's id",sessionName:"The session's name, if the test gave it one",failureReason:"Why it failed",deviceName:"The device's name",osVersion:"The device's OS version",startTime:"When it started (ISO 8601)",endTime:"When it ended (ISO 8601)",windowDays:"How many days the digest covers",totalHeals:"Heals in that time",distinctSelectors:"Selectors that were healed"};function te(o){const c=["eventType"];for(const u of o)for(const h of ee[u]??[])c.includes(h)||c.push(h);return c}const ae=[{id:"slack",label:"Slack message"},{id:"webhook",label:"JSON (event and payload)"}];function oe(o){return o.payloadTemplate?"CUSTOM":o.type==="slack"?"SLACK":"JSON"}const j=[{id:"device_offline",label:"Device offline",icon:V,tone:"red"},{id:"device_new",label:"New device",icon:_,tone:"green"},{id:"session_failed",label:"Session failed",icon:J,tone:"amber"},{id:"selector_health_digest",label:"Selector health digest",icon:I,tone:"green"}],be=()=>{const{toast:o}=P(),[c,u]=l.useState([]),[h,N]=l.useState(!0),[r,y]=l.useState(""),[i,S]=l.useState(["device_offline","session_failed"]),[f,T]=l.useState(!1),[C,A]=l.useState(!1),[d,E]=l.useState(!1),[m,g]=l.useState(""),[b,O]=l.useState("slack");l.useEffect(()=>{v()},[]);const v=async()=>{N(!0);try{const s=await p.getWebhookConfigs();u(s||[])}catch(s){console.error("Failed to load webhook configs",s),o("Failed to load webhooks.","error")}finally{N(!1)}},W=async()=>{if(!r.trim()){o("Webhook URL is required.","error");return}if(i.length===0){o("Select at least one trigger event.","error");return}T(!0);try{await p.addWebhookConfig(r.trim(),i,b,m||void 0),y(""),g(""),E(!1),O("slack"),S(["device_offline","session_failed"]),await v(),o("Webhook saved.","success")}catch(s){console.error("Failed to add webhook",s),o(`Failed to add webhook: ${s.message||"unknown error"}`,"error")}finally{T(!1)}},$=async(s,t)=>{if(window.confirm(`Remove webhook for "${t}"?`))try{await p.deleteWebhookConfig(s),await v(),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(!r.trim()||i.length===0)return;A(!0);const s=t=>{var a;return((a=j.find(n=>n.id===t))==null?void 0:a.label)??t};try{for(const t of i)try{await p.testWebhook(r.trim(),b,m||void 0,t)}catch(a){o(`Test failed for ${s(t)}: ${a.message||"check the URL"}`,"error");return}o(i.length===1?`Test message delivered: ${s(i[0])}.`:`Test messages delivered: ${i.map(s).join(", ")}.`,"success")}finally{A(!1)}},R=s=>{S(t=>t.includes(s)?t.filter(a=>a!==s):[...t,s])},z=s=>{g(t=>t+`{{${s}}} `)};return e.jsxs("div",{className:"settings-container",children:[e.jsx(B,{icon:w,title:"Notifications",subtitle:"Configure Slack or generic webhooks to receive alerts for critical infrastructure events."}),e.jsxs("div",{className:"settings-content",children:[h?e.jsxs("div",{className:"settings-loading",style:{height:200},children:[e.jsx(x,{className:"animate-spin",size:28}),e.jsx("span",{children:"Loading webhooks…"})]}):c.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-state__icon",children:e.jsx(w,{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:c.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:oe(s)}),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:()=>$(s.id,s.url),"aria-label":`Remove webhook for ${s.url}`,children:[e.jsx(D,{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 n=j.find(H=>H.id===a),F=(n==null?void 0:n.icon)??w;return e.jsxs("span",{className:`event-pill event-pill--${(n==null?void 0:n.tone)||"gray"}`,children:[e.jsx(F,{size:11}),(n==null?void 0:n.label)||a]},a)})})]},s.id)})}),e.jsxs("div",{className:"add-webhook-card",children:[e.jsxs("div",{className:"add-webhook-card__header",children:[e.jsx(_,{size:14}),e.jsx("span",{children:"Add a new webhook"})]}),e.jsxs("div",{className:"add-webhook-card__body",children:[e.jsx(k,{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:r,onChange:s=>y(s.target.value)})})}),e.jsx(k,{label:"Message format",description:m.trim()?"Your custom payload is sent as written, whatever the format.":"Slack message for a Slack incoming webhook; JSON for anything else.",htmlFor:"webhook-format",children:e.jsx(U,{id:"webhook-format",value:b,onChange:s=>O(s.target.value),children:ae.map(s=>e.jsx("option",{value:s.id,children:s.label},s.id))})}),e.jsx(k,{label:"Trigger events",children:e.jsx("div",{className:"event-toggle-grid",children:j.map(s=>{const t=i.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:()=>R(s.id),children:[e.jsx(a,{size:14,className:"event-toggle__lead"}),e.jsx("span",{children:s.label}),t&&e.jsx(K,{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:()=>E(!d),"aria-expanded":d,children:[e.jsx(M,{size:14,className:d?"tone-amber":"tone-dim"}),e.jsx("span",{children:"Use custom payload (optional)"}),e.jsx("span",{className:"template-section__indicator",children:d?"−":"+"})]}),d&&e.jsxs("div",{className:"template-editor",children:[e.jsx("p",{className:"template-editor__hint",children:"Define a JSON or text template. Click a name below to insert it; the names are the ones your selected events carry."}),e.jsx("div",{className:"template-editor__chips",children:te(i).map(s=>e.jsx("button",{type:"button",className:"template-editor__chip","aria-label":`{{${s}}}`,title:se[s],onClick:()=>z(s),children:`{{${s}}}`},s))}),e.jsx("textarea",{className:"template-editor__textarea",placeholder:'Example: { "text": "Alert: Device {{udid}} is offline!" }',value:m,onChange:s=>g(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:!r.trim()||i.length===0||C,"aria-label":"Send test",title:i.length===0?"Select a trigger event to send a sample of":"Sends a sample message for each selected event",children:[C?e.jsx(x,{size:14,className:"animate-spin"}):e.jsx(X,{size:14}),e.jsx("span",{children:"Send test"})]}),e.jsxs("button",{type:"button",className:"page-header-action",onClick:W,disabled:f||!r.trim(),children:[f?e.jsx(x,{size:14,className:"animate-spin"}):e.jsx(_,{size:14}),e.jsx("span",{children:f?"Saving…":"Save webhook"})]})]})]})]})]})};export{be as WebhookSettings};
|
package/lib/public/index.html
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
Learn how to configure a non-root public URL by running `npm run build`.
|
|
37
37
|
-->
|
|
38
38
|
<title>Xenon</title>
|
|
39
|
-
<script type="module" crossorigin src="/xenon/assets/index-
|
|
39
|
+
<script type="module" crossorigin src="/xenon/assets/index-Dk5J6HB1.js"></script>
|
|
40
40
|
<link rel="stylesheet" crossorigin href="/xenon/assets/index-Bxoxt9sV.css">
|
|
41
41
|
</head>
|
|
42
42
|
|
package/lib/schema.json
CHANGED
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
"sqlite",
|
|
164
164
|
"postgresql"
|
|
165
165
|
],
|
|
166
|
-
"description": "Has no effect, and is accepted only so older configs still start. Xenon stores its data in SQLite, one database per server, hub or node (see databaseUrl). At startup the database itself decides how its tables are brought up to date: from its migration history if that history matches its tables
|
|
166
|
+
"description": "Has no effect, and is accepted only so older configs still start. Xenon stores its data in SQLite, one database per server, hub or node (see databaseUrl). At startup the database itself decides how its tables are brought up to date: from its migration history if that history matches its tables, otherwise by matching them to this version's schema."
|
|
167
167
|
},
|
|
168
168
|
"databaseUrl": {
|
|
169
169
|
"type": "string",
|
package/lib/src/app/index.js
CHANGED
|
@@ -90,6 +90,7 @@ const PluginContext_1 = require("../PluginContext");
|
|
|
90
90
|
const nodeWebDriverUrl_1 = require("../gateway/nodeWebDriverUrl");
|
|
91
91
|
const nodeSessionStatus_1 = require("../gateway/nodeSessionStatus");
|
|
92
92
|
const nodeSessionMetrics_1 = require("../gateway/nodeSessionMetrics");
|
|
93
|
+
const nodeSessionDeviceLogs_1 = require("../gateway/nodeSessionDeviceLogs");
|
|
93
94
|
const dashboardPluginLink_1 = require("./dashboardPluginLink");
|
|
94
95
|
const ASYNC_LOCK = new async_lock_1.default();
|
|
95
96
|
// An async handler's error reaches the error handlers, as on Express 5.
|
|
@@ -219,6 +220,7 @@ function createRouter(pluginArgs, ownServer) {
|
|
|
219
220
|
// credentials for the node); it takes the hub's session token instead.
|
|
220
221
|
(0, nodeSessionStatus_1.registerNodeSessionStatus)(apiRouter, pluginArgs);
|
|
221
222
|
(0, nodeSessionMetrics_1.registerNodeSessionMetrics)(apiRouter, pluginArgs);
|
|
223
|
+
(0, nodeSessionDeviceLogs_1.registerNodeSessionDeviceLogs)(apiRouter, pluginArgs);
|
|
222
224
|
// Dashboard login: unauthenticated (rate-limited internally via separate IP logic)
|
|
223
225
|
apiRouter.use('/auth', (0, auth_1.authPublicRouter)()); // login, logout — unauthenticated
|
|
224
226
|
// All remaining /api/* requires authentication (cookie session for
|