@xenon-device-management/xenon 2.13.1 → 2.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/package.json +1 -1
- package/lib/public/assets/{AnnotationOverlay-Cvpkk4r9.js → AnnotationOverlay-BXruClDk.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-Bhn5ZtVM.js → ApiKeyGate-qTS4LPLC.js} +1 -1
- package/lib/public/assets/{BugReportButton-BA6aEjTC.js → BugReportButton-BoGhlI7F.js} +1 -1
- package/lib/public/assets/{DeviceMosaicView-ChhHnpFQ.js → DeviceMosaicView-8J0oYqMM.js} +1 -1
- package/lib/public/assets/{EmptyState-CgzbuUvO.js → EmptyState-Cr17PdDv.js} +1 -1
- package/lib/public/assets/{FieldGroup-BHc_5qNc.js → FieldGroup-Bvn08svv.js} +1 -1
- package/lib/public/assets/{FilterMenu-D7OMGuRc.js → FilterMenu-DDSO8aDf.js} +1 -1
- package/lib/public/assets/{Menu-C173JJ0l.js → Menu-B-Qn2X3T.js} +1 -1
- package/lib/public/assets/{Modal-ChvVUI3B.js → Modal-Cg9V5JYU.js} +1 -1
- package/lib/public/assets/{RecordingPage-Dhw4d_O6.js → RecordingPage-BTUs9VTW.js} +1 -1
- package/lib/public/assets/{RecordingsPage-z97ftGK-.js → RecordingsPage-DYcfxITK.js} +1 -1
- package/lib/public/assets/{SegmentedControl-DbXhBGZC.js → SegmentedControl-Zy7suhT5.js} +1 -1
- package/lib/public/assets/{SettingCard-C908GgiI.js → SettingCard-CWYmycoe.js} +1 -1
- package/lib/public/assets/{Table-B1DB_ZN-.js → Table-io9fwa98.js} +1 -1
- package/lib/public/assets/{activity-Css0xNvp.js → activity-Bdf_uIWl.js} +1 -1
- package/lib/public/assets/{ai-settings-BMQK3-yN.js → ai-settings-Bce5SJob.js} +1 -1
- package/lib/public/assets/{api-keys-CbJ42eDQ.js → api-keys-CPqtVpm3.js} +1 -1
- package/lib/public/assets/{apps-DW1pw2re.js → apps-BgZan0H9.js} +1 -1
- package/lib/public/assets/{arrow-left-Dpog9L0F.js → arrow-left-B27ckB31.js} +1 -1
- package/lib/public/assets/{arrow-right-CsBIlSIw.js → arrow-right-DOF8ITCz.js} +1 -1
- package/lib/public/assets/{arrow-up-right-z1tRQE4R.js → arrow-up-right-eFG5KTFS.js} +1 -1
- package/lib/public/assets/{auth-shell-mXiX5vNk.js → auth-shell-DlC3_27j.js} +2 -2
- package/lib/public/assets/{builds-page-BcFn23bE.js → builds-page-BuXpPfKo.js} +1 -1
- package/lib/public/assets/{button-Cq_zHETv.js → button-fIW8owMI.js} +1 -1
- package/lib/public/assets/{calendar-CKxABX9q.js → calendar-WNjmrF-a.js} +1 -1
- package/lib/public/assets/{check-C7xu-hGU.js → check-BdTh1Sq7.js} +1 -1
- package/lib/public/assets/{chevron-right-BiqfdCGA.js → chevron-right-B4BKlsqk.js} +1 -1
- package/lib/public/assets/{circle-check-CyBjjPxR.js → circle-check-zJU80_EF.js} +1 -1
- package/lib/public/assets/{circle-x-6gMRcx6z.js → circle-x-5Uwquo0h.js} +1 -1
- package/lib/public/assets/{clock-U1rDTT1h.js → clock-BtUprDsZ.js} +1 -1
- package/lib/public/assets/{copy-BIM0ZX0L.js → copy-DGSbZI3u.js} +1 -1
- package/lib/public/assets/{device-explorer-DSpAA_t9.js → device-explorer-C-KnhMHI.js} +1 -1
- package/lib/public/assets/{download-DDBRXnAZ.js → download-WG6l7BgD.js} +1 -1
- package/lib/public/assets/{forgot-password-Dpg7yxM8.js → forgot-password-DnudqFFo.js} +1 -1
- package/lib/public/assets/{index-BTYMAqNW.css → index-Bxoxt9sV.css} +1 -1
- package/lib/public/assets/{index-BGBqohfQ.js → index-DI1fGIyj.js} +2 -2
- package/lib/public/assets/{index-BQ2luT7L.js → index-HDQVe1gi.js} +1 -1
- package/lib/public/assets/{input-BvB1cjHE.js → input-BjDRKCea.js} +1 -1
- package/lib/public/assets/{line-chart-m78VggnW.js → line-chart-C0zdgd0v.js} +1 -1
- package/lib/public/assets/{list-checks-cY7o1QTL.js → list-checks-BdW5CjM8.js} +1 -1
- package/lib/public/assets/{lock-loz8txQm.js → lock-Dy3L4I8E.js} +1 -1
- package/lib/public/assets/{login-BYOvz4lx.js → login-BHbY9CLG.js} +1 -1
- package/lib/public/assets/{maintenance-settings-BxbA7SkB.js → maintenance-settings-zDVlOMVK.js} +1 -1
- package/lib/public/assets/{monitor-C4HPHb6s.js → monitor-8xjkeHHi.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-BkdKtCzi.js → mouse-pointer-2-CXdKMXQ8.js} +1 -1
- package/lib/public/assets/{network-C5e0MIWo.js → network-Cj8JOKON.js} +1 -1
- package/lib/public/assets/{overview-tlu8ijGd.js → overview-CTg7F0im.js} +1 -1
- package/lib/public/assets/{page-header-FXPlo6az.js → page-header--aTyJXq6.js} +1 -1
- package/lib/public/assets/{play-CWdk0_Sy.js → play-B-FodgHV.js} +1 -1
- package/lib/public/assets/{plus-B2xCnntV.js → plus-CsIk10Cq.js} +1 -1
- package/lib/public/assets/{profile-page-D080qzr4.js → profile-page-DkFKkMfl.js} +1 -1
- package/lib/public/assets/{recording-group-store-B75435xR.js → recording-group-store-WVM3hoXn.js} +1 -1
- package/lib/public/assets/{reset-password-D3u4Mbvn.js → reset-password-I2-zqLlI.js} +1 -1
- package/lib/public/assets/{runbook-page-hPRZMy7s.js → runbook-page-DJTVCJX7.js} +1 -1
- package/lib/public/assets/{select-12RThAXc.js → select-DvCSv25z.js} +1 -1
- package/lib/public/assets/{selector-detail-redirect-uQwvV7EQ.js → selector-detail-redirect-BLVfMJs_.js} +1 -1
- package/lib/public/assets/{selector-health-page-CThoxDFO.js → selector-health-page-oqadRokn.js} +1 -1
- package/lib/public/assets/session-detail-page-CGn55Pvl.js +4 -0
- package/lib/public/assets/{settings-C1VIluuT.js → settings-MsVU2ukE.js} +1 -1
- package/lib/public/assets/{stat-tile-B02WEte8.js → stat-tile-DV9Ix-4O.js} +1 -1
- package/lib/public/assets/{tablet-Dj30VGZl.js → tablet-6b_SEN2X.js} +1 -1
- package/lib/public/assets/{teams-uL4fjTDR.js → teams-BjZyfuHh.js} +1 -1
- package/lib/public/assets/{trash-2-B54e-TfY.js → trash-2-CTZpPSdd.js} +1 -1
- package/lib/public/assets/{upload-B3k8WCSP.js → upload-P3XzwQUb.js} +1 -1
- package/lib/public/assets/{use-builds-data-BFn90NH3.js → use-builds-data-1cOCMf-z.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-DL2TmWZ0.js → use-password-reset-mode-DgF9w088.js} +1 -1
- package/lib/public/assets/{users-DjQSucKb.js → users-CiNTKUiz.js} +1 -1
- package/lib/public/assets/{users-CqBuxMlX.js → users-DMF9Mk5H.js} +1 -1
- package/lib/public/assets/{video-off-T0rlt0GA.js → video-off-CHp1KU_T.js} +1 -1
- package/lib/public/assets/{webhook-settings-DAHgnJyZ.js → webhook-settings-DiS8geIm.js} +1 -1
- package/lib/public/assets/{zap-gBHqutZV.js → zap-DRz0tDq5.js} +1 -1
- package/lib/public/index.html +2 -2
- package/lib/schema.json +0 -25
- package/lib/src/app/routers/control.js +19 -4
- package/lib/src/app/routers/streamStartConflict.js +3 -0
- package/lib/src/data-service/device-service.js +40 -0
- package/lib/src/data-service/device-store.js +4 -1
- package/lib/src/data-service/prisma-store.js +2 -1
- package/lib/src/device-utils.js +63 -13
- package/lib/src/middleware/deviceAccessGuard.js +63 -9
- package/lib/src/services/ServerManager.js +3 -0
- package/lib/src/services/SessionLifecycleService.js +16 -4
- package/lib/src/services/device-access/SessionOwnerResolver.js +26 -0
- package/lib/src/services/device-access/deviceAccessPolicy.js +38 -6
- package/lib/src/services/device-access/leaseHold.js +26 -0
- package/lib/src/services/device-access/ticketActorAccess.js +6 -0
- package/lib/src/services/lease/activeLeases.js +37 -0
- package/lib/src/services/recording/busy-precheck.js +24 -3
- package/lib/test/unit/device-access-lease.spec.js +270 -0
- package/lib/test/unit/lease/allocate-lease-branch.spec.js +22 -0
- package/lib/test/unit/lease/session-lease-proof.spec.js +59 -0
- package/lib/test/unit/lease-held-phone.spec.js +196 -0
- package/lib/test/unit/partial-config-file.spec.js +100 -0
- package/lib/test/unit/schema-required-args.spec.js +21 -16
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/schema.json +0 -25
- package/scripts/generate-types-from-schema.js +6 -0
- package/scripts/lib/always-present-plugin-args.js +39 -0
- package/lib/public/assets/session-detail-page-Bc02YjMm.js +0 -4
package/schema.json
CHANGED
|
@@ -288,31 +288,6 @@
|
|
|
288
288
|
"description": "Record CPU and memory every 2 s for each session on this server's own phones (Android: the app under test and the device; iPhone: device CPU), shown in the session page's Performance panel. Needs the dashboard."
|
|
289
289
|
}
|
|
290
290
|
},
|
|
291
|
-
"required": [
|
|
292
|
-
"platform",
|
|
293
|
-
"androidDeviceType",
|
|
294
|
-
"iosDeviceType",
|
|
295
|
-
"skipChromeDownload",
|
|
296
|
-
"maxSessions",
|
|
297
|
-
"deviceAvailabilityTimeoutMs",
|
|
298
|
-
"deviceAvailabilityQueryIntervalMs",
|
|
299
|
-
"sendNodeDevicesToHubIntervalMs",
|
|
300
|
-
"checkStaleDevicesIntervalMs",
|
|
301
|
-
"checkBlockedDevicesIntervalMs",
|
|
302
|
-
"newCommandTimeoutSec",
|
|
303
|
-
"bindHostOrIp",
|
|
304
|
-
"enableDashboard",
|
|
305
|
-
"bootedSimulators",
|
|
306
|
-
"removeDevicesFromDatabaseBeforeRunningThePlugin",
|
|
307
|
-
"healthCheckIntervalMs",
|
|
308
|
-
"enableSelfHealing",
|
|
309
|
-
"buildCleanupDays",
|
|
310
|
-
"buildCleanupMaxCount",
|
|
311
|
-
"buildCleanupSchedule",
|
|
312
|
-
"deleteBuildAssets",
|
|
313
|
-
"sessionHeartbeatIntervalMs",
|
|
314
|
-
"enableJsonLogging"
|
|
315
|
-
],
|
|
316
291
|
"title": "IPluginArgs",
|
|
317
292
|
"type": "object",
|
|
318
293
|
"definitions": {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { compileFromFile } = require('json-schema-to-typescript');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const { ALWAYS_PRESENT_PLUGIN_ARGS } = require('./lib/always-present-plugin-args');
|
|
4
5
|
|
|
5
6
|
async function generate() {
|
|
6
7
|
const schemaPath = path.join(__dirname, '../schema.json');
|
|
@@ -19,6 +20,11 @@ async function generate() {
|
|
|
19
20
|
schema.properties.autowait = { '$ref': '#/definitions/AutowaitConfig' };
|
|
20
21
|
schema.properties.streaming = { '$ref': '#/definitions/StreamingConfig' };
|
|
21
22
|
|
|
23
|
+
// Args Appium always fills from their defaults stay non-optional in
|
|
24
|
+
// IPluginArgs. schema.json itself has no `required` list: Appium would
|
|
25
|
+
// refuse any config file that left one out.
|
|
26
|
+
schema.required = ALWAYS_PRESENT_PLUGIN_ARGS;
|
|
27
|
+
|
|
22
28
|
const { compile } = require('json-schema-to-typescript');
|
|
23
29
|
const ts = await compile(schema, 'IPluginArgs', {
|
|
24
30
|
bannerComment: `/* eslint-disable */
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin args that are always present at runtime, so IPluginArgs types them as
|
|
3
|
+
* non-optional. Appium fills every schema default after it validates a config
|
|
4
|
+
* file, and XenonPlugin merges DefaultPluginArgs as well.
|
|
5
|
+
*
|
|
6
|
+
* Used only by scripts/generate-types-from-schema.js, which marks these
|
|
7
|
+
* required for type generation. schema.json itself has no `required` list:
|
|
8
|
+
* Appium checks a config file against it before applying defaults, so a
|
|
9
|
+
* required arg made it refuse any file that left one out (the README sample
|
|
10
|
+
* included). These are the 23 args that were required until then; keep it to
|
|
11
|
+
* them. test/unit/schema-required-args.spec.ts checks each still has a default.
|
|
12
|
+
*/
|
|
13
|
+
const ALWAYS_PRESENT_PLUGIN_ARGS = [
|
|
14
|
+
'platform',
|
|
15
|
+
'androidDeviceType',
|
|
16
|
+
'iosDeviceType',
|
|
17
|
+
'skipChromeDownload',
|
|
18
|
+
'maxSessions',
|
|
19
|
+
'deviceAvailabilityTimeoutMs',
|
|
20
|
+
'deviceAvailabilityQueryIntervalMs',
|
|
21
|
+
'sendNodeDevicesToHubIntervalMs',
|
|
22
|
+
'checkStaleDevicesIntervalMs',
|
|
23
|
+
'checkBlockedDevicesIntervalMs',
|
|
24
|
+
'newCommandTimeoutSec',
|
|
25
|
+
'bindHostOrIp',
|
|
26
|
+
'enableDashboard',
|
|
27
|
+
'bootedSimulators',
|
|
28
|
+
'removeDevicesFromDatabaseBeforeRunningThePlugin',
|
|
29
|
+
'healthCheckIntervalMs',
|
|
30
|
+
'enableSelfHealing',
|
|
31
|
+
'buildCleanupDays',
|
|
32
|
+
'buildCleanupMaxCount',
|
|
33
|
+
'buildCleanupSchedule',
|
|
34
|
+
'deleteBuildAssets',
|
|
35
|
+
'sessionHeartbeatIntervalMs',
|
|
36
|
+
'enableJsonLogging',
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
module.exports = { ALWAYS_PRESENT_PLUGIN_ARGS };
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{r as x,X as k,j as t,e as Q,f as Se,t as P,y as _e,R as Ce,p as Re,i as Te,u as $e}from"./index-BGBqohfQ.js";import{C as Ae,d as Me,p as ve,e as De,r as be,f as Ee,j as ge,k as V,c as Oe,h as Z,u as Le,b as Pe,q as Fe}from"./use-builds-data-BFn90NH3.js";import{A as Be}from"./arrow-left-Dpog9L0F.js";import{C as I}from"./chevron-right-BiqfdCGA.js";import{s as He}from"./labels-C21SA4JG.js";import{B as Ue,S as je,P as _}from"./BugReportButton-BA6aEjTC.js";import{C as Ie}from"./circle-x-6gMRcx6z.js";import{C as Je}from"./circle-check-CyBjjPxR.js";import{S as E}from"./stat-tile-B02WEte8.js";import{C as z}from"./copy-BIM0ZX0L.js";import{A as Ve}from"./arrow-up-right-z1tRQE4R.js";import{A as ze}from"./arrow-right-CsBIlSIw.js";import{f as Ke,a as ee,C as Ne,b as We,s as Xe,L as Ye,n as te}from"./line-chart-m78VggnW.js";import{E as M}from"./EmptyState-CgzbuUvO.js";import{A as qe}from"./activity-Css0xNvp.js";import{V as Ge}from"./video-off-T0rlt0GA.js";import{D as ye}from"./download-DDBRXnAZ.js";import{T as Qe,a as Ze,b as re,c as T,d as et,e as $}from"./Table-B1DB_ZN-.js";import{M as tt}from"./Modal-ChvVUI3B.js";import{N as rt}from"./network-C5e0MIWo.js";import"./time-D-gjmE1o.js";import"./index-BQ2luT7L.js";const st=4e3,se=e=>!!e&&typeof e=="object"&&typeof e.id=="string"&&typeof e.status=="string";function at(e){const[r,s]=x.useState(null),[a,n]=x.useState([]),[l,c]=x.useState([]),[i,d]=x.useState([]),[u,N]=x.useState([]),[p,v]=x.useState(!0),[g,j]=x.useState(null),[y,w]=x.useState(!1),[A,C]=x.useState(0),b=x.useRef(null);x.useEffect(()=>{if(!e){s(null),n([]),c([]),d([]),N([]),w(!1),v(!1);return}let m=!0;return b.current!==e&&v(!0),Promise.all([k.getSession(e),k.getSessionLogs(e),k.getDeviceLogs(e),k.getDebugLogs(e),k.getProfilingData(e).catch(()=>[])]).then(([f,h,R,D,q])=>{if(!m)return;const G=se(f);s(G?f:null),G?(b.current=e,w(!1),j(null)):(w(!!f&&typeof f=="object"&&(f.error===!0||typeof f.message=="string")),j(typeof(f==null?void 0:f.message)=="string"?f.message:"Session not found")),n(Array.isArray(h)?h:[]),c(Array.isArray(R)?R:[]),d(Array.isArray(D)?D:[]),N(Array.isArray(q)?q:[])}).catch(f=>{m&&j(f instanceof Error?f.message:String(f))}).finally(()=>{m&&v(!1)}),()=>{m=!1}},[e,A]);const o=(r==null?void 0:r.status)==="running";return x.useEffect(()=>{if(!e||!o)return;let m=!0;const f=setInterval(async()=>{try{const[h,R]=await Promise.all([k.getSession(e),k.getSessionLogs(e)]);if(!m||!se(h))return;s(h),Array.isArray(R)&&n(R),h.status!=="running"&&C(D=>D+1)}catch{}},st);return()=>{m=!1,clearInterval(f)}},[e,o]),{session:r,sessionLogs:a,deviceLogs:l,debugLogs:i,profiling:u,loading:p,error:g,notFound:y,refresh:()=>C(m=>m+1)}}const ae=({buildId:e,buildName:r,sessionId:s})=>t.jsxs("nav",{"aria-label":"Breadcrumb",className:"flex items-center gap-2 px-4 py-2 border-b border-[var(--border)] bg-[var(--surface)]",children:[t.jsxs(Q,{to:"/builds",className:"inline-flex items-center gap-1.5 text-xs text-[var(--text-muted)] hover:text-[var(--text)] transition-colors",children:[t.jsx(Be,{className:"h-3.5 w-3.5"}),"Sessions"]}),t.jsx(I,{className:"h-3 w-3 text-[var(--text-dim)]"}),r&&t.jsxs(t.Fragment,{children:[t.jsx(Q,{to:`/builds/${e}`,className:"text-[11px] text-[var(--color-accent)] hover:underline",children:r}),t.jsx(I,{className:"h-3 w-3 text-[var(--text-dim)]"})]}),t.jsxs("span",{className:"font-mono text-[11px] text-[var(--text-muted)]",title:s,children:["#",s.slice(0,8)]})]}),nt={passed:{label:"Passed",Icon:Je,cls:"text-[var(--color-success)]"},failed:{label:"Failed",Icon:Ie,cls:"text-[var(--color-danger)]"},running:{label:"Running",Icon:Se,cls:"text-[var(--color-warning)]"},other:{Icon:Ae,cls:"text-[var(--text-dim)]"}};function K(e){const r=Oe(e),s=nt[r];return{bucket:r,...s,label:s.label??He(e||"unknown")}}const it=({session:e})=>{var l,c;const r=K(e.status),s=Me(e),a=[ve(e),e.device_version].filter(i=>i&&i!=="—").join(" "),n=[[De(e),a].filter(Boolean).join(" · "),be(e.ranOn),(l=e.owner)==null?void 0:l.name,Ee(e.startTime),ge(V(e))].filter(i=>i&&i!=="—");return t.jsxs("header",{className:"flex items-start justify-between gap-4 px-4 py-4 border-b border-[var(--border)] bg-[var(--surface)]",children:[t.jsxs("div",{className:"min-w-0",children:[t.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[t.jsxs("span",{className:`inline-flex items-center gap-1.5 text-xs font-medium shrink-0 ${r.cls}`,children:[t.jsx(r.Icon,{className:`h-3.5 w-3.5 ${r.bucket==="running"?"animate-spin":""}`,"aria-hidden":"true"}),r.label]}),t.jsx("h1",{className:`text-lg font-semibold truncate ${s.source==="name"?"text-[var(--text)]":"text-[var(--text-muted)]"}`,title:s.source==="name"?s.text:`${s.text}
|
|
2
|
-
Unnamed session. Set xe:options.name in its capabilities to name it.`,children:s.text})]}),t.jsx("p",{"data-testid":"outcome-meta",className:"mt-1 text-xs text-[var(--text-muted)] tabular-nums truncate",title:((c=e.owner)==null?void 0:c.email)??void 0,children:n.join(" · ")})]}),t.jsx("div",{className:"shrink-0",children:t.jsx(Ue,{sessionId:e.id,mode:"full",variant:"inline"})})]})};function ot(e,r=8){if(!e)return[];const s=e.split(/\r?\n/),a=[];for(const n of s){const l=n.trim();if(/^at\s+/.test(l)&&(a.push(l),a.length>=r))break}return a}function W(e){return e?e.split(/[_\s]+/).filter(Boolean).map(r=>r.charAt(0).toUpperCase()+r.slice(1).toLowerCase()).join(" "):""}function ne(e){if(!e)return{};try{const r=JSON.parse(e);return r&&typeof r=="object"&&!Array.isArray(r)?r:{}}catch{return{}}}function ie(e,r=200){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean")return String(e);try{const s=JSON.stringify(e);return s.length>r?s.slice(0,r)+"…":s}catch{return String(e)}}const F=e=>e.command_name||e.title||"command",J=e=>{const r=e.createdAt?Date.parse(e.createdAt):NaN;return Number.isFinite(r)?r:0},X=e=>[...e].sort((r,s)=>J(r)-J(s));function B(e){return e.is_success===!1||e.is_error===!0}function we(e){const r=[];let s=null;for(const a of X(e))a.is_healed&&a.healing_tier&&!r.includes(a.healing_tier)&&r.push(a.healing_tier),typeof a.duration=="number"&&(!s||a.duration>s.ms)&&(s={name:F(a),ms:a.duration});return{total:e.length,failed:e.filter(B).length,healed:e.filter(a=>a.is_healed).length,healedTiers:r,slowest:s,screenshots:e.filter(a=>a.screenshot).length}}const oe=(e,r)=>r?e?`${e}=${r}`:r:null;function lt(e){return X(e.filter(r=>r.is_healed)).map(r=>({id:r.id,command:F(r),original:oe(r.original_strategy,r.original_selector),healed:oe(r.healed_strategy,r.healed_selector),tier:r.healing_tier??null,confidence:typeof r.healing_confidence=="number"?Math.round(r.healing_confidence*100):null,at:r.createdAt}))}function ct(e){var r;if(!e.response)return null;try{const s=(r=JSON.parse(e.response))==null?void 0:r.value;if(!s||typeof s!="object")return null;const a=typeof s.message=="string"?s.message.trim():"";return a||(typeof s.error=="string"&&s.error?s.error:null)}catch{return null}}function dt(e){if(e.length===0)return{spanMs:0,bars:[]};const r=e.map(n=>{const l=J(n),c=typeof n.duration=="number"&&n.duration>0?n.duration:0;return{l:n,start:l-c,end:l,durationMs:c}}),s=Math.min(...r.map(n=>n.start)),a=Math.max(...r.map(n=>n.end));return{spanMs:Math.max(1,a-s),bars:r.sort((n,l)=>n.start-l.start).map(({l:n,start:l,durationMs:c})=>({id:n.id,label:F(n),startMs:l-s,durationMs:c,failed:B(n),healed:!!n.is_healed}))}}function xt(e){return X(e.filter(r=>r.screenshot)).map(r=>({id:r.id,path:r.screenshot,command:F(r),at:r.createdAt,failed:B(r)}))}function L(e){if(e==null||!Number.isFinite(e)||e<0)return"—";if(e<1e3)return`${Math.round(e)}ms`;if(e<6e4)return`${(e/1e3).toFixed(1)}s`;const r=Math.round(e/1e3);return`${Math.floor(r/60)}m ${r%60}s`}const mt=({session:e,commands:r})=>{var c,i;const s=K(e.status),a=we(r),n=ge(V(e)),l=s.bucket==="failed"?W(e.failure_category)||"No category recorded":s.bucket==="running"?`Running for ${n}`:`Ran ${n}`;return t.jsxs("div",{className:"grid grid-cols-4 gap-3",children:[t.jsx(E,{label:"Result",value:s.label,valueCls:s.cls,note:l,noteTone:s.bucket==="failed"?"bad":"neutral"}),t.jsx(E,{label:"Commands",value:a.total,note:a.failed>0?`${a.failed} failed`:"None failed",noteTone:a.failed>0?"bad":"neutral"}),t.jsx(E,{label:"Self-healing",value:a.healed>0?`${a.healed} healed`:"None",note:a.healed>0?a.healedTiers.join(", ")||"Tier not recorded":"No selector needed healing"}),t.jsx(E,{label:"Slowest command",value:L((c=a.slowest)==null?void 0:c.ms),note:((i=a.slowest)==null?void 0:i.name)??"No timings recorded"})]})};function ut(e){const r=e.filter(B),s=r[r.length-1];return s?{command:s.command_name||s.title||"command",message:ct(s)}:null}function ht(e,r,s,a,n){var u;const l=W(e.failure_category),c=((u=e.device_name)==null?void 0:u.trim())||"Unknown Device",i=e.device_version?` ${e.device_version}`:"",d=n?`${n.command}${n.message?`: ${n.message}`:""}`:"(no failed command recorded)";return[`**Session:** ${e.id}`,`**Build:** ${r} (#${s})`,`**Device:** ${c} (${e.node_id||"unknown-node"}) · ${e.device_platform||"unknown"}${i}`,`**Duration:** ${a}`,`**Failure category:** ${l||"(uncategorized)"}`,`**Failure reason:** ${e.failure_reason||"(not set)"}`,`**First failed command:** ${d}`,...e.ai_analysis?[`**AI analysis:** ${e.ai_analysis}`]:[]].join(`
|
|
3
|
-
`)}function ft({text:e}){return t.jsx(t.Fragment,{children:e.split(/(\*\*[^*]+\*\*|`[^`]+`)/g).map((r,s)=>/^\*\*[^*]+\*\*$/.test(r)?t.jsx("strong",{children:r.slice(2,-2)},s):/^`[^`]+`$/.test(r)?t.jsx("code",{className:"font-mono text-[11px] px-1 rounded bg-[var(--surface-2)]",children:r.slice(1,-1)},s):t.jsx(Ce.Fragment,{children:r},s))})}const O=({children:e})=>t.jsx("div",{className:"text-[11px] font-semibold text-[var(--text-dim)] mb-1",children:e}),pt=({session:e,buildName:r,buildId:s,commands:a,durationText:n})=>{var y;const{toast:l}=P(),[c,i]=x.useState(!1),d=W(e.failure_category),u=ut(a),N=ot(e.failure_reason),p=(y=e.ai_analysis)==null?void 0:y.trim(),v=!!p&&p.length>480,g=async()=>{const w=ht(e,r,s,n,u);try{await navigator.clipboard.writeText(w),l("Failure report copied","success")}catch{l("Clipboard unavailable","error")}},j=e.failure_category?`/xenon/runbooks/${encodeURIComponent(e.failure_category.toLowerCase())}`:void 0;return t.jsxs("section",{className:"rounded-lg border border-[var(--red)]/30 bg-[var(--surface)] overflow-hidden",children:[t.jsxs("header",{className:"flex items-center justify-between gap-4 px-4 py-3 border-b border-[var(--border)] bg-[var(--red)]/5",children:[t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx(_e,{className:"h-4 w-4 text-[var(--red)]","aria-hidden":"true"}),t.jsx("h2",{className:"text-sm font-semibold text-[var(--red)]",children:"Why it failed"}),d&&t.jsx("span",{className:"text-[11px] text-[var(--text-dim)] ml-2",children:d})]}),t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsxs("button",{type:"button",onClick:g,className:"inline-flex items-center gap-1.5 h-7 px-2.5 rounded-md border border-[var(--border)] text-xs text-[var(--text)] hover:border-[var(--border-strong)] hover:bg-[var(--surface-2)] transition-colors",children:[t.jsx(z,{className:"h-3 w-3"}),"Copy"]}),t.jsxs("a",{...j?{href:j,target:"_blank",rel:"noreferrer"}:{"aria-disabled":!0,onClick:w=>w.preventDefault()},className:`inline-flex items-center gap-1.5 h-7 px-2.5 rounded-md border border-[var(--border)] text-xs text-[var(--text)] hover:border-[var(--border-strong)] hover:bg-[var(--surface-2)] transition-colors ${j?"":"opacity-50 cursor-not-allowed"}`,title:j?"Open runbook in new tab":"No runbook — failure category not set",children:[t.jsx(Ve,{className:"h-3 w-3"}),"Open runbook"]})]})]}),t.jsxs("div",{className:"px-4 py-4 space-y-4",children:[t.jsxs("div",{children:[t.jsx(O,{children:"Reason"}),t.jsx("div",{className:"text-sm text-[var(--text)] break-words",children:e.failure_reason||t.jsx("span",{className:"text-[var(--text-dim)]",children:"(not set)"})})]}),t.jsxs("div",{children:[t.jsx(O,{children:"First failed command"}),u?t.jsxs("div",{className:"flex items-baseline gap-2 min-w-0 text-sm",children:[t.jsx("span",{className:"font-mono text-xs text-[var(--color-danger)] shrink-0",children:u.command}),u.message&&t.jsx("span",{className:"text-[var(--text-muted)] break-words min-w-0",children:u.message})]}):t.jsx("span",{className:"text-sm text-[var(--text-dim)]",children:"No failed command recorded."})]}),p&&t.jsxs("div",{"data-testid":"ai-analysis",children:[t.jsx(O,{children:t.jsxs("span",{className:"inline-flex items-center gap-1.5",children:[t.jsx(je,{className:"h-3 w-3","aria-hidden":"true"}),"AI analysis"]})}),t.jsx("div",{className:`text-sm text-[var(--text)] whitespace-pre-wrap break-words ${v&&!c?"line-clamp-6":""}`,children:t.jsx(ft,{text:p})}),v&&t.jsx("button",{type:"button",onClick:()=>i(w=>!w),"aria-expanded":c,className:"mt-1 text-xs text-[var(--color-accent)] hover:underline",children:c?"Show less":"Show all"})]}),N.length>0&&t.jsxs("div",{children:[t.jsx(O,{children:"Stack trace"}),t.jsx("pre",{className:"rounded-md border border-[var(--border)] bg-[var(--bg)] p-3 font-mono text-[11px] leading-relaxed text-[var(--color-accent)] overflow-x-auto",children:N.join(`
|
|
4
|
-
`)})]})]})]})};function vt(e){return e>=1e3?`${(e/1e3).toFixed(1).replace(/\.0$/,"")}k`:String(e)}function H(e){const r=e.timestamp;if(!r){const c=e.createdAt;return c?H({timestamp:c}):"--:--:--"}const s=typeof r=="number"?new Date(r):new Date(String(r));if(Number.isNaN(s.getTime()))return"--:--:--";const a=String(s.getHours()).padStart(2,"0"),n=String(s.getMinutes()).padStart(2,"0"),l=String(s.getSeconds()).padStart(2,"0");return`${a}:${n}:${l}`}function bt(e){if(e.is_healed===!0)return{label:String(e.command_name||"healed"),tone:"amber"};if(e.is_success===!1||e.is_error===!0)return{label:String(e.command_name||e.title||"error"),tone:"red"};if(e.is_success===!0)return{label:String(e.command_name||e.title||"ok"),tone:"green"};const r=e.title||e.event||e.command_name;return typeof r=="string"?/stop|fail|error/i.test(r)?{label:r,tone:"red"}:/start|begin|init/i.test(r)?{label:r,tone:"green"}:{label:r,tone:"blue"}:{label:"log",tone:"neutral"}}function gt(e){if(e==null)return"";if(typeof e=="string")try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}try{return JSON.stringify(e,null,2)}catch{return String(e)}}function jt(e){if(!e||typeof e!="string")return[{text:String(e??""),kind:"plain"}];const r=[],s=/("(?:\\.|[^"\\])*")(\s*:)|("(?:\\.|[^"\\])*")|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|\b(true|false)\b|\b(null)\b|([{}[\],])/g;let a=0,n;for(;(n=s.exec(e))!==null;)n.index>a&&r.push({text:e.slice(a,n.index),kind:"plain"}),n[1]&&n[2]!==void 0?(r.push({text:n[1],kind:"key"}),r.push({text:n[2],kind:"punct"})):n[3]?r.push({text:n[3],kind:"string"}):n[4]?r.push({text:n[4],kind:"number"}):n[5]?r.push({text:n[5],kind:"boolean"}):n[6]?r.push({text:n[6],kind:"null"}):n[7]&&r.push({text:n[7],kind:"punct"}),a=s.lastIndex;return a<e.length&&r.push({text:e.slice(a),kind:"plain"}),r}function Nt(e){const r=e,s=typeof r.title=="string"&&r.title.trim()?r.title.trim():null,a=typeof r.command_name=="string"&&r.command_name.trim()?r.command_name.trim():null;return s??a??"Command"}function le(e){const r=e.subtitle;return typeof r=="string"&&r.trim()?r.trim():null}function yt(e,r){return r?e.filter(s=>s.is_success===!1||s.is_error===!0):e}const wt=({commands:e})=>{const r=lt(e);return r.length===0?null:t.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[t.jsxs("header",{className:"flex items-center gap-2 px-4 py-3 border-b border-[var(--border)]",children:[t.jsx(je,{className:"h-4 w-4 text-[var(--color-warning)]","aria-hidden":"true"}),t.jsx("h2",{className:"text-sm font-semibold text-[var(--text)]",children:"Self-healing"}),t.jsxs("span",{className:"text-[11px] text-[var(--text-muted)]",children:[r.length," selector",r.length===1?"":"s"," healed. Update the test so the next run doesn't need to."]})]}),t.jsxs("table",{className:"w-full table-fixed text-left",children:[t.jsx("thead",{children:t.jsxs("tr",{className:"border-b border-[var(--border)] text-[11px] text-[var(--text-dim)]",children:[t.jsx("th",{className:"px-4 py-2 font-medium w-[88px]",children:"Time"}),t.jsx("th",{className:"px-3 py-2 font-medium w-[128px]",children:"Command"}),t.jsx("th",{className:"px-3 py-2 font-medium",children:"Asked for"}),t.jsx("th",{className:"w-[28px]",children:t.jsx("span",{className:"sr-only",children:"healed to"})}),t.jsx("th",{className:"px-3 py-2 font-medium",children:"Healed to"}),t.jsx("th",{className:"px-3 py-2 font-medium w-[112px]",children:"Tier"}),t.jsx("th",{className:"px-4 py-2 font-medium w-[96px] text-right",children:"Confidence"})]})}),t.jsx("tbody",{children:r.map((s,a)=>t.jsxs("tr",{className:"border-b border-[var(--border)] last:border-b-0 align-top",children:[t.jsx("td",{className:"px-4 py-2 text-xs text-[var(--text-muted)] tabular-nums",children:H({timestamp:s.at??null})}),t.jsx("td",{className:"px-3 py-2 text-xs text-[var(--text)] truncate",children:s.command}),t.jsx("td",{className:"px-3 py-2 font-mono text-[11px] text-[var(--text-muted)] break-all",children:s.original??"—"}),t.jsx("td",{className:"py-2 text-[var(--text-dim)]",children:t.jsx(ze,{className:"h-3.5 w-3.5","aria-hidden":"true"})}),t.jsx("td",{className:"px-3 py-2 font-mono text-[11px] text-[var(--text)] break-all",children:s.healed??"—"}),t.jsx("td",{className:"px-3 py-2 text-xs text-[var(--text)]",children:s.tier??"—"}),t.jsx("td",{className:"px-4 py-2 text-xs text-[var(--text)] tabular-nums text-right",children:s.confidence===null?"—":`${s.confidence}%`})]},s.id??a))})]})]})},kt=1e4,ce="var(--color-info)",de="var(--color-accent)",St="var(--border-strong)";function xe(e){return e>=1024?te(e/1024)*1024:te(e)}const me=e=>{let r=0;for(const s of e)for(const a of s.values)a!==null&&a>r&&(r=a);return r},U=({title:e,series:r,times:s,yMax:a,format:n,ariaLabel:l})=>t.jsxs("section",{"aria-label":e,children:[t.jsxs("div",{className:"mb-2 flex flex-wrap items-baseline gap-x-4 gap-y-1",children:[t.jsx("h3",{className:"text-xs font-semibold text-[var(--text)]",children:e}),r.map(c=>{const i=Xe(c.values);return t.jsxs("span",{className:"flex min-w-0 items-center gap-1.5 text-[11px] tabular-nums text-[var(--text-muted)]",children:[t.jsx("span",{className:"inline-block h-0.5 w-3 shrink-0",style:{background:c.color}}),t.jsx("span",{className:"max-w-[16rem] truncate",title:c.label,children:c.label}),i&&(c.dashed?t.jsx("span",{children:n(i.peak)}):t.jsxs("span",{children:["peak ",n(i.peak)," · avg ",n(i.average)]}))]},c.key)})]}),t.jsx(Ye,{times:s,series:r,yMax:a,formatValue:n,ariaLabel:l})]}),_t=({sessionId:e,running:r,hasTrace:s})=>{const[a,n]=x.useState(null),[l,c]=x.useState(!1);x.useEffect(()=>{let o=!0;const m=()=>k.getSessionMetrics(e).then(h=>{o&&(n(We(h)),c(!0))}).catch(()=>{o&&c(!0)});if(m(),!r)return()=>{o=!1};const f=setInterval(()=>void m(),kt);return()=>{o=!1,clearInterval(f)}},[e,r]);const i=(a==null?void 0:a.samples)??[],d=x.useMemo(()=>i.map(o=>{var m;return o.t-(((m=i[0])==null?void 0:m.t)??0)}),[i]),u=new Set(i.map(o=>o.app).filter(o=>!!o)),N=u.size>1?"Foreground app":Array.from(u)[0]??(a==null?void 0:a.appId)??"App",p=i.map(o=>o.app??null),v=o=>o>0&&!!p[o]&&!!p[o-1]&&p[o]!==p[o-1],g=[],j=[],y=[];a&&(a.series.deviceCpu&&g.push({key:"deviceCpu",label:"Device",color:ce,values:i.map(o=>o.deviceCpu)}),a.series.appCpu&&g.push({key:"appCpu",label:N,color:de,values:i.map((o,m)=>v(m)?null:o.appCpu),pointLabels:p}),a.series.appMem&&j.push({key:"appMem",label:N,color:de,values:i.map((o,m)=>v(m)?null:o.appMemMb),pointLabels:p}),a.series.deviceMem&&(y.push({key:"deviceMem",label:"Device used",color:ce,values:i.map(o=>o.deviceMemMb)}),y.push({key:"deviceTotal",label:"Device total",color:St,values:i.map(o=>o.deviceMemTotalMb),dashed:!0})));const w=r&&(a==null?void 0:a.recording)==="off",A=(a==null?void 0:a.recording)==="stopped",C="Recording stopped: the device stopped responding.";let b;return l?w?b=t.jsx(M,{title:"Performance isn't recorded for this session",description:"CPU and memory are recorded for Android devices and iPhones. This session runs on an iOS simulator or on a machine that needs updating, or recording is turned off."}):i.length===0&&!r?b=t.jsx(M,{title:"No performance data for this session",description:"CPU and memory are recorded for Android devices and iPhones. This session ran on an iOS simulator or on a machine that needed updating, before recording was available, or with recording turned off."}):i.length<2?b=t.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:A?C:r?"Collecting… The first figures appear within 10 seconds.":"Too few figures were recorded to draw a chart."}):b=t.jsxs("div",{className:"space-y-4",children:[A&&t.jsxs("p",{className:"text-xs text-[var(--color-warning)]",children:[C," The data ends there."]}),(a==null?void 0:a.platform)==="ios"&&t.jsxs("p",{className:"text-xs text-[var(--text-dim)]",children:["On iPhones, only the device's overall CPU is recorded.",s?" For more detail, download the Performance trace under Details.":""]}),g.length>0&&t.jsx(U,{title:"CPU",series:g,times:d,yMax:100,format:Ke,ariaLabel:"CPU over the session"}),j.length>0&&t.jsx(U,{title:"App memory",series:j,times:d,yMax:xe(me(j)),format:ee,ariaLabel:"App memory over the session"}),y.length>0&&t.jsx(U,{title:"Device memory",series:y,times:d,yMax:xe(me(y)),format:ee,ariaLabel:"Device memory over the session"})]}):b=t.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:"Loading performance…"}),t.jsx(Ne,{header:t.jsxs("span",{className:"flex items-center gap-2",children:[t.jsx(qe,{size:14})," Performance"]}),children:b})},Ct=({session:e})=>{const r=e.status==="running",s=!!e.video_recording;return t.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[t.jsxs("header",{className:"px-3 py-2 border-b border-[var(--border)] flex items-center justify-between",children:[t.jsx("span",{className:"text-[11px] font-semibold text-[var(--text-dim)]",children:"Recording"}),r&&t.jsxs("span",{className:"inline-flex items-center gap-1.5 text-[11px] text-[var(--red)]",children:[t.jsx("span",{className:"h-1.5 w-1.5 rounded-full bg-[var(--red)] pulse-dot"}),"Live"]})]}),t.jsx("div",{className:"aspect-video bg-[var(--bg)] flex items-center justify-center",children:r&&e.has_live_video?t.jsx("img",{src:k.getSessionLiveVideoUrl(e.id),alt:`Live view of session ${e.id}`,className:"w-full h-full object-contain"}):s?t.jsx("video",{controls:!0,src:k.getAssetUrl(e.video_recording),className:"w-full h-full object-contain"}):t.jsxs("div",{className:"flex flex-col items-center text-center px-6 py-8 gap-2",children:[t.jsx("div",{className:"h-10 w-10 rounded-full bg-[var(--surface-2)] border border-[var(--border)] flex items-center justify-center",children:t.jsx(Ge,{className:"h-5 w-5 text-[var(--text-dim)]"})}),t.jsx("div",{className:"text-sm text-[var(--text)] font-medium",children:"No video available"}),t.jsx("div",{className:"text-xs text-[var(--text-dim)] max-w-xs",children:e.status==="failed"?"Session failed before recording started.":e.video_recording_enabled===!1?"Recording was disabled for this session.":"No recording was captured."})]})})]})},S=({label:e,children:r,title:s,mono:a})=>t.jsxs("div",{className:"flex items-baseline justify-between gap-3 px-3 py-2",children:[t.jsx("dt",{className:"text-xs text-[var(--text-muted)] shrink-0",children:e}),t.jsx("dd",{className:`text-xs text-[var(--text)] text-right min-w-0 break-all ${a?"font-mono text-[11px]":""}`,title:s,children:r})]}),Rt=({session:e,buildName:r})=>{var c,i;const{toast:s}=P(),a=be(e.ranOn),n=e.performance_trace?k.getAssetUrl(e.performance_trace):"",l=async()=>{try{await navigator.clipboard.writeText(e.id),s("Session ID copied","success")}catch{s("Clipboard unavailable","error")}};return t.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[t.jsx("header",{className:"px-3 py-2 border-b border-[var(--border)]",children:t.jsx("h2",{className:"text-[11px] font-semibold text-[var(--text-dim)]",children:"Details"})}),t.jsxs("dl",{className:"divide-y divide-[var(--border)]",children:[t.jsx(S,{label:"Session ID",mono:!0,children:t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{children:e.id}),t.jsx("button",{type:"button",onClick:l,"aria-label":"Copy session ID",className:"p-0.5 rounded text-[var(--text-dim)] hover:text-[var(--text)]",children:t.jsx(z,{className:"h-3 w-3"})})]})}),r&&t.jsx(S,{label:"Build",children:r}),t.jsx(S,{label:"Device UDID",mono:!0,children:e.device_udid||"—"}),t.jsx(S,{label:"Platform",children:[ve(e),e.device_version].filter(d=>d&&d!=="—").join(" ")||"—"}),t.jsx(S,{label:"Ran on",title:e.node_id?`Node ${e.node_id}`:void 0,children:a??"Not known"}),t.jsx(S,{label:"Run by",title:(c=e.owner)==null?void 0:c.email,children:((i=e.owner)==null?void 0:i.name)??"Not recorded"}),t.jsx(S,{label:"Started",children:Z(e.startTime)}),t.jsx(S,{label:"Ended",children:e.endTime?Z(e.endTime):"—"}),e.tags&&t.jsx(S,{label:"Tags",children:e.tags}),n&&t.jsx(S,{label:"iOS trace",children:t.jsxs("a",{href:n,className:"inline-flex items-center gap-1 text-[var(--color-accent)] hover:underline",children:[t.jsx(ye,{className:"h-3 w-3","aria-hidden":"true"}),"Performance trace"]})})]})]})},ue={desired:"Requested",session:"Actual"},Tt=({session:e})=>{const[r,s]=x.useState("desired"),a=ne(e.desired_capabilities),n=ne(e.session_capabilities),c=Object.entries(r==="desired"?a:n);return t.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] overflow-hidden",children:[t.jsx("header",{className:"flex items-center border-b border-[var(--border)]",children:["desired","session"].map(i=>t.jsx("button",{type:"button",onClick:()=>s(i),"aria-pressed":r===i,className:`flex-1 h-9 text-[11px] font-semibold transition-colors border-b-2 ${r===i?"border-[var(--color-accent)] text-[var(--text)]":"border-transparent text-[var(--text-muted)] hover:text-[var(--text)]"}`,children:ue[i]},i))}),c.length===0?t.jsxs("div",{className:"px-3 py-6 text-center text-xs text-[var(--text-dim)]",children:["No ",ue[r].toLowerCase()," capabilities recorded for this session."]}):t.jsx("dl",{className:"divide-y divide-[var(--border)]",children:c.map(([i,d])=>t.jsxs("div",{className:"grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] items-baseline gap-3 px-3 py-2",children:[t.jsx("dt",{className:"text-xs text-[var(--text-muted)] break-all",children:i}),t.jsx("dd",{className:"font-mono text-xs text-[var(--text)] truncate text-right min-w-0",title:ie(d,500),children:ie(d)})]},i))})]})},$t=({tabs:e,active:r,onChange:s,errorsOnly:a,onErrorsOnlyChange:n,showErrorsOnly:l=!0})=>{const c=e.filter(i=>!(i.hideWhenEmpty&&i.count===0));return t.jsxs("div",{className:"flex items-center justify-between gap-4 px-3 border-b border-[var(--border)] bg-[var(--surface)]",children:[t.jsx("div",{className:"flex items-center gap-1 overflow-x-auto",children:c.map(i=>{const d=i.key===r;return t.jsxs("button",{type:"button",onClick:()=>s(i.key),"aria-pressed":d,"data-empty":i.count===0?"true":"false",className:`relative inline-flex items-center gap-1.5 h-9 px-3 text-xs whitespace-nowrap transition-colors ${d?"text-[var(--text)]":i.count===0?"text-[var(--text-dim)] hover:text-[var(--text-muted)]":"text-[var(--text-muted)] hover:text-[var(--text)]"}`,children:[t.jsx("span",{children:i.label}),i.count>0&&t.jsx("span",{className:`text-[10px] tabular-nums ${d?"text-[var(--text-muted)]":"text-[var(--text-dim)]"}`,children:vt(i.count)}),d&&t.jsx("span",{className:"absolute left-2 right-2 bottom-0 h-[2px] bg-[var(--color-accent)] rounded-t"})]},i.key)})}),l&&t.jsxs("label",{className:"inline-flex items-center gap-2 text-[11px] text-[var(--text-muted)] cursor-pointer select-none whitespace-nowrap",children:[t.jsx("input",{type:"checkbox",checked:a,onChange:i=>n(i.target.checked),"aria-label":"Show only error rows"}),"Errors only"]})]})},At={key:"text-[var(--blue)]",string:"text-[var(--color-accent)]",number:"text-[var(--amber)]",boolean:"text-[var(--amber)]",null:"text-[var(--text-dim)] italic",punct:"text-[var(--text-muted)]",plain:"text-[var(--text)]"},Mt=({value:e,maxHeightPx:r})=>{const s=gt(e);if(!s)return t.jsx("span",{className:"text-[var(--text-dim)] text-xs",children:"(empty)"});let a;try{a=jt(s)}catch{a=[{text:s,kind:"plain"}]}return t.jsx("pre",{className:"font-mono text-[11px] leading-relaxed bg-[var(--bg)] border border-[var(--border)] rounded-md p-3 overflow-x-auto whitespace-pre",style:r?{maxHeight:r,overflowY:"auto"}:void 0,children:a.map((n,l)=>t.jsx("span",{className:At[n.kind],children:n.text},l))})},Dt={green:"bg-[var(--color-success)]",red:"bg-[var(--red)]",amber:"bg-[var(--amber)]",blue:"bg-[var(--blue)]",neutral:"bg-[var(--text-dim)]"},Et={green:"text-[var(--color-success)]",red:"text-[var(--red)]",amber:"text-[var(--amber)]",blue:"text-[var(--blue)]",neutral:"text-[var(--text-muted)]"},Ot=({log:e})=>{const[r,s]=x.useState(!1),{toast:a}=P(),n=H(e),l=bt(e),c=async i=>{i.stopPropagation();try{const d=e.body??e,u=typeof d=="string"?d:JSON.stringify(d,null,2);await navigator.clipboard.writeText(u),a("Log entry copied","success")}catch{a("Clipboard unavailable","error")}};return t.jsxs("div",{className:"border-b border-[var(--border)] last:border-b-0",children:[t.jsxs("button",{type:"button",onClick:()=>s(i=>!i),"aria-expanded":r,className:"group w-full flex items-center gap-2 px-3 py-2 text-left hover:bg-[var(--surface-2)]/60 transition-colors",children:[t.jsx(I,{className:`h-3 w-3 text-[var(--text-dim)] transition-transform shrink-0 ${r?"rotate-90":""}`}),t.jsx("span",{className:"font-mono text-[11px] text-[var(--text-muted)] w-[64px] shrink-0",children:n}),t.jsx("span",{className:`h-1.5 w-1.5 rounded-full shrink-0 ${Dt[l.tone]}`}),t.jsx("span",{className:`font-mono text-[11px] font-medium shrink-0 ${Et[l.tone]}`,children:l.label}),t.jsxs("span",{className:"min-w-0 flex-1 flex items-baseline gap-2 truncate",children:[t.jsx("span",{className:"text-[12px] text-[var(--text)] shrink-0",children:Nt(e)}),le(e)&&t.jsx("span",{className:"font-mono text-[11px] text-[var(--text-dim)] truncate min-w-0",children:le(e)})]}),typeof e.duration=="number"&&t.jsx("span",{className:"text-[11px] tabular-nums text-[var(--text-muted)] shrink-0",children:L(e.duration)}),t.jsx("span",{role:"button",tabIndex:0,onClick:c,onKeyDown:i=>{(i.key==="Enter"||i.key===" ")&&c(i)},"aria-label":"Copy log entry",className:"opacity-0 group-hover:opacity-100 p-1 rounded text-[var(--text-dim)] hover:text-[var(--text)] transition-opacity shrink-0",children:t.jsx(z,{className:"h-3 w-3"})})]}),r&&t.jsx("div",{className:"px-3 pb-3 pl-9",children:t.jsx(Mt,{value:e.body??e,maxHeightPx:400})})]})},Lt=(e,r)=>e?"bg-[var(--color-danger)]":r?"bg-[var(--color-warning)]":"bg-[var(--color-success)]",Pt=({commands:e})=>{const{spanMs:r,bars:s}=dt(e);return s.length===0?t.jsx("div",{className:"px-4 py-10 text-center text-xs text-[var(--text-dim)]",children:"No commands recorded for this session."}):t.jsxs("div",{className:"px-3 py-3",children:[t.jsxs("div",{className:"flex items-center justify-between pb-2 text-[11px] text-[var(--text-dim)] tabular-nums",children:[t.jsxs("span",{children:[s.length," command",s.length===1?"":"s"," over ",L(r)]}),t.jsxs("span",{className:"inline-flex items-center gap-3",children:[t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{className:"h-2 w-2 rounded-sm bg-[var(--color-danger)]"})," Failed"]}),t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{className:"h-2 w-2 rounded-sm bg-[var(--color-warning)]"})," Healed"]})]})]}),t.jsx("ol",{className:"space-y-1",children:s.map((a,n)=>t.jsxs("li",{"data-testid":"timeline-bar","data-command":a.label,"data-failed":a.failed?"true":"false",className:"grid grid-cols-[128px_minmax(0,1fr)_64px] items-center gap-2",children:[t.jsx("span",{className:"truncate text-[11px] font-mono text-[var(--text-muted)]",title:a.label,children:a.label}),t.jsx("span",{className:"relative h-3 rounded-sm bg-[rgb(var(--rgb-fg)/0.05)]",children:t.jsx("span",{className:`absolute inset-y-0 rounded-sm ${Lt(a.failed,a.healed)}`,style:{left:`${a.startMs/r*100}%`,width:`max(2px, ${a.durationMs/r*100}%)`}})}),t.jsx("span",{className:"text-right text-[11px] tabular-nums text-[var(--text-muted)]",children:a.durationMs>0?L(a.durationMs):"—"})]},a.id??n))})]})},Ft=({commands:e})=>{const r=xt(e);return r.length===0?t.jsxs("div",{className:"px-4 py-10 text-center text-xs text-[var(--text-dim)]",children:["No screenshots were kept. A failed command keeps one unless"," ",t.jsx("code",{className:"font-mono",children:"xe:options.screenshotOnFailure"})," is false; set"," ",t.jsx("code",{className:"font-mono",children:"xe:options.screenshotOnEveryCommand"})," to keep one for every command."]}):t.jsx("ul",{className:"grid grid-cols-[repeat(auto-fill,minmax(140px,1fr))] gap-3 p-3",children:r.map((s,a)=>{const n=k.getAssetUrl(s.path),l=H({timestamp:s.at??null});return t.jsx("li",{children:t.jsxs("a",{href:n,target:"_blank",rel:"noreferrer",className:`block rounded-md border overflow-hidden hover:border-[var(--border-strong)] ${s.failed?"border-[var(--color-danger)]":"border-[var(--border)]"}`,children:[t.jsx("img",{src:n,alt:`${s.command} at ${l}`,loading:"lazy",className:"block w-full aspect-[9/16] object-contain bg-[rgb(var(--rgb-fg)/0.03)]"}),t.jsxs("span",{className:"flex items-center justify-between gap-2 px-2 py-1 text-[11px]",children:[t.jsx("span",{className:`truncate font-mono ${s.failed?"text-[var(--color-danger)]":"text-[var(--text)]"}`,children:s.command}),t.jsx("span",{className:"tabular-nums text-[var(--text-dim)] shrink-0",children:l})]})]})},s.id??a)})})},he=["commands","device","debug","profiling"],Bt=({sessionLogs:e,deviceLogs:r,debugLogs:s,profiling:a})=>{const[n,l]=x.useState("commands"),[c,i]=x.useState(!1),d=e,u=x.useMemo(()=>we(d).screenshots,[d]),N=x.useMemo(()=>[{key:"commands",label:"Commands",count:e.length},{key:"timeline",label:"Timeline",count:e.length},{key:"screenshots",label:"Screenshots",count:u},{key:"device",label:"Device logs",count:r.length},{key:"debug",label:"Debug logs",count:s.length},{key:"profiling",label:"System profiling",count:a.length,hideWhenEmpty:!0}],[e.length,u,r.length,s.length,a.length]),p=x.useMemo(()=>{let v=[];return n==="commands"?v=e:n==="device"?v=r:n==="debug"?v=s:n==="profiling"&&(v=a),yt(v,c)},[n,e,r,s,a,c]);return t.jsxs("section",{className:"rounded-lg border border-[var(--border)] bg-[var(--surface)] flex flex-col min-h-[280px] max-h-[640px]",children:[t.jsx($t,{tabs:N,active:n,onChange:l,errorsOnly:c,onErrorsOnlyChange:i,showErrorsOnly:he.includes(n)}),t.jsxs("div",{className:"flex-1 overflow-y-auto",children:[n==="timeline"&&t.jsx(Pt,{commands:d}),n==="screenshots"&&t.jsx(Ft,{commands:d}),he.includes(n)&&(p.length===0?t.jsx("div",{className:"px-4 py-10 text-center text-xs text-[var(--text-dim)]",children:c?"No error rows in this tab.":"No log entries in this tab."}):p.map((v,g)=>t.jsx(Ot,{log:v},v.id??g)))]})]})};function ke(e){if(!e)return"failed";switch(e){case"HTTPS_CLIENT_ERROR":case"HTTPS_SERVER_ERROR":case"OPEN_HTTPS_SERVER_ERROR":case"ON_CONNECT_ERROR":return"tls";case"PROXY_TO_SERVER_REQUEST_ERROR":return"net";default:return"failed"}}const Y="/xenon/api/interceptor";async function Ht(e){const r=await fetch(`${Y}/sessions/${encodeURIComponent(e)}/requests`);if(r.status===404){const a=await r.json().catch(()=>({}));return{active:!1,error:a==null?void 0:a.error}}return r.ok?{active:!0,requests:(await r.json()).requests||[]}:{active:!1,error:`request failed (${r.status})`}}async function Ut(e,r){const s=await fetch(`${Y}/sessions/${encodeURIComponent(e)}/requests/${encodeURIComponent(r)}`);return s.ok?s.json():null}function It(e){return`${Y}/sessions/${encodeURIComponent(e)}/har`}function Jt(e){return e>=200&&e<300?"ready":e>=300&&e<400?"neutral":e>=400&&e<500?"busy":e>=500?"error":"neutral"}const fe=({headers:e})=>{const r=Object.entries(e||{});return r.length===0?t.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:"No headers"}):t.jsx("div",{className:"space-y-0.5 font-mono text-xs",children:r.map(([s,a])=>t.jsxs("div",{className:"flex gap-2",children:[t.jsxs("span",{className:"text-[var(--text-dim)] shrink-0",children:[s,":"]}),t.jsx("span",{className:"break-all",children:a})]},s))})},pe=({body:e})=>{if(e==null||e==="")return t.jsx("div",{className:"text-xs text-[var(--text-dim)]",children:"No body"});let r=e;try{r=JSON.stringify(JSON.parse(e),null,2)}catch{}return t.jsx("pre",{className:"font-mono text-xs whitespace-pre-wrap break-all bg-[var(--surface-2,var(--surface))] rounded p-2 max-h-64 overflow-auto",children:r})},Vt=({request:e,onClose:r})=>{if(!e)return null;const s=t.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[t.jsx(_,{tone:"accent",children:e.method}),e.failed?t.jsx(_,{tone:"error",children:ke(e.failureKind)}):t.jsx(_,{tone:Jt(e.resStatus),children:e.resStatus||"—"}),t.jsx("span",{className:"font-mono text-xs truncate",title:e.url,children:e.url})]});return t.jsx(tt,{open:!!e,title:s,onClose:r,width:720,children:t.jsxs("div",{className:"space-y-4 text-xs",children:[e.failed&&t.jsxs("div",{className:"border border-[var(--red)] rounded p-2 bg-[rgb(var(--rgb-red-600)/0.08)]",children:[t.jsx("div",{className:"text-[var(--red)] text-[11px] mb-1",children:"Failure"}),t.jsx("div",{className:"text-xs",children:e.failureReason||"Request failed"}),e.failureKind&&t.jsx("div",{className:"font-mono text-[10px] text-[var(--text-dim)] mt-1",children:e.failureKind})]}),!e.failed&&t.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Duration"}),t.jsxs("div",{className:"font-mono",children:[e.durationMs," ms"]})]}),t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Flags"}),t.jsxs("div",{className:"flex gap-1",children:[e.mocked&&t.jsx(_,{tone:"accent",children:"mocked"}),e.modified&&t.jsx(_,{tone:"busy",children:"modified"}),!e.mocked&&!e.modified&&t.jsx("span",{className:"text-[var(--text-dim)]",children:"passthrough"})]})]})]}),e.commandHint&&t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Triggered by"}),t.jsx("div",{className:"font-mono",children:e.commandHint.commandName})]}),!e.failed&&t.jsxs(t.Fragment,{children:[t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Request Headers"}),t.jsx(fe,{headers:e.reqHeaders})]}),t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Request Body"}),t.jsx(pe,{body:e.reqBody})]}),t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Response Headers"}),t.jsx(fe,{headers:e.resHeaders})]}),t.jsxs("div",{children:[t.jsx("div",{className:"text-[var(--text-dim)] text-[11px] mb-1",children:"Response Body"}),t.jsx(pe,{body:e.resBody})]})]})]})})};function zt(e){return e?new Date(e).toLocaleTimeString(void 0,{hour12:!1}):"—"}function Kt(e){return e>=200&&e<300?"ready":e>=300&&e<400?"neutral":e>=400&&e<500?"busy":e>=500?"error":"neutral"}const Wt=({sessionId:e,sessionEnded:r})=>{const[s,a]=x.useState(null),[n,l]=x.useState(null),[c,i]=x.useState([]),[d,u]=x.useState(!0),[N,p]=x.useState(null),v=x.useRef(new Set),{on:g,isConnected:j}=Re();x.useEffect(()=>{let o=!0;return u(!0),Ht(e).then(m=>{if(!o)return;a(m.active),l(m.error||null);const f=m.requests||[];i(f),v.current=new Set(f.map(h=>h.id))}).finally(()=>{o&&u(!1)}),()=>{o=!1}},[e]),x.useEffect(()=>{if(r)return;const o=g("interceptor_request",h=>{!h||h.sessionId!==e||v.current.has(h.id)||(v.current.add(h.id),i(R=>[h,...R]),a(!0))}),m=g("interceptor_session_started",h=>{(h==null?void 0:h.sessionId)===e&&a(!0)}),f=g("interceptor_session_stopped",h=>{(h==null?void 0:h.sessionId)===e&&a(!1)});return()=>{o(),m(),f()}},[g,e,r,j]);const y=x.useMemo(()=>[...c].sort((o,m)=>m.ts-o.ts),[c]),w=async o=>{if(p(o),s){const m=await Ut(e,o.id);m&&p(m)}},A=s&&c.length>0?t.jsxs("a",{href:It(e),className:"inline-flex items-center gap-1 text-xs text-[var(--text-dim)] hover:text-[var(--text)]",title:"Download HAR",children:[t.jsx(ye,{className:"h-3 w-3"}),"HAR"]}):null,C=t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx(rt,{className:"h-3.5 w-3.5 text-[var(--text-dim)]"}),t.jsx("span",{children:"Network"}),s&&c.length>0&&t.jsxs("span",{className:"text-xs text-[var(--text-dim)]",children:["(",c.length,")"]})]});let b;return d?b=t.jsx("div",{className:"p-4 text-xs text-[var(--text-dim)]",children:"Loading…"}):s===!1&&r?b=t.jsx(M,{title:"No network capture",description:"Network interception was not enabled for this session."}):s===!1?b=t.jsx(M,{title:"Network interception disabled",description:"Enable interceptor.enabled in plugin config to capture requests for Android sessions."}):n?b=t.jsxs("div",{className:"p-4 text-xs text-[var(--red)]",children:["Failed to load requests: ",n]}):y.length===0?b=t.jsx(M,{title:"Waiting for requests",description:r?"No HTTP traffic was captured during this session.":"Captured requests will appear here as the device makes calls."}):b=t.jsx("div",{className:"overflow-auto max-h-96",children:t.jsxs(Qe,{children:[t.jsx(Ze,{children:t.jsxs(re,{children:[t.jsx(T,{style:{width:80},children:"Time"}),t.jsx(T,{style:{width:70},children:"Method"}),t.jsx(T,{style:{width:70},children:"Status"}),t.jsx(T,{children:"Host"}),t.jsx(T,{children:"Path"}),t.jsx(T,{style:{width:80},children:"Duration"}),t.jsx(T,{style:{width:90},children:"Flags"})]})}),t.jsx(et,{children:y.map(o=>t.jsxs(re,{onClick:()=>w(o),style:{cursor:"pointer"},title:o.failed?o.failureReason:void 0,children:[t.jsx($,{className:"font-mono text-[11px]",children:zt(o.ts)}),t.jsx($,{children:t.jsx(_,{tone:"accent",children:o.method})}),t.jsx($,{children:o.failed?t.jsx(_,{tone:"error",children:ke(o.failureKind)}):t.jsx(_,{tone:Kt(o.resStatus),children:o.resStatus||"—"})}),t.jsx($,{className:"font-mono text-[11px] truncate",title:o.host,children:o.host}),t.jsx($,{className:"font-mono text-[11px] truncate",title:o.path,children:o.failed?"—":o.path}),t.jsx($,{className:"font-mono text-[11px]",children:o.failed?"—":`${o.durationMs}ms`}),t.jsx($,{children:t.jsxs("div",{className:"flex gap-1",children:[o.mocked&&t.jsx(_,{tone:"accent",children:"mock"}),o.modified&&t.jsx(_,{tone:"busy",children:"mod"})]})})]},o.id))})]})}),t.jsxs(t.Fragment,{children:[t.jsx(Ne,{header:C,action:A,padded:!1,children:b}),t.jsx(Vt,{request:N,onClose:()=>p(null)})]})},pr=()=>{const{buildId:e="",sessionId:r=""}=Te(),{toast:s}=P(),a=$e(),n=Le(),l=at(r||null),c=x.useMemo(()=>n.builds.find(p=>p.id===e)??null,[n.builds,e]),i=c?Pe(c):"Build";if(x.useEffect(()=>{!l.loading&&l.notFound&&(s("Session not available — it may belong to a team you are not on.","info"),a("/builds",{replace:!0}))},[l.loading,l.notFound,a,s]),l.loading)return t.jsx("div",{className:"p-6 text-xs text-[var(--text-dim)]",children:"Loading session…"});if(l.error||!l.session)return t.jsxs("div",{className:"p-6 space-y-2",children:[t.jsx(ae,{buildId:e,buildName:c?i:null,sessionId:r}),t.jsx("div",{className:"mt-6 rounded-md border border-[var(--red)]/30 bg-[var(--surface)] p-4 text-xs text-[var(--red)]",children:l.error||"Session not found."})]});const d=l.session,u=l.sessionLogs,N=K(d.status).bucket==="failed";return t.jsxs("div",{className:"flex flex-col h-full min-h-0",children:[t.jsx(ae,{buildId:e,buildName:c?i:null,sessionId:d.id}),t.jsx(it,{session:d}),t.jsx("div",{className:"flex-1 overflow-y-auto overflow-x-hidden",children:t.jsxs("div",{className:"px-4 py-4 space-y-4",children:[t.jsx(mt,{session:d,commands:u}),N&&t.jsx(pt,{session:d,buildName:i,buildId:e,commands:u,durationText:Fe(V(d))}),t.jsx(wt,{commands:u}),t.jsx(_t,{sessionId:d.id,running:d.status==="running",hasTrace:!!d.performance_trace}),t.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-[minmax(0,1fr)_340px] gap-4",children:[t.jsx("div",{className:"min-w-0",children:t.jsx(Bt,{sessionLogs:l.sessionLogs,deviceLogs:l.deviceLogs,debugLogs:l.debugLogs,profiling:l.profiling})}),t.jsxs("div",{className:"flex flex-col gap-4 min-w-0",children:[t.jsx(Ct,{session:d}),t.jsx(Rt,{session:d,buildName:c?i:null}),t.jsx(Tt,{session:d})]})]}),t.jsx(Wt,{sessionId:d.id,sessionEnded:d.status!=="running"})]})})]})};export{pr as SessionDetailPage,pr as default};
|