@xenon-device-management/xenon 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +91 -0
  2. package/lib/package.json +1 -1
  3. package/lib/public/assets/{Layouts-D0WSzKOh.js → Layouts-BV_fQEKa.js} +1 -1
  4. package/lib/public/assets/{ai-settings-DQWDdNd7.js → ai-settings-BnoeJplt.js} +2 -2
  5. package/lib/public/assets/api-keys-TLWTJYrE.js +1 -0
  6. package/lib/public/assets/{apps-1sLWHOGO.js → apps-BGCcB8_p.js} +2 -2
  7. package/lib/public/assets/arrow-left-B6-SH02F.js +6 -0
  8. package/lib/public/assets/{badge-BiR1gmMm.js → badge-BtQKmQsj.js} +1 -1
  9. package/lib/public/assets/button-CNLKazSa.js +16 -0
  10. package/lib/public/assets/{calendar-yMyP2_Nc.js → calendar-CPNkf0zl.js} +1 -1
  11. package/lib/public/assets/{clock-CsVplnJ2.js → clock-C8ZaLIQA.js} +1 -1
  12. package/lib/public/assets/copy-DCAmOyY9.js +11 -0
  13. package/lib/public/assets/{cpu-DNC8n7kK.js → cpu-D3PzrmhN.js} +1 -1
  14. package/lib/public/assets/{device-explorer-DFu8Gxj4.js → device-explorer-BfFe3bRe.js} +36 -41
  15. package/lib/public/assets/{index-S71J2rWg.js → index-Do3u3FMx.js} +44 -34
  16. package/lib/public/assets/{lock-BstCxnX6.js → lock-DNzAbx6Q.js} +1 -1
  17. package/lib/public/assets/{maintenance-settings-BwfG9cu2.js → maintenance-settings-yHAKnfz_.js} +2 -2
  18. package/lib/public/assets/{mouse-pointer-2-CSn_Wnc9.js → mouse-pointer-2-B0KBsfLG.js} +1 -1
  19. package/lib/public/assets/{plus-DfjM7G6e.js → plus-DecbehZh.js} +1 -1
  20. package/lib/public/assets/{session-dashboard-C6ek4z65.js → session-dashboard-CJvDLc6D.js} +17 -22
  21. package/lib/public/assets/settings-DjTQTjSq.js +1 -0
  22. package/lib/public/assets/shield-alert-Br1Ebl1G.js +6 -0
  23. package/lib/public/assets/teams-LILlQka3.js +1 -0
  24. package/lib/public/assets/{trash-2-CZWUMK5b.js → trash-2-DEafUZlN.js} +1 -1
  25. package/lib/public/assets/{useSocket-CliVeWS3.js → useSocket-Dzv_TTsO.js} +2 -2
  26. package/lib/public/assets/{webhook-settings-tPiwWf8y.js → webhook-settings-CVwvtwI7.js} +1 -1
  27. package/lib/public/assets/{zap-ZrK5B58i.js → zap-DCsIYdwr.js} +1 -1
  28. package/lib/public/index.html +1 -1
  29. package/lib/schema.json +85 -38
  30. package/lib/src/InternalHttpClient.js +69 -14
  31. package/lib/src/XenonCapabilityManager.js +30 -0
  32. package/lib/src/app/index.js +95 -24
  33. package/lib/src/app/routers/apikeys.js +36 -0
  34. package/lib/src/app/routers/apps.js +4 -0
  35. package/lib/src/app/routers/auth.js +36 -0
  36. package/lib/src/app/routers/config.js +4 -0
  37. package/lib/src/app/routers/control.js +61 -10
  38. package/lib/src/app/routers/dashboard.js +5 -6
  39. package/lib/src/app/routers/grid.js +76 -14
  40. package/lib/src/app/routers/processes.js +24 -0
  41. package/lib/src/app/routers/reservation.js +15 -0
  42. package/lib/src/app/routers/teams.js +66 -0
  43. package/lib/src/app/routers/webhook.js +6 -3
  44. package/lib/src/auth/nodeSecret.js +33 -0
  45. package/lib/src/config.js +5 -0
  46. package/lib/src/dashboard/event-manager.js +2 -0
  47. package/lib/src/data-service/device-store.js +12 -0
  48. package/lib/src/data-service/prisma-store.js +24 -1
  49. package/lib/src/device-managers/AndroidDeviceManager.js +2 -2
  50. package/lib/src/device-managers/NodeDevices.js +8 -1
  51. package/lib/src/device-managers/ios/IOSDiscoveryService.js +7 -4
  52. package/lib/src/device-managers/ios/IOSStreamService.js +7 -0
  53. package/lib/src/device-managers/ios/WDAClient.js +2 -0
  54. package/lib/src/device-utils.js +36 -5
  55. package/lib/src/generated/client/edge.js +18 -7
  56. package/lib/src/generated/client/index-browser.js +15 -4
  57. package/lib/src/generated/client/index.d.ts +2613 -391
  58. package/lib/src/generated/client/index.js +18 -7
  59. package/lib/src/generated/client/package.json +1 -1
  60. package/lib/src/generated/client/schema.prisma +21 -0
  61. package/lib/src/generated/client/wasm.js +15 -4
  62. package/lib/src/helpers/UniversalMjpegProxy.js +23 -0
  63. package/lib/src/index.js +10 -2
  64. package/lib/src/interceptors/CommandInterceptor.js +29 -0
  65. package/lib/src/interfaces/IPluginArgs.js +0 -1
  66. package/lib/src/logger.js +47 -2
  67. package/lib/src/logging/sessionContext.js +28 -0
  68. package/lib/src/middleware/apiKeyMiddleware.js +69 -0
  69. package/lib/src/middleware/csrfMiddleware.js +73 -0
  70. package/lib/src/middleware/nodeSecretMiddleware.js +38 -0
  71. package/lib/src/middleware/rateLimitMiddleware.js +68 -0
  72. package/lib/src/middleware/scopeGuard.js +41 -0
  73. package/lib/src/plugin.js +1 -1
  74. package/lib/src/services/AIService.js +43 -8
  75. package/lib/src/services/ApiKeyService.js +121 -0
  76. package/lib/src/services/CircuitBreaker.js +158 -0
  77. package/lib/src/services/CleanupService.js +137 -39
  78. package/lib/src/services/DeviceReconciler.js +102 -0
  79. package/lib/src/services/MetricsService.js +78 -0
  80. package/lib/src/services/PortAllocator.js +13 -0
  81. package/lib/src/services/ProcessMetricsService.js +99 -0
  82. package/lib/src/services/ProcessRegistry.js +123 -0
  83. package/lib/src/services/ServerManager.js +23 -4
  84. package/lib/src/services/SessionLifecycleService.js +133 -27
  85. package/lib/src/services/ShutdownCoordinator.js +89 -0
  86. package/lib/src/services/SocketClient.js +11 -0
  87. package/lib/src/services/SocketServer.js +109 -6
  88. package/lib/src/services/TeamService.js +100 -0
  89. package/lib/src/services/VideoPipelineService.js +2 -0
  90. package/lib/src/services/healing/HealingMetrics.js +63 -0
  91. package/lib/src/services/healing/HealingOrchestrator.js +32 -4
  92. package/lib/src/services/healing/OcrHealingProvider.js +7 -0
  93. package/lib/src/sessions/XenonSession.js +3 -0
  94. package/lib/test/unit/ApiKeyService.test.js +101 -0
  95. package/lib/test/unit/PortAllocator.test.js +14 -0
  96. package/lib/test/unit/ProcessRegistry.test.js +70 -0
  97. package/lib/test/unit/apiKeyMiddleware.test.js +58 -0
  98. package/lib/test/unit/nodeSecretMiddleware.test.js +38 -0
  99. package/lib/test/unit/rateLimitMiddleware.test.js +37 -0
  100. package/lib/tsconfig.tsbuildinfo +1 -1
  101. package/package.json +2 -2
  102. package/prisma/migrations/20260423081701_add_session_indexes/migration.sql +8 -0
  103. package/prisma/migrations/20260424033547_add_teams/migration.sql +101 -0
  104. package/prisma/schema.prisma +21 -0
  105. package/schema.json +85 -38
  106. package/lib/public/assets/button-BVazt4Z1.js +0 -26
  107. package/lib/public/assets/settings-BDYP8ULf.js +0 -1
  108. /package/lib/public/assets/{Layouts-DPMls9vh.css → settings-DPMls9vh.css} +0 -0
package/README.md CHANGED
@@ -421,6 +421,97 @@ npm run test:ios # iOS integration
421
421
 
422
422
  ---
423
423
 
