@xenon-device-management/xenon 2.12.0 → 2.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +223 -488
  3. package/lib/package.json +2 -2
  4. package/lib/public/assets/{AnnotationOverlay-Crkn72_3.js → AnnotationOverlay-Cvpkk4r9.js} +1 -1
  5. package/lib/public/assets/{ApiKeyGate-BThVlj_j.js → ApiKeyGate-Bhn5ZtVM.js} +1 -1
  6. package/lib/public/assets/{BugReportButton-Dwa0GnBh.js → BugReportButton-BA6aEjTC.js} +1 -1
  7. package/lib/public/assets/{DeviceMosaicView-Ccc-pOls.js → DeviceMosaicView-ChhHnpFQ.js} +1 -1
  8. package/lib/public/assets/{EmptyState-BeuTrlN_.js → EmptyState-CgzbuUvO.js} +1 -1
  9. package/lib/public/assets/{FieldGroup-DBIaNxl1.js → FieldGroup-BHc_5qNc.js} +1 -1
  10. package/lib/public/assets/{FilterMenu-BgSAS6iT.js → FilterMenu-D7OMGuRc.js} +1 -1
  11. package/lib/public/assets/{Menu-GCbP6gYN.js → Menu-C173JJ0l.js} +1 -1
  12. package/lib/public/assets/{Modal-ByESeBRC.js → Modal-ChvVUI3B.js} +1 -1
  13. package/lib/public/assets/{RecordingPage-Bwyf3WMl.js → RecordingPage-Dhw4d_O6.js} +1 -1
  14. package/lib/public/assets/{RecordingsPage-7kM1rB3i.js → RecordingsPage-z97ftGK-.js} +2 -2
  15. package/lib/public/assets/{SegmentedControl-B8tTU8kD.js → SegmentedControl-DbXhBGZC.js} +1 -1
  16. package/lib/public/assets/{SettingCard-Clg_Ijbc.js → SettingCard-C908GgiI.js} +1 -1
  17. package/lib/public/assets/{Table-CIaR0QWV.js → Table-B1DB_ZN-.js} +1 -1
  18. package/lib/public/assets/{activity-Bengh9SD.js → activity-Css0xNvp.js} +1 -1
  19. package/lib/public/assets/ai-settings-BMQK3-yN.js +21 -0
  20. package/lib/public/assets/{api-keys-CrGlbfn-.js → api-keys-CbJ42eDQ.js} +1 -1
  21. package/lib/public/assets/{apps-DIbcGVlU.js → apps-DW1pw2re.js} +2 -2
  22. package/lib/public/assets/{arrow-left-BtJIXXMY.js → arrow-left-Dpog9L0F.js} +1 -1
  23. package/lib/public/assets/{arrow-right-zm-NagQh.js → arrow-right-CsBIlSIw.js} +1 -1
  24. package/lib/public/assets/{arrow-up-right-DdU8YEYp.js → arrow-up-right-z1tRQE4R.js} +1 -1
  25. package/lib/public/assets/{auth-shell-CHmBFmOz.js → auth-shell-mXiX5vNk.js} +2 -2
  26. package/lib/public/assets/{builds-page-JkzWKRpU.js → builds-page-BcFn23bE.js} +1 -1
  27. package/lib/public/assets/{button-CqhPBGRj.js → button-Cq_zHETv.js} +1 -1
  28. package/lib/public/assets/{calendar-C7eYYJTo.js → calendar-CKxABX9q.js} +1 -1
  29. package/lib/public/assets/{check-DpbIM4E0.js → check-C7xu-hGU.js} +1 -1
  30. package/lib/public/assets/{chevron-right-Dl4X1PRz.js → chevron-right-BiqfdCGA.js} +1 -1
  31. package/lib/public/assets/{circle-check-Du3sjbfV.js → circle-check-CyBjjPxR.js} +1 -1
  32. package/lib/public/assets/{circle-x-BSSqhpXi.js → circle-x-6gMRcx6z.js} +1 -1
  33. package/lib/public/assets/{clock-DMT60v1C.js → clock-U1rDTT1h.js} +1 -1
  34. package/lib/public/assets/{copy-BKNyOehd.js → copy-BIM0ZX0L.js} +1 -1
  35. package/lib/public/assets/device-explorer-DSpAA_t9.js +279 -0
  36. package/lib/public/assets/{download-BM6Xn22t.js → download-DDBRXnAZ.js} +1 -1
  37. package/lib/public/assets/{forgot-password-B8WqMqBT.js → forgot-password-Dpg7yxM8.js} +1 -1
  38. package/lib/public/assets/{index-ClrpAMAT.js → index-BGBqohfQ.js} +43 -43
  39. package/lib/public/assets/{index-DauQh6ie.js → index-BQ2luT7L.js} +1 -1
  40. package/lib/public/assets/{input-CyKdLnEx.js → input-BvB1cjHE.js} +1 -1
  41. package/lib/public/assets/{line-chart-EIBXwYGo.js → line-chart-m78VggnW.js} +1 -1
  42. package/lib/public/assets/{list-checks-fWsgD9bI.js → list-checks-cY7o1QTL.js} +1 -1
  43. package/lib/public/assets/{lock-CVCe56TH.js → lock-loz8txQm.js} +1 -1
  44. package/lib/public/assets/{login-BJ8a7yVD.js → login-BYOvz4lx.js} +1 -1
  45. package/lib/public/assets/maintenance-settings-BxbA7SkB.js +6 -0
  46. package/lib/public/assets/{monitor-Bw1YQZnL.js → monitor-C4HPHb6s.js} +1 -1
  47. package/lib/public/assets/{mouse-pointer-2-C01jbqkO.js → mouse-pointer-2-BkdKtCzi.js} +1 -1
  48. package/lib/public/assets/{network-CBGUjJDJ.js → network-C5e0MIWo.js} +1 -1
  49. package/lib/public/assets/{overview-Cxe8aQ7C.js → overview-tlu8ijGd.js} +1 -1
  50. package/lib/public/assets/{page-header-B92DKLiq.js → page-header-FXPlo6az.js} +1 -1
  51. package/lib/public/assets/{play-Ck0L-0_m.js → play-CWdk0_Sy.js} +1 -1
  52. package/lib/public/assets/{plus-Dq3tCy2N.js → plus-B2xCnntV.js} +1 -1
  53. package/lib/public/assets/{profile-page--dkDiKbr.js → profile-page-D080qzr4.js} +1 -1
  54. package/lib/public/assets/{recording-group-store-5BYIFFN9.js → recording-group-store-B75435xR.js} +1 -1
  55. package/lib/public/assets/{reset-password-KYWlfjia.js → reset-password-D3u4Mbvn.js} +1 -1
  56. package/lib/public/assets/{runbook-page-mnsxgS1d.js → runbook-page-hPRZMy7s.js} +1 -1
  57. package/lib/public/assets/{select-BBOIZTYm.js → select-12RThAXc.js} +1 -1
  58. package/lib/public/assets/{selector-detail-redirect-CcI2j_Su.js → selector-detail-redirect-uQwvV7EQ.js} +1 -1
  59. package/lib/public/assets/{selector-health-page-BdJb_x5L.js → selector-health-page-CThoxDFO.js} +2 -2
  60. package/lib/public/assets/{session-detail-page-BjLaJy8z.js → session-detail-page-Bc02YjMm.js} +1 -1
  61. package/lib/public/assets/settings-C1VIluuT.js +6 -0
  62. package/lib/public/assets/{stat-tile-BU9e4s36.js → stat-tile-B02WEte8.js} +1 -1
  63. package/lib/public/assets/{tablet-hgbEwrWq.js → tablet-Dj30VGZl.js} +1 -1
  64. package/lib/public/assets/{teams-uiiG4hZM.js → teams-uL4fjTDR.js} +1 -1
  65. package/lib/public/assets/{trash-2-NK_Iazmg.js → trash-2-B54e-TfY.js} +1 -1
  66. package/lib/public/assets/{upload-BUX8TNFi.js → upload-B3k8WCSP.js} +1 -1
  67. package/lib/public/assets/{use-builds-data-DZszmFyl.js → use-builds-data-BFn90NH3.js} +1 -1
  68. package/lib/public/assets/{use-password-reset-mode-BK4B9kRC.js → use-password-reset-mode-DL2TmWZ0.js} +1 -1
  69. package/lib/public/assets/{users-BU4XBbMT.js → users-CqBuxMlX.js} +1 -1
  70. package/lib/public/assets/{users-BDh1xjad.js → users-DjQSucKb.js} +1 -1
  71. package/lib/public/assets/{video-off-BOjNwT4R.js → video-off-T0rlt0GA.js} +1 -1
  72. package/lib/public/assets/webhook-settings-DAHgnJyZ.js +1 -0
  73. package/lib/public/assets/{zap-DTWwVMUg.js → zap-gBHqutZV.js} +1 -1
  74. package/lib/public/index.html +1 -1
  75. package/lib/src/app/apiErrors.js +118 -0
  76. package/lib/src/app/index.js +6 -1
  77. package/lib/src/app/openapi/control.yaml +3129 -0
  78. package/lib/src/app/openapi/grid.yaml +2295 -0
  79. package/lib/src/app/openapi/identity.yaml +2168 -0
  80. package/lib/src/app/openapi/platform.yaml +2885 -0
  81. package/lib/src/app/openapi/sessions.yaml +3784 -0
  82. package/lib/src/app/routers/bug-report.js +4 -1
  83. package/lib/src/app/routers/config.js +6 -107
  84. package/lib/src/app/routers/control.js +117 -59
  85. package/lib/src/app/routers/dashboard.js +13 -7
  86. package/lib/src/app/routers/grid.js +54 -14
  87. package/lib/src/app/routers/profile.js +27 -13
  88. package/lib/src/app/routers/recordings.js +11 -5
  89. package/lib/src/app/routers/reservation.js +63 -15
  90. package/lib/src/app/routers/users.js +4 -0
  91. package/lib/src/app/routers/webhook.js +17 -8
  92. package/lib/src/app/swagger.js +259 -177
  93. package/lib/src/data-service/device-service.js +4 -1
  94. package/lib/src/data-service/deviceFieldOwners.js +1 -0
  95. package/lib/src/device-managers/AndroidDeviceManager.js +5 -2
  96. package/lib/src/device-managers/ios/WDAClient.js +153 -41
  97. package/lib/src/generated/client/edge.js +4 -3
  98. package/lib/src/generated/client/index-browser.js +1 -0
  99. package/lib/src/generated/client/index.d.ts +38 -0
  100. package/lib/src/generated/client/index.js +4 -3
  101. package/lib/src/generated/client/package.json +1 -1
  102. package/lib/src/generated/client/schema.prisma +2 -0
  103. package/lib/src/generated/client/wasm.js +1 -0
  104. package/lib/src/middleware/csrfMiddleware.js +13 -5
  105. package/lib/src/middleware/rateLimitMiddleware.js +19 -5
  106. package/lib/src/middleware/roleGuard.js +20 -0
  107. package/lib/src/services/AIService.js +13 -3
  108. package/lib/src/services/NotificationService.js +28 -28
  109. package/lib/src/services/bug-report/BugReportService.js +8 -2
  110. package/lib/src/services/lease/LeaseService.js +71 -20
  111. package/lib/src/services/omni-vision/OmniVisionService.js +14 -5
  112. package/lib/src/services/recording/RecordingOrchestrator.js +16 -2
  113. package/lib/test/helpers/expressRoutes.js +41 -0
  114. package/lib/test/integration/team-visibility-control.spec.js +2 -2
  115. package/lib/test/unit/access-scopes.spec.js +227 -0
  116. package/lib/test/unit/api-error-handling.spec.js +179 -0
  117. package/lib/test/unit/bug-report/route.spec.js +29 -0
  118. package/lib/test/unit/bug-report/service.spec.js +30 -0
  119. package/lib/test/unit/control-honest-answers.spec.js +134 -0
  120. package/lib/test/unit/device-allocation-routes.spec.js +232 -0
  121. package/lib/test/unit/healing-state-endpoints.spec.js +8 -5
  122. package/lib/test/unit/install-repository-app-team.spec.js +4 -1
  123. package/lib/test/unit/lease/LeaseService.spec.js +5 -4
  124. package/lib/test/unit/lease/lease-device-match.spec.js +161 -0
  125. package/lib/test/unit/lease/lease-session-ownership.spec.js +10 -9
  126. package/lib/test/unit/omni-vision-failures.spec.js +81 -0
  127. package/lib/test/unit/openapi-coverage.spec.js +114 -0
  128. package/lib/test/unit/profile-router.test.js +42 -0
  129. package/lib/test/unit/rateLimitMiddleware.test.js +49 -0
  130. package/lib/test/unit/recording-orchestrator.spec.js +80 -0
  131. package/lib/test/unit/recordings-library-routes.spec.js +37 -0
  132. package/lib/test/unit/reservation-team-visibility.spec.js +4 -3
  133. package/lib/test/unit/reset-link.test.js +2 -1
  134. package/lib/test/unit/stream-ticket-identity.spec.js +1 -1
  135. package/lib/test/unit/users-router.test.js +14 -1
  136. package/lib/test/unit/wda-client-failures.spec.js +90 -0
  137. package/lib/test/unit/wda-client-session.spec.js +128 -0
  138. package/lib/test/unit/wda-clipboard-write.spec.js +135 -0
  139. package/lib/test/unit/webhook-delivery.spec.js +142 -0
  140. package/lib/tsconfig.tsbuildinfo +1 -1
  141. package/package.json +2 -2
  142. package/prisma/migrations/20261004120000_reservation_holder/migration.sql +2 -0
  143. package/prisma/schema.prisma +2 -0
  144. package/scripts/dev/readme-screenshots.js +231 -0
  145. package/lib/public/assets/ai-settings-G9SymiPH.js +0 -21
  146. package/lib/public/assets/device-explorer-CoPR8DW3.js +0 -279
  147. package/lib/public/assets/maintenance-settings-DcRmTLS6.js +0 -6
  148. package/lib/public/assets/settings-B6cpMhsx.js +0 -6
  149. package/lib/public/assets/webhook-settings-BMzyUcx5.js +0 -1
  150. package/lib/src/app/swagger-docs.js +0 -1701
