@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.
- package/LICENSE +15 -0
- package/README.md +223 -488
- package/lib/package.json +2 -2
- package/lib/public/assets/{AnnotationOverlay-Crkn72_3.js → AnnotationOverlay-Cvpkk4r9.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-BThVlj_j.js → ApiKeyGate-Bhn5ZtVM.js} +1 -1
- package/lib/public/assets/{BugReportButton-Dwa0GnBh.js → BugReportButton-BA6aEjTC.js} +1 -1
- package/lib/public/assets/{DeviceMosaicView-Ccc-pOls.js → DeviceMosaicView-ChhHnpFQ.js} +1 -1
- package/lib/public/assets/{EmptyState-BeuTrlN_.js → EmptyState-CgzbuUvO.js} +1 -1
- package/lib/public/assets/{FieldGroup-DBIaNxl1.js → FieldGroup-BHc_5qNc.js} +1 -1
- package/lib/public/assets/{FilterMenu-BgSAS6iT.js → FilterMenu-D7OMGuRc.js} +1 -1
- package/lib/public/assets/{Menu-GCbP6gYN.js → Menu-C173JJ0l.js} +1 -1
- package/lib/public/assets/{Modal-ByESeBRC.js → Modal-ChvVUI3B.js} +1 -1
- package/lib/public/assets/{RecordingPage-Bwyf3WMl.js → RecordingPage-Dhw4d_O6.js} +1 -1
- package/lib/public/assets/{RecordingsPage-7kM1rB3i.js → RecordingsPage-z97ftGK-.js} +2 -2
- package/lib/public/assets/{SegmentedControl-B8tTU8kD.js → SegmentedControl-DbXhBGZC.js} +1 -1
- package/lib/public/assets/{SettingCard-Clg_Ijbc.js → SettingCard-C908GgiI.js} +1 -1
- package/lib/public/assets/{Table-CIaR0QWV.js → Table-B1DB_ZN-.js} +1 -1
- package/lib/public/assets/{activity-Bengh9SD.js → activity-Css0xNvp.js} +1 -1
- package/lib/public/assets/ai-settings-BMQK3-yN.js +21 -0
- package/lib/public/assets/{api-keys-CrGlbfn-.js → api-keys-CbJ42eDQ.js} +1 -1
- package/lib/public/assets/{apps-DIbcGVlU.js → apps-DW1pw2re.js} +2 -2
- package/lib/public/assets/{arrow-left-BtJIXXMY.js → arrow-left-Dpog9L0F.js} +1 -1
- package/lib/public/assets/{arrow-right-zm-NagQh.js → arrow-right-CsBIlSIw.js} +1 -1
- package/lib/public/assets/{arrow-up-right-DdU8YEYp.js → arrow-up-right-z1tRQE4R.js} +1 -1
- package/lib/public/assets/{auth-shell-CHmBFmOz.js → auth-shell-mXiX5vNk.js} +2 -2
- package/lib/public/assets/{builds-page-JkzWKRpU.js → builds-page-BcFn23bE.js} +1 -1
- package/lib/public/assets/{button-CqhPBGRj.js → button-Cq_zHETv.js} +1 -1
- package/lib/public/assets/{calendar-C7eYYJTo.js → calendar-CKxABX9q.js} +1 -1
- package/lib/public/assets/{check-DpbIM4E0.js → check-C7xu-hGU.js} +1 -1
- package/lib/public/assets/{chevron-right-Dl4X1PRz.js → chevron-right-BiqfdCGA.js} +1 -1
- package/lib/public/assets/{circle-check-Du3sjbfV.js → circle-check-CyBjjPxR.js} +1 -1
- package/lib/public/assets/{circle-x-BSSqhpXi.js → circle-x-6gMRcx6z.js} +1 -1
- package/lib/public/assets/{clock-DMT60v1C.js → clock-U1rDTT1h.js} +1 -1
- package/lib/public/assets/{copy-BKNyOehd.js → copy-BIM0ZX0L.js} +1 -1
- package/lib/public/assets/device-explorer-DSpAA_t9.js +279 -0
- package/lib/public/assets/{download-BM6Xn22t.js → download-DDBRXnAZ.js} +1 -1
- package/lib/public/assets/{forgot-password-B8WqMqBT.js → forgot-password-Dpg7yxM8.js} +1 -1
- package/lib/public/assets/{index-ClrpAMAT.js → index-BGBqohfQ.js} +43 -43
- package/lib/public/assets/{index-DauQh6ie.js → index-BQ2luT7L.js} +1 -1
- package/lib/public/assets/{input-CyKdLnEx.js → input-BvB1cjHE.js} +1 -1
- package/lib/public/assets/{line-chart-EIBXwYGo.js → line-chart-m78VggnW.js} +1 -1
- package/lib/public/assets/{list-checks-fWsgD9bI.js → list-checks-cY7o1QTL.js} +1 -1
- package/lib/public/assets/{lock-CVCe56TH.js → lock-loz8txQm.js} +1 -1
- package/lib/public/assets/{login-BJ8a7yVD.js → login-BYOvz4lx.js} +1 -1
- package/lib/public/assets/maintenance-settings-BxbA7SkB.js +6 -0
- package/lib/public/assets/{monitor-Bw1YQZnL.js → monitor-C4HPHb6s.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-C01jbqkO.js → mouse-pointer-2-BkdKtCzi.js} +1 -1
- package/lib/public/assets/{network-CBGUjJDJ.js → network-C5e0MIWo.js} +1 -1
- package/lib/public/assets/{overview-Cxe8aQ7C.js → overview-tlu8ijGd.js} +1 -1
- package/lib/public/assets/{page-header-B92DKLiq.js → page-header-FXPlo6az.js} +1 -1
- package/lib/public/assets/{play-Ck0L-0_m.js → play-CWdk0_Sy.js} +1 -1
- package/lib/public/assets/{plus-Dq3tCy2N.js → plus-B2xCnntV.js} +1 -1
- package/lib/public/assets/{profile-page--dkDiKbr.js → profile-page-D080qzr4.js} +1 -1
- package/lib/public/assets/{recording-group-store-5BYIFFN9.js → recording-group-store-B75435xR.js} +1 -1
- package/lib/public/assets/{reset-password-KYWlfjia.js → reset-password-D3u4Mbvn.js} +1 -1
- package/lib/public/assets/{runbook-page-mnsxgS1d.js → runbook-page-hPRZMy7s.js} +1 -1
- package/lib/public/assets/{select-BBOIZTYm.js → select-12RThAXc.js} +1 -1
- package/lib/public/assets/{selector-detail-redirect-CcI2j_Su.js → selector-detail-redirect-uQwvV7EQ.js} +1 -1
- package/lib/public/assets/{selector-health-page-BdJb_x5L.js → selector-health-page-CThoxDFO.js} +2 -2
- package/lib/public/assets/{session-detail-page-BjLaJy8z.js → session-detail-page-Bc02YjMm.js} +1 -1
- package/lib/public/assets/settings-C1VIluuT.js +6 -0
- package/lib/public/assets/{stat-tile-BU9e4s36.js → stat-tile-B02WEte8.js} +1 -1
- package/lib/public/assets/{tablet-hgbEwrWq.js → tablet-Dj30VGZl.js} +1 -1
- package/lib/public/assets/{teams-uiiG4hZM.js → teams-uL4fjTDR.js} +1 -1
- package/lib/public/assets/{trash-2-NK_Iazmg.js → trash-2-B54e-TfY.js} +1 -1
- package/lib/public/assets/{upload-BUX8TNFi.js → upload-B3k8WCSP.js} +1 -1
- package/lib/public/assets/{use-builds-data-DZszmFyl.js → use-builds-data-BFn90NH3.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-BK4B9kRC.js → use-password-reset-mode-DL2TmWZ0.js} +1 -1
- package/lib/public/assets/{users-BU4XBbMT.js → users-CqBuxMlX.js} +1 -1
- package/lib/public/assets/{users-BDh1xjad.js → users-DjQSucKb.js} +1 -1
- package/lib/public/assets/{video-off-BOjNwT4R.js → video-off-T0rlt0GA.js} +1 -1
- package/lib/public/assets/webhook-settings-DAHgnJyZ.js +1 -0
- package/lib/public/assets/{zap-DTWwVMUg.js → zap-gBHqutZV.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/src/app/apiErrors.js +118 -0
- package/lib/src/app/index.js +6 -1
- package/lib/src/app/openapi/control.yaml +3129 -0
- package/lib/src/app/openapi/grid.yaml +2295 -0
- package/lib/src/app/openapi/identity.yaml +2168 -0
- package/lib/src/app/openapi/platform.yaml +2885 -0
- package/lib/src/app/openapi/sessions.yaml +3784 -0
- package/lib/src/app/routers/bug-report.js +4 -1
- package/lib/src/app/routers/config.js +6 -107
- package/lib/src/app/routers/control.js +117 -59
- package/lib/src/app/routers/dashboard.js +13 -7
- package/lib/src/app/routers/grid.js +54 -14
- package/lib/src/app/routers/profile.js +27 -13
- package/lib/src/app/routers/recordings.js +11 -5
- package/lib/src/app/routers/reservation.js +63 -15
- package/lib/src/app/routers/users.js +4 -0
- package/lib/src/app/routers/webhook.js +17 -8
- package/lib/src/app/swagger.js +259 -177
- package/lib/src/data-service/device-service.js +4 -1
- package/lib/src/data-service/deviceFieldOwners.js +1 -0
- package/lib/src/device-managers/AndroidDeviceManager.js +5 -2
- package/lib/src/device-managers/ios/WDAClient.js +153 -41
- package/lib/src/generated/client/edge.js +4 -3
- package/lib/src/generated/client/index-browser.js +1 -0
- package/lib/src/generated/client/index.d.ts +38 -0
- package/lib/src/generated/client/index.js +4 -3
- package/lib/src/generated/client/package.json +1 -1
- package/lib/src/generated/client/schema.prisma +2 -0
- package/lib/src/generated/client/wasm.js +1 -0
- package/lib/src/middleware/csrfMiddleware.js +13 -5
- package/lib/src/middleware/rateLimitMiddleware.js +19 -5
- package/lib/src/middleware/roleGuard.js +20 -0
- package/lib/src/services/AIService.js +13 -3
- package/lib/src/services/NotificationService.js +28 -28
- package/lib/src/services/bug-report/BugReportService.js +8 -2
- package/lib/src/services/lease/LeaseService.js +71 -20
- package/lib/src/services/omni-vision/OmniVisionService.js +14 -5
- package/lib/src/services/recording/RecordingOrchestrator.js +16 -2
- package/lib/test/helpers/expressRoutes.js +41 -0
- package/lib/test/integration/team-visibility-control.spec.js +2 -2
- package/lib/test/unit/access-scopes.spec.js +227 -0
- package/lib/test/unit/api-error-handling.spec.js +179 -0
- package/lib/test/unit/bug-report/route.spec.js +29 -0
- package/lib/test/unit/bug-report/service.spec.js +30 -0
- package/lib/test/unit/control-honest-answers.spec.js +134 -0
- package/lib/test/unit/device-allocation-routes.spec.js +232 -0
- package/lib/test/unit/healing-state-endpoints.spec.js +8 -5
- package/lib/test/unit/install-repository-app-team.spec.js +4 -1
- package/lib/test/unit/lease/LeaseService.spec.js +5 -4
- package/lib/test/unit/lease/lease-device-match.spec.js +161 -0
- package/lib/test/unit/lease/lease-session-ownership.spec.js +10 -9
- package/lib/test/unit/omni-vision-failures.spec.js +81 -0
- package/lib/test/unit/openapi-coverage.spec.js +114 -0
- package/lib/test/unit/profile-router.test.js +42 -0
- package/lib/test/unit/rateLimitMiddleware.test.js +49 -0
- package/lib/test/unit/recording-orchestrator.spec.js +80 -0
- package/lib/test/unit/recordings-library-routes.spec.js +37 -0
- package/lib/test/unit/reservation-team-visibility.spec.js +4 -3
- package/lib/test/unit/reset-link.test.js +2 -1
- package/lib/test/unit/stream-ticket-identity.spec.js +1 -1
- package/lib/test/unit/users-router.test.js +14 -1
- package/lib/test/unit/wda-client-failures.spec.js +90 -0
- package/lib/test/unit/wda-client-session.spec.js +128 -0
- package/lib/test/unit/wda-clipboard-write.spec.js +135 -0
- package/lib/test/unit/webhook-delivery.spec.js +142 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/prisma/migrations/20261004120000_reservation_holder/migration.sql +2 -0
- package/prisma/schema.prisma +2 -0
- package/scripts/dev/readme-screenshots.js +231 -0
- package/lib/public/assets/ai-settings-G9SymiPH.js +0 -21
- package/lib/public/assets/device-explorer-CoPR8DW3.js +0 -279
- package/lib/public/assets/maintenance-settings-DcRmTLS6.js +0 -6
- package/lib/public/assets/settings-B6cpMhsx.js +0 -6
- package/lib/public/assets/webhook-settings-BMzyUcx5.js +0 -1
- package/lib/src/app/swagger-docs.js +0 -1701
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Takes the README's dashboard screenshots: assets/dashboard-dark.png and
|
|
4
|
+
* assets/dashboard-light.png.
|
|
5
|
+
*
|
|
6
|
+
* Serves the built dashboard (web/build) at /xenon/ and answers its API calls
|
|
7
|
+
* with sample devices, so no server, database or phone is needed and the
|
|
8
|
+
* pictures are the same on every machine.
|
|
9
|
+
*
|
|
10
|
+
* cd web && npm run build && cd ..
|
|
11
|
+
* node scripts/dev/readme-screenshots.js
|
|
12
|
+
*
|
|
13
|
+
* Uses web/'s Playwright. Set CHROME_BIN to use a particular Chromium.
|
|
14
|
+
*/
|
|
15
|
+
/* eslint-disable @typescript-eslint/no-var-requires -- a plain Node script, run without a build */
|
|
16
|
+
/* global document, localStorage -- used inside the page, by Playwright */
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const http = require('http');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
const ROOT = path.resolve(__dirname, '..', '..');
|
|
22
|
+
const BUILD = path.join(ROOT, 'web', 'build');
|
|
23
|
+
const ASSETS = path.join(ROOT, 'assets');
|
|
24
|
+
const PORT = 5179;
|
|
25
|
+
|
|
26
|
+
const { chromium } = require(path.join(ROOT, 'web', 'node_modules', 'playwright'));
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(path.join(BUILD, 'index.html'))) {
|
|
29
|
+
console.error('web/build is missing: run `npm run build` in web/ first.');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const TYPES = {
|
|
34
|
+
'.html': 'text/html',
|
|
35
|
+
'.js': 'text/javascript',
|
|
36
|
+
'.css': 'text/css',
|
|
37
|
+
'.svg': 'image/svg+xml',
|
|
38
|
+
'.png': 'image/png',
|
|
39
|
+
'.ico': 'image/x-icon',
|
|
40
|
+
'.json': 'application/json',
|
|
41
|
+
'.woff2': 'font/woff2',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** The built dashboard, with index.html for every route it handles itself. */
|
|
45
|
+
const server = http.createServer((req, res) => {
|
|
46
|
+
const requested = decodeURIComponent(req.url.split('?')[0]).replace(/^\/xenon/, '');
|
|
47
|
+
let file = path.join(BUILD, requested);
|
|
48
|
+
if (!file.startsWith(BUILD) || !fs.existsSync(file) || fs.statSync(file).isDirectory()) {
|
|
49
|
+
file = path.join(BUILD, 'index.html');
|
|
50
|
+
}
|
|
51
|
+
res.writeHead(200, { 'Content-Type': TYPES[path.extname(file)] || 'application/octet-stream' });
|
|
52
|
+
fs.createReadStream(file).pipe(res);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const now = Date.now();
|
|
56
|
+
const MINUTE = 60_000;
|
|
57
|
+
const base = {
|
|
58
|
+
host: 'http://10.0.4.21:4723',
|
|
59
|
+
state: 'device',
|
|
60
|
+
busy: false,
|
|
61
|
+
userBlocked: false,
|
|
62
|
+
offline: false,
|
|
63
|
+
session_id: null,
|
|
64
|
+
healthStatus: 'Healthy',
|
|
65
|
+
thermalStatus: 'Normal',
|
|
66
|
+
teamId: null,
|
|
67
|
+
teamName: null,
|
|
68
|
+
tags: [],
|
|
69
|
+
reservedBy: null,
|
|
70
|
+
reservedUntil: null,
|
|
71
|
+
reservationReason: null,
|
|
72
|
+
totalUtilizationTimeMilliSec: 0,
|
|
73
|
+
total_session_count: 0,
|
|
74
|
+
sessionStartTime: 0,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const DEVICES = [
|
|
78
|
+
{
|
|
79
|
+
...base,
|
|
80
|
+
udid: '28141FDH2000AB',
|
|
81
|
+
name: 'Pixel 8 Pro',
|
|
82
|
+
platform: 'android',
|
|
83
|
+
sdk: '15',
|
|
84
|
+
deviceType: 'real',
|
|
85
|
+
realDevice: true,
|
|
86
|
+
batteryLevel: 92,
|
|
87
|
+
tags: ['payments'],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
...base,
|
|
91
|
+
udid: '00008120-001A2B3C4D5E6F01',
|
|
92
|
+
name: 'iPhone 15',
|
|
93
|
+
platform: 'ios',
|
|
94
|
+
sdk: '18.1',
|
|
95
|
+
deviceType: 'real',
|
|
96
|
+
realDevice: true,
|
|
97
|
+
batteryLevel: 78,
|
|
98
|
+
busy: true,
|
|
99
|
+
session_id: 'b6f0c3e8-8d1a-4f5b-9c7e-1a2b3c4d5e6f',
|
|
100
|
+
sessionStartTime: now - 6 * MINUTE,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
...base,
|
|
104
|
+
udid: 'R5CT32ABCDE',
|
|
105
|
+
name: 'Galaxy S24',
|
|
106
|
+
platform: 'android',
|
|
107
|
+
sdk: '14',
|
|
108
|
+
deviceType: 'real',
|
|
109
|
+
realDevice: true,
|
|
110
|
+
batteryLevel: 64,
|
|
111
|
+
busy: true,
|
|
112
|
+
session_id: 'c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f',
|
|
113
|
+
sessionStartTime: now - 2 * MINUTE,
|
|
114
|
+
host: 'http://10.0.4.22:4723',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
...base,
|
|
118
|
+
udid: '00008110-000A1C2E3F4B5D6E',
|
|
119
|
+
name: 'iPhone 14 Pro',
|
|
120
|
+
platform: 'ios',
|
|
121
|
+
sdk: '17.6',
|
|
122
|
+
deviceType: 'real',
|
|
123
|
+
realDevice: true,
|
|
124
|
+
batteryLevel: 100,
|
|
125
|
+
reservedBy: 'Priya',
|
|
126
|
+
reservedUntil: now + 90 * MINUTE,
|
|
127
|
+
reservationReason: 'Release checkout',
|
|
128
|
+
host: 'http://10.0.4.22:4723',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
...base,
|
|
132
|
+
udid: 'emulator-5554',
|
|
133
|
+
name: 'Pixel 7 API 34',
|
|
134
|
+
platform: 'android',
|
|
135
|
+
sdk: '14',
|
|
136
|
+
deviceType: 'emulator',
|
|
137
|
+
realDevice: false,
|
|
138
|
+
batteryLevel: 100,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
...base,
|
|
142
|
+
udid: '7A1C9E52-3B4D-4F6A-8C2E-1D0F9B8A7C65',
|
|
143
|
+
name: 'iPad Air (5th generation)',
|
|
144
|
+
platform: 'ios',
|
|
145
|
+
sdk: '18.1',
|
|
146
|
+
deviceType: 'simulator',
|
|
147
|
+
realDevice: false,
|
|
148
|
+
state: 'Booted',
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
const ME = {
|
|
153
|
+
userId: 'u-admin',
|
|
154
|
+
role: 'SUPER_ADMIN',
|
|
155
|
+
scopes: 'admin,devices,sessions,read',
|
|
156
|
+
teamId: null,
|
|
157
|
+
name: 'Lab admin',
|
|
158
|
+
email: 'admin@example.com',
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/** Sample answers for the calls the Devices page makes; an empty object for the rest. */
|
|
162
|
+
function answer(apiPath) {
|
|
163
|
+
if (apiPath === '/auth/me') return ME;
|
|
164
|
+
if (apiPath === '/devices' || apiPath.startsWith('/device')) return DEVICES;
|
|
165
|
+
if (apiPath === '/queue/length') return 0;
|
|
166
|
+
if (apiPath === '/queue/summary') return { total: 0, byPlatform: {}, otherCount: 0 };
|
|
167
|
+
if (apiPath === '/queue' || apiPath === '/teams' || apiPath === '/build') return [];
|
|
168
|
+
if (apiPath.startsWith('/session')) return [];
|
|
169
|
+
if (apiPath.startsWith('/healing/events')) return { events: [], todayCount: 0 };
|
|
170
|
+
return {};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function shoot(browser, theme, file) {
|
|
174
|
+
const context = await browser.newContext({
|
|
175
|
+
viewport: { width: 1440, height: 700 },
|
|
176
|
+
deviceScaleFactor: 2,
|
|
177
|
+
colorScheme: theme,
|
|
178
|
+
});
|
|
179
|
+
await context.addInitScript((t) => {
|
|
180
|
+
try {
|
|
181
|
+
localStorage.setItem('xenon.theme', t);
|
|
182
|
+
} catch {
|
|
183
|
+
// The dashboard follows colorScheme without it.
|
|
184
|
+
}
|
|
185
|
+
}, theme);
|
|
186
|
+
const page = await context.newPage();
|
|
187
|
+
await page.route('**/socket.io/**', (route) => route.abort());
|
|
188
|
+
await page.route('**/xenon/api/**', (route) => {
|
|
189
|
+
const apiPath = new URL(route.request().url()).pathname.replace('/xenon/api', '');
|
|
190
|
+
if (apiPath.includes('/stream')) return route.abort();
|
|
191
|
+
return route.fulfill({ json: answer(apiPath) });
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
await page.goto(`http://127.0.0.1:${PORT}/xenon/devices`, { waitUntil: 'load' });
|
|
195
|
+
await page.waitForSelector('text=Pixel 8 Pro');
|
|
196
|
+
await page.waitForTimeout(1000);
|
|
197
|
+
// The live-updates badge says "Connecting" here only because the socket is
|
|
198
|
+
// blocked; hide it rather than picture a connection problem.
|
|
199
|
+
await page.evaluate(() => {
|
|
200
|
+
for (const el of document.querySelectorAll('header *')) {
|
|
201
|
+
if (el.children.length === 0 && /^Connecting/.test((el.textContent || '').trim())) {
|
|
202
|
+
let badge = el;
|
|
203
|
+
while (
|
|
204
|
+
badge.parentElement &&
|
|
205
|
+
/^Connecting/.test((badge.parentElement.textContent || '').trim())
|
|
206
|
+
) {
|
|
207
|
+
badge = badge.parentElement;
|
|
208
|
+
}
|
|
209
|
+
badge.style.visibility = 'hidden';
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
await page.screenshot({ path: path.join(ASSETS, file) });
|
|
214
|
+
await context.close();
|
|
215
|
+
console.log(`assets/${file}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
(async () => {
|
|
219
|
+
await new Promise((resolve) => server.listen(PORT, '127.0.0.1', resolve));
|
|
220
|
+
const browser = await chromium.launch({ executablePath: process.env.CHROME_BIN || undefined });
|
|
221
|
+
try {
|
|
222
|
+
await shoot(browser, 'dark', 'dashboard-dark.png');
|
|
223
|
+
await shoot(browser, 'light', 'dashboard-light.png');
|
|
224
|
+
} finally {
|
|
225
|
+
await browser.close();
|
|
226
|
+
server.close();
|
|
227
|
+
}
|
|
228
|
+
})().catch((err) => {
|
|
229
|
+
console.error(err);
|
|
230
|
+
process.exit(1);
|
|
231
|
+
});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import{c as p,t as z,r as c,X as m,j as e,S as k,A as N,Q as I,V as $}from"./index-ClrpAMAT.js";/* empty css */import{S,A as F}from"./SettingCard-Clg_Ijbc.js";import{P as B}from"./page-header-B92DKLiq.js";import{L as T}from"./lock-CVCe56TH.js";import{C as b}from"./circle-check-Du3sjbfV.js";import{A as E}from"./activity-Bengh9SD.js";import"./button-CqhPBGRj.js";import"./utils-CDN07tui.js";/**
|
|
2
|
-
* @license lucide-react v0.555.0 - ISC
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the ISC license.
|
|
5
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/const q=[["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M17 20v2",key:"1rnc9c"}],["path",{d:"M17 2v2",key:"11trls"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M2 17h2",key:"7oei6x"}],["path",{d:"M2 7h2",key:"asdhe0"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"M20 17h2",key:"1fpfkl"}],["path",{d:"M20 7h2",key:"1o8tra"}],["path",{d:"M7 20v2",key:"4gnj0m"}],["path",{d:"M7 2v2",key:"1i4yhu"}],["rect",{x:"4",y:"4",width:"16",height:"16",rx:"2",key:"1vbyd7"}],["rect",{x:"8",y:"8",width:"8",height:"8",rx:"1",key:"z9xiuo"}]],D=p("cpu",q);/**
|
|
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 U=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20",key:"13o1zl"}],["path",{d:"M2 12h20",key:"9i4pu4"}]],L=p("globe",U);/**
|
|
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 G=[["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"}]],R=p("server",G);/**
|
|
17
|
-
* @license lucide-react v0.555.0 - ISC
|
|
18
|
-
*
|
|
19
|
-
* This source code is licensed under the ISC license.
|
|
20
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
21
|
-
*/const V=[["path",{d:"M10 8h4",key:"1sr2af"}],["path",{d:"M12 21v-9",key:"17s77i"}],["path",{d:"M12 8V3",key:"13r4qs"}],["path",{d:"M17 16h4",key:"h1uq16"}],["path",{d:"M19 12V3",key:"o1uvq1"}],["path",{d:"M19 21v-5",key:"qua636"}],["path",{d:"M3 14h4",key:"bcjad9"}],["path",{d:"M5 10V3",key:"cb8scm"}],["path",{d:"M5 21v-7",key:"1w1uti"}]],O=p("sliders-vertical",V),_={aiProvider:"gemini",aiModel:"",aiBaseUrl:"",geminiModel:"",openaiModel:"",anthropicModel:"",ollamaModel:"",geminiSet:!1,openaiSet:!1,anthropicSet:!1,aiTemperature:1,aiMaxTokens:4096,aiTopP:1},X=(s,i)=>s.aiProvider===i.aiProvider&&s.aiTemperature===i.aiTemperature&&s.aiMaxTokens===i.aiMaxTokens&&s.aiTopP===i.aiTopP,o=s=>{switch(s){case"gemini":return"gemini-3-flash-preview";case"openai":return"gpt-4o";case"anthropic":return"claude-sonnet-4-6";case"ollama":return"llama3";default:return"—"}},Y=s=>{switch(s){case"ollama":return"http://localhost:11434";default:return"Provider default"}},se=()=>{const{toast:s}=z(),[i,r]=c.useState(_),[h,u]=c.useState(_),[w,g]=c.useState(!0),[x,v]=c.useState(!1),[f,j]=c.useState(!1);c.useEffect(()=>{P()},[]);const P=async()=>{g(!0);try{const a=await m.getGlobalConfig(),n={aiProvider:a.aiProvider||"gemini",aiModel:a.aiModel||"",aiBaseUrl:a.aiBaseUrl||"",geminiModel:a.geminiModel||"",openaiModel:a.openaiModel||"",anthropicModel:a.anthropicModel||"",ollamaModel:a.ollamaModel||"",geminiSet:!!a.geminiSet,openaiSet:!!a.openaiSet,anthropicSet:!!a.anthropicSet,aiTemperature:typeof a.aiTemperature=="number"?a.aiTemperature:1,aiMaxTokens:typeof a.aiMaxTokens=="number"?a.aiMaxTokens:4096,aiTopP:typeof a.aiTopP=="number"?a.aiTopP:1};r(n),u(n)}catch(a){console.error("Failed to load AISettings",a),s("Failed to access AI configuration.","error")}finally{g(!1)}},C=async()=>{v(!0);try{await m.updateGlobalConfig({aiProvider:i.aiProvider,aiTemperature:i.aiTemperature,aiMaxTokens:i.aiMaxTokens,aiTopP:i.aiTopP}),u(i),s("AI engine configuration saved.","success")}catch(a){console.error("Failed to save AISettings",a),s($(a),"error")}finally{v(!1)}},A=async()=>{j(!0);const a=Date.now();try{const n=await m.testAIConfig({aiProvider:i.aiProvider}),l=Date.now()-a;n.success?s(`${n.message} (${l}ms)`,"success"):s(n.message,"error")}catch(n){s(`Connection failed: ${(n==null?void 0:n.message)||"unknown error"}`,"error")}finally{j(!1)}},d=[{id:"gemini",name:"Google Gemini",description:`${o("gemini")} — Multimodal reasoning`,icon:e.jsx("span",{className:"ai-provider-glyph",children:"G"}),isConfigured:!!i.geminiSet},{id:"openai",name:"OpenAI",description:`${o("openai")} — OpenAI v1 compatible`,icon:e.jsx(D,{size:18}),isConfigured:!!i.openaiSet},{id:"anthropic",name:"Anthropic",description:`${o("anthropic")} — Advanced analysis`,icon:e.jsx(k,{size:18}),isConfigured:!!i.anthropicSet},{id:"ollama",name:"Ollama",description:"Local / self-hosted — no API key required",icon:e.jsx(R,{size:18}),isConfigured:!!i.ollamaModel||!!i.aiModel||!!i.aiBaseUrl}],t=d.find(a=>a.id===i.aiProvider),y=d.filter(a=>a.isConfigured).length,M=!X(i,h);return w?e.jsxs("div",{className:"settings-loading",children:[e.jsx(N,{className:"animate-spin",size:32}),e.jsx("span",{children:"Synchronizing Global State..."})]}):e.jsxs("div",{className:"settings-container",children:[e.jsx(B,{icon:I,title:"AI engine",subtitle:e.jsxs(e.Fragment,{children:["All credentials and endpoints are managed via environment variables.",e.jsxs("span",{className:"page-header-subnote",children:[e.jsx(T,{size:12}),"Select the active provider from configured options below."]})]})}),e.jsx("div",{className:"settings-content",children:e.jsxs("div",{className:"settings-grid settings-grid--two-equal",children:[e.jsx(S,{icon:e.jsx(k,{size:16}),title:"Provider registry",titleExtra:e.jsxs("span",{className:`provider-count-pill ${y>0?"is-ok":"is-empty"}`,children:[y," / ",d.length," configured"]}),description:"Providers are activated via environment variables. Select a configured engine to activate.",children:e.jsx("div",{className:"provider-list",children:d.map(a=>{const n=i.aiProvider===a.id,l=a.isConfigured;return e.jsxs("button",{type:"button",className:`provider-row ${n?"is-active":""} ${l?"":"is-disabled"}`,onClick:()=>l&&r({...i,aiProvider:a.id}),disabled:!l,title:l?`Activate ${a.name}`:`Set XENON_${a.id.toUpperCase()}_API_KEY to enable`,children:[e.jsx("div",{className:"provider-row__icon",children:a.icon}),e.jsxs("div",{className:"provider-row__body",children:[e.jsx("div",{className:"provider-row__name",children:a.name}),e.jsx("div",{className:"provider-row__desc",children:a.description}),e.jsx("div",{className:"provider-row__status",children:n?e.jsxs("span",{className:"provider-status provider-status--active",children:[e.jsx(b,{size:11}),a.isConfigured?"Active":"Active — no key"]}):a.isConfigured?e.jsxs("span",{className:"provider-status provider-status--ready",children:[e.jsx(b,{size:11}),"READY"]}):e.jsxs("span",{className:"provider-status provider-status--off",children:[e.jsx(T,{size:10}),"Not set"]})})]})]},a.id)})})}),e.jsxs(S,{icon:e.jsx(L,{size:16}),title:"Runtime configuration",description:"Environmental overrides for AI model endpoints and identifiers.",children:[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:[t==null?void 0:t.icon,(t==null?void 0:t.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:[e.jsxs("span",{children:[i.aiProvider==="gemini"&&(i.geminiModel||i.aiModel||o("gemini")),i.aiProvider==="openai"&&(i.openaiModel||i.aiModel||o("openai")),i.aiProvider==="anthropic"&&(i.anthropicModel||i.aiModel||o("anthropic")),i.aiProvider==="ollama"&&(i.ollamaModel||i.aiModel||o("ollama"))]}),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:[e.jsx("span",{children:i.aiBaseUrl||Y(i.aiProvider)}),!i.aiBaseUrl&&e.jsx("span",{className:"ai-config-default",children:"Default"})]})]})]}),e.jsxs("div",{className:"model-params",children:[e.jsxs("div",{className:"model-params__header",children:[e.jsx(O,{size:14}),e.jsx("span",{children:"Model Parameters"})]}),e.jsxs("div",{className:"model-param",children:[e.jsxs("div",{className:"model-param__label-row",children:[e.jsx("label",{htmlFor:"ai-temp",children:"Temperature"}),e.jsx("span",{className:"model-param__value",children:i.aiTemperature.toFixed(1)})]}),e.jsx("input",{id:"ai-temp",type:"range",min:0,max:2,step:.1,value:i.aiTemperature,onChange:a=>r({...i,aiTemperature:parseFloat(a.target.value)}),className:"model-param__slider"})]}),e.jsxs("div",{className:"model-param",children:[e.jsx("div",{className:"model-param__label-row",children:e.jsx("label",{htmlFor:"ai-max-tokens",children:"Max tokens"})}),e.jsx("div",{className:"setting-input-wrapper",children:e.jsx("input",{id:"ai-max-tokens",type:"number",min:256,step:128,value:i.aiMaxTokens,onChange:a=>r({...i,aiMaxTokens:parseInt(a.target.value,10)||0})})})]}),e.jsxs("div",{className:"model-param",children:[e.jsxs("div",{className:"model-param__label-row",children:[e.jsx("label",{htmlFor:"ai-top-p",children:"Top P"}),e.jsx("span",{className:"model-param__value",children:i.aiTopP.toFixed(2)})]}),e.jsx("input",{id:"ai-top-p",type:"range",min:0,max:1,step:.01,value:i.aiTopP,onChange:a=>r({...i,aiTopP:parseFloat(a.target.value)}),className:"model-param__slider"})]})]}),e.jsxs("button",{type:"button",className:"test-connection-btn",onClick:A,disabled:f||!(t!=null&&t.isConfigured),children:[f?e.jsx(N,{size:14,className:"animate-spin"}):e.jsx(E,{size:14}),e.jsx("span",{children:"Test Connection"})]})]})]})}),(M||x)&&e.jsx(F,{onSave:C,onDiscard:()=>r(h),isSaving:x,isDirty:M,saveLabel:"Save configuration"})]})};export{se as AISettings};
|