424
+ ## 🔐 Authentication
425
+
426
+ Xenon REST endpoints under `/xenon/api/*` require an API key passed in the `X-Xenon-API-Key` header.
427
+
428
+ ### Bootstrap key
429
+
430
+ On first start, Xenon writes a one-time bootstrap key (admin-scoped) to:
431
+
432
+ ```
433
+ ~/.cache/xenon/bootstrap-key.txt (0600 permissions)
434
+ ```
435
+
436
+ The startup log prints a WARN pointing at this path. **Rotate the bootstrap key within 24 hours.**
437
+
438
+ ### Creating a permanent key
439
+
440
+ ```bash
441
+ # Create a scoped key for CI
442
+ curl -X POST \
443
+ -H "X-Xenon-API-Key: $(cat ~/.cache/xenon/bootstrap-key.txt)" \
444
+ -H 'Content-Type: application/json' \
445
+ -d '{"name":"ci","scopes":["sessions","read"],"rateLimit":600}' \
446
+ http://localhost:4723/xenon/api/apikeys
447
+
448
+ # Revoke the bootstrap key after saving the returned `key` value
449
+ curl -X DELETE \
450
+ -H "X-Xenon-API-Key: $NEW_ADMIN_KEY" \
451
+ http://localhost:4723/xenon/api/apikeys/<bootstrap-id>
452
+ ```
453
+
454
+ ### Scopes
455
+
456
+ | Scope | Access |
457
+ |-------|--------|
458
+ | `read` | GET sessions, devices, logs, apps |
459
+ | `sessions` | Create/delete sessions and reservations |
460
+ | `devices` | Block/unblock devices, install apps |
461
+ | `admin` | API key management, webhooks, node registration |
462
+
463
+ ### Teams (device access control)
464
+
465
+ Scopes govern *which verbs* a key can call; **teams** govern *which devices* it can reach. A key bound to a team sees its team's devices plus the shared pool (`teamId = null`). `admin`-scoped keys bypass team filtering.
466
+
467
+ Test clients pass the key via the `xenon:accessKey` capability:
468
+
469
+ ```js
470
+ const caps = {
471
+ platformName: 'iOS',
472
+ 'appium:automationName': 'XCUITest',
473
+ 'xenon:accessKey': process.env.XENON_CI_KEY, // key bound to a team
474
+ // optional: 'xenon:team': '<team-id>' to pin allocation to a specific team (admins only for cross-team)
475
+ };
476
+ ```
477
+
478
+ See [docs/teams.md](docs/teams.md) for creating teams, assigning devices, and the full error taxonomy.
479
+
480
+ ### Hub-node channel
481
+
482
+ Set `--plugin-xenon-node-secret` (or `XENON_NODE_SECRET`) to the **same value** on both hub and node. When unset, the channel permits with a WARN (back-compat for single-node installs).
483
+
484
+ For zero-downtime secret rotation, set `XENON_NODE_SECRET` to the new secret and `XENON_NODE_SECRET_PREVIOUS` to the old one. The hub accepts either during the overlap window — flip nodes one at a time, then drop `XENON_NODE_SECRET_PREVIOUS`.
485
+
486
+ ### Local development
487
+
488
+ Pass `--plugin-xenon-auth-disabled` to skip auth entirely. A WARN is logged every 60 s as a reminder.
489
+
490
+ ---
491
+
492
+ ## 🌱 Environment Variables
493
+
494
+ 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.
495
+
496
+ | Variable | Purpose |
497
+ |----------|---------|
498
+ | `XENON_AI_PROVIDER` | AI backend: `gemini`, `openai`, `anthropic`, or `ollama`. Same as `--plugin-xenon-aiProvider`. |
499
+ | `XENON_AI_MODEL` | Override the default model for the selected provider. |
500
+ | `XENON_AI_BASE_URL` | Custom base URL (local Ollama, OpenAI-compatible gateway). |
501
+ | `XENON_GEMINI_API_KEY` / `GEMINI_API_KEY` | Gemini credentials. `XENON_`-prefixed form wins if both set. |
502
+ | `XENON_OPENAI_API_KEY` / `OPENAI_API_KEY` | OpenAI credentials. |
503
+ | `XENON_ANTHROPIC_API_KEY` / `ANTHROPIC_API_KEY` | Anthropic credentials. |
504
+ | `XENON_OPENAI_MODEL` | Alternate way to set the OpenAI model. |
505
+ | `XENON_OTEL_DEBUG` | When `true`, OpenTelemetry adds a ConsoleSpanExporter so every span is logged. Dev/tracing only. |
506
+ | `XENON_DB_PROVIDER` | `sqlite` or `postgresql`. Same as `--plugin-xenon-databaseProvider`. |
507
+ | `DATABASE_URL` | Prisma database URL. Falls back to `file:~/.cache/xenon/xenon.db`. |
508
+ | `XENON_NODE_SECRET` | Shared hub↔node secret (see above). |
509
+ | `XENON_NODE_SECRET_PREVIOUS` | Secondary secret accepted during rotation overlap. |
510
+
511
+ See [`docs/server-args.md`](docs/server-args.md) for the full CLI-flag reference and how these variables interact with config files.
512
+
513
+ ---
514
+
424
515
  ## 🤝 Contributing
425
516
 
426
517
  We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenon-device-management/xenon",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Xenon - Intelligent Mobile Infrastructure. A self-healing device orchestration platform for Appium.",
5
5
  "main": "./lib/src/index.js",
