@xenon-device-management/xenon 1.7.2 → 1.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/package.json +2 -1
- package/lib/public/assets/{ApiKeyGate-BousCIQZ.js → ApiKeyGate-DX4HKDBA.js} +1 -1
- package/lib/public/assets/{BugReportButton-Gw4lnGnV.js → BugReportButton-N-NBBb3l.js} +1 -1
- package/lib/public/assets/{DeviceMosaicView-BX2hfhBf.js → DeviceMosaicView-BEDnJqk5.js} +1 -1
- package/lib/public/assets/{EmptyState-CPOfuV3O.js → EmptyState-X7DYPoPj.js} +1 -1
- package/lib/public/assets/{FieldGroup-C5UkCHbe.js → FieldGroup-a9gzUh4W.js} +1 -1
- package/lib/public/assets/{Modal-MMGhL8eW.js → Modal-Cuva-zxU.js} +1 -1
- package/lib/public/assets/{SegmentedControl-CZfAicws.js → SegmentedControl-Bdc4A0UR.js} +1 -1
- package/lib/public/assets/{SettingCard-Dp7XIdcl.js → SettingCard-DVZUpQJR.js} +1 -1
- package/lib/public/assets/{Table-DsN_IX6J.js → Table-Casq-_Hh.js} +1 -1
- package/lib/public/assets/{activity-DCBAvxuo.js → activity-KiLkzk_U.js} +1 -1
- package/lib/public/assets/{ai-settings-yNS_bU1m.js → ai-settings-0dEPftlc.js} +1 -1
- package/lib/public/assets/{api-keys-CQ9g1g1b.js → api-keys-Cg1cbSUt.js} +1 -1
- package/lib/public/assets/{apps-Bqh1eav9.js → apps-CyetRRfI.js} +1 -1
- package/lib/public/assets/{arrow-left-GX0vzYtn.js → arrow-left-C1v7ukDM.js} +1 -1
- package/lib/public/assets/{arrow-up-right-DePHHOby.js → arrow-up-right-B-YmrLSY.js} +1 -1
- package/lib/public/assets/{auth-shell-BBGlC6i3.js → auth-shell-BXwxuIPK.js} +1 -1
- package/lib/public/assets/{builds-page-Czf7RsmD.js → builds-page-wquO6Xb2.js} +1 -1
- package/lib/public/assets/{button-ybNY3GYx.js → button-xZrEx8cE.js} +1 -1
- package/lib/public/assets/{check-BZMx8AhR.js → check-CDOZKQih.js} +1 -1
- package/lib/public/assets/{chevron-right-WL_bcB02.js → chevron-right-CnpP0W_l.js} +1 -1
- package/lib/public/assets/{circle-check-CI2xUlDo.js → circle-check-DDqpikKW.js} +1 -1
- package/lib/public/assets/{circle-x-De3thbcr.js → circle-x-DcBe5J67.js} +1 -1
- package/lib/public/assets/{clock-BFVLE7wW.js → clock-CfNmWqai.js} +1 -1
- package/lib/public/assets/{copy-ve3lNhva.js → copy-DkXT5Ggc.js} +1 -1
- package/lib/public/assets/{device-explorer-vzZxT4-Y.js → device-explorer-B3PK2XW9.js} +1 -1
- package/lib/public/assets/{download-BDZj44ke.js → download-B0tbRUtY.js} +1 -1
- package/lib/public/assets/{forgot-password-BSYQX2oR.js → forgot-password-DvHMwK0-.js} +1 -1
- package/lib/public/assets/{funnel-CptlpFZ9.js → funnel-DfsfiM0o.js} +1 -1
- package/lib/public/assets/{index-PtnKslHU.js → index-wbxPcwMM.js} +2 -2
- package/lib/public/assets/{layers-DRpwoKhI.js → layers-D-4LYQG3.js} +1 -1
- package/lib/public/assets/{lock-CrQhC34I.js → lock-Cf_J2NEl.js} +1 -1
- package/lib/public/assets/{login-DaJOt8Qz.js → login-DCIOMQb8.js} +1 -1
- package/lib/public/assets/{maintenance-settings-D629C2IC.js → maintenance-settings-CDDl4Q8B.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-Dpcc76zN.js → mouse-pointer-2-DoCqhosb.js} +1 -1
- package/lib/public/assets/{overview-CGEKYX62.js → overview-DNeH4FfG.js} +1 -1
- package/lib/public/assets/{page-header-CSvuGbSO.js → page-header-DZpQDeCw.js} +1 -1
- package/lib/public/assets/{plus-vJJF9g79.js → plus-CTjPrkU5.js} +1 -1
- package/lib/public/assets/{profile-page-CF7ZQNQz.js → profile-page-DKiZOb_Y.js} +1 -1
- package/lib/public/assets/{reset-password-BgpjbCac.js → reset-password-6rl6YA7S.js} +1 -1
- package/lib/public/assets/{runbook-page-Dxuzftyv.js → runbook-page-DTTw_cb3.js} +1 -1
- package/lib/public/assets/{selector-detail-page-B_puCd9g.js → selector-detail-page-BVPMp3ZK.js} +1 -1
- package/lib/public/assets/{selector-health-BjMHf106.js → selector-health-BVJ8fGyw.js} +1 -1
- package/lib/public/assets/{selector-health-page-Du_bGD2p.js → selector-health-page-CZ_AAjtV.js} +1 -1
- package/lib/public/assets/{session-detail-page-lQTe5_B1.js → session-detail-page-BApz2wZr.js} +1 -1
- package/lib/public/assets/{settings-DhxWGUzy.js → settings-CuYjTdtb.js} +1 -1
- package/lib/public/assets/{shield-JGzamJo5.js → shield-DVYhrtS6.js} +1 -1
- package/lib/public/assets/{shield-alert-DzE36jcb.js → shield-alert-BhxlmXys.js} +1 -1
- package/lib/public/assets/{status-pill-outline-50aN0wRU.js → status-pill-outline-DjKuXDLG.js} +1 -1
- package/lib/public/assets/{tablet-DHY88mxw.js → tablet-CxgLRRaU.js} +1 -1
- package/lib/public/assets/{teams-EOAIPbZC.js → teams-EuX6i7H2.js} +1 -1
- package/lib/public/assets/{trash-2-BrTSfFh5.js → trash-2-8zA1h40t.js} +1 -1
- package/lib/public/assets/useSocket-qyqK9PWr.js +1 -0
- package/lib/public/assets/{users-Dd44jLAB.js → users-CPafGHcL.js} +1 -1
- package/lib/public/assets/{webhook-settings-DBYmqim_.js → webhook-settings-y8EAM79b.js} +1 -1
- package/lib/public/assets/{zap-JUbOoK3o.js → zap-C-b2-XlO.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/src/app/index.js +6 -0
- package/lib/src/app/routers/auth.js +30 -0
- package/lib/src/app/routers/capabilities.js +25 -0
- package/lib/src/app/routers/control.js +14 -0
- package/lib/src/app/routers/projects.js +55 -0
- package/lib/src/device-managers/IOSDeviceManager.js +24 -3
- package/lib/src/device-managers/ios/IOSStreamService.js +39 -1
- package/lib/src/device-managers/ios/iosBattery.js +24 -0
- package/lib/src/device-managers/ios/iosStreamDiagnostics.js +61 -0
- package/lib/src/generated/client/edge.js +22 -4
- package/lib/src/generated/client/index-browser.js +19 -1
- package/lib/src/generated/client/index.d.ts +2376 -158
- package/lib/src/generated/client/index.js +22 -4
- package/lib/src/generated/client/package.json +1 -1
- package/lib/src/generated/client/schema.prisma +21 -0
- package/lib/src/generated/client/wasm.js +19 -1
- package/lib/src/middleware/authMiddleware.js +64 -3
- package/lib/src/prisma.js +1 -1
- package/lib/src/services/EventLogService.js +81 -0
- package/lib/src/services/ServerManager.js +36 -0
- package/lib/src/services/SocketServer.js +2 -0
- package/lib/src/services/VideoPipelineService.js +51 -43
- package/lib/src/services/artifacts/ArtifactStore.js +60 -0
- package/lib/src/services/recording/RecordingOrchestrator.js +4 -5
- package/lib/src/services/token/JwtKeyService.js +137 -0
- package/lib/src/services/token/StreamTicketService.js +61 -0
- package/lib/test/unit/ArtifactStore.spec.js +59 -0
- package/lib/test/unit/CapabilitiesRouter.spec.js +18 -0
- package/lib/test/unit/EventLogService.spec.js +67 -0
- package/lib/test/unit/JwtKeyService.spec.js +86 -0
- package/lib/test/unit/ProjectsRouter.spec.js +45 -0
- package/lib/test/unit/StreamTicketService.spec.js +95 -0
- package/lib/test/unit/TokenEndpoint.spec.js +68 -0
- package/lib/test/unit/authMiddleware.bearer.spec.js +102 -0
- package/lib/test/unit/authMiddleware.streamTicket.spec.js +217 -0
- package/lib/test/unit/iosBattery.spec.js +29 -0
- package/lib/test/unit/iosStreamDiagnostics.spec.js +61 -0
- package/lib/test/unit/recording-instrumentation.spec.js +8 -0
- package/lib/test/unit/recording-orchestrator.spec.js +14 -0
- package/lib/test/unit/video-pipeline-args.spec.js +46 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/prisma/migrations/20260717063103_event_log/migration.sql +15 -0
- package/prisma/migrations/20260717070124_project_entity/migration.sql +10 -0
- package/prisma/schema.prisma +21 -0
- package/lib/public/assets/useSocket-BRto9a5m.js +0 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
require("reflect-metadata");
|
|
16
|
+
const chai_1 = require("chai");
|
|
17
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
18
|
+
const typedi_1 = require("typedi");
|
|
19
|
+
const fs_1 = __importDefault(require("fs"));
|
|
20
|
+
const os_1 = __importDefault(require("os"));
|
|
21
|
+
const path_1 = __importDefault(require("path"));
|
|
22
|
+
const JwtKeyService_1 = require("../../src/services/token/JwtKeyService");
|
|
23
|
+
const authMiddleware_1 = require("../../src/middleware/authMiddleware");
|
|
24
|
+
const UserService_1 = require("../../src/services/UserService");
|
|
25
|
+
const ApiKeyService_1 = require("../../src/services/ApiKeyService");
|
|
26
|
+
const UserSessionService_1 = require("../../src/services/UserSessionService");
|
|
27
|
+
const prisma_1 = require("../../src/prisma");
|
|
28
|
+
function fakeRes() {
|
|
29
|
+
const res = { statusCode: 200 };
|
|
30
|
+
res.status = (c) => { res.statusCode = c; return res; };
|
|
31
|
+
res.json = (b) => { res.body = b; return res; };
|
|
32
|
+
res.cookie = () => res;
|
|
33
|
+
return res;
|
|
34
|
+
}
|
|
35
|
+
describe('authMiddleware — Bearer branch', () => {
|
|
36
|
+
let dir;
|
|
37
|
+
let keySvc;
|
|
38
|
+
const user = { id: 'u1', role: 'MEMBER', status: 'ACTIVE' };
|
|
39
|
+
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
+
dir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'xenon-bearer-'));
|
|
41
|
+
keySvc = new JwtKeyService_1.JwtKeyService();
|
|
42
|
+
yield keySvc.init(dir);
|
|
43
|
+
typedi_1.Container.set(JwtKeyService_1.JwtKeyService, keySvc);
|
|
44
|
+
typedi_1.Container.set(UserService_1.UserService, { findById: sinon_1.default.stub().resolves(user) });
|
|
45
|
+
typedi_1.Container.set(ApiKeyService_1.ApiKeyService, { verifyPair: sinon_1.default.stub().resolves(null), verify: sinon_1.default.stub().resolves(null) });
|
|
46
|
+
typedi_1.Container.set(UserSessionService_1.UserSessionService, { resolve: sinon_1.default.stub().resolves(null) });
|
|
47
|
+
sinon_1.default.stub(prisma_1.prisma.teamMember, 'findMany').resolves([{ teamId: 't1' }]);
|
|
48
|
+
}));
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
sinon_1.default.restore();
|
|
51
|
+
typedi_1.Container.reset();
|
|
52
|
+
fs_1.default.rmSync(dir, { recursive: true, force: true });
|
|
53
|
+
});
|
|
54
|
+
it('accepts a valid xenon-rest Bearer token and sets req.auth', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
55
|
+
const token = yield keySvc.sign({ sub: 'u1', role: 'MEMBER', scopes: 'devices,read', teamId: 't1' }, { audience: 'xenon-rest', ttlSeconds: 60 });
|
|
56
|
+
const req = { headers: { authorization: `Bearer ${token}` }, query: {} };
|
|
57
|
+
const res = fakeRes();
|
|
58
|
+
const next = sinon_1.default.spy();
|
|
59
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, next);
|
|
60
|
+
(0, chai_1.expect)(next.calledOnce).to.equal(true);
|
|
61
|
+
(0, chai_1.expect)(req.auth.kind).to.equal('bearer');
|
|
62
|
+
(0, chai_1.expect)(req.auth.userId).to.equal('u1');
|
|
63
|
+
(0, chai_1.expect)(req.auth.scopes).to.equal('devices,read');
|
|
64
|
+
}));
|
|
65
|
+
it('rejects an xenon-mcp-audience token on the REST surface', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
66
|
+
const token = yield keySvc.sign({ sub: 'u1' }, { audience: 'xenon-mcp', ttlSeconds: 60 });
|
|
67
|
+
const req = { headers: { authorization: `Bearer ${token}` }, query: {} };
|
|
68
|
+
const res = fakeRes();
|
|
69
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, sinon_1.default.spy());
|
|
70
|
+
(0, chai_1.expect)(res.statusCode).to.equal(401);
|
|
71
|
+
}));
|
|
72
|
+
it('rejects a token for a disabled user (live revocation)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
73
|
+
typedi_1.Container.get(UserService_1.UserService).findById.resolves(Object.assign(Object.assign({}, user), { status: 'DISABLED' }));
|
|
74
|
+
const token = yield keySvc.sign({ sub: 'u1' }, { audience: 'xenon-rest', ttlSeconds: 60 });
|
|
75
|
+
const req = { headers: { authorization: `Bearer ${token}` }, query: {} };
|
|
76
|
+
const res = fakeRes();
|
|
77
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, sinon_1.default.spy());
|
|
78
|
+
(0, chai_1.expect)(res.statusCode).to.equal(401);
|
|
79
|
+
}));
|
|
80
|
+
// ---- regression pins: old inputs behave exactly as before ----
|
|
81
|
+
it('REGRESSION: no credentials → 401 unauthenticated (unchanged)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
82
|
+
const req = { headers: {}, query: {} };
|
|
83
|
+
const res = fakeRes();
|
|
84
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, sinon_1.default.spy());
|
|
85
|
+
(0, chai_1.expect)(res.statusCode).to.equal(401);
|
|
86
|
+
(0, chai_1.expect)(res.body).to.deep.equal({ error: 'unauthenticated' });
|
|
87
|
+
}));
|
|
88
|
+
it('REGRESSION: header pair still takes priority over a Bearer header', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
89
|
+
const row = { id: 'k1', userId: 'u1', scopes: 'admin', rateLimit: 100, teamId: null };
|
|
90
|
+
typedi_1.Container.get(ApiKeyService_1.ApiKeyService).verifyPair.resolves(row);
|
|
91
|
+
const token = yield keySvc.sign({ sub: 'other' }, { audience: 'xenon-rest', ttlSeconds: 60 });
|
|
92
|
+
const req = {
|
|
93
|
+
headers: { 'x-xenon-access-key': 'ak', 'x-xenon-token': 'tk', authorization: `Bearer ${token}` },
|
|
94
|
+
query: {},
|
|
95
|
+
};
|
|
96
|
+
const res = fakeRes();
|
|
97
|
+
const next = sinon_1.default.spy();
|
|
98
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, next);
|
|
99
|
+
(0, chai_1.expect)(next.calledOnce).to.equal(true);
|
|
100
|
+
(0, chai_1.expect)(req.auth.kind).to.equal('api-key'); // pair wins, Bearer never evaluated
|
|
101
|
+
}));
|
|
102
|
+
});
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
require("reflect-metadata");
|
|
16
|
+
const chai_1 = require("chai");
|
|
17
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
18
|
+
const express_1 = __importDefault(require("express"));
|
|
19
|
+
const supertest_1 = __importDefault(require("supertest"));
|
|
20
|
+
const typedi_1 = require("typedi");
|
|
21
|
+
const fs_1 = __importDefault(require("fs"));
|
|
22
|
+
const os_1 = __importDefault(require("os"));
|
|
23
|
+
const path_1 = __importDefault(require("path"));
|
|
24
|
+
const JwtKeyService_1 = require("../../src/services/token/JwtKeyService");
|
|
25
|
+
const StreamTicketService_1 = require("../../src/services/token/StreamTicketService");
|
|
26
|
+
const authMiddleware_1 = require("../../src/middleware/authMiddleware");
|
|
27
|
+
const control_1 = __importDefault(require("../../src/app/routers/control"));
|
|
28
|
+
const device_store_1 = require("../../src/data-service/device-store");
|
|
29
|
+
const UserService_1 = require("../../src/services/UserService");
|
|
30
|
+
const ApiKeyService_1 = require("../../src/services/ApiKeyService");
|
|
31
|
+
const UserSessionService_1 = require("../../src/services/UserSessionService");
|
|
32
|
+
function fakeRes() {
|
|
33
|
+
const res = { statusCode: 200 };
|
|
34
|
+
res.status = (c) => {
|
|
35
|
+
res.statusCode = c;
|
|
36
|
+
return res;
|
|
37
|
+
};
|
|
38
|
+
res.json = (b) => {
|
|
39
|
+
res.body = b;
|
|
40
|
+
return res;
|
|
41
|
+
};
|
|
42
|
+
res.cookie = () => res;
|
|
43
|
+
return res;
|
|
44
|
+
}
|
|
45
|
+
describe('authMiddleware — stream-ticket branch', () => {
|
|
46
|
+
let dir;
|
|
47
|
+
let keySvc;
|
|
48
|
+
let ticketSvc;
|
|
49
|
+
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
50
|
+
dir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'xenon-authmw-ticket-'));
|
|
51
|
+
keySvc = new JwtKeyService_1.JwtKeyService();
|
|
52
|
+
yield keySvc.init(dir);
|
|
53
|
+
typedi_1.Container.set(JwtKeyService_1.JwtKeyService, keySvc);
|
|
54
|
+
ticketSvc = new StreamTicketService_1.StreamTicketService();
|
|
55
|
+
typedi_1.Container.set(StreamTicketService_1.StreamTicketService, ticketSvc);
|
|
56
|
+
typedi_1.Container.set(UserService_1.UserService, { findById: sinon_1.default.stub().resolves(null) });
|
|
57
|
+
typedi_1.Container.set(ApiKeyService_1.ApiKeyService, {
|
|
58
|
+
verifyPair: sinon_1.default.stub().resolves(null),
|
|
59
|
+
verify: sinon_1.default.stub().resolves(null),
|
|
60
|
+
});
|
|
61
|
+
typedi_1.Container.set(UserSessionService_1.UserSessionService, { resolve: sinon_1.default.stub().resolves(null) });
|
|
62
|
+
}));
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
sinon_1.default.restore();
|
|
65
|
+
typedi_1.Container.reset();
|
|
66
|
+
fs_1.default.rmSync(dir, { recursive: true, force: true });
|
|
67
|
+
});
|
|
68
|
+
// req.path here mirrors what authMiddleware actually sees at runtime:
|
|
69
|
+
// it's mounted directly on apiRouter (src/app/index.ts line 223) BEFORE
|
|
70
|
+
// ControlRouter.register(apiRouter) mounts '/control' onto that same
|
|
71
|
+
// apiRouter (control.ts: parentRouter.use('/control', router)), so the
|
|
72
|
+
// apiRouter-relative path for GET /xenon/api/control/UDID-1/stream is
|
|
73
|
+
// '/control/UDID-1/stream' — not the full external URL.
|
|
74
|
+
it('accepts a valid ticket for the matching udid on GET /control/:udid/stream', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
75
|
+
const ticket = yield ticketSvc.mint('UDID-1', 'actor-1');
|
|
76
|
+
const req = {
|
|
77
|
+
method: 'GET',
|
|
78
|
+
path: '/control/UDID-1/stream',
|
|
79
|
+
query: { ticket },
|
|
80
|
+
headers: {},
|
|
81
|
+
};
|
|
82
|
+
const res = fakeRes();
|
|
83
|
+
const next = sinon_1.default.spy();
|
|
84
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, next);
|
|
85
|
+
(0, chai_1.expect)(next.calledOnce).to.equal(true);
|
|
86
|
+
(0, chai_1.expect)(req.auth.kind).to.equal('stream-ticket');
|
|
87
|
+
(0, chai_1.expect)(req.auth.userId).to.equal('actor-1');
|
|
88
|
+
}));
|
|
89
|
+
it('rejects a ticket minted for a different udid with 401 invalid ticket', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
|
+
const ticket = yield ticketSvc.mint('UDID-1', 'actor-1');
|
|
91
|
+
const req = {
|
|
92
|
+
method: 'GET',
|
|
93
|
+
path: '/control/UDID-2/stream',
|
|
94
|
+
query: { ticket },
|
|
95
|
+
headers: {},
|
|
96
|
+
};
|
|
97
|
+
const res = fakeRes();
|
|
98
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, sinon_1.default.spy());
|
|
99
|
+
(0, chai_1.expect)(res.statusCode).to.equal(401);
|
|
100
|
+
(0, chai_1.expect)(res.body).to.deep.equal({ error: 'invalid ticket' });
|
|
101
|
+
}));
|
|
102
|
+
it('rejects reuse of an already-redeemed ticket', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
103
|
+
const ticket = yield ticketSvc.mint('UDID-1', 'actor-1');
|
|
104
|
+
yield ticketSvc.redeem(ticket, 'UDID-1');
|
|
105
|
+
const req = {
|
|
106
|
+
method: 'GET',
|
|
107
|
+
path: '/control/UDID-1/stream',
|
|
108
|
+
query: { ticket },
|
|
109
|
+
headers: {},
|
|
110
|
+
};
|
|
111
|
+
const res = fakeRes();
|
|
112
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, sinon_1.default.spy());
|
|
113
|
+
(0, chai_1.expect)(res.statusCode).to.equal(401);
|
|
114
|
+
(0, chai_1.expect)(res.body).to.deep.equal({ error: 'invalid ticket' });
|
|
115
|
+
}));
|
|
116
|
+
it('does NOT match a sibling stream route (stream/status) even with a valid ticket', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
117
|
+
const ticket = yield ticketSvc.mint('UDID-1', 'actor-1');
|
|
118
|
+
const req = {
|
|
119
|
+
method: 'GET',
|
|
120
|
+
path: '/control/UDID-1/stream/status',
|
|
121
|
+
query: { ticket },
|
|
122
|
+
headers: {},
|
|
123
|
+
};
|
|
124
|
+
const res = fakeRes();
|
|
125
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, sinon_1.default.spy());
|
|
126
|
+
// Falls through to the final unauthenticated 401, not the ticket branch.
|
|
127
|
+
(0, chai_1.expect)(res.statusCode).to.equal(401);
|
|
128
|
+
(0, chai_1.expect)(res.body).to.deep.equal({ error: 'unauthenticated' });
|
|
129
|
+
}));
|
|
130
|
+
it('does NOT match a POST to the stream route (ticket-mint or otherwise)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
131
|
+
const ticket = yield ticketSvc.mint('UDID-1', 'actor-1');
|
|
132
|
+
const req = {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
path: '/control/UDID-1/stream',
|
|
135
|
+
query: { ticket },
|
|
136
|
+
headers: {},
|
|
137
|
+
};
|
|
138
|
+
const res = fakeRes();
|
|
139
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, sinon_1.default.spy());
|
|
140
|
+
(0, chai_1.expect)(res.statusCode).to.equal(401);
|
|
141
|
+
(0, chai_1.expect)(res.body).to.deep.equal({ error: 'unauthenticated' });
|
|
142
|
+
}));
|
|
143
|
+
it('ignores a non-string ticket query value (e.g. array from ?ticket=a&ticket=b)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
144
|
+
const req = {
|
|
145
|
+
method: 'GET',
|
|
146
|
+
path: '/control/UDID-1/stream',
|
|
147
|
+
query: { ticket: ['a', 'b'] },
|
|
148
|
+
headers: {},
|
|
149
|
+
};
|
|
150
|
+
const res = fakeRes();
|
|
151
|
+
yield (0, authMiddleware_1.authMiddleware)(req, res, sinon_1.default.spy());
|
|
152
|
+
(0, chai_1.expect)(res.statusCode).to.equal(401);
|
|
153
|
+
(0, chai_1.expect)(res.body).to.deep.equal({ error: 'unauthenticated' });
|
|
154
|
+
}));
|
|
155
|
+
});
|
|
156
|
+
// Mount-order guard: the hand-built-req tests above hardcode
|
|
157
|
+
// req.path='/control/UDID-1/stream', so they'd keep passing even if a future
|
|
158
|
+
// change to how authMiddleware / ControlRouter are mounted stopped delivering
|
|
159
|
+
// that apiRouter-relative path to the middleware — silently reopening "does
|
|
160
|
+
// the ticket branch even fire?". This block wires the REAL chain the same way
|
|
161
|
+
// src/app/index.ts does (apiRouter.use(authMiddleware); ControlRouter mounts
|
|
162
|
+
// '/control' onto apiRouter) and drives an actual HTTP request through it, so
|
|
163
|
+
// a regression in the mount path is caught end-to-end. The device store is
|
|
164
|
+
// stubbed to report "no such device", so a request that clears auth lands on
|
|
165
|
+
// the route handler's 404 — distinct from authMiddleware's 401. Asserting
|
|
166
|
+
// "not 401 unauthenticated" therefore proves the ticket branch fired.
|
|
167
|
+
describe('authMiddleware — stream-ticket branch (real mount-order, supertest)', () => {
|
|
168
|
+
let dir;
|
|
169
|
+
let app;
|
|
170
|
+
let ticketSvc;
|
|
171
|
+
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
172
|
+
dir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'xenon-authmw-mount-'));
|
|
173
|
+
const keySvc = new JwtKeyService_1.JwtKeyService();
|
|
174
|
+
yield keySvc.init(dir);
|
|
175
|
+
typedi_1.Container.set(JwtKeyService_1.JwtKeyService, keySvc);
|
|
176
|
+
ticketSvc = new StreamTicketService_1.StreamTicketService();
|
|
177
|
+
typedi_1.Container.set(StreamTicketService_1.StreamTicketService, ticketSvc);
|
|
178
|
+
typedi_1.Container.set(UserService_1.UserService, { findById: sinon_1.default.stub().resolves(null) });
|
|
179
|
+
typedi_1.Container.set(ApiKeyService_1.ApiKeyService, {
|
|
180
|
+
verifyPair: sinon_1.default.stub().resolves(null),
|
|
181
|
+
verify: sinon_1.default.stub().resolves(null),
|
|
182
|
+
});
|
|
183
|
+
typedi_1.Container.set(UserSessionService_1.UserSessionService, { resolve: sinon_1.default.stub().resolves(null) });
|
|
184
|
+
// Clean 404 path: the GET /:udid/stream handler's first act is
|
|
185
|
+
// getDeviceInfo(udid) -> DeviceStoreFactory.getStore().findDevice(...).
|
|
186
|
+
// Return no device so the handler responds 404 'Device not found' the
|
|
187
|
+
// moment it runs — which only happens if auth (the ticket branch) passed.
|
|
188
|
+
sinon_1.default
|
|
189
|
+
.stub(device_store_1.DeviceStoreFactory, 'getStore')
|
|
190
|
+
.returns({ findDevice: sinon_1.default.stub().resolves(undefined) });
|
|
191
|
+
// Mount exactly like src/app/index.ts: authMiddleware directly on the
|
|
192
|
+
// apiRouter, THEN ControlRouter.register(apiRouter) mounts '/control'.
|
|
193
|
+
const apiRouter = express_1.default.Router();
|
|
194
|
+
apiRouter.use(authMiddleware_1.authMiddleware);
|
|
195
|
+
control_1.default.register(apiRouter);
|
|
196
|
+
app = (0, express_1.default)();
|
|
197
|
+
app.use('/xenon/api', apiRouter);
|
|
198
|
+
}));
|
|
199
|
+
afterEach(() => {
|
|
200
|
+
sinon_1.default.restore();
|
|
201
|
+
typedi_1.Container.reset();
|
|
202
|
+
fs_1.default.rmSync(dir, { recursive: true, force: true });
|
|
203
|
+
});
|
|
204
|
+
it('a valid ticket on GET /xenon/api/control/:udid/stream reaches the route (past 401)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
205
|
+
const ticket = yield ticketSvc.mint('UDID-1', 'actor-1');
|
|
206
|
+
const res = yield (0, supertest_1.default)(app).get('/xenon/api/control/UDID-1/stream').query({ ticket });
|
|
207
|
+
// The ticket branch fired: request cleared auth and hit the handler's
|
|
208
|
+
// device lookup (404), rather than being rejected at the middleware (401).
|
|
209
|
+
(0, chai_1.expect)(res.status).to.not.equal(401);
|
|
210
|
+
(0, chai_1.expect)(res.status).to.equal(404); // 'Device not found' from the stubbed store
|
|
211
|
+
}));
|
|
212
|
+
it('the SAME request without a ticket is rejected 401 (proves the guard is non-vacuous)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
213
|
+
const res = yield (0, supertest_1.default)(app).get('/xenon/api/control/UDID-1/stream');
|
|
214
|
+
(0, chai_1.expect)(res.status).to.equal(401);
|
|
215
|
+
(0, chai_1.expect)(res.body).to.deep.equal({ error: 'unauthenticated' });
|
|
216
|
+
}));
|
|
217
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const iosBattery_1 = require("../../src/device-managers/ios/iosBattery");
|
|
5
|
+
describe('parseBatteryCapacity', () => {
|
|
6
|
+
it('reads BatteryCurrentCapacity from ideviceinfo battery-domain output', () => {
|
|
7
|
+
const out = [
|
|
8
|
+
'BatteryCurrentCapacity: 87',
|
|
9
|
+
'BatteryIsCharging: false',
|
|
10
|
+
'ExternalConnected: true',
|
|
11
|
+
'FullyCharged: false',
|
|
12
|
+
'HasBattery: true',
|
|
13
|
+
].join('\n');
|
|
14
|
+
(0, chai_1.expect)((0, iosBattery_1.parseBatteryCapacity)(out)).to.equal(87);
|
|
15
|
+
});
|
|
16
|
+
it('handles a full charge', () => {
|
|
17
|
+
(0, chai_1.expect)((0, iosBattery_1.parseBatteryCapacity)('BatteryCurrentCapacity: 100\nFullyCharged: true')).to.equal(100);
|
|
18
|
+
});
|
|
19
|
+
it('clamps out-of-range values into 0–100', () => {
|
|
20
|
+
(0, chai_1.expect)((0, iosBattery_1.parseBatteryCapacity)('BatteryCurrentCapacity: 250')).to.equal(100);
|
|
21
|
+
});
|
|
22
|
+
it('returns undefined when the field is absent', () => {
|
|
23
|
+
(0, chai_1.expect)((0, iosBattery_1.parseBatteryCapacity)('HasBattery: true\nExternalConnected: false')).to.equal(undefined);
|
|
24
|
+
});
|
|
25
|
+
it('is safe on empty / undefined input', () => {
|
|
26
|
+
(0, chai_1.expect)((0, iosBattery_1.parseBatteryCapacity)('')).to.equal(undefined);
|
|
27
|
+
(0, chai_1.expect)((0, iosBattery_1.parseBatteryCapacity)(undefined)).to.equal(undefined);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const iosStreamDiagnostics_1 = require("../../src/device-managers/ios/iosStreamDiagnostics");
|
|
5
|
+
describe('iosStreamDiagnostics', () => {
|
|
6
|
+
describe('isMissingWdaError', () => {
|
|
7
|
+
it('detects the go-ios "Did not find test app" WDA crash', () => {
|
|
8
|
+
const log = `setup test config: Did not find test app for ` +
|
|
9
|
+
`'com.qasecret.WebDriverAgentRunner.xctrunner' on device. Is it installed?`;
|
|
10
|
+
(0, chai_1.expect)((0, iosStreamDiagnostics_1.isMissingWdaError)(log)).to.equal(true);
|
|
11
|
+
});
|
|
12
|
+
it('detects a "WebDriverAgentRunner ... not installed" phrasing', () => {
|
|
13
|
+
(0, chai_1.expect)((0, iosStreamDiagnostics_1.isMissingWdaError)('WebDriverAgentRunner app is not installed on the device')).to.equal(true);
|
|
14
|
+
});
|
|
15
|
+
it('does not flag unrelated WDA crash logs', () => {
|
|
16
|
+
(0, chai_1.expect)((0, iosStreamDiagnostics_1.isMissingWdaError)('WDA crashed: signal 11 (SIGSEGV)')).to.equal(false);
|
|
17
|
+
});
|
|
18
|
+
it('is safe on empty input', () => {
|
|
19
|
+
(0, chai_1.expect)((0, iosStreamDiagnostics_1.isMissingWdaError)('')).to.equal(false);
|
|
20
|
+
});
|
|
21
|
+
it('produces an actionable message naming the udid', () => {
|
|
22
|
+
const msg = (0, iosStreamDiagnostics_1.missingWdaMessage)('c008789a75');
|
|
23
|
+
(0, chai_1.expect)(msg).to.contain('c008789a75');
|
|
24
|
+
(0, chai_1.expect)(msg).to.contain('WebDriverAgent');
|
|
25
|
+
(0, chai_1.expect)(msg.toLowerCase()).to.contain('install');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('classifyTunnelStderr', () => {
|
|
29
|
+
it('extracts the real target udid from the go-ios JSON payload', () => {
|
|
30
|
+
const line = JSON.stringify({
|
|
31
|
+
error: 'manualPairingTunnelStart: unsupported iOS version 15.8.8',
|
|
32
|
+
level: 'warning',
|
|
33
|
+
msg: 'failed to start tunnel',
|
|
34
|
+
udid: 'c008789a75aac6b5cfb1c014e306beeb5d45b85d',
|
|
35
|
+
});
|
|
36
|
+
(0, chai_1.expect)((0, iosStreamDiagnostics_1.classifyTunnelStderr)(line)).to.deep.equal({
|
|
37
|
+
unsupported: true,
|
|
38
|
+
udid: 'c008789a75aac6b5cfb1c014e306beeb5d45b85d',
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
it('recovers the udid via regex when the chunk is not clean JSON', () => {
|
|
42
|
+
const noisy = 'time=... level=warning "error":"unsupported iOS version 15.8.8" "udid":"abc123" trailing';
|
|
43
|
+
(0, chai_1.expect)((0, iosStreamDiagnostics_1.classifyTunnelStderr)(noisy)).to.deep.equal({ unsupported: true, udid: 'abc123' });
|
|
44
|
+
});
|
|
45
|
+
it('reports unsupported with a null udid when none can be parsed', () => {
|
|
46
|
+
(0, chai_1.expect)((0, iosStreamDiagnostics_1.classifyTunnelStderr)('go-ios: unsupported iOS version 15.8.8')).to.deep.equal({
|
|
47
|
+
unsupported: true,
|
|
48
|
+
udid: null,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
it('classifies ordinary tunnel stderr as not-unsupported', () => {
|
|
52
|
+
(0, chai_1.expect)((0, iosStreamDiagnostics_1.classifyTunnelStderr)('tunnel established on port 60105')).to.deep.equal({
|
|
53
|
+
unsupported: false,
|
|
54
|
+
udid: null,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
it('is safe on empty input', () => {
|
|
58
|
+
(0, chai_1.expect)((0, iosStreamDiagnostics_1.classifyTunnelStderr)('')).to.deep.equal({ unsupported: false, udid: null });
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -15,8 +15,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
require("reflect-metadata");
|
|
16
16
|
const chai_1 = require("chai");
|
|
17
17
|
const sinon_1 = __importDefault(require("sinon"));
|
|
18
|
+
const os_1 = __importDefault(require("os"));
|
|
19
|
+
const typedi_1 = require("typedi");
|
|
18
20
|
const api_1 = require("@opentelemetry/api");
|
|
19
21
|
const attributes_1 = require("../../src/services/telemetry/attributes");
|
|
22
|
+
const ArtifactStore_1 = require("../../src/services/artifacts/ArtifactStore");
|
|
20
23
|
// Stub metrics + trace BEFORE the module loads so the module-level
|
|
21
24
|
// ensureOtelInstruments() picks up our mocks. Inline import after stubbing.
|
|
22
25
|
function makeMockMeter() {
|
|
@@ -63,12 +66,17 @@ describe('RecordingOrchestrator instrumentation', () => {
|
|
|
63
66
|
// Pre-stub once to get clean instrument creation in the SUT.
|
|
64
67
|
getMeterStub = sinon_1.default.stub(api_1.metrics, 'getMeter');
|
|
65
68
|
getTracerStub = sinon_1.default.stub(api_1.trace, 'getTracer');
|
|
69
|
+
// RecordingOrchestrator resolves ARTIFACT_STORE from the container at
|
|
70
|
+
// runtime (registered at boot in ServerManager); register it here so the
|
|
71
|
+
// path-construction helpers work under test.
|
|
72
|
+
typedi_1.Container.set(ArtifactStore_1.ARTIFACT_STORE, new ArtifactStore_1.FsArtifactStore(os_1.default.tmpdir()));
|
|
66
73
|
// Now import — module-level let bindings will populate against the stub.
|
|
67
74
|
RecordingOrchestratorMod = require('../../src/services/recording/RecordingOrchestrator');
|
|
68
75
|
});
|
|
69
76
|
after(() => {
|
|
70
77
|
getMeterStub.restore();
|
|
71
78
|
getTracerStub.restore();
|
|
79
|
+
typedi_1.Container.remove(ArtifactStore_1.ARTIFACT_STORE);
|
|
72
80
|
});
|
|
73
81
|
beforeEach(() => {
|
|
74
82
|
mockMeterCtx = makeMockMeter();
|
|
@@ -48,10 +48,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
48
48
|
require("reflect-metadata");
|
|
49
49
|
const chai_1 = require("chai");
|
|
50
50
|
const sinon_1 = __importDefault(require("sinon"));
|
|
51
|
+
const os_1 = __importDefault(require("os"));
|
|
52
|
+
const typedi_1 = require("typedi");
|
|
51
53
|
const RecordingOrchestrator_1 = require("../../src/services/recording/RecordingOrchestrator");
|
|
52
54
|
const concurrency_gate_1 = require("../../src/services/recording/concurrency-gate");
|
|
55
|
+
const ArtifactStore_1 = require("../../src/services/artifacts/ArtifactStore");
|
|
53
56
|
// Stub the device store factory (used to look up host).
|
|
54
57
|
const deviceStoreModule = __importStar(require("../../src/data-service/device-store"));
|
|
58
|
+
// compositeOutputPath / per-device filePath construction resolve through
|
|
59
|
+
// ArtifactStore now (Task 7) — register a store the same way ServerManager
|
|
60
|
+
// does at boot, so RecordingOrchestrator's Container.get(ARTIFACT_STORE)
|
|
61
|
+
// doesn't throw in this unit test context.
|
|
62
|
+
before(() => {
|
|
63
|
+
typedi_1.Container.set(ArtifactStore_1.ARTIFACT_STORE, new ArtifactStore_1.FsArtifactStore(os_1.default.tmpdir()));
|
|
64
|
+
});
|
|
65
|
+
// Don't leave the global container polluted for later spec files in the run.
|
|
66
|
+
after(() => {
|
|
67
|
+
typedi_1.Container.remove(ArtifactStore_1.ARTIFACT_STORE);
|
|
68
|
+
});
|
|
55
69
|
function makeOrch(overrides = {}) {
|
|
56
70
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
57
71
|
const busyPrecheck = (_a = overrides.busyPrecheck) !== null && _a !== void 0 ? _a : { findBusy: sinon_1.default.stub().resolves([]) };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
require("reflect-metadata");
|
|
4
|
+
const chai_1 = require("chai");
|
|
5
|
+
const VideoPipelineService_1 = require("../../src/services/VideoPipelineService");
|
|
6
|
+
/**
|
|
7
|
+
* Guards the F7 fix: the single-device recorder must stamp MJPEG frames with
|
|
8
|
+
* wall-clock arrival time so the mp4 duration tracks real elapsed time, rather
|
|
9
|
+
* than assuming a fixed 25fps (verified live: a 15s capture produced 7.48s
|
|
10
|
+
* before this flag and 14.84s after).
|
|
11
|
+
*/
|
|
12
|
+
describe('VideoPipelineService.buildRecordArgs — F7 wall-clock timing', () => {
|
|
13
|
+
const base = { mjpegUrl: 'http://127.0.0.1:9101', outputPath: '/tmp/rec/out.mp4' };
|
|
14
|
+
it('stamps frames with wall-clock time, positioned before the input', () => {
|
|
15
|
+
const args = (0, VideoPipelineService_1.buildRecordArgs)(Object.assign(Object.assign({}, base), { isMac: true }));
|
|
16
|
+
const wallIdx = args.indexOf('-use_wallclock_as_timestamps');
|
|
17
|
+
const inputIdx = args.indexOf('-i');
|
|
18
|
+
(0, chai_1.expect)(wallIdx).to.be.greaterThan(-1);
|
|
19
|
+
(0, chai_1.expect)(args[wallIdx + 1]).to.equal('1');
|
|
20
|
+
// Must precede -i to apply to that input.
|
|
21
|
+
(0, chai_1.expect)(wallIdx).to.be.lessThan(inputIdx);
|
|
22
|
+
});
|
|
23
|
+
it('keeps variable frame timing on output (-vsync vfr)', () => {
|
|
24
|
+
const args = (0, VideoPipelineService_1.buildRecordArgs)(Object.assign(Object.assign({}, base), { isMac: true }));
|
|
25
|
+
const vsyncIdx = args.indexOf('-vsync');
|
|
26
|
+
(0, chai_1.expect)(vsyncIdx).to.be.greaterThan(-1);
|
|
27
|
+
(0, chai_1.expect)(args[vsyncIdx + 1]).to.equal('vfr');
|
|
28
|
+
});
|
|
29
|
+
it('feeds the mjpeg source and writes the output path last', () => {
|
|
30
|
+
const args = (0, VideoPipelineService_1.buildRecordArgs)(Object.assign(Object.assign({}, base), { isMac: false }));
|
|
31
|
+
(0, chai_1.expect)(args.indexOf('-f')).to.be.greaterThan(-1);
|
|
32
|
+
(0, chai_1.expect)(args[args.indexOf('-f') + 1]).to.equal('mjpeg');
|
|
33
|
+
(0, chai_1.expect)(args[args.indexOf('-i') + 1]).to.equal(base.mjpegUrl);
|
|
34
|
+
(0, chai_1.expect)(args[args.length - 1]).to.equal(base.outputPath);
|
|
35
|
+
});
|
|
36
|
+
it('uses VideoToolbox on mac and libx264 elsewhere', () => {
|
|
37
|
+
(0, chai_1.expect)((0, VideoPipelineService_1.buildRecordArgs)(Object.assign(Object.assign({}, base), { isMac: true }))).to.include('h264_videotoolbox');
|
|
38
|
+
(0, chai_1.expect)((0, VideoPipelineService_1.buildRecordArgs)(Object.assign(Object.assign({}, base), { isMac: false }))).to.include('libx264');
|
|
39
|
+
});
|
|
40
|
+
it('still emits fragmented-mp4 flags for instant playback / crash resiliency', () => {
|
|
41
|
+
const args = (0, VideoPipelineService_1.buildRecordArgs)(Object.assign(Object.assign({}, base), { isMac: true }));
|
|
42
|
+
const mv = args.indexOf('-movflags');
|
|
43
|
+
(0, chai_1.expect)(mv).to.be.greaterThan(-1);
|
|
44
|
+
(0, chai_1.expect)(args[mv + 1]).to.equal('frag_keyframe+empty_moov+default_base_moof');
|
|
45
|
+
});
|
|
46
|
+
});
|
package/lib/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/InternalHttpClient.ts","../src/PluginContext.ts","../src/XenonCapabilityManager.ts","../src/chromeUtils.ts","../src/config.ts","../src/device-utils.ts","../src/index.ts","../src/logger.ts","../src/plugin.ts","../src/prisma.ts","../src/app/index.ts","../src/app/swagger-docs.ts","../src/app/swagger.ts","../src/app/routers/apikeys.ts","../src/app/routers/apps.ts","../src/app/routers/auth.ts","../src/app/routers/bug-report.ts","../src/app/routers/build-export.ts","../src/app/routers/config.ts","../src/app/routers/control.ts","../src/app/routers/dashboard.ts","../src/app/routers/grid.ts","../src/app/routers/interceptor.ts","../src/app/routers/ports.ts","../src/app/routers/processes.ts","../src/app/routers/profile.ts","../src/app/routers/recordings.ts","../src/app/routers/reservation.ts","../src/app/routers/sdk-leases.ts","../src/app/routers/sdk-version.ts","../src/app/routers/teams.ts","../src/app/routers/users.ts","../src/app/routers/webhook.ts","../src/commands/handle.ts","../src/commands/index.ts","../src/dashboard/asset-manager.ts","../src/dashboard/commands.ts","../src/dashboard/event-manager.ts","../src/dashboard/services/app-service.ts","../src/dashboard/services/failure-analysis-service.ts","../src/dashboard/services/session-service.ts","../src/data-service/CircuitBreaker.ts","../src/data-service/config-service.ts","../src/data-service/db.ts","../src/data-service/device-service.ts","../src/data-service/device-store.interface.ts","../src/data-service/device-store.ts","../src/data-service/pending-sessions-service.ts","../src/data-service/pluginArgs.ts","../src/data-service/prisma-service.ts","../src/data-service/prisma-store.ts","../src/data-service/queue-service.ts","../src/data-service/web-config-service.ts","../src/device-managers/AndroidDeviceManager.ts","../src/device-managers/ChromeDriverManager.ts","../src/device-managers/HealthMonitorService.ts","../src/device-managers/IOSDeviceManager.ts","../src/device-managers/NodeDevices.ts","../src/device-managers/iOSTracker.ts","../src/device-managers/index.ts","../src/device-managers/android/AndroidStreamService.ts","../src/device-managers/android/DeviceLockManager.ts","../src/device-managers/cloud/CapabilityManager.ts","../src/device-managers/cloud/Devices.ts","../src/device-managers/ios/IOSDiscoveryService.ts","../src/device-managers/ios/IOSStreamService.ts","../src/device-managers/ios/WDAClient.ts","../src/enums/Capabilities.ts","../src/enums/Cloud.ts","../src/enums/Platform.ts","../src/enums/SessionType.ts","../src/enums/SocketEvents.ts","../src/generated/client/default.d.ts","../src/generated/client/default.js","../src/generated/client/edge.d.ts","../src/generated/client/edge.js","../src/generated/client/index-browser.js","../src/generated/client/index.d.ts","../src/generated/client/index.js","../src/generated/client/wasm.d.ts","../src/generated/client/wasm.js","../src/generated/client/runtime/edge-esm.js","../src/generated/client/runtime/edge.js","../src/generated/client/runtime/index-browser.d.ts","../src/generated/client/runtime/index-browser.js","../src/generated/client/runtime/library.d.ts","../src/generated/client/runtime/library.js","../src/generated/client/runtime/react-native.js","../src/generated/client/runtime/wasm.js","../src/helpers/UniversalMjpegProxy.ts","../src/helpers/index.ts","../src/interceptors/CommandInterceptor.ts","../src/interfaces/ICloudManager.ts","../src/interfaces/IDevice.ts","../src/interfaces/IDeviceFilterOptions.ts","../src/interfaces/IDeviceManager.ts","../src/interfaces/IOptions.ts","../src/interfaces/IPluginArgs.ts","../src/interfaces/ISessionCapability.ts","../src/logging/sessionContext.ts","../src/middleware/authMiddleware.ts","../src/middleware/csrfMiddleware.ts","../src/middleware/leaseTokenMiddleware.ts","../src/middleware/loginRateLimiter.ts","../src/middleware/rateLimitMiddleware.ts","../src/middleware/roleGuard.ts","../src/middleware/scopeGuard.ts","../src/profiling/AndroidAppProfiler.ts","../src/proxy/wd-command-proxy.ts","../src/scripts/generate-database-migration.ts","../src/scripts/initialize-database.ts","../src/scripts/install-go-ios.ts","../src/scripts/prepare-prisma.ts","../src/scripts/run-migrations.ts","../src/services/AICommandService.ts","../src/services/AIService.ts","../src/services/ApiKeyService.ts","../src/services/CircuitBreaker.ts","../src/services/CleanupService.ts","../src/services/DeviceReconciler.ts","../src/services/EmailService.ts","../src/services/EventBus.ts","../src/services/InspectorService.ts","../src/services/InterceptorService.ts","../src/services/MetricsService.ts","../src/services/NetworkConditioningService.ts","../src/services/NotificationService.ts","../src/services/OrphanSweeper.ts","../src/services/PasswordResetService.ts","../src/services/PortAllocator.ts","../src/services/ProcessMetricsService.ts","../src/services/ProcessRegistry.ts","../src/services/RequestLogService.ts","../src/services/ResourceIsolationService.ts","../src/services/SecurityService.ts","../src/services/SelectorStateService.ts","../src/services/SelectorVerificationJob.ts","../src/services/ServerManager.ts","../src/services/SessionHeartbeatService.ts","../src/services/SessionLifecycleService.ts","../src/services/ShutdownCoordinator.ts","../src/services/SocketClient.ts","../src/services/SocketServer.ts","../src/services/TeamService.ts","../src/services/TracingService.ts","../src/services/UserService.ts","../src/services/UserSessionService.ts","../src/services/VideoPipelineService.ts","../src/services/autowait/AutowaitService.ts","../src/services/autowait/waitFor.ts","../src/services/bug-report/BugReportService.ts","../src/services/bug-report/archive.ts","../src/services/bug-report/har-collector.ts","../src/services/bug-report/manifest.ts","../src/services/bug-report/readme.ts","../src/services/bug-report/types.ts","../src/services/bug-report/video-slice.ts","../src/services/bug-report/window.ts","../src/services/healing/FuzzyXmlHealingProvider.ts","../src/services/healing/HealEtalonService.ts","../src/services/healing/HealedLocatorGenerator.ts","../src/services/healing/HealingMetrics.ts","../src/services/healing/HealingOrchestrator.ts","../src/services/healing/LlmHealingProvider.ts","../src/services/healing/OcrHealingProvider.ts","../src/services/healing/ResilioTreeHealingProvider.ts","../src/services/healing/VisualAiHealingProvider.ts","../src/services/healing/types.ts","../src/services/identity/bootstrap.ts","../src/services/identity/sessionCleanupCron.ts","../src/services/identity/userAuthorization.ts","../src/services/interceptor/AndroidProxyAdapter.ts","../src/services/interceptor/CertManager.ts","../src/services/interceptor/HarBuilder.ts","../src/services/interceptor/HostFilter.ts","../src/services/interceptor/MitmProxyHost.ts","../src/services/interceptor/MockEngine.ts","../src/services/interceptor/RequestBuffer.ts","../src/services/interceptor/SessionArchive.ts","../src/services/interceptor/types.ts","../src/services/lease/LeaseOrphanSweeper.ts","../src/services/lease/LeaseService.ts","../src/services/lease/buildCapabilityBag.ts","../src/services/lease/leaseToken.ts","../src/services/omni-vision/OmniVisionService.ts","../src/services/omni-vision/VisionAssertionService.ts","../src/services/ports/PortAllocatorClient.ts","../src/services/ports/PortAllocatorService.ts","../src/services/recording/RecordingOrchestrator.ts","../src/services/recording/annotation-render.ts","../src/services/recording/busy-precheck.ts","../src/services/recording/concurrency-gate.ts","../src/services/recording/manualLock.ts","../src/services/recording/proof-bundle.ts","../src/services/recording/recording-store.ts","../src/services/telemetry/attributes.ts","../src/sessions/CloudSession.ts","../src/sessions/LocalSession.ts","../src/sessions/RemoteSession.ts","../src/sessions/SessionManager.ts","../src/sessions/XenonSession.ts","../src/types/CLIArgs.ts","../src/types/CloudArgs.ts","../src/types/CloudSchema.ts","../src/types/DeviceType.ts","../src/types/DeviceUpdate.ts","../src/types/IOSDevice.ts","../src/types/Platform.ts","../src/types/SessionStatus.ts","../src/types/express.d.ts","../src/types/healing.d.ts","../src/types/identity.ts","../src/validators/CapabilityValidator.ts","../test/e2e/e2ehelper.ts","../test/e2e/plugin-harness.ts","../test/e2e/plugin.spec.ts","../test/e2e/startup-verification.spec.ts","../test/e2e/telemetry_verification.spec.ts","../test/e2e/video-recording-test.spec.js","../test/e2e/android/conf.spec.js","../test/e2e/android/conf2.spec.js","../test/e2e/android/conf3.spec.js","../test/e2e/hubnode/forward-request.spec.ts","../test/e2e/hubnode/hubnode.spec.ts","../test/e2e/ios/conf1.spec.js","../test/e2e/ios/conf2.spec.js","../test/helpers/seedUser.ts","../test/helpers/test-container.ts","../test/integration/androidDevices.spec.ts","../test/integration/auth-flow.spec.ts","../test/integration/auth-rate-limit.spec.ts","../test/integration/bug-report-route.spec.ts","../test/integration/cliArgs.js","../test/integration/cross-workflow.spec.ts","../test/integration/forgot-password-rate-limit.spec.ts","../test/integration/forgot-password.spec.ts","../test/integration/legacy-deprecation-headers.spec.ts","../test/integration/migration.spec.ts","../test/integration/node-pair-auth-rest.spec.ts","../test/integration/ports-router.spec.ts","../test/integration/profile-tokens.spec.ts","../test/integration/recordings-router.spec.ts","../test/integration/reset-revokes-sessions.spec.ts","../test/integration/role-matrix.spec.ts","../test/integration/sdk-leases-router.spec.ts","../test/integration/sdk-version.spec.ts","../test/integration/selector-state-flow.spec.ts","../test/integration/team-membership-migration.spec.ts","../test/integration/team-visibility-dashboard.spec.ts","../test/integration/team-visibility-grid.spec.ts","../test/integration/team-visibility-recordings.spec.ts","../test/integration/users-flow.spec.ts","../test/integration/users-lockout.spec.ts","../test/integration/ios/01iOSSimulator.spec.ts","../test/integration/ios/02iOSDevices.spec.ts","../test/unit/AndroidDeviceManager.spec.ts","../test/unit/ApiKeyService.test.ts","../test/unit/AutowaitService.spec.ts","../test/unit/ChromeDriverManager.spec.js","../test/unit/CleanupService.spec.ts","../test/unit/CommandInterceptor.autowait.spec.ts","../test/unit/DeviceModel.spec.ts","../test/unit/EmailService.test.ts","../test/unit/FuzzyXmlHealingProvider.test.ts","../test/unit/GetAdbOriginal.js","../test/unit/HealingCascade.test.ts","../test/unit/IOSDeviceManager.spec.js","../test/unit/OrphanSweeper.test.ts","../test/unit/PasswordResetService.test.ts","../test/unit/PortAllocator.test.ts","../test/unit/ProcessRegistry.test.ts","../test/unit/RemoteIOs.spec.js","../test/unit/ResilioTreeHealingProvider.test.ts","../test/unit/SessionHeartbeatWrite.test.ts","../test/unit/TeamService.test.ts","../test/unit/UserService.test.ts","../test/unit/UserSessionService.test.ts","../test/unit/XenonCapabilityManager.spec.ts","../test/unit/aggregate-hotspots-tuple-key.spec.ts","../test/unit/auth-me-auth-disabled.test.ts","../test/unit/authMiddleware.test.ts","../test/unit/bootstrap-identity.test.ts","../test/unit/build-export.spec.ts","../test/unit/busy-precheck.spec.ts","../test/unit/commands.spec.js","../test/unit/concurrency-gate.spec.ts","../test/unit/config.spec.js","../test/unit/device-service.spec.ts","../test/unit/device-utils.spec.ts","../test/unit/event-manager-regression-hook.spec.ts","../test/unit/extract-access-key-token-pair.test.ts","../test/unit/findAndLockDevice-leasemerge.spec.ts","../test/unit/healing-instrumentation.spec.ts","../test/unit/healing-state-endpoints.spec.ts","../test/unit/helpers.spec.ts","../test/unit/loginRateLimiter.test.ts","../test/unit/omni-vision.spec.ts","../test/unit/plugin.spec.ts","../test/unit/prisma.spec.ts","../test/unit/profile-router.test.ts","../test/unit/proof-bundle.spec.ts","../test/unit/rateLimitMiddleware.test.ts","../test/unit/recording-instrumentation.spec.ts","../test/unit/recording-orchestrator.spec.ts","../test/unit/recording-store.spec.ts","../test/unit/roleGuard.test.ts","../test/unit/run-migrations-fail-fast.test.ts","../test/unit/run-migrations.test.ts","../test/unit/selector-state-service.spec.ts","../test/unit/selector-verification-job.spec.ts","../test/unit/team-filter.test.ts","../test/unit/tracing-service-init.spec.ts","../test/unit/userAuthorization.test.ts","../test/unit/users-router.test.ts","../test/unit/video-pipeline-outputpath.spec.ts","../test/unit/bug-report/archive.spec.ts","../test/unit/bug-report/har-collector.spec.ts","../test/unit/bug-report/manifest.spec.ts","../test/unit/bug-report/readme.spec.ts","../test/unit/bug-report/route.spec.ts","../test/unit/bug-report/service.spec.ts","../test/unit/bug-report/video-slice.spec.ts","../test/unit/bug-report/window.spec.ts","../test/unit/fixtures/device.config.js","../test/unit/fixtures/devices.js","../test/unit/interceptor/AndroidProxyAdapter.spec.ts","../test/unit/interceptor/CertManager.spec.ts","../test/unit/interceptor/HarBuilder.spec.ts","../test/unit/interceptor/HostFilter.spec.ts","../test/unit/interceptor/MitmProxyHost.spec.ts","../test/unit/interceptor/MockEngine.spec.ts","../test/unit/interceptor/RequestBuffer.spec.ts","../test/unit/interceptor/SessionArchive.spec.ts","../test/unit/lease/LeaseOrphanSweeper.spec.ts","../test/unit/lease/LeaseService.spec.ts","../test/unit/lease/buildCapabilityBag.spec.ts","../test/unit/lease/leaseToken.spec.ts","../test/unit/ports/PortAllocatorService.spec.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/InternalHttpClient.ts","../src/PluginContext.ts","../src/XenonCapabilityManager.ts","../src/chromeUtils.ts","../src/config.ts","../src/device-utils.ts","../src/index.ts","../src/logger.ts","../src/plugin.ts","../src/prisma.ts","../src/app/index.ts","../src/app/swagger-docs.ts","../src/app/swagger.ts","../src/app/routers/apikeys.ts","../src/app/routers/apps.ts","../src/app/routers/auth.ts","../src/app/routers/bug-report.ts","../src/app/routers/build-export.ts","../src/app/routers/capabilities.ts","../src/app/routers/config.ts","../src/app/routers/control.ts","../src/app/routers/dashboard.ts","../src/app/routers/grid.ts","../src/app/routers/interceptor.ts","../src/app/routers/ports.ts","../src/app/routers/processes.ts","../src/app/routers/profile.ts","../src/app/routers/projects.ts","../src/app/routers/recordings.ts","../src/app/routers/reservation.ts","../src/app/routers/sdk-leases.ts","../src/app/routers/sdk-version.ts","../src/app/routers/teams.ts","../src/app/routers/users.ts","../src/app/routers/webhook.ts","../src/commands/handle.ts","../src/commands/index.ts","../src/dashboard/asset-manager.ts","../src/dashboard/commands.ts","../src/dashboard/event-manager.ts","../src/dashboard/services/app-service.ts","../src/dashboard/services/failure-analysis-service.ts","../src/dashboard/services/session-service.ts","../src/data-service/CircuitBreaker.ts","../src/data-service/config-service.ts","../src/data-service/db.ts","../src/data-service/device-service.ts","../src/data-service/device-store.interface.ts","../src/data-service/device-store.ts","../src/data-service/pending-sessions-service.ts","../src/data-service/pluginArgs.ts","../src/data-service/prisma-service.ts","../src/data-service/prisma-store.ts","../src/data-service/queue-service.ts","../src/data-service/web-config-service.ts","../src/device-managers/AndroidDeviceManager.ts","../src/device-managers/ChromeDriverManager.ts","../src/device-managers/HealthMonitorService.ts","../src/device-managers/IOSDeviceManager.ts","../src/device-managers/NodeDevices.ts","../src/device-managers/iOSTracker.ts","../src/device-managers/index.ts","../src/device-managers/android/AndroidStreamService.ts","../src/device-managers/android/DeviceLockManager.ts","../src/device-managers/cloud/CapabilityManager.ts","../src/device-managers/cloud/Devices.ts","../src/device-managers/ios/IOSDiscoveryService.ts","../src/device-managers/ios/IOSStreamService.ts","../src/device-managers/ios/WDAClient.ts","../src/device-managers/ios/iosBattery.ts","../src/device-managers/ios/iosStreamDiagnostics.ts","../src/enums/Capabilities.ts","../src/enums/Cloud.ts","../src/enums/Platform.ts","../src/enums/SessionType.ts","../src/enums/SocketEvents.ts","../src/generated/client/default.d.ts","../src/generated/client/default.js","../src/generated/client/edge.d.ts","../src/generated/client/edge.js","../src/generated/client/index-browser.js","../src/generated/client/index.d.ts","../src/generated/client/index.js","../src/generated/client/wasm.d.ts","../src/generated/client/wasm.js","../src/generated/client/runtime/edge-esm.js","../src/generated/client/runtime/edge.js","../src/generated/client/runtime/index-browser.d.ts","../src/generated/client/runtime/index-browser.js","../src/generated/client/runtime/library.d.ts","../src/generated/client/runtime/library.js","../src/generated/client/runtime/react-native.js","../src/generated/client/runtime/wasm.js","../src/helpers/UniversalMjpegProxy.ts","../src/helpers/index.ts","../src/interceptors/CommandInterceptor.ts","../src/interfaces/ICloudManager.ts","../src/interfaces/IDevice.ts","../src/interfaces/IDeviceFilterOptions.ts","../src/interfaces/IDeviceManager.ts","../src/interfaces/IOptions.ts","../src/interfaces/IPluginArgs.ts","../src/interfaces/ISessionCapability.ts","../src/logging/sessionContext.ts","../src/middleware/authMiddleware.ts","../src/middleware/csrfMiddleware.ts","../src/middleware/leaseTokenMiddleware.ts","../src/middleware/loginRateLimiter.ts","../src/middleware/rateLimitMiddleware.ts","../src/middleware/roleGuard.ts","../src/middleware/scopeGuard.ts","../src/profiling/AndroidAppProfiler.ts","../src/proxy/wd-command-proxy.ts","../src/scripts/generate-database-migration.ts","../src/scripts/initialize-database.ts","../src/scripts/install-go-ios.ts","../src/scripts/prepare-prisma.ts","../src/scripts/run-migrations.ts","../src/services/AICommandService.ts","../src/services/AIService.ts","../src/services/ApiKeyService.ts","../src/services/CircuitBreaker.ts","../src/services/CleanupService.ts","../src/services/DeviceReconciler.ts","../src/services/EmailService.ts","../src/services/EventBus.ts","../src/services/EventLogService.ts","../src/services/InspectorService.ts","../src/services/InterceptorService.ts","../src/services/MetricsService.ts","../src/services/NetworkConditioningService.ts","../src/services/NotificationService.ts","../src/services/OrphanSweeper.ts","../src/services/PasswordResetService.ts","../src/services/PortAllocator.ts","../src/services/ProcessMetricsService.ts","../src/services/ProcessRegistry.ts","../src/services/RequestLogService.ts","../src/services/ResourceIsolationService.ts","../src/services/SecurityService.ts","../src/services/SelectorStateService.ts","../src/services/SelectorVerificationJob.ts","../src/services/ServerManager.ts","../src/services/SessionHeartbeatService.ts","../src/services/SessionLifecycleService.ts","../src/services/ShutdownCoordinator.ts","../src/services/SocketClient.ts","../src/services/SocketServer.ts","../src/services/TeamService.ts","../src/services/TracingService.ts","../src/services/UserService.ts","../src/services/UserSessionService.ts","../src/services/VideoPipelineService.ts","../src/services/artifacts/ArtifactStore.ts","../src/services/autowait/AutowaitService.ts","../src/services/autowait/waitFor.ts","../src/services/bug-report/BugReportService.ts","../src/services/bug-report/archive.ts","../src/services/bug-report/har-collector.ts","../src/services/bug-report/manifest.ts","../src/services/bug-report/readme.ts","../src/services/bug-report/types.ts","../src/services/bug-report/video-slice.ts","../src/services/bug-report/window.ts","../src/services/healing/FuzzyXmlHealingProvider.ts","../src/services/healing/HealEtalonService.ts","../src/services/healing/HealedLocatorGenerator.ts","../src/services/healing/HealingMetrics.ts","../src/services/healing/HealingOrchestrator.ts","../src/services/healing/LlmHealingProvider.ts","../src/services/healing/OcrHealingProvider.ts","../src/services/healing/ResilioTreeHealingProvider.ts","../src/services/healing/VisualAiHealingProvider.ts","../src/services/healing/types.ts","../src/services/identity/bootstrap.ts","../src/services/identity/sessionCleanupCron.ts","../src/services/identity/userAuthorization.ts","../src/services/interceptor/AndroidProxyAdapter.ts","../src/services/interceptor/CertManager.ts","../src/services/interceptor/HarBuilder.ts","../src/services/interceptor/HostFilter.ts","../src/services/interceptor/MitmProxyHost.ts","../src/services/interceptor/MockEngine.ts","../src/services/interceptor/RequestBuffer.ts","../src/services/interceptor/SessionArchive.ts","../src/services/interceptor/types.ts","../src/services/lease/LeaseOrphanSweeper.ts","../src/services/lease/LeaseService.ts","../src/services/lease/buildCapabilityBag.ts","../src/services/lease/leaseToken.ts","../src/services/omni-vision/OmniVisionService.ts","../src/services/omni-vision/VisionAssertionService.ts","../src/services/ports/PortAllocatorClient.ts","../src/services/ports/PortAllocatorService.ts","../src/services/recording/RecordingOrchestrator.ts","../src/services/recording/annotation-render.ts","../src/services/recording/busy-precheck.ts","../src/services/recording/concurrency-gate.ts","../src/services/recording/manualLock.ts","../src/services/recording/proof-bundle.ts","../src/services/recording/recording-store.ts","../src/services/telemetry/attributes.ts","../src/services/token/JwtKeyService.ts","../src/services/token/StreamTicketService.ts","../src/sessions/CloudSession.ts","../src/sessions/LocalSession.ts","../src/sessions/RemoteSession.ts","../src/sessions/SessionManager.ts","../src/sessions/XenonSession.ts","../src/types/CLIArgs.ts","../src/types/CloudArgs.ts","../src/types/CloudSchema.ts","../src/types/DeviceType.ts","../src/types/DeviceUpdate.ts","../src/types/IOSDevice.ts","../src/types/Platform.ts","../src/types/SessionStatus.ts","../src/types/express.d.ts","../src/types/healing.d.ts","../src/types/identity.ts","../src/validators/CapabilityValidator.ts","../test/e2e/e2ehelper.ts","../test/e2e/plugin-harness.ts","../test/e2e/plugin.spec.ts","../test/e2e/startup-verification.spec.ts","../test/e2e/telemetry_verification.spec.ts","../test/e2e/video-recording-test.spec.js","../test/e2e/android/conf.spec.js","../test/e2e/android/conf2.spec.js","../test/e2e/android/conf3.spec.js","../test/e2e/hubnode/forward-request.spec.ts","../test/e2e/hubnode/hubnode.spec.ts","../test/e2e/ios/conf1.spec.js","../test/e2e/ios/conf2.spec.js","../test/helpers/seedUser.ts","../test/helpers/test-container.ts","../test/integration/androidDevices.spec.ts","../test/integration/auth-flow.spec.ts","../test/integration/auth-rate-limit.spec.ts","../test/integration/bug-report-route.spec.ts","../test/integration/cliArgs.js","../test/integration/cross-workflow.spec.ts","../test/integration/forgot-password-rate-limit.spec.ts","../test/integration/forgot-password.spec.ts","../test/integration/legacy-deprecation-headers.spec.ts","../test/integration/migration.spec.ts","../test/integration/node-pair-auth-rest.spec.ts","../test/integration/ports-router.spec.ts","../test/integration/profile-tokens.spec.ts","../test/integration/recordings-router.spec.ts","../test/integration/reset-revokes-sessions.spec.ts","../test/integration/role-matrix.spec.ts","../test/integration/sdk-leases-router.spec.ts","../test/integration/sdk-version.spec.ts","../test/integration/selector-state-flow.spec.ts","../test/integration/team-membership-migration.spec.ts","../test/integration/team-visibility-dashboard.spec.ts","../test/integration/team-visibility-grid.spec.ts","../test/integration/team-visibility-recordings.spec.ts","../test/integration/users-flow.spec.ts","../test/integration/users-lockout.spec.ts","../test/integration/ios/01iOSSimulator.spec.ts","../test/integration/ios/02iOSDevices.spec.ts","../test/unit/AndroidDeviceManager.spec.ts","../test/unit/ApiKeyService.test.ts","../test/unit/ArtifactStore.spec.ts","../test/unit/AutowaitService.spec.ts","../test/unit/CapabilitiesRouter.spec.ts","../test/unit/ChromeDriverManager.spec.js","../test/unit/CleanupService.spec.ts","../test/unit/CommandInterceptor.autowait.spec.ts","../test/unit/DeviceModel.spec.ts","../test/unit/EmailService.test.ts","../test/unit/EventLogService.spec.ts","../test/unit/FuzzyXmlHealingProvider.test.ts","../test/unit/GetAdbOriginal.js","../test/unit/HealingCascade.test.ts","../test/unit/IOSDeviceManager.spec.js","../test/unit/JwtKeyService.spec.ts","../test/unit/OrphanSweeper.test.ts","../test/unit/PasswordResetService.test.ts","../test/unit/PortAllocator.test.ts","../test/unit/ProcessRegistry.test.ts","../test/unit/ProjectsRouter.spec.ts","../test/unit/RemoteIOs.spec.js","../test/unit/ResilioTreeHealingProvider.test.ts","../test/unit/SessionHeartbeatWrite.test.ts","../test/unit/StreamTicketService.spec.ts","../test/unit/TeamService.test.ts","../test/unit/TokenEndpoint.spec.ts","../test/unit/UserService.test.ts","../test/unit/UserSessionService.test.ts","../test/unit/XenonCapabilityManager.spec.ts","../test/unit/aggregate-hotspots-tuple-key.spec.ts","../test/unit/auth-me-auth-disabled.test.ts","../test/unit/authMiddleware.bearer.spec.ts","../test/unit/authMiddleware.streamTicket.spec.ts","../test/unit/authMiddleware.test.ts","../test/unit/bootstrap-identity.test.ts","../test/unit/build-export.spec.ts","../test/unit/busy-precheck.spec.ts","../test/unit/commands.spec.js","../test/unit/concurrency-gate.spec.ts","../test/unit/config.spec.js","../test/unit/device-service.spec.ts","../test/unit/device-utils.spec.ts","../test/unit/event-manager-regression-hook.spec.ts","../test/unit/extract-access-key-token-pair.test.ts","../test/unit/findAndLockDevice-leasemerge.spec.ts","../test/unit/healing-instrumentation.spec.ts","../test/unit/healing-state-endpoints.spec.ts","../test/unit/helpers.spec.ts","../test/unit/iosBattery.spec.ts","../test/unit/iosStreamDiagnostics.spec.ts","../test/unit/loginRateLimiter.test.ts","../test/unit/omni-vision.spec.ts","../test/unit/plugin.spec.ts","../test/unit/prisma.spec.ts","../test/unit/profile-router.test.ts","../test/unit/proof-bundle.spec.ts","../test/unit/rateLimitMiddleware.test.ts","../test/unit/recording-instrumentation.spec.ts","../test/unit/recording-orchestrator.spec.ts","../test/unit/recording-store.spec.ts","../test/unit/roleGuard.test.ts","../test/unit/run-migrations-fail-fast.test.ts","../test/unit/run-migrations.test.ts","../test/unit/selector-state-service.spec.ts","../test/unit/selector-verification-job.spec.ts","../test/unit/team-filter.test.ts","../test/unit/tracing-service-init.spec.ts","../test/unit/userAuthorization.test.ts","../test/unit/users-router.test.ts","../test/unit/video-pipeline-args.spec.ts","../test/unit/video-pipeline-outputpath.spec.ts","../test/unit/bug-report/archive.spec.ts","../test/unit/bug-report/har-collector.spec.ts","../test/unit/bug-report/manifest.spec.ts","../test/unit/bug-report/readme.spec.ts","../test/unit/bug-report/route.spec.ts","../test/unit/bug-report/service.spec.ts","../test/unit/bug-report/video-slice.spec.ts","../test/unit/bug-report/window.spec.ts","../test/unit/fixtures/device.config.js","../test/unit/fixtures/devices.js","../test/unit/interceptor/AndroidProxyAdapter.spec.ts","../test/unit/interceptor/CertManager.spec.ts","../test/unit/interceptor/HarBuilder.spec.ts","../test/unit/interceptor/HostFilter.spec.ts","../test/unit/interceptor/MitmProxyHost.spec.ts","../test/unit/interceptor/MockEngine.spec.ts","../test/unit/interceptor/RequestBuffer.spec.ts","../test/unit/interceptor/SessionArchive.spec.ts","../test/unit/lease/LeaseOrphanSweeper.spec.ts","../test/unit/lease/LeaseService.spec.ts","../test/unit/lease/buildCapabilityBag.spec.ts","../test/unit/lease/leaseToken.spec.ts","../test/unit/ports/PortAllocatorService.spec.ts"],"version":"5.9.3"}
|