package/README.md CHANGED
@@ -1,580 +1,315 @@
1
- # Xenon
2
-
3
1
  <h1 align="center">
4
- <br>
5
- <img src="assets/xenon-logo.png" alt="Xenon" width="200">
6
- <br>
7
- <br>
8
- Intelligent Mobile Infrastructure
9
- <br>
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="assets/logo-dark.svg">
4
+ <img src="assets/logo-light.svg" alt="Xenon" width="280">
5
+ </picture>
10
6
  </h1>
11
7
 
12
8
  <p align="center">
13
- <strong>Self-healing device orchestration platform for Appium</strong>
9
+ <strong>Run your mobile device lab from one place: allocation, live control, recording and self-healing tests, as an Appium 3 plugin.</strong>
14
10
  </p>
15
11
 
16
12
  <p align="center">
17
- <a href="#features">Features</a> •
18
- <a href="#quick-start">Quick Start</a> •
19
- <a href="#capabilities">Capabilities</a> •
20
- <a href="#api-documentation">API Docs</a> •
21
- <a href="#documentation">Documentation</a> •
22
- <a href="#contributing">Contributing</a>
13
+ <a href="https://www.npmjs.com/package/@xenon-device-management/xenon"><img alt="npm" src="https://img.shields.io/npm/v/@xenon-device-management/xenon?label=npm"></a>
14
+ <a href="https://github.com/Rabindra184/xenon/actions/workflows/npm-publish.yml"><img alt="Publish" src="https://img.shields.io/github/actions/workflow/status/Rabindra184/xenon/npm-publish.yml?branch=main&label=publish"></a>
15
+ <a href="https://appium.io"><img alt="Appium 3" src="https://img.shields.io/badge/appium-3.x-662d91"></a>
16
+ <a href="LICENSE"><img alt="License: ISC" src="https://img.shields.io/badge/license-ISC-blue"></a>
23
17
  </p>