6
6
  "exports": {
@@ -1,4 +1,4 @@
1
- import{c,j as e,g as l}from"./index-S71J2rWg.js";/**
1
+ import{c,j as e,g as l}from"./index-Do3u3FMx.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as j,r as l,j as e,B as u,d as f,g as C,i as A,T as z}from"./index-S71J2rWg.js";import{X as x}from"./index-C1DBaoSh.js";import{A as P}from"./Layouts-D0WSzKOh.js";import{C as w}from"./cpu-DNC8n7kK.js";import{L as v}from"./lock-BstCxnX6.js";/**
1
+ import{c as j,r as l,j as e,B as u,d as f,g as C,i as A,T as z}from"./index-Do3u3FMx.js";import{X as x}from"./index-C1DBaoSh.js";/* empty css */import{A as P}from"./Layouts-BV_fQEKa.js";import{C as w}from"./cpu-D3PzrmhN.js";import{L as v}from"./lock-DNzAbx6Q.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -8,4 +8,4 @@ import{c as j,r as l,j as e,B as u,d as f,g as C,i as A,T as z}from"./index-S71J
8
8
  *
9
9
  * This source code is licensed under the ISC license.
10
10
  * See the LICENSE file in the root directory of this source tree.
11
- */const B=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],E=j("server",B),_=()=>{const[s,d]=l.useState({aiProvider:"gemini",aiModel:"",aiBaseUrl:"",geminiModel:"",openaiModel:"",anthropicModel:"",ollamaModel:"",geminiSet:!1,openaiSet:!1,anthropicSet:!1}),[N,m]=l.useState(!0),[y,g]=l.useState(!1),[o,n]=l.useState(null);l.useEffect(()=>{p()},[]);const p=async()=>{m(!0);try{const i=await x.getGlobalConfig();d({aiProvider:i.aiProvider||"gemini",aiModel:i.aiModel||"",aiBaseUrl:i.aiBaseUrl||"",geminiModel:i.geminiModel||"",openaiModel:i.openaiModel||"",anthropicModel:i.anthropicModel||"",ollamaModel:i.ollamaModel||"",geminiSet:i.geminiSet||!1,openaiSet:i.openaiSet||!1,anthropicSet:i.anthropicSet||!1})}catch(i){console.error("Failed to load AISettings",i),n({type:"error",message:"Failed to access AI configuration."})}finally{m(!1)}},S=async()=>{g(!0),n(null);try{await x.updateGlobalConfig({aiProvider:s.aiProvider}),n({type:"success",message:"Active provider updated successfully."}),setTimeout(()=>n(null),5e3)}catch(i){console.error("Failed to save AISettings",i),n({type:"error",message:"Failed to persist updates."})}finally{g(!1)}},t=[{id:"gemini",name:"Google Gemini",description:"Gemini 1.5 Pro — Multimodal reasoning",icon:e.jsx(u,{size:18}),isConfigured:!!s.geminiSet},{id:"openai",name:"OpenAI",description:"GPT-4o — OpenAI v1 compatible",icon:e.jsx(w,{size:18}),isConfigured:!!s.openaiSet},{id:"anthropic",name:"Anthropic",description:"Claude 3.5 Sonnet — Advanced analysis",icon:e.jsx(f,{size:18}),isConfigured:!!s.anthropicSet},{id:"ollama",name:"Ollama",description:"Local / Self-hosted — No API key required",icon:e.jsx(E,{size:18}),isConfigured:!!s.ollamaModel||!!s.aiModel||!!s.aiBaseUrl}],a=t.find(i=>i.id===s.aiProvider),M=t.filter(i=>i.isConfigured).length,r=i=>{switch(i){case"gemini":return"gemini-3-flash-preview";case"openai":return"gpt-4o";case"anthropic":return"claude-3-5-sonnet-20240620";case"ollama":return"llama3";default:return"—"}},b=i=>{switch(i){case"ollama":return"http://localhost:11434";default:return"Provider default"}};return N?e.jsxs("div",{className:"settings-loading",children:[e.jsx(C,{className:"animate-spin",size:32}),e.jsx("span",{children:"Synchronizing Global State..."})]}):e.jsxs("div",{className:"settings-container mesh-gradient-ai",children:[e.jsx("div",{className:"scanline",style:{position:"absolute",inset:0,pointerEvents:"none",opacity:.05,zIndex:1001}}),e.jsxs("div",{className:"settings-header",children:[e.jsxs("div",{className:"settings-title-group",children:[e.jsx(u,{className:"settings-icon ai-engine-icon",size:28}),e.jsx("h2",{children:"AI Engine Configuration"})]}),e.jsxs("p",{className:"settings-subtitle",children:["All credentials and endpoints are managed via environment variables.",e.jsx("br",{}),e.jsxs("span",{style:{fontSize:"0.85rem",opacity:.7},children:[e.jsx(v,{size:12,style:{display:"inline",marginBottom:-2,marginRight:4}}),"Select the active provider from configured options below."]})]})]}),e.jsxs("div",{className:"settings-content",children:[e.jsxs("div",{className:"settings-grid",children:[e.jsxs("div",{className:"setting-card stagger-1",children:[e.jsxs("div",{className:"setting-card-header",children:[e.jsx(f,{size:16}),e.jsx("h4",{children:"Provider Registry"}),e.jsxs("span",{className:"badge-elite",style:{marginLeft:"auto"},children:[M," / ",t.length," CONFIGURED"]})]}),e.jsx("p",{className:"section-description-dense",children:"Providers are activated via environment variables. Select a configured engine to activate."}),e.jsx("div",{className:"ai-provider-grid",children:t.map(i=>{const h=s.aiProvider===i.id,c=i.isConfigured;return e.jsxs("button",{className:`ai-provider-card ${h?"active":""} ${c?"":"disabled"}`,onClick:()=>c&&d({...s,aiProvider:i.id}),disabled:!c,title:c?`Select ${i.name}`:`Set XENON_${i.id.toUpperCase()}_API_KEY or model/URL to enable`,children:[e.jsxs("div",{className:"ai-provider-card-header",children:[e.jsx("div",{className:"ai-provider-icon",children:i.icon}),e.jsxs("div",{className:"ai-provider-info",children:[e.jsx("span",{className:"ai-provider-name",children:i.name}),e.jsx("span",{className:"ai-provider-desc",children:i.description})]})]}),e.jsx("div",{className:"ai-provider-status",children:h?e.jsxs("span",{className:"status-badge success-filled",children:[e.jsx("div",{className:"live-signal"}),"ACTIVE"]}):i.isConfigured?e.jsxs("span",{className:"status-badge success-filled",children:[e.jsx("div",{className:"live-signal"}),"READY"]}):e.jsxs("span",{className:"status-badge error-filled",children:[e.jsx(v,{size:10}),"NOT SET"]})})]},i.id)})})]}),e.jsxs("div",{className:"setting-card stagger-2",children:[e.jsxs("div",{className:"setting-card-header",children:[e.jsx(k,{size:16}),e.jsx("h4",{children:"Runtime Configuration"})]}),e.jsx("p",{className:"section-description-dense",children:"Environmental overrides for AI model endpoints and identifiers."}),e.jsxs("div",{className:"ai-config-display",children:[e.jsxs("div",{className:"ai-config-row",children:[e.jsx("span",{className:"ai-config-label",children:"Active Provider"}),e.jsxs("span",{className:"ai-config-value",children:[a==null?void 0:a.icon,(a==null?void 0:a.name)||"—"]})]}),e.jsxs("div",{className:"ai-config-row",children:[e.jsx("span",{className:"ai-config-label",children:"Model"}),e.jsxs("span",{className:"ai-config-value mono",children:[s.aiProvider==="gemini"&&(s.geminiModel||s.aiModel||r("gemini")),s.aiProvider==="openai"&&(s.openaiModel||s.aiModel||r("openai")),s.aiProvider==="anthropic"&&(s.anthropicModel||s.aiModel||r("anthropic")),s.aiProvider==="ollama"&&(s.ollamaModel||s.aiModel||r("ollama")),!s.aiModel&&!s.geminiModel&&!s.openaiModel&&!s.anthropicModel&&!s.ollamaModel&&e.jsx("span",{className:"ai-config-default",children:"default"})]})]}),e.jsxs("div",{className:"ai-config-row",children:[e.jsx("span",{className:"ai-config-label",children:"Base URL"}),e.jsxs("span",{className:"ai-config-value mono",children:[s.aiBaseUrl||b(s.aiProvider),!s.aiBaseUrl&&e.jsx("span",{className:"ai-config-default",children:"default"})]})]})]})]})]}),o&&e.jsxs("div",{className:`status-banner ${o.type}`,style:{borderRadius:"var(--radius-enterprise)",padding:"var(--gap-2)",marginTop:"var(--gap-3)",justifyContent:"center"},children:[o.type==="success"?e.jsx(A,{size:16}):e.jsx(z,{size:16}),e.jsx("span",{style:{fontWeight:600},children:o.message})]})]}),e.jsx(P,{onSave:S,onDiscard:p,isSaving:y,saveLabel:"Save Configuration"})]})};export{_ as AISettings};
11
+ */const B=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],E=j("server",B),O=()=>{const[s,d]=l.useState({aiProvider:"gemini",aiModel:"",aiBaseUrl:"",geminiModel:"",openaiModel:"",anthropicModel:"",ollamaModel:"",geminiSet:!1,openaiSet:!1,anthropicSet:!1}),[N,m]=l.useState(!0),[y,g]=l.useState(!1),[o,n]=l.useState(null);l.useEffect(()=>{p()},[]);const p=async()=>{m(!0);try{const i=await x.getGlobalConfig();d({aiProvider:i.aiProvider||"gemini",aiModel:i.aiModel||"",aiBaseUrl:i.aiBaseUrl||"",geminiModel:i.geminiModel||"",openaiModel:i.openaiModel||"",anthropicModel:i.anthropicModel||"",ollamaModel:i.ollamaModel||"",geminiSet:i.geminiSet||!1,openaiSet:i.openaiSet||!1,anthropicSet:i.anthropicSet||!1})}catch(i){console.error("Failed to load AISettings",i),n({type:"error",message:"Failed to access AI configuration."})}finally{m(!1)}},S=async()=>{g(!0),n(null);try{await x.updateGlobalConfig({aiProvider:s.aiProvider}),n({type:"success",message:"Active provider updated successfully."}),setTimeout(()=>n(null),5e3)}catch(i){console.error("Failed to save AISettings",i),n({type:"error",message:"Failed to persist updates."})}finally{g(!1)}},t=[{id:"gemini",name:"Google Gemini",description:"Gemini 1.5 Pro — Multimodal reasoning",icon:e.jsx(u,{size:18}),isConfigured:!!s.geminiSet},{id:"openai",name:"OpenAI",description:"GPT-4o — OpenAI v1 compatible",icon:e.jsx(w,{size:18}),isConfigured:!!s.openaiSet},{id:"anthropic",name:"Anthropic",description:"Claude 3.5 Sonnet — Advanced analysis",icon:e.jsx(f,{size:18}),isConfigured:!!s.anthropicSet},{id:"ollama",name:"Ollama",description:"Local / Self-hosted — No API key required",icon:e.jsx(E,{size:18}),isConfigured:!!s.ollamaModel||!!s.aiModel||!!s.aiBaseUrl}],a=t.find(i=>i.id===s.aiProvider),M=t.filter(i=>i.isConfigured).length,r=i=>{switch(i){case"gemini":return"gemini-3-flash-preview";case"openai":return"gpt-4o";case"anthropic":return"claude-3-5-sonnet-20240620";case"ollama":return"llama3";default:return"—"}},b=i=>{switch(i){case"ollama":return"http://localhost:11434";default:return"Provider default"}};return N?e.jsxs("div",{className:"settings-loading",children:[e.jsx(C,{className:"animate-spin",size:32}),e.jsx("span",{children:"Synchronizing Global State..."})]}):e.jsxs("div",{className:"settings-container mesh-gradient-ai",children:[e.jsx("div",{className:"scanline",style:{position:"absolute",inset:0,pointerEvents:"none",opacity:.05,zIndex:1001}}),e.jsxs("div",{className:"settings-header",children:[e.jsxs("div",{className:"settings-title-group",children:[e.jsx(u,{className:"settings-icon ai-engine-icon",size:28}),e.jsx("h2",{children:"AI Engine Configuration"})]}),e.jsxs("p",{className:"settings-subtitle",children:["All credentials and endpoints are managed via environment variables.",e.jsx("br",{}),e.jsxs("span",{style:{fontSize:"0.85rem",opacity:.7},children:[e.jsx(v,{size:12,style:{display:"inline",marginBottom:-2,marginRight:4}}),"Select the active provider from configured options below."]})]})]}),e.jsxs("div",{className:"settings-content",children:[e.jsxs("div",{className:"settings-grid",children:[e.jsxs("div",{className:"setting-card stagger-1",children:[e.jsxs("div",{className:"setting-card-header",children:[e.jsx(f,{size:16}),e.jsx("h4",{children:"Provider Registry"}),e.jsxs("span",{className:"badge-elite",style:{marginLeft:"auto"},children:[M," / ",t.length," CONFIGURED"]})]}),e.jsx("p",{className:"section-description-dense",children:"Providers are activated via environment variables. Select a configured engine to activate."}),e.jsx("div",{className:"ai-provider-grid",children:t.map(i=>{const h=s.aiProvider===i.id,c=i.isConfigured;return e.jsxs("button",{className:`ai-provider-card ${h?"active":""} ${c?"":"disabled"}`,onClick:()=>c&&d({...s,aiProvider:i.id}),disabled:!c,title:c?`Select ${i.name}`:`Set XENON_${i.id.toUpperCase()}_API_KEY or model/URL to enable`,children:[e.jsxs("div",{className:"ai-provider-card-header",children:[e.jsx("div",{className:"ai-provider-icon",children:i.icon}),e.jsxs("div",{className:"ai-provider-info",children:[e.jsx("span",{className:"ai-provider-name",children:i.name}),e.jsx("span",{className:"ai-provider-desc",children:i.description})]})]}),e.jsx("div",{className:"ai-provider-status",children:h?e.jsxs("span",{className:"status-badge success-filled",children:[e.jsx("div",{className:"live-signal"}),"ACTIVE"]}):i.isConfigured?e.jsxs("span",{className:"status-badge success-filled",children:[e.jsx("div",{className:"live-signal"}),"READY"]}):e.jsxs("span",{className:"status-badge error-filled",children:[e.jsx(v,{size:10}),"NOT SET"]})})]},i.id)})})]}),e.jsxs("div",{className:"setting-card stagger-2",children:[e.jsxs("div",{className:"setting-card-header",children:[e.jsx(k,{size:16}),e.jsx("h4",{children:"Runtime Configuration"})]}),e.jsx("p",{className:"section-description-dense",children:"Environmental overrides for AI model endpoints and identifiers."}),e.jsxs("div",{className:"ai-config-display",children:[e.jsxs("div",{className:"ai-config-row",children:[e.jsx("span",{className:"ai-config-label",children:"Active Provider"}),e.jsxs("span",{className:"ai-config-value",children:[a==null?void 0:a.icon,(a==null?void 0:a.name)||"—"]})]}),e.jsxs("div",{className:"ai-config-row",children:[e.jsx("span",{className:"ai-config-label",children:"Model"}),e.jsxs("span",{className:"ai-config-value mono",children:[s.aiProvider==="gemini"&&(s.geminiModel||s.aiModel||r("gemini")),s.aiProvider==="openai"&&(s.openaiModel||s.aiModel||r("openai")),s.aiProvider==="anthropic"&&(s.anthropicModel||s.aiModel||r("anthropic")),s.aiProvider==="ollama"&&(s.ollamaModel||s.aiModel||r("ollama")),!s.aiModel&&!s.geminiModel&&!s.openaiModel&&!s.anthropicModel&&!s.ollamaModel&&e.jsx("span",{className:"ai-config-default",children:"default"})]})]}),e.jsxs("div",{className:"ai-config-row",children:[e.jsx("span",{className:"ai-config-label",children:"Base URL"}),e.jsxs("span",{className:"ai-config-value mono",children:[s.aiBaseUrl||b(s.aiProvider),!s.aiBaseUrl&&e.jsx("span",{className:"ai-config-default",children:"default"})]})]})]})]})]}),o&&e.jsxs("div",{className:`status-banner ${o.type}`,style:{borderRadius:"var(--radius-enterprise)",padding:"var(--gap-2)",marginTop:"var(--gap-3)",justifyContent:"center"},children:[o.type==="success"?e.jsx(A,{size:16}):e.jsx(z,{size:16}),e.jsx("span",{style:{fontWeight:600},children:o.message})]})]}),e.jsx(P,{onSave:S,onDiscard:p,isSaving:y,saveLabel:"Save Configuration"})]})};export{O as AISettings};
@@ -0,0 +1 @@
1
+ import{u as _,r as i,j as e,g as $,K as G,T as E,X as Q}from"./index-Do3u3FMx.js";/* empty css */import{S as V}from"./shield-alert-Br1Ebl1G.js";import{P as L}from"./plus-DecbehZh.js";import{T as Z}from"./trash-2-DEafUZlN.js";import{C as ee,a as se}from"./copy-DCAmOyY9.js";const B=["read","sessions","devices","admin"];function D(a){if(!a)return"Never";const n=Date.now()-new Date(a).getTime();return n<6e4?"Just now":n<36e5?`${Math.floor(n/6e4)}m ago`:n<864e5?`${Math.floor(n/36e5)}h ago`:`${Math.floor(n/864e5)}d ago`}const de=()=>{const{toast:a}=_(),[n,o]=i.useState([]),[d,y]=i.useState([]),[H,v]=i.useState(!0),[M,w]=i.useState(!1),[O,c]=i.useState(!1),[m,u]=i.useState(null),[k,C]=i.useState(null),[S,N]=i.useState(!1),[h,T]=i.useState(""),[g,I]=i.useState({read:!0,sessions:!0,devices:!1,admin:!1}),[z,R]=i.useState(300),[A,K]=i.useState(""),[x,P]=i.useState(!1),q=s=>{var t;return s?((t=d.find(j=>j.id===s))==null?void 0:t.name)||s.slice(0,8):"Shared"},f=async()=>{v(!0);try{const[s,t]=await Promise.all([fetch("/xenon/api/apikeys",{credentials:"include"}),fetch("/xenon/api/teams",{credentials:"include"})]);if(s.status===403){w(!0),o([]);return}if(!s.ok)throw new Error(`HTTP ${s.status}`);o(await s.json()),t.ok&&y(await t.json()),w(!1)}catch(s){a(`Failed to load API keys: ${s.message}`,"error")}finally{v(!1)}};i.useEffect(()=>{f()},[]);const J=()=>{T(""),I({read:!0,sessions:!0,devices:!1,admin:!1}),R(300),K("")},X=async()=>{const s=B.filter(t=>g[t]);if(!h.trim()){a("Key name is required","error");return}if(s.length===0){a("Select at least one scope","error");return}P(!0);try{const t=await fetch("/xenon/api/apikeys",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:h.trim(),scopes:s,rateLimit:z,teamId:A||null})});if(!t.ok)throw new Error(`HTTP ${t.status}`);const j=await t.json();c(!1),J(),u({name:h.trim(),raw:j.key}),await f()}catch(t){a(`Create failed: ${t.message}`,"error")}finally{P(!1)}},Y=async s=>{C(s);try{const t=await fetch(`/xenon/api/apikeys/${s}`,{method:"DELETE",credentials:"include"});if(!t.ok)throw new Error(`HTTP ${t.status}`);a("Key revoked","success"),await f()}catch(t){a(`Revoke failed: ${t.message}`,"error")}finally{C(null)}},U=async s=>{try{await navigator.clipboard.writeText(s),N(!0),setTimeout(()=>N(!1),1500)}catch{a("Copy failed — select the text manually","error")}};return H?e.jsxs("div",{className:"settings-loading",children:[e.jsx($,{className:"animate-spin",size:32}),e.jsx("span",{children:"Loading API keys…"})]}):M?e.jsx("div",{className:"settings-container mesh-gradient-infra",children:e.jsxs("div",{className:"settings-header",children:[e.jsxs("div",{className:"settings-title-group",children:[e.jsx(V,{className:"settings-icon infra-icon",size:28}),e.jsx("h2",{children:"API Keys"})]}),e.jsxs("p",{className:"settings-subtitle",children:["Your key lacks the ",e.jsx("code",{children:"admin"})," scope. Ask an administrator to issue an admin key so you can manage access for other users."]})]})}):e.jsxs("div",{className:"settings-container mesh-gradient-infra",children:[e.jsx("div",{className:"scanline",style:{position:"absolute",inset:0,pointerEvents:"none",opacity:.05,zIndex:1001}}),e.jsxs("div",{className:"settings-header",children:[e.jsxs("div",{className:"settings-title-group",children:[e.jsx(G,{className:"settings-icon infra-icon",size:28}),e.jsx("h2",{children:"API Keys"})]}),e.jsxs("p",{className:"settings-subtitle",children:["Issue scoped credentials for humans (dashboard login) and machines (CI, WebDriver clients via ",e.jsx("code",{children:"xenon:accessKey"}),"). Keys are shown only once at creation — copy the value before closing the dialog."]})]}),e.jsxs("div",{className:"settings-content",children:[e.jsx("div",{style:{display:"flex",justifyContent:"flex-end",marginBottom:16},children:e.jsxs("button",{className:"save-btn",onClick:()=>c(!0),children:[e.jsx(L,{size:18}),"Create new key"]})}),n.length===0?e.jsxs("div",{className:"setting-card",style:{textAlign:"center",padding:48},children:[e.jsx(E,{size:32,style:{opacity:.4}}),e.jsx("p",{style:{marginTop:12},children:"No active keys. Create one above, or revoke the bootstrap key after a replacement is in place."})]}):e.jsx("div",{className:"setting-card",style:{padding:0,overflowX:"auto"},children:e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{borderBottom:"1px solid rgba(255,255,255,0.08)"},children:[e.jsx("th",{style:r,children:"Name"}),e.jsx("th",{style:r,children:"Team"}),e.jsx("th",{style:r,children:"Scopes"}),e.jsx("th",{style:r,children:"Rate limit"}),e.jsx("th",{style:r,children:"Last used"}),e.jsx("th",{style:r,children:"Created"}),e.jsx("th",{style:{...r,textAlign:"right"},children:"Actions"})]})}),e.jsx("tbody",{children:n.map(s=>e.jsxs("tr",{style:{borderBottom:"1px solid rgba(255,255,255,0.05)"},children:[e.jsxs("td",{style:l,children:[e.jsx("strong",{children:s.name}),e.jsx("div",{style:{fontSize:"0.75em",opacity:.5,fontFamily:"monospace"},children:s.id.slice(0,8)})]}),e.jsx("td",{style:l,children:e.jsx("span",{style:{...W,background:s.teamId?"rgba(16,185,129,0.15)":"rgba(255,255,255,0.06)",color:s.teamId?"#34d399":"#9ca3af"},children:q(s.teamId)})}),e.jsx("td",{style:l,children:s.scopes.split(",").map(t=>e.jsx("span",{style:W,children:t.trim()},t))}),e.jsxs("td",{style:l,children:[s.rateLimit,"/min"]}),e.jsx("td",{style:l,children:D(s.lastUsedAt)}),e.jsx("td",{style:l,children:D(s.createdAt)}),e.jsx("td",{style:{...l,textAlign:"right"},children:e.jsxs("button",{className:"reset-btn",disabled:k===s.id,onClick:()=>{window.confirm(`Revoke "${s.name}"? Any client using this key will immediately fail.`)&&Y(s.id)},style:{color:"var(--accent-red)"},children:[e.jsx(Z,{size:14}),k===s.id?"Revoking…":"Revoke"]})})]},s.id))})]})})]}),O&&e.jsx(F,{onClose:()=>c(!1),title:"Create API key",children:e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:16},children:[e.jsxs("label",{children:[e.jsx("div",{style:p,children:"Name"}),e.jsx("input",{type:"text",value:h,onChange:s=>T(s.target.value),placeholder:"alice-laptop, ci-main, etc.",style:b,autoFocus:!0})]}),e.jsxs("div",{children:[e.jsx("div",{style:p,children:"Scopes"}),e.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:8},children:B.map(s=>e.jsxs("label",{style:te,children:[e.jsx("input",{type:"checkbox",checked:g[s],onChange:t=>I({...g,[s]:t.target.checked})}),e.jsx("span",{children:s})]},s))}),e.jsxs("div",{style:{fontSize:"0.8em",opacity:.6,marginTop:6},children:[e.jsx("code",{children:"admin"})," grants full access including API-key management."," ",e.jsx("code",{children:"sessions"})," is required for WebDriver ",e.jsx("code",{children:"xenon:accessKey"}),"."]})]}),e.jsxs("label",{children:[e.jsx("div",{style:p,children:"Rate limit (requests/min)"}),e.jsx("input",{type:"number",min:10,step:10,value:z,onChange:s=>R(parseInt(s.target.value)||300),style:b})]}),e.jsxs("label",{children:[e.jsx("div",{style:p,children:"Default team"}),e.jsxs("select",{value:A,onChange:s=>K(s.target.value),style:b,children:[e.jsx("option",{value:"",children:"No team (shared pool only)"}),d.map(s=>e.jsx("option",{value:s.id,children:s.name},s.id))]}),e.jsxs("div",{style:{fontSize:"0.8em",opacity:.6,marginTop:6},children:["Keys without a team can only reach shared-pool devices. Admins can override at session time via ",e.jsx("code",{children:"xenon:team"}),"."]})]}),e.jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:8,marginTop:8},children:[e.jsx("button",{className:"reset-btn",onClick:()=>c(!1),disabled:x,children:"Cancel"}),e.jsxs("button",{className:"save-btn",onClick:X,disabled:x,children:[x?e.jsx($,{className:"animate-spin",size:18}):e.jsx(L,{size:18}),x?"Creating…":"Create key"]})]})]})}),m&&e.jsx(F,{onClose:()=>u(null),title:`Key created: ${m.name}`,closeOnBackdrop:!1,children:e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:16},children:[e.jsxs("div",{style:{background:"rgba(245, 158, 11, 0.1)",border:"1px solid rgba(245, 158, 11, 0.4)",borderRadius:6,padding:12,display:"flex",gap:8,alignItems:"flex-start"},children:[e.jsx(E,{size:18,style:{flexShrink:0,marginTop:2}}),e.jsxs("div",{children:[e.jsx("strong",{children:"This key will not be shown again."})," Copy it now and store it in your password manager, CI secret store, or client config."]})]}),e.jsx("div",{style:{fontFamily:"monospace",padding:12,background:"rgba(0,0,0,0.3)",borderRadius:6,wordBreak:"break-all",fontSize:"0.9em"},children:m.raw}),e.jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:8},children:[e.jsxs("button",{className:"save-btn",onClick:()=>U(m.raw),children:[S?e.jsx(ee,{size:18}):e.jsx(se,{size:18}),S?"Copied":"Copy to clipboard"]}),e.jsx("button",{className:"reset-btn",onClick:()=>u(null),children:"I've saved it"})]})]})})]})},F=({onClose:a,title:n,children:o,closeOnBackdrop:d=!0})=>e.jsx("div",{onClick:d?a:void 0,style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.6)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:2e3},children:e.jsxs("div",{onClick:y=>y.stopPropagation(),style:{background:"var(--bg-main, #0d0d12)",border:"1px solid rgba(255,255,255,0.08)",borderRadius:10,padding:24,width:"min(520px, 90vw)",maxHeight:"85vh",overflowY:"auto"},children:[e.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16},children:[e.jsx("h3",{style:{margin:0},children:n}),e.jsx("button",{onClick:a,style:{background:"none",border:"none",cursor:"pointer",color:"inherit"},"aria-label":"Close",children:e.jsx(Q,{size:20})})]}),o]})}),r={textAlign:"left",padding:"12px 16px",fontSize:"0.85em",textTransform:"uppercase",letterSpacing:"0.05em",opacity:.7,fontWeight:600},l={padding:"12px 16px",verticalAlign:"top"},W={display:"inline-block",padding:"2px 8px",margin:"0 4px 2px 0",background:"rgba(59, 130, 246, 0.15)",color:"#60a5fa",borderRadius:4,fontSize:"0.8em",fontFamily:"monospace"},p={fontSize:"0.85em",textTransform:"uppercase",letterSpacing:"0.05em",opacity:.7,marginBottom:6,fontWeight:600},b={width:"100%",padding:"8px 12px",background:"rgba(0,0,0,0.3)",border:"1px solid rgba(255,255,255,0.1)",borderRadius:6,color:"inherit",fontSize:"0.95em",boxSizing:"border-box"},te={display:"flex",alignItems:"center",gap:6,padding:"6px 12px",background:"rgba(255,255,255,0.04)",borderRadius:6,cursor:"pointer",fontFamily:"monospace",fontSize:"0.9em"};export{de as ApiKeys};
@@ -1,4 +1,4 @@
1
- import{c as g,u as X,r,j as e,S as z,g as q,X as V}from"./index-S71J2rWg.js";import{X as c}from"./index-C1DBaoSh.js";import{S as H,B as S,D as K}from"./badge-BiR1gmMm.js";import{A as D,B as T,U as Z,C as J,a as Q,T as ee}from"./button-BVazt4Z1.js";import{Z as se}from"./zap-ZrK5B58i.js";import{T as ae}from"./trash-2-CZWUMK5b.js";/**
1
+ import{c as g,u as X,r,j as e,S as z,g as q,X as V}from"./index-Do3u3FMx.js";import{X as c}from"./index-C1DBaoSh.js";import{S as H,B as S,D as K}from"./badge-BtQKmQsj.js";import{A as D,B as T,U as Z,T as J}from"./button-CNLKazSa.js";import{C as Q,a as ee}from"./copy-DCAmOyY9.js";import{Z as se}from"./zap-DCsIYdwr.js";import{T as ae}from"./trash-2-DEafUZlN.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -13,4 +13,4 @@ import{c as g,u as X,r,j as e,S as z,g as q,X as V}from"./index-S71J2rWg.js";imp
13
13
  *
14
14
  * This source code is licensed under the ISC license.
15
15
  * See the LICENSE file in the root directory of this source tree.
16
- */const ne=[["path",{d:"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z",key:"m3kijz"}],["path",{d:"m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z",key:"1fmvmk"}],["path",{d:"M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0",key:"1f8sc4"}],["path",{d:"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5",key:"qeys4"}]],I=g("rocket",ne),xe=()=>{const{toast:n,removeToast:M}=X(),[l,N]=r.useState([]),[E,F]=r.useState([]),[o,k]=r.useState(!1),[x,R]=r.useState(""),[$,w]=r.useState(!1),[L,b]=r.useState(null),[d,B]=r.useState({android:!0,ios:!0}),[u,U]=r.useState("desc"),[m,v]=r.useState(null),[j,y]=r.useState(""),h=r.useCallback(async()=>{k(!0);try{const s=await c.getApps();N(Array.isArray(s)?s:[])}catch(s){console.error("Failed to fetch apps",s)}finally{k(!1)}},[]),f=r.useCallback(async()=>{try{const s=await c.getDevices();F(Array.isArray(s)?s:[])}catch(s){console.error("Failed to fetch devices",s)}},[]);r.useEffect(()=>{h(),f();const s=setInterval(f,1e4);return()=>clearInterval(s)},[h,f]);const A=async s=>{var t;const a=(t=s.target.files)==null?void 0:t[0];if(a){w(!0);try{await c.uploadApp(a),await h()}catch(i){console.error("Upload failed",i)}finally{w(!1),s.target&&(s.target.value="")}}},_=async(s,a)=>{if(window.confirm(`Permanently remove artifact "${a}"?`))try{await c.deleteApp(s),N(t=>t.filter(i=>i.id!==s))}catch(t){console.error("Delete failed",t)}},G=(s,a)=>{navigator.clipboard.writeText(s),b(`${a}-${s}`),setTimeout(()=>b(null),2e3)},O=async(s,a)=>{const t=n(`Deploying app to ${a}...`,"loading",0);try{const i=await c.installRepositoryApp(a,s);i.success?(n("App deployed successfully","success"),v(null),y("")):n(`Deployment failed: ${i.error}`,"error")}catch(i){n(`Execution Error: ${i.message}`,"error")}finally{M(t)}},p=l.filter(s=>{const a=s.name.toLowerCase().includes(x.toLowerCase())||s.packageName&&s.packageName.toLowerCase().includes(x.toLowerCase()),t=d.android&&s.platform==="android"||d.ios&&s.platform==="ios";return a&&t}).sort((s,a)=>{const t=new Date(s.createdAt).getTime(),i=new Date(a.createdAt).getTime();return u==="desc"?i-t:t-i}),Y=s=>{if(!s)return"0 B";const a=1024,t=Math.floor(Math.log(s)/Math.log(a));return parseFloat((s/Math.pow(a,t)).toFixed(1))+" "+["B","KB","MB","GB"][t]},P=s=>new Date(s).toLocaleDateString(void 0,{month:"short",day:"numeric",year:"numeric",hour:"2-digit",minute:"2-digit"}),C=s=>{B(a=>({...a,[s]:!a[s]}))},W=s=>{const a=s.toLowerCase();return a.includes("wda-signed")||a.includes("wda-resign")||a.includes("webdriveragent")};return e.jsxs("div",{className:"apps-container device-explorer-container",children:[e.jsx("div",{className:"scanline",style:{position:"absolute",inset:0,pointerEvents:"none",opacity:.05,zIndex:1001}}),e.jsxs("div",{className:"device-explorer-header-container",children:[e.jsxs("div",{className:"device-explorer-header-left-container",children:[e.jsx("div",{className:"device-explorer-header-entry",children:e.jsxs("div",{className:"device-explorer-header-value",children:[e.jsxs("button",{className:`device-explorer-header__platform-btn ${d.android&&"selected"}`,onClick:()=>C("android"),children:[e.jsx(z,{size:18,color:"currentColor"})," Android"]}),e.jsxs("button",{className:`device-explorer-header__platform-btn ${d.ios&&"selected"}`,onClick:()=>C("ios"),children:[e.jsx(D,{size:18,color:"currentColor"})," iOS"]})]})}),e.jsx("div",{className:"device-explorer-header-entry",children:e.jsx("div",{className:"device-explorer-header-value",children:e.jsxs("div",{className:"device-explorer-search-wrapper",children:[e.jsx(H,{size:16,color:"#94a3b8",className:"device-explorer-search-icon"}),e.jsx("input",{type:"text",className:"device-explorer-header-text-filter",placeholder:"Filter by bundle or name...",value:x,onChange:s=>R(s.target.value)})]})})}),e.jsx("div",{className:"device-explorer-header-filter-count",children:e.jsxs(S,{variant:"secondary",children:[e.jsx("span",{className:"font-bold",children:p.length})," of"," ",e.jsx("span",{className:"font-bold",children:l.length})," ",l.length===1?"artifact":"artifacts"]})})]}),e.jsxs("div",{className:"device-explorer-header-right-container",children:[e.jsx("button",{className:"icon-btn-secondary sort-btn",onClick:()=>U(s=>s==="desc"?"asc":"desc"),title:u==="desc"?"Newest First":"Oldest First",children:u==="desc"?e.jsx(re,{size:18}):e.jsx(ce,{size:18})}),e.jsxs(T,{size:"sm",variant:"default",onClick:()=>h(),disabled:o,children:[e.jsx(q,{size:14,className:`mr-1 ${o&&"animate-spin"}`}),"Refresh"]}),e.jsxs("label",{className:"upload-trigger",children:[e.jsx("input",{type:"file",accept:".apk,.ipa",onChange:A,style:{display:"none"}}),e.jsxs(T,{size:"sm",variant:"default",className:"upload-button",children:[e.jsx(Z,{size:14,className:"mr-1"}),"Upload App"]})]})]})]}),e.jsx("main",{className:"apps-registry-content",children:o&&l.length===0?e.jsxs("div",{className:"empty-data-vignette",children:[e.jsx("div",{className:"processing-loader"}),e.jsx("p",{className:"text-muted text-xs font-bold tracking-widest",children:"SYNCING REGISTRY..."})]}):e.jsxs(e.Fragment,{children:[p.length>0&&e.jsxs("div",{className:"registry-table-header",children:[e.jsx("div",{children:"Artifact Bundle"}),e.jsx("div",{children:"Version"}),e.jsx("div",{children:"Size"}),e.jsx("div",{children:"Registry Date"}),e.jsx("div",{style:{textAlign:"right"},children:"Management"})]}),e.jsx("div",{className:"registry-list",children:p.map(s=>{const a=E.filter(t=>t.platform===s.platform&&!t.busy&&!t.offline);return e.jsxs("div",{className:`artifact-row ${s.platform}`,children:[e.jsxs("div",{className:"col-app-info",children:[e.jsx("div",{className:"app-platform-badge",children:s.platform==="android"?e.jsx(z,{size:18}):e.jsx(D,{size:18})}),e.jsxs("div",{className:"app-text-block",children:[e.jsxs("div",{className:"app-display-name",title:s.name,children:[s.name,W(s.name)&&e.jsx(S,{variant:"outline",className:"ml-2 text-[10px] h-4 py-0 border-primary text-primary",children:"WDA"})]}),e.jsxs("div",{className:"app-package-id",children:[e.jsx("code",{children:s.packageName||"internal.bundle"}),e.jsx("div",{className:"copy-hint",onClick:()=>G(s.packageName||"","pkg"),children:L===`pkg-${s.packageName}`?e.jsx(J,{size:10,className:"text-primary"}):e.jsx(Q,{size:10})})]})]})]}),e.jsx("div",{className:"col-version",children:s.version||"v1.0.0"}),e.jsx("div",{className:"col-size",children:Y(s.size)}),e.jsx("div",{className:"col-timestamp",children:P(s.createdAt)}),e.jsxs("div",{className:"col-actions",children:[e.jsx("button",{className:`instant-deploy-trigger ${m===s.id&&"active"}`,onClick:()=>{m===s.id?(v(null),y("")):v(s.id)},children:m===s.id?e.jsxs(e.Fragment,{children:[e.jsx(V,{size:12})," CANCEL"]}):e.jsxs(e.Fragment,{children:[e.jsx(se,{size:12})," DEPLOY"]})}),m===s.id&&e.jsx("div",{className:"deployment-flyout",children:a.length>0?e.jsxs("div",{className:"deploy-actions-group",children:[e.jsxs("select",{className:"target-device-select",autoFocus:!0,value:j,onChange:t=>y(t.target.value),children:[e.jsx("option",{value:"",children:"SELECT TARGET..."}),a.map(t=>e.jsxs("option",{value:t.udid,children:[t.name.toUpperCase()," (",t.udid,")"]},t.udid))]}),e.jsx("button",{className:"confirm-deploy-btn",disabled:!j,onClick:()=>O(s.id,j),title:"Confirm Deployment",children:e.jsx(I,{size:14})})]}):e.jsx("div",{className:"target-device-select empty",children:"NO TARGETS"})}),e.jsx("button",{className:"utility-icon-btn",onClick:()=>window.open(`/xenon/api/apps/${s.id}/download`,"_blank"),children:e.jsx(K,{size:14})}),e.jsx("button",{className:"utility-icon-btn danger",onClick:()=>_(s.id,s.name),children:e.jsx(ae,{size:14})})]})]},s.id)})}),p.length===0&&!o&&e.jsxs("div",{className:"empty-registry-technical",children:[e.jsx("div",{className:"empty-icon-container",children:e.jsx(ee,{size:48,className:"text-primary opacity-40 animate-pulse"})}),e.jsx("h2",{className:"empty-title brand-font",children:"Centralized Artifact Registry"}),e.jsxs("p",{className:"empty-description",children:["The Xenon Registry is a secure, high-performance vault for your mobile binaries. Upload your ",e.jsx("code",{className:"text-primary",children:".apk"})," or"," ",e.jsx("code",{className:"text-primary",children:".ipa"}),"artifacts to enable rapid deployment, version tracking, and instant installation across your entire device fleet."]}),e.jsx("div",{className:"empty-actions",children:e.jsxs("label",{className:"upload-trigger-massive",children:[e.jsx("input",{type:"file",accept:".apk,.ipa",onChange:A,style:{display:"none"}}),e.jsxs("div",{className:"massive-upload-content",children:[e.jsx(I,{size:24,className:"mb-2"}),e.jsx("span",{children:"Ingest Your First Artifact"})]})]})})]})]})}),$&&e.jsx("div",{className:"upload-overlay-technical",children:e.jsxs("div",{className:"upload-card-technical",children:[e.jsx("div",{className:"processing-loader"}),e.jsx("h2",{className:"brand-font",style:{fontSize:"20px",marginBottom:"8px"},children:"Artifact Ingestion"}),e.jsx("p",{className:"text-muted text-xs",children:"VERIFYING CHECKSUM & DEPLOYING TO REGISTRY..."})]})})]})};export{xe as default};
16
+ */const ne=[["path",{d:"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z",key:"m3kijz"}],["path",{d:"m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z",key:"1fmvmk"}],["path",{d:"M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0",key:"1f8sc4"}],["path",{d:"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5",key:"qeys4"}]],I=g("rocket",ne),ue=()=>{const{toast:n,removeToast:M}=X(),[l,N]=r.useState([]),[E,F]=r.useState([]),[o,k]=r.useState(!1),[x,R]=r.useState(""),[$,w]=r.useState(!1),[L,b]=r.useState(null),[d,B]=r.useState({android:!0,ios:!0}),[u,U]=r.useState("desc"),[m,v]=r.useState(null),[j,y]=r.useState(""),h=r.useCallback(async()=>{k(!0);try{const s=await c.getApps();N(Array.isArray(s)?s:[])}catch(s){console.error("Failed to fetch apps",s)}finally{k(!1)}},[]),f=r.useCallback(async()=>{try{const s=await c.getDevices();F(Array.isArray(s)?s:[])}catch(s){console.error("Failed to fetch devices",s)}},[]);r.useEffect(()=>{h(),f();const s=setInterval(f,1e4);return()=>clearInterval(s)},[h,f]);const A=async s=>{var t;const a=(t=s.target.files)==null?void 0:t[0];if(a){w(!0);try{await c.uploadApp(a),await h()}catch(i){console.error("Upload failed",i)}finally{w(!1),s.target&&(s.target.value="")}}},_=async(s,a)=>{if(window.confirm(`Permanently remove artifact "${a}"?`))try{await c.deleteApp(s),N(t=>t.filter(i=>i.id!==s))}catch(t){console.error("Delete failed",t)}},G=(s,a)=>{navigator.clipboard.writeText(s),b(`${a}-${s}`),setTimeout(()=>b(null),2e3)},O=async(s,a)=>{const t=n(`Deploying app to ${a}...`,"loading",0);try{const i=await c.installRepositoryApp(a,s);i.success?(n("App deployed successfully","success"),v(null),y("")):n(`Deployment failed: ${i.error}`,"error")}catch(i){n(`Execution Error: ${i.message}`,"error")}finally{M(t)}},p=l.filter(s=>{const a=s.name.toLowerCase().includes(x.toLowerCase())||s.packageName&&s.packageName.toLowerCase().includes(x.toLowerCase()),t=d.android&&s.platform==="android"||d.ios&&s.platform==="ios";return a&&t}).sort((s,a)=>{const t=new Date(s.createdAt).getTime(),i=new Date(a.createdAt).getTime();return u==="desc"?i-t:t-i}),Y=s=>{if(!s)return"0 B";const a=1024,t=Math.floor(Math.log(s)/Math.log(a));return parseFloat((s/Math.pow(a,t)).toFixed(1))+" "+["B","KB","MB","GB"][t]},P=s=>new Date(s).toLocaleDateString(void 0,{month:"short",day:"numeric",year:"numeric",hour:"2-digit",minute:"2-digit"}),C=s=>{B(a=>({...a,[s]:!a[s]}))},W=s=>{const a=s.toLowerCase();return a.includes("wda-signed")||a.includes("wda-resign")||a.includes("webdriveragent")};return e.jsxs("div",{className:"apps-container device-explorer-container",children:[e.jsx("div",{className:"scanline",style:{position:"absolute",inset:0,pointerEvents:"none",opacity:.05,zIndex:1001}}),e.jsxs("div",{className:"device-explorer-header-container",children:[e.jsxs("div",{className:"device-explorer-header-left-container",children:[e.jsx("div",{className:"device-explorer-header-entry",children:e.jsxs("div",{className:"device-explorer-header-value",children:[e.jsxs("button",{className:`device-explorer-header__platform-btn ${d.android&&"selected"}`,onClick:()=>C("android"),children:[e.jsx(z,{size:18,color:"currentColor"})," Android"]}),e.jsxs("button",{className:`device-explorer-header__platform-btn ${d.ios&&"selected"}`,onClick:()=>C("ios"),children:[e.jsx(D,{size:18,color:"currentColor"})," iOS"]})]})}),e.jsx("div",{className:"device-explorer-header-entry",children:e.jsx("div",{className:"device-explorer-header-value",children:e.jsxs("div",{className:"device-explorer-search-wrapper",children:[e.jsx(H,{size:16,color:"#94a3b8",className:"device-explorer-search-icon"}),e.jsx("input",{type:"text",className:"device-explorer-header-text-filter",placeholder:"Filter by bundle or name...",value:x,onChange:s=>R(s.target.value)})]})})}),e.jsx("div",{className:"device-explorer-header-filter-count",children:e.jsxs(S,{variant:"secondary",children:[e.jsx("span",{className:"font-bold",children:p.length})," of"," ",e.jsx("span",{className:"font-bold",children:l.length})," ",l.length===1?"artifact":"artifacts"]})})]}),e.jsxs("div",{className:"device-explorer-header-right-container",children:[e.jsx("button",{className:"icon-btn-secondary sort-btn",onClick:()=>U(s=>s==="desc"?"asc":"desc"),title:u==="desc"?"Newest First":"Oldest First",children:u==="desc"?e.jsx(re,{size:18}):e.jsx(ce,{size:18})}),e.jsxs(T,{size:"sm",variant:"default",onClick:()=>h(),disabled:o,children:[e.jsx(q,{size:14,className:`mr-1 ${o&&"animate-spin"}`}),"Refresh"]}),e.jsxs("label",{className:"upload-trigger",children:[e.jsx("input",{type:"file",accept:".apk,.ipa",onChange:A,style:{display:"none"}}),e.jsxs(T,{size:"sm",variant:"default",className:"upload-button",children:[e.jsx(Z,{size:14,className:"mr-1"}),"Upload App"]})]})]})]}),e.jsx("main",{className:"apps-registry-content",children:o&&l.length===0?e.jsxs("div",{className:"empty-data-vignette",children:[e.jsx("div",{className:"processing-loader"}),e.jsx("p",{className:"text-muted text-xs font-bold tracking-widest",children:"SYNCING REGISTRY..."})]}):e.jsxs(e.Fragment,{children:[p.length>0&&e.jsxs("div",{className:"registry-table-header",children:[e.jsx("div",{children:"Artifact Bundle"}),e.jsx("div",{children:"Version"}),e.jsx("div",{children:"Size"}),e.jsx("div",{children:"Registry Date"}),e.jsx("div",{style:{textAlign:"right"},children:"Management"})]}),e.jsx("div",{className:"registry-list",children:p.map(s=>{const a=E.filter(t=>t.platform===s.platform&&!t.busy&&!t.offline);return e.jsxs("div",{className:`artifact-row ${s.platform}`,children:[e.jsxs("div",{className:"col-app-info",children:[e.jsx("div",{className:"app-platform-badge",children:s.platform==="android"?e.jsx(z,{size:18}):e.jsx(D,{size:18})}),e.jsxs("div",{className:"app-text-block",children:[e.jsxs("div",{className:"app-display-name",title:s.name,children:[s.name,W(s.name)&&e.jsx(S,{variant:"outline",className:"ml-2 text-[10px] h-4 py-0 border-primary text-primary",children:"WDA"})]}),e.jsxs("div",{className:"app-package-id",children:[e.jsx("code",{children:s.packageName||"internal.bundle"}),e.jsx("div",{className:"copy-hint",onClick:()=>G(s.packageName||"","pkg"),children:L===`pkg-${s.packageName}`?e.jsx(Q,{size:10,className:"text-primary"}):e.jsx(ee,{size:10})})]})]})]}),e.jsx("div",{className:"col-version",children:s.version||"v1.0.0"}),e.jsx("div",{className:"col-size",children:Y(s.size)}),e.jsx("div",{className:"col-timestamp",children:P(s.createdAt)}),e.jsxs("div",{className:"col-actions",children:[e.jsx("button",{className:`instant-deploy-trigger ${m===s.id&&"active"}`,onClick:()=>{m===s.id?(v(null),y("")):v(s.id)},children:m===s.id?e.jsxs(e.Fragment,{children:[e.jsx(V,{size:12})," CANCEL"]}):e.jsxs(e.Fragment,{children:[e.jsx(se,{size:12})," DEPLOY"]})}),m===s.id&&e.jsx("div",{className:"deployment-flyout",children:a.length>0?e.jsxs("div",{className:"deploy-actions-group",children:[e.jsxs("select",{className:"target-device-select",autoFocus:!0,value:j,onChange:t=>y(t.target.value),children:[e.jsx("option",{value:"",children:"SELECT TARGET..."}),a.map(t=>e.jsxs("option",{value:t.udid,children:[t.name.toUpperCase()," (",t.udid,")"]},t.udid))]}),e.jsx("button",{className:"confirm-deploy-btn",disabled:!j,onClick:()=>O(s.id,j),title:"Confirm Deployment",children:e.jsx(I,{size:14})})]}):e.jsx("div",{className:"target-device-select empty",children:"NO TARGETS"})}),e.jsx("button",{className:"utility-icon-btn",onClick:()=>window.open(`/xenon/api/apps/${s.id}/download`,"_blank"),children:e.jsx(K,{size:14})}),e.jsx("button",{className:"utility-icon-btn danger",onClick:()=>_(s.id,s.name),children:e.jsx(ae,{size:14})})]})]},s.id)})}),p.length===0&&!o&&e.jsxs("div",{className:"empty-registry-technical",children:[e.jsx("div",{className:"empty-icon-container",children:e.jsx(J,{size:48,className:"text-primary opacity-40 animate-pulse"})}),e.jsx("h2",{className:"empty-title brand-font",children:"Centralized Artifact Registry"}),e.jsxs("p",{className:"empty-description",children:["The Xenon Registry is a secure, high-performance vault for your mobile binaries. Upload your ",e.jsx("code",{className:"text-primary",children:".apk"})," or"," ",e.jsx("code",{className:"text-primary",children:".ipa"}),"artifacts to enable rapid deployment, version tracking, and instant installation across your entire device fleet."]}),e.jsx("div",{className:"empty-actions",children:e.jsxs("label",{className:"upload-trigger-massive",children:[e.jsx("input",{type:"file",accept:".apk,.ipa",onChange:A,style:{display:"none"}}),e.jsxs("div",{className:"massive-upload-content",children:[e.jsx(I,{size:24,className:"mb-2"}),e.jsx("span",{children:"Ingest Your First Artifact"})]})]})})]})]})}),$&&e.jsx("div",{className:"upload-overlay-technical",children:e.jsxs("div",{className:"upload-card-technical",children:[e.jsx("div",{className:"processing-loader"}),e.jsx("h2",{className:"brand-font",style:{fontSize:"20px",marginBottom:"8px"},children:"Artifact Ingestion"}),e.jsx("p",{className:"text-muted text-xs",children:"VERIFYING CHECKSUM & DEPLOYING TO REGISTRY..."})]})})]})};export{ue as default};
@@ -0,0 +1,6 @@
1
+ import{c as o}from"./index-Do3u3FMx.js";/**
2
+ * @license lucide-react v0.555.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const e=[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]],r=o("arrow-left",e);export{r as A};
@@ -1,4 +1,4 @@
1
- import{c as ye,j as Ve}from"./index-S71J2rWg.js";/**
1
+ import{c as ye,j as Ve}from"./index-Do3u3FMx.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -0,0 +1,16 @@
1
+ import{c as t,r as i,j as r}from"./index-Do3u3FMx.js";import{c as u,a as c}from"./badge-BtQKmQsj.js";/**
2
+ * @license lucide-react v0.555.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const d=[["path",{d:"M12 6.528V3a1 1 0 0 1 1-1h0",key:"11qiee"}],["path",{d:"M18.237 21A15 15 0 0 0 22 11a6 6 0 0 0-10-4.472A6 6 0 0 0 2 11a15.1 15.1 0 0 0 3.763 10 3 3 0 0 0 3.648.648 5.5 5.5 0 0 1 5.178 0A3 3 0 0 0 18.237 21",key:"110c12"}]],f=t("apple",d);/**
7
+ * @license lucide-react v0.555.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const l=[["path",{d:"M12 19h8",key:"baeox8"}],["path",{d:"m4 17 6-6-6-6",key:"1yngyt"}]],y=t("terminal",l);/**
12
+ * @license lucide-react v0.555.0 - ISC
13
+ *
14
+ * This source code is licensed under the ISC license.
15
+ * See the LICENSE file in the root directory of this source tree.
16
+ */const p=[["path",{d:"M12 3v12",key:"1x0j5s"}],["path",{d:"m17 8-5-5-5 5",key:"7q97r8"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}]],k=t("upload",p),b=c("button-base",{variants:{variant:{default:"button-variant-default",outline:"button-variant-outline",ghost:"button-variant-ghost",link:"button-variant-link",destructive:"button-variant-destructive"},size:{default:"button-default",sm:"button-sm",lg:"button-lg",icon:"button-icon"}},defaultVariants:{variant:"default",size:"default"}}),v=i.forwardRef(({className:a,variant:n,size:o,...e},s)=>r.jsx("button",{className:u(b({variant:n,size:o,className:a})),ref:s,...e}));v.displayName="Button";export{f as A,v as B,y as T,k as U};
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-S71J2rWg.js";/**
1
+ import{c as e}from"./index-Do3u3FMx.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c}from"./index-S71J2rWg.js";/**
1
+ import{c}from"./index-Do3u3FMx.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -0,0 +1,11 @@
1
+ import{c}from"./index-Do3u3FMx.js";/**
2
+ * @license lucide-react v0.555.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const e=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],a=c("check",e);/**
7
+ * @license lucide-react v0.555.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const o=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],h=c("copy",o);export{a as C,h as a};
@@ -1,4 +1,4 @@
1
- import{c as h}from"./index-S71J2rWg.js";/**
1
+ import{c as h}from"./index-Do3u3FMx.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.