24
18
 
25
- ---
26
-
27
- ## ✨ What is Xenon?
28
-
29
- **Xenon** is an intelligent Appium plugin that transforms your mobile device lab into a **self-healing, autonomous infrastructure**. Named after the noble gas known for its stability and reliability, Xenon brings enterprise-grade device orchestration to your testing pipeline.
30
-
31
- ### Why Xenon?
32
-
33
- | Problem | Xenon Solution |
34
- |---------|----------------|
35
- | Tests fail due to device state | **Auto-recovery** - Devices heal themselves |
36
- | Manual device management | **Smart allocation** - Queue, reserve, prioritize |
37
- | Debugging is painful | **Interactive control** - Live stream, touch, shell |
38
- | No visibility into failures | **Rich artifacts** - Video, screenshots, profiling |
39
- | Infrastructure silos | **Unified dashboard** - One view for all devices |
40
-
41
- ---
42
-
43
- ## 🚀 Features
44
-
45
- ### Device Orchestration
46
- - ✅ **Automatic device discovery** - Android (USB + emulators), iOS (devices + simulators)
47
- - ✅ **Smart session allocation** - Queue management with ETA
48
- - ✅ **WebSocket-First Sync** - Real-time Hub-Node-Dashboard bidirectional sync
49
- - ✅ **Device reservation** - Manual mode for debugging
50
- - ✅ **Team-based quotas** - Fair resource sharing
51
-
52
- ### Interactive Control
53
- - ✅ **Live streaming** - Real-time device screen in browser
54
- - ✅ **Touch interaction** - Tap, swipe, scroll remotely
55
- - ✅ **App management** - Install, uninstall, clear data
56
- - ✅ **Interactive Shell** - Execute ADB/iOS commands directly
57
- - ✅ **Device information** - Battery, storage, network status
58
-
59
- ### 🧠 AI Self-Healing (Flagship)
60
- - ✅ **5-Tier Healing Orchestration** - From DOM to LLM recovery
61
- - ✅ **Signature-Based Learning** - Persistent "Etalon" signatures for high-confidence recovery
62
- - ✅ **Multi-Modal Fallback** - Syntactic -> OCR -> Visual AI -> LLM reasoning
63
- - ✅ **Infrastructure-Free** - Works with existing LokiJS (local) or PostgreSQL (remote)
64
-
65
- #### Selector lifecycle (Trust & Truth layer)
66
-
67
- Healed selectors flow through a state machine: **Active → Pending → Resolved**, with an optional **Muted** branch.
68
-
69
- - **Active** — a hot selector that's healing in flight; surfaces on the Selector Health dashboard, the CI gate, and the webhook digest.
70
- - **Mark as Fixed** — after rewriting the selector in your test source, click "Mark as Fixed" in the dashboard. The row moves to **Pending**.
71
- - **Pending** — Xenon watches subsequent CI builds. When 3 distinct `build_id`s have run the selector with no heals, it auto-promotes to **Resolved** (`SelectorVerificationJob` runs every 15 minutes).
72
- - **Resolved** — the rewrite stuck. Excluded from the CI gate and digest. A future heal flips the row back to Active with a regression badge (institutional memory).
73
- - **Muted** — a selector you've intentionally chosen to ignore (legacy flow, etc.). Hidden from dashboard, CI gate, and digest until unmuted.
74
-
75
- The dashboard shows strategy + value (`Accessibility ID: login-btn`) so suggested rewrites are copy-ready in JS / Java / Python / C# / Ruby — your client language is remembered in `localStorage.xenon.copyLang`.
76
-
77
- > **CI gate behavior change:** muted/pending/resolved selectors no longer count toward `/healing/hotspots/violations`. If your CI was passing/failing based on that endpoint, the signal will get quieter after this release — selectors actively being managed are no longer flagged. Pass `?status=all` to opt back into the old behavior.
78
-
79
- ### Recording & Artifacts
80
- - ✅ **Video recording** - Full session capture
81
- - ✅ **Screenshot capture** - On-demand and per-command
82
- - ✅ **Network interceptor** - Live HTTP/HTTPS capture, mocking, and HAR export ([docs](https://xenon-docs.vercel.app/docs/network-interceptor))
83
- - ✅ **Distributed Tracing** - OpenTelemetry spans for exact command latency
84
- - ✅ **Performance profiling** - CPU, memory, FPS metrics
85
- - ✅ **Log aggregation** - Appium, device, app logs
86
- - ✅ **OpenTelemetry Integration** - Standardized distributed tracing for all sessions
87
-
88
- ### Intelligence (Roadmap)
89
- - 🔲 **Flaky test detection** - Auto-identify unstable tests
90
- - 🔲 **Error categorization** - Crash vs timeout vs element not found
91
- - 🔲 **Predictive health** - USB/battery failure prediction
92
-
93
- ---
19
+ <p align="center">
20
+ <a href="#quick-start">Quick start</a> ·
21
+ <a href="https://xenon-6e6.pages.dev">Documentation</a> ·
22
+ <a href="#api">API reference</a> ·
23
+ <a href="CHANGELOG.md">Changelog</a>
24
+ </p>
94
25
 
95
- ## ⚡ Quick Start
26
+ <p align="center">
27
+ <picture>
28
+ <source media="(prefers-color-scheme: dark)" srcset="assets/dashboard-dark.png">
29
+ <img src="assets/dashboard-light.png" alt="The Devices page of the Xenon dashboard: six phones, tablets and emulators, ready, busy with a test or reserved" width="100%">
30
+ </picture>
31
+ </p>
96
32
 
97
- ### Installation
33
+ Xenon sits inside Appium and turns a set of Android and iOS devices, real or virtual, on one machine or many, into a shared lab. Tests ask for a device with ordinary Appium capabilities. Xenon picks a free one the caller is allowed to use, records the session, heals selectors that broke, and shows everything on a live dashboard. People use the same dashboard to watch, control, record and reserve devices.
34
+
35
+ ## Contents
36
+
37
+ - [Highlights](#highlights)
38
+ - [Requirements](#requirements)
39
+ - [Quick start](#quick-start)
40
+ - [Hub and nodes](#hub-and-nodes)
41
+ - [Configuration](#configuration)
42
+ - [Capabilities for your tests](#capabilities-for-your-tests)
43
+ - [Self-healing](#self-healing)
44
+ - [Security and access](#security-and-access)
45
+ - [API](#api)
46
+ - [Observability](#observability)
47
+ - [Development](#development)
48
+ - [Upgrading](#upgrading)
49
+ - [Getting help](#getting-help)
50
+ - [Contributing](#contributing)
51
+ - [License](#license)
52
+
53
+ ## Highlights
54
+
55
+ **Device lab**
56
+ - Finds Android devices and emulators (adb), iPhones (go-ios) and iOS simulators by itself.
57
+ - Allocates a free, healthy device per session, and queues requests when none is free.
58
+ - One hub, many nodes: every machine's devices form one pool, behind one URL and one set of rules.
59
+ - Teams decide who may use which device; reservations and programmatic leases hold one for a person or a pipeline.
60
+
61
+ **Live control**
62
+ - Live preview of any device in the browser (MJPEG, or hardware H.264 on Android).
63
+ - Tap, swipe, type, press keys, take screenshots, install apps and read the clipboard remotely.
64
+ - Streaming Android logs with filters; a multi-device view that records several phones side by side.
65
+
66
+ **Test evidence**
67
+ - Video, screenshots, device logs and the full command log for every session, grouped by build.
68
+ - CPU and memory charts per session, on both platforms.
69
+ - Network capture with mocks and HAR export (Android), and one-click bug report bundles.
70
+
71
+ **Self-healing**
72
+ - When `findElement` fails, six escalating strategies look for the element, from stored fingerprints to an LLM.
73
+ - The **Selector health** page lists the selectors that needed healing, with a suggested fix to copy into your test.
74
+
75
+ **Built for teams**
76
+ - Users, roles (`SUPER_ADMIN`, `ADMIN`, `MEMBER`), teams and scoped API tokens.
77
+ - Per-user rate limits, single-use tickets for streams and downloads, and a complete [OpenAPI reference](#api).
78
+
79
+ ## Requirements
80
+
81
+ | Component | Needed |
82
+ |---|---|
83
+ | **Node.js** | 20.19 or later (what Appium 3 needs) |
84
+ | **Appium** | 3.x (`npm i -g appium`) |
85
+ | **Android** | Android SDK platform tools (`adb`) and the UiAutomator2 driver |
86
+ | **iOS** | macOS with Xcode, [go-ios](https://github.com/danielpaulus/go-ios) and the XCUITest driver |
87
+ | **Database** | SQLite, built in. PostgreSQL is supported for larger hubs. |
88
+ | **Optional** | `ffmpeg` for recordings; an AI provider key (Gemini, OpenAI, Anthropic or a local Ollama) for the AI healing tiers |
89
+
90
+ ## Quick start
91
+
92
+ **1. Install the plugin and a driver.**
98
93
 
99
94
  ```bash
100
- # Install Xenon plugin
101
95
  appium plugin install --source=npm @xenon-device-management/xenon
102
-
103
- # Or install from source
104
- git clone https://github.com/xenon-platform/xenon.git
105
- cd xenon
106
- npm install
107
- npm run build:all
108
- appium plugin install --source=local .
96
+ appium driver install uiautomator2 # Android
97
+ appium driver install xcuitest # iOS (macOS only)
109
98
  ```
110
99
 
111
- ### Running
100
+ **2. Start Appium with Xenon and its dashboard.**
112
101
 
113
102
  ```bash
114
- # Start Appium with Xenon
115
103
  appium server --use-plugins=xenon \
116
104
  --plugin-xenon-platform=both \
117
105
  --plugin-xenon-enable-dashboard
118
106
  ```
119
107
 
120
- ## 🔧 Configuration
108
+ **3. Open the dashboard** at [http://localhost:4723/xenon/](http://localhost:4723/xenon/) and sign in as the first super admin. Unless you set `XENON_BOOTSTRAP_ADMIN_EMAIL` and `XENON_BOOTSTRAP_ADMIN_PASSWORD` before the first start, that is `admin@xenon.local` / `Admin@123`. **Change it at once** on any machine others can reach.
121
109
 
122
- Xenon supports configuration via CLI arguments or a configuration file (YAML/JSON). We recommend using a configuration file for production deployments.
110
+ **4. Point a test at it.** Use your access key and an API token; both are under **Profile** in the dashboard, where you create the token:
123
111
 
124
- ### Using Configuration File (Recommended)
125
-
126
- Create a `xenon-config.yaml` file:
127
- ```yaml
128
- server:
129
- usePlugins: ["xenon"]
130
- plugin:
131
- xenon:
132
- platform: both
133
- maxSessions: 8
134
- enableDashboard: true
135
- enableSelfHealing: true
112
+ ```js
113
+ const capabilities = {
114
+ platformName: 'Android',
115
+ 'appium:automationName': 'UiAutomator2',
116
+ 'xe:options': {
117
+ accessKey: process.env.XENON_ACCESS_KEY,
118
+ token: process.env.XENON_TOKEN,
119
+ },
120
+ 'xe:build': 'nightly-2026-10-04',
121
+ 'xe:name': 'Checkout: pay by card',
122
+ };
123
+ // WebdriverIO, Appium's Java client, Python client and others all work:
124
+ // connect to http://localhost:4723 with these capabilities.
136
125
  ```
137
126
 
138
- Run Appium with the config:
139
- ```bash
140
- appium server --config xenon-config.yaml
141
- ```
127
+ The session appears on the dashboard under **Sessions**, with its video and logs once it ends.
142
128
 
143
- ### Runtime Configuration ⚡️
129
+ ## Hub and nodes
144
130
 
145
- You can update configuration options at runtime without restarting the server using the API:
131
+ A **hub** is the server your tests and people talk to. A **node** is any other machine with devices attached. Every node runs Xenon too and reports its devices to the hub, so the whole lab is one pool behind the hub's URL. Sessions, live control and recordings on a node's device all go through the hub, which applies the team rules and checks access.
146
132
 
147
133
  ```bash
148
- # Get current config
149
- GET /xenon/api/config
150
-
151
- # Update config (e.g. change max sessions)
152
- PUT /xenon/api/config
153
- { "maxSessions": 10 }
134
+ # On each node
135
+ export XENON_HUB_ACCESS_KEY="xen_..." # a node user's access key, from the hub
136
+ export XENON_HUB_TOKEN="..." # that user's token, with the devices scope
137
+ appium server --use-plugins=xenon \
138
+ --plugin-xenon-platform=both \
139
+ --plugin-xenon-hub=http://hub.example.com:4723
154
140
  ```
155
141
 
156
- > **Note:** Some changes (like `platform` or `hub` URL) require a server restart to take full effect. The API response will indicate if a restart is required.
142
+ [Node provisioning](docs/node-provisioning.md) covers creating the node's user, its token, and recovering a lost one.
157
143
 
158
- ### Build & Session Retention 🧹
144
+ ## Configuration
159
145
 
160
- Xenon includes an enterprise-ready cleanup job that automatically purges older builds, sessions, and associated assets (videos/screenshots) to manage disk space.
146
+ Xenon reads its settings from Appium's config file or from `--plugin-xenon-*` flags. A config file suits anything beyond a quick try:
161
147
 
162
- | Option | Description | Default |
163
- |--------|-------------|---------|
164
- | `buildCleanupDays` | Retention period in days | `30` |
165
- | `buildCleanupMaxCount` | Maximum number of builds to keep | `100` |
166
- | `buildCleanupSchedule` | Cron schedule for the cleanup job | `"0 0 * * *"` |
167
- | `deleteBuildAssets` | Delete video recordings and screenshots from disk | `true` |
168
-
169
- **Example (YAML):**
170
148
  ```yaml
171
- plugin:
172
- xenon:
173
- buildCleanupDays: 14
174
- buildCleanupMaxCount: 50
175
- buildCleanupSchedule: "0 0 * * *"
176
- deleteBuildAssets: true
149
+ # xenon.yaml: run with appium server --config xenon.yaml
150
+ server:
151
+ use-plugins: [xenon]
152
+ plugin:
153
+ xenon:
154
+ platform: both # android, ios or both
155
+ enableDashboard: true
156
+ maxSessions: 8 # sessions this server runs at once
157
+ enableSelfHealing: true
158
+ buildCleanupDays: 30 # how long builds, videos and screenshots are kept
177
159
  ```
178
160
 
179
- Detailed explanation of how the retention logic works can be found in the **[Data Retention & Maintenance Guide](docs/retention.md)**.
180
-
181
- See [docs/server-args.md](docs/server-args.md) for all available options.
182
-
183
- ---
184
-
185
- ## 📋 Capabilities
186
-
187
- Xenon uses the `xe:` prefix for its custom capabilities. You can also use `xenon:` as an alternative.
188
-
189
- ### `xe:options`: credentials, leases and nested options
161
+ Every option, with its default, is in [Server arguments](docs/server-args.md), and [Data retention](docs/retention.md) explains the cleanup job. Lab-wide settings such as health checks, cleanup and the AI provider can also be changed in the dashboard's **Settings**, **AI engine** and **Maintenance** pages; changing them needs a super admin.
190
162
 
191
- `xe:options` is Xenon's capability namespace. It holds the session's credentials (`accessKey` + `token`, or `sessionToken`), a lease (`leaseId` + `leaseToken`, which the lease-create response's `appiumCapabilities` already carry), and options such as `healingTiers`. `xenon:options` is still accepted as an alias; when both are sent, `xe:options` wins field by field. `df:options` is not read. See [Teams](#teams-device-access-control) for an example.
163
+ ### Environment variables
192
164
 
193
- ### Session & Build Tracking
165
+ Keep credentials in the environment, not in config files or shell history.
194
166
 
195
- | Capability | Description | Example |
196
- |------------|-------------|---------|
197
- | `xe:build` | Build name for grouping sessions | `"xe:build": "Release-v2.0"` |
198
- | `xe:name` | Session name for identification | `"xe:name": "Login Test Suite"` |
167
+ | Variable | What it does |
168
+ |---|---|
169
+ | `XENON_BOOTSTRAP_ADMIN_EMAIL`, `XENON_BOOTSTRAP_ADMIN_PASSWORD` | The first super admin, created on the hub's first start. |
170
+ | `XENON_AI_PROVIDER` | `gemini`, `openai`, `anthropic` or `ollama`, for the AI healing tiers. |
171
+ | `XENON_GEMINI_API_KEY`, `XENON_OPENAI_API_KEY`, `XENON_ANTHROPIC_API_KEY` | The provider's key. The dashboard never stores or shows keys. |
172
+ | `XENON_AI_MODEL`, `XENON_AI_BASE_URL` | A different model, or a custom endpoint such as a local Ollama. |
173
+ | `XENON_DB_PROVIDER`, `DATABASE_URL` | `sqlite` (default, a file under `~/.cache/xenon`) or `postgresql`, and its URL. |
174
+ | `XENON_AUTO_MIGRATE` | `true` (default) applies database migrations at startup. Set `false` if your pipeline applies them. |
175
+ | `XENON_HUB_ACCESS_KEY`, `XENON_HUB_TOKEN` | On a node: the credentials it uses to talk to its hub. |
176
+ | `XENON_REQUIRE_SESSION_TOKEN` | Refuse sessions created without valid credentials. |
177
+ | `XENON_REQUIRE_COMMAND_AUTH` | Check credentials on every Appium command, not only when the session is created. |
178
+ | `XENON_ALLOWED_ORIGINS` | Extra origins the dashboard may be served from, for a reverse proxy on another host. |
179
+ | `XENON_AUTH_DISABLED` | `true` turns sign-in off. For local development only. |
199
180
 
200
- ### Recording & Screenshots
181
+ ## Capabilities for your tests
201
182
 
202
- | Capability | Description | Default |
203
- |------------|-------------|---------|
204
- | `xe:record_video` | Enable video recording | `true` |
205
- | `xe:screenshot_on_failure` | Capture screenshot on test failure | `true` |
206
- | `xe:screenshot_on_every_command` | Capture screenshot after each command | `false` |
207
- | `xe:save_device_logs` | Save device logs (logcat/syslog) | `false` |
183
+ Xenon's own capabilities use the `xe:` prefix. Credentials and other options go in `xe:options`.
208
184
 
209
- ### Device Filtering
185
+ | Capability | Purpose |
186
+ |---|---|
187
+ | `xe:options` | `accessKey` and `token`, or a `sessionToken`; a lease's `leaseId` and `leaseToken`; optionally a `team`. Xenon removes the credentials before the driver or any record sees them. |
188
+ | `xe:build`, `xe:name` | Group sessions into a build and name them on the dashboard. |
189
+ | `xe:record_video` | Record the session's video. |
190
+ | `xe:screenshot_on_failure`, `xe:screenshot_on_every_command` | Take screenshots when a command fails, or after every command. |
191
+ | `xe:save_device_logs` | Keep the device's logs with the session. |
192
+ | `appium:udids`, `appium:minSDK`, `appium:maxSDK`, `appium:tags` | Narrow which devices the session may get. |
193
+ | `appium:iPhoneOnly`, `appium:iPadOnly`, `appium:filterByHost` | Limit to iPhone or iPad simulators, or to one node. |
194
+ | `appium:deviceAvailabilityTimeout`, `appium:deviceRetryInterval` | How long to wait for a free device, and how often to look (ms). |
210
195
 
211
- | Capability | Description | Example |
212
- |------------|-------------|---------|
213
- | `appium:udids` | Comma-separated list of allowed UDIDs | `"device1,device2"` |
214
- | `appium:minSDK` | Minimum OS version | `"15"` |
215
- | `appium:maxSDK` | Maximum OS version | `"17"` |
216
- | `appium:iPhoneOnly` | Use only iPhone simulators | `true` |
217
- | `appium:iPadOnly` | Use only iPad simulators | `true` |
218
- | `appium:filterByHost` | Filter by node IP address | `"192.168.0.100"` |
196
+ From inside a test, the `xenon:` execute commands report to the dashboard:
219
197
 
220
- ### Timeouts
221
-
222
- | Capability | Description | Default |
223
- |------------|-------------|---------|
224
- | `appium:deviceAvailabilityTimeout` | Wait time for device availability (ms) | `180000` |
225
- | `appium:deviceRetryInterval` | Polling interval for device check (ms) | `10000` |
226
-
227
- ### Example Configuration
228
-
229
- ```javascript
230
- const capabilities = {
231
- platformName: 'iOS',
232
- 'appium:automationName': 'XCUITest',
233
- 'appium:app': '/path/to/app.ipa',
234
-
235
- // Xenon capabilities
236
- 'xe:build': 'Sprint-42',
237
- 'xe:name': 'Login Flow Test',
238
- 'xe:record_video': true,
239
- 'xe:screenshot_on_failure': true,
240
- 'xe:save_device_logs': true,
241
-
242
- // Device filtering
243
- 'appium:minSDK': '16',
244
- 'appium:iPhoneOnly': true
245
- };
198
+ ```js
199
+ await driver.execute('xenon: setSessionStatus', { status: 'passed', reason: 'All steps OK' });
200
+ await driver.execute('xenon: captureEvidence', { reason: 'Payment confirmed' });
246
201
  ```
247
202
 
248
- ### Custom Execute Script Commands
249
-
250
- Xenon supports extended control and reporting via the `xenon:` execute script namespace. These commands allow you to interact with the Xenon dashboard and session management directly from your test code.
251
-
252
- | Command | Description | Example |
253
- |---------|-------------|---------|
254
- | `xenon: setSessionStatus` | Mark session as passed/failed in dashboard | `{"status": "passed", "reason": "All steps OK"}` |
255
- | `xenon: setSessionName` | Update session name at runtime | `{"name": "Step 2: Payment Verification"}` |
256
- | `xenon: captureEvidence` | Trigger manual screenshot with custom label | `{"reason": "Checkpoint reached", "label": "success"}` |
257
- | `xenon: addTag` | Add searchable tags to the session | `{"tag": "regression"}` |
258
- | `xenon: debug` | Send custom debug logs to Xenon dashboard | `{"message": "API Response: 200 OK"}` |
259
-
260
- Full reference for these commands is in the Swagger UI at `/xenon/api-docs`.
261
-
262
- ---
263
-
264
- ## 🧠 AI Self-Healing
265
-
266
- Xenon features a best-in-class, 5-tier self-healing system that ensures your tests never fail due to minor UI changes. It automatically intercepts `NoSuchElementError` and attempts to recover the locator using increasingly advanced methods.
267
-
268
- ### 🛡️ The 5-Tier Strategy
269
-
270
- | Tier | Provider | Mechanism | Stability |
271
- |:---|:---|:---|:---|
272
- | **1** | **Native** | Standard Appium `findElement` | Baseline |
273
- | **2** | **Fuzzy XML**| **Weighted Signature Matching** (Dice Coefficient) | **85%+** |
274
- | **3** | **OCR** | Local Text Recognition (Tesseract.js) | High |
275
- | **4** | **Visual AI**| AI-powered coordinate discovery | High |
276
- | **5** | **LLM** | Deep Reasoning (Gemini/OpenAI) | Absolute |
277
-
278
- ### ⚡ Signature-Based Learning (Etalon)
279
-
280
- Xenon "learns" during every successful run. When an element is found, it captures a persistent **Element Signature (Etalon)**.
281
- - **Zero Configuration**: Learning is fully automatic and backgrounded.
282
- - **Persistent Memory**: Signatures are stored in your database (LokiJS or PostgreSQL).
283
- - **Extreme Precision**: Even if `id`, `text`, or `class` changes, Xenon uses anchor attributes (`content-desc`, `resource-id`) from its memory to find the match with industrial-grade confidence.
284
-
285
- ### 🎛️ Control & Transparency
286
-
287
- Xenon provides full visibility and control over its self-healing system:
203
+ Also available: `setSessionName`, `addTag` and `debug`, and on Android with network capture on, `addMock`, `getRequests` and `exportHar`.
288
204
 
289
- - **Global Toggle**: Enable or disable healing via CLI:
290
- ```bash
291
- appium server --use-plugins=xenon --plugin-xenon-enable-self-healing=true
292
- ```
293
- - **Live Configuration**: Toggle self-healing directly from the **Xenon Dashboard Settings** at runtime without restarting the server.
294
- - **Audit Logs**: Every healing event is recorded in the session command history. You can see:
295
- - **Original Selector**: The locator that failed.
296
- - **Recovered Selector**: The replacement locator found by Xenon.
297
- - **Confidence Score**: The mathematical match probability (0-1.0).
298
- - **Healing Tier**: Which tier (Fuzzy XML, OCR, etc.) performed the recovery.
205
+ For CI, a **lease** reserves a device before the test starts and hands back ready-made capabilities: `POST /xenon/api/sdk/leases`. See the [API reference](#api).
299
206
 
300
- ---
207
+ ## Self-healing
301
208
 
302
- ## 📖 API Documentation
209
+ When `findElement` can't find an element, Xenon tries six strategies in turn, cheapest first, before the test sees a failure:
303
210
 
304
- Xenon provides a comprehensive REST API for device management, session control, and more.
305
-
306
- ### Swagger UI
307
-
308
- Access interactive API documentation at:
309
- ```
310
- http://localhost:4723/xenon/api-docs
311
- ```
312
-
313
- ### OpenAPI Spec
314
-
315
- Get the raw OpenAPI specification:
316
- ```
317
- http://localhost:4723/xenon/api-docs.json
318
- ```
211
+ | Tier | Strategy | How it finds the element |
212
+ |---|---|---|
213
+ | 0 | **Resilio** | Fingerprints of the element stored from earlier successful runs |
214
+ | 1 | **Native** | The original selector, retried |
215
+ | 2 | **Fuzzy XML** | The page source compared with the stored fingerprint |
216
+ | 3 | **OCR** | The element's text read from a screenshot |
217
+ | 4 | **Visual AI** | A screenshot analysed by the configured AI provider |
218
+ | 5 | **LLM** | The page source and the failed selector reasoned about by an LLM |
319
219
 
320
- ### API Categories
220
+ Before healing, an optional **autowait** retries `findElement` for a while, since most "broken" selectors are slow screens. Turn healing off with `--plugin-xenon-enable-self-healing=false`, or per session with `xe:options.healingTiers`.
321
221
 
322
- | Category | Base Path | Description |
323
- |----------|-----------|-------------|
324
- | **Devices** | `/xenon/api/devices` | Device discovery and management |
325
- | **Sessions** | `/xenon/api/session` | Session management and logs |
326
- | **Builds** | `/xenon/api/build` | Build and test execution tracking |
327
- | **Control** | `/xenon/api/control` | Interactive device control |
328
- | **Reservations** | `/xenon/api/reservation` | Device reservation for exclusive use |
329
- | **Applications** | `/xenon/api/apps` | App repository and installation |
330
- | **Webhooks** | `/xenon/api/webhook` | Notification webhook configuration |
222
+ The dashboard's **Selector health** page lists every selector that needed healing in a period, how often and in which sessions, with a suggested fix to copy in JavaScript, Java, Python, C# or Ruby. Mark one as fixed and Xenon watches later runs to confirm it: it moves from **To fix** to **Being verified** to **Fixed**, and back to **To fix** if it breaks again. **Muted** hides a selector you've decided to leave.
331
223
 
332
- ### Key Endpoints
224
+ ## Security and access
333
225
 
334
- #### Devices
335
- ```bash
336
- # Get all devices
337
- GET /xenon/api/devices
226
+ Every `/xenon/api` request needs a credential:
338
227
 
339
- # Get device by platform
340
- GET /xenon/api/device/{platform}
228
+ | Credential | How to send it | Use it for |
229
+ |---|---|---|
230
+ | Dashboard session | The cookie `POST /xenon/api/auth/login` sets | People, in the browser |
231
+ | Access key and token | `x-xenon-access-key` and `x-xenon-token` headers | CI, scripts, nodes |
232
+ | Bearer token | `Authorization: Bearer <jwt>`, from `POST /xenon/api/auth/token` | SDKs, MCP tools, short-lived access |
341
233
 
342
- # Block/Unblock device
343
- POST /xenon/api/device/{udid}/block
344
- POST /xenon/api/device/{udid}/unblock
345
- ```
234
+ **Roles** decide what a person may do; a token's **scopes** narrow it further, and a token can never have more than the credential that created it.
346
235
 
347
- #### Control API
348
- ```bash
349
- # Take screenshot
350
- GET /xenon/api/control/{udid}/screenshot
236
+ | Scope | Allows |
237
+ |---|---|
238
+ | `read` | Reading devices, sessions, builds, apps and logs |
239
+ | `sessions` | Running Appium sessions and acting on selectors |
240
+ | `devices` | Controlling devices, previews, recordings, reservations and leases |
241
+ | `admin` | Users, teams, API keys, webhooks and lab settings (with the matching role) |
351
242
 
352
- # Tap at coordinates
353
- POST /xenon/api/control/{udid}/tap
354
- { "x": 100, "y": 200 }
243
+ **Teams** decide which devices someone can reach: a member sees their teams' devices and the shared pool, and everything else answers as if it didn't exist. [Teams](docs/teams.md) explains setting them up.
355
244
 
356
- # Swipe gesture
357
- POST /xenon/api/control/{udid}/swipe
358
- { "x": 100, "y": 500, "endX": 100, "endY": 100, "duration": 1000 }
245
+ For a lab others can reach, we recommend:
246
+ - set your own bootstrap admin password before the first start;
247
+ - turn on `XENON_REQUIRE_SESSION_TOKEN`, so every session has an owner, and `XENON_REQUIRE_COMMAND_AUTH` on the hub;
248
+ - serve Xenon over HTTPS, and keep nodes on a trusted network.
359
249
 
360
- # Type text
361
- POST /xenon/api/control/{udid}/text
362
- { "text": "Hello World" }
250
+ ## API
363
251
 
364
- # Execute shell command (Android)
365
- POST /xenon/api/control/{udid}/shell
366
- { "command": "pm list packages" }
252
+ Every endpoint is documented in the OpenAPI reference that each server serves:
367
253
 
368
- # Live stream
369
- GET /xenon/api/control/{udid}/stream
370
- ```
254
+ - **Interactive reference:** `http://<your-host>:4723/xenon/api-docs`
255
+ - **Raw OpenAPI document:** `http://<your-host>:4723/xenon/api-docs.json`
371
256
 
372
- #### Reservations
373
257
  ```bash
374
- # Reserve a device
375
- POST /xenon/api/reservation
376
- { "udid": "...", "host": "...", "reservedBy": "John", "duration": "2h" }
377
-
378
- # Release reservation
379
- DELETE /xenon/api/reservation/{udid}/{host}
258
+ # List the devices you can see
259
+ curl -H "x-xenon-access-key: $XENON_ACCESS_KEY" -H "x-xenon-token: $XENON_TOKEN" \
260
+ http://localhost:4723/xenon/api/devices
380
261
 
381
- # Extend reservation
382
- POST /xenon/api/reservation/{udid}/{host}/extend
383
- { "duration": "1h" }
262
+ # Lease an Android device for 30 minutes
263
+ curl -X POST -H "Content-Type: application/json" \
264
+ -H "x-xenon-access-key: $XENON_ACCESS_KEY" -H "x-xenon-token: $XENON_TOKEN" \
265
+ -d '{"filters":{"platform":"android"},"durationMs":1800000}' \
266
+ http://localhost:4723/xenon/api/sdk/leases
384
267
  ```
385
268
 
386
- ---
387
-
388
- ## 🎨 Dashboard
389
-
390
- Access the dashboard at `http://localhost:4723/xenon/`
269
+ Errors are JSON with an `error` field. Requests are rate limited per API key or per user; a limited answer carries `X-RateLimit-*` headers, and `429` with `Retry-After` past the budget.
391
270
 
392
- <p align="center">
393
- <img src="assets/dashboard.png" alt="Xenon Dashboard" width="100%">
394
- </p>
395
-
396
- ### Views
397
-
398
- | View | Description |
399
- |------|-------------|
400
- | **Devices** | Real-time device grid with status indicators |
401
- | **Sessions** | Active and historical session management |
402
- | **Builds** | Test runs grouped by build identifier |
403
- | **Control** | Interactive device control interface |
404
-
405
- ---
271
+ ## Observability
406
272
 
407
- ## 📚 Documentation
273
+ Xenon emits OpenTelemetry traces and logs for every session and command. [`examples/observability`](examples/observability) has a ready Docker Compose stack (Grafana, Tempo and Loki) to view them.
408
274
 
409
- The full documentation is available at:
410
- **[https://xenon-docs.vercel.app/](https://xenon-docs.vercel.app/)**
411
-
412
- ### Quick Links
413
- - [Server arguments & env vars](docs/server-args.md)
414
- - [Node provisioning](docs/node-provisioning.md) — pair-auth credentials for hub-node deployments
415
- - [Teams & device access](docs/teams.md)
416
- - [Data retention & cleanup](docs/retention.md)
417
- - API reference: live Swagger at `/xenon/api-docs`
418
-
419
- ---
420
-
421
- ## 🏗️ Development
275
+ ## Development
422
276
 
423
277
  ```bash
424
- # Clone and install
425
- git clone https://github.com/xenon-platform/xenon.git
278
+ git clone https://github.com/Rabindra184/xenon.git
426
279
  cd xenon
427
280
  npm install
428
-
429
- # Build everything (Plugin + Dashboard)
430
- npm run build:all
431
-
432
- # High-velocity development loop
433
- # (Auto-rebuilds and restarts Appium server)
434
- npm run dev
435
-
436
- # Run tests
437
- npm run test:all # Unit tests
438
- npm run test:android # Android integration
439
- npm run test:ios # iOS integration
440
- ```
441
-
442
- ---
443
-
444
- ## 🔐 Authentication
445
-
446
- All `/xenon/api/*` endpoints are authenticated. Xenon supports three shapes — pick whichever matches your caller.
447
-
448
- ### Identity model
449
-
450
- Xenon ships an enterprise identity stack: **users** with roles (`SUPER_ADMIN` / `ADMIN` / `MEMBER`), **teams** that scope which devices a user can reach, and **API tokens** minted per-user with their own scope set. The dashboard, programmatic clients, and hub-node channel all flow through the same identity.
451
-
452
- ### Auth shapes
453
-
454
- | Shape | Header(s) | When to use |
455
- |---|---|---|
456
- | **Cookie session** | `Cookie: xenon_dashboard_session=…` | Dashboard browser sessions. Set by `POST /api/auth/login` with `{email, password}`. |
457
- | **Pair auth** | `X-Xenon-Access-Key` + `X-Xenon-Token` | Programmatic clients (CI, SDK, hub→node). Each user has one access key (rotatable) and any number of scoped tokens. |
458
- | **Auth disabled** | _(none)_ | Local dev only. Set `--plugin-xenon-auth-disabled` (or `XENON_AUTH_DISABLED=true`). A WARN logs every 60 s. |
459
-
460
- ### First-run bootstrap
461
-
462
- On first start Xenon creates a `SUPER_ADMIN` user from these env vars (defaults `admin@xenon.local` / `Admin@123`):
463
-
464
- ```bash
465
- export XENON_BOOTSTRAP_ADMIN_EMAIL="you@example.com"
466
- export XENON_BOOTSTRAP_ADMIN_PASSWORD="..." # change me
467
- ```
468
-
469
- Sign in at `https://<host>/xenon/` with these credentials. From `/profile` you can mint API tokens and rotate your access key. For CI use, programmatically `POST /api/auth/login` to get the cookie, then `POST /api/profile/tokens` to mint a scoped token.
470
-
471
- ### Generating a programmatic token
472
-
473
- ```bash
474
- # 1. Get your access key + a fresh token from /profile in the dashboard, or:
475
- curl -s -X POST -b "xenon_dashboard_session=$COOKIE" \
476
- -H 'Content-Type: application/json' \
477
- -d '{"name":"ci","scopes":["sessions","read"]}' \
478
- http://localhost:4723/xenon/api/profile/tokens
479
-
480
- # 2. Use it on every subsequent call:
481
- curl -H "X-Xenon-Access-Key: xen_..." -H "X-Xenon-Token: ..." \
482
- http://localhost:4723/xenon/api/devices
483
- ```
484
-
485
- ### Scopes
486
-
487
- Tokens carry one or more scopes; the user's role controls which scopes they can grant.
488
-
489
- | Scope | Access |
490
- |-------|--------|
491
- | `read` | GET sessions, devices, logs, apps |
492
- | `sessions` | Create/delete sessions and reservations |
493
- | `devices` | Block/unblock devices, install apps, hub-node `/register` and `/unblock` |
494
- | `admin` | User / team / API-key management, webhooks |
495
-
496
- ### Teams (device access control)
497
-
498
- Scopes govern *which verbs* a token can call; **teams** govern *which devices* it can reach. A user bound to a team sees the team's devices plus the shared pool (`teamId = null`). `admin`-scope tokens bypass team filtering.
499
-
500
- Test clients authenticate a session with the access key and a token (with the `sessions` scope) in `xe:options`, Xenon's capability namespace:
501
-
502
- ```js
503
- const caps = {
504
- platformName: 'iOS',
505
- 'appium:automationName': 'XCUITest',
506
- 'xe:options': {
507
- accessKey: process.env.XENON_ACCESS_KEY, // user with team membership
508
- token: process.env.XENON_TOKEN,
509
- // optional: team: '<team-id>' to pin allocation to one of your teams
510
- // (any team for an admin)
511
- },
512
- };
281
+ npm run dev # migrate the database, build, install the plugin and start Appium
513
282
  ```
514
283
 
515
- A client that holds a hub-minted session token (`POST /xenon/api/auth/token`) sends `'xe:options': { sessionToken }` instead. `xenon:options` is still accepted as an alias (when both are sent, `xe:options` wins field by field); `df:options` is not read. Xenon takes these credentials out of the capabilities before the driver, the queue or the session record sees them.
284
+ | Command | Does |
285
+ |---|---|
286
+ | `npm run build:all` | Build the plugin and the dashboard |
287
+ | `npm run test:all` | Run the unit tests |
288
+ | `npm run test:android`, `npm run test:ios` | Run the integration tests on real devices |
289
+ | `npm run db:generate -- --name <change>` | Add a database migration after editing `prisma/schema.prisma` |
516
290
 
517
- See [docs/teams.md](docs/teams.md) for creating teams, assigning devices, and the full error taxonomy.
291
+ The dashboard is a React app in [`web/`](web), and the documentation site is in [`website/`](website).
518
292
 
519
- ### Hub-node channel
293
+ ## Upgrading
520
294
 
521
- Hub and node authenticate using the same pair-auth shape. Provision a User on the hub for each node, mint a `devices`-scoped token, and set both env vars on the node:
295
+ Read the [changelog](CHANGELOG.md) before upgrading: each release says whether it brings a database migration and what behaves differently. Then update the plugin and restart Appium:
522
296
 
523
297
  ```bash
524
- export XENON_HUB_ACCESS_KEY="xen_..."
525
- export XENON_HUB_TOKEN="..."
298
+ appium plugin update xenon
526
299
  ```
527
300
 
528
- Both REST `/register` calls and the Socket.io handshake will use this pair. See [docs/node-provisioning.md](docs/node-provisioning.md) for the full provisioning + recovery flow.
529
-
530
- ---
531
-
532
- ## 🌱 Environment Variables
301
+ Migrations are applied when Xenon starts. If you set `XENON_AUTO_MIGRATE=false`, apply them yourself first; from a source checkout, that is `npm run db:migrate`.
533
302
 
534
- Xenon reads these env vars in addition to the CLI flags. Prefer env vars for credentials so keys don't end up in shell history or config files.
303
+ ## Getting help
535
304
 
536
- | Variable | Purpose |
537
- |----------|---------|
538
- | `XENON_AI_PROVIDER` | AI backend: `gemini`, `openai`, `anthropic`, or `ollama`. Same as `--plugin-xenon-aiProvider`. |
539
- | `XENON_AI_MODEL` | Override the default model for the selected provider. |
540
- | `XENON_AI_BASE_URL` | Custom base URL (local Ollama, OpenAI-compatible gateway). |
541
- | `XENON_GEMINI_API_KEY` / `GEMINI_API_KEY` | Gemini credentials. `XENON_`-prefixed form wins if both set. |
542
- | `XENON_OPENAI_API_KEY` / `OPENAI_API_KEY` | OpenAI credentials. |
543
- | `XENON_ANTHROPIC_API_KEY` / `ANTHROPIC_API_KEY` | Anthropic credentials. |
544
- | `XENON_OPENAI_MODEL` | Alternate way to set the OpenAI model. |
545
- | `XENON_OTEL_DEBUG` | When `true`, OpenTelemetry adds a ConsoleSpanExporter so every span is logged. Dev/tracing only. |
546
- | `XENON_DB_PROVIDER` | `sqlite` or `postgresql`. Same as `--plugin-xenon-databaseProvider`. |
547
- | `DATABASE_URL` | Prisma database URL. Falls back to `file:~/.cache/xenon/xenon.db`. |
548
- | `XENON_HUB_ACCESS_KEY` | Node→hub outbound: access key the node sends in `X-Xenon-Access-Key`. Required alongside `XENON_HUB_TOKEN`. See [docs/node-provisioning.md](docs/node-provisioning.md). |
549
- | `XENON_HUB_TOKEN` | Node→hub outbound: token the node sends in `X-Xenon-Token`. Required alongside `XENON_HUB_ACCESS_KEY`. |
550
- | `XENON_BOOTSTRAP_ADMIN_EMAIL` / `XENON_BOOTSTRAP_ADMIN_PASSWORD` | First-run super-admin user, created on first hub boot. Defaults `admin@xenon.local` / `Admin@123`. Change in any non-throwaway environment. |
551
- | `XENON_AUTH_DISABLED` | `true` to disable all auth. Local dev only. |
552
- | `XENON_AUTO_MIGRATE` | When `true` (default), the hub auto-applies pending schema changes at startup (`prisma db push` for SQLite, `prisma migrate deploy` for PostgreSQL). Set `false` if you manage migrations externally via CI for auditable change-control. |
305
+ - **Questions and setup:** the [documentation](https://xenon-6e6.pages.dev), then the API reference on your own server.
306
+ - **Bugs and ideas:** [open an issue](https://github.com/Rabindra184/xenon/issues/new/choose).
307
+ - **Security problems:** report them privately, as [SECURITY.md](SECURITY.md) explains; please don't open a public issue.
553
308
 
554
- See [`docs/server-args.md`](docs/server-args.md) for the full CLI-flag reference and how these variables interact with config files.
309
+ ## Contributing
555
310
 
556
- ---
311
+ Issues and pull requests are welcome. [CONTRIBUTING.md](CONTRIBUTING.md) covers the development setup, tests, database changes and what a pull request needs. Everyone taking part is expected to follow the [code of conduct](CODE_OF_CONDUCT.md). To refresh the screenshots above after a dashboard change, build `web/` and run `node scripts/dev/readme-screenshots.js`.
557
312
 
558
- ## 🤝 Contributing
313
+ ## License
559
314
 
560
- We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
561
-
562
- ### Contributors
563
-
564
- <a href="https://github.com/xenon-platform/xenon/graphs/contributors">
565
- <img src="https://contrib.rocks/image?repo=xenon-platform/xenon" />
566
- </a>
567
-
568
- ---
569
-
570
- ## 📜 License
571
-
572
- ISC License - See [LICENSE](LICENSE) for details.
573
-
574
- ---
575
-
576
- <p align="center">
577
- <strong>Xenon</strong> - Stable. Reliable. Intelligent.
578
- <br>
579
- <em>Named after Element 54 - the noble gas known for stability</em>
580
- </p>
315
+ Xenon is released under the [ISC License](LICENSE).