@xenon-device-management/xenon 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +446 -0
- package/lib/package.json +207 -0
- package/lib/public/assets/Layouts-7IT8aFLI.js +11 -0
- package/lib/public/assets/Layouts-DPMls9vh.css +1 -0
- package/lib/public/assets/ai-settings-BbnfgdEx.js +11 -0
- package/lib/public/assets/apps-CRMrI4_p.js +16 -0
- package/lib/public/assets/apps-CcM77dgg.css +1 -0
- package/lib/public/assets/badge-B1nKs8zj.css +1 -0
- package/lib/public/assets/badge-CSvl5xIU.js +11 -0
- package/lib/public/assets/button-CJlKn4PZ.css +1 -0
- package/lib/public/assets/button-CvLaGFYj.js +26 -0
- package/lib/public/assets/calendar-6w-D6Oaw.js +6 -0
- package/lib/public/assets/clock-DcdeWBPr.js +6 -0
- package/lib/public/assets/cpu-DiSoXT9n.js +6 -0
- package/lib/public/assets/device-explorer-CajM63OJ.js +193 -0
- package/lib/public/assets/device-explorer-CxdUAoTL.css +1 -0
- package/lib/public/assets/index-ByQwMN5T.js +174 -0
- package/lib/public/assets/index-C1DBaoSh.js +1 -0
- package/lib/public/assets/index-qzCez_kk.css +1 -0
- package/lib/public/assets/lock-B23ibZmo.js +6 -0
- package/lib/public/assets/maintenance-settings-CirzA6yG.js +6 -0
- package/lib/public/assets/mouse-pointer-2-Cz76SHFb.js +6 -0
- package/lib/public/assets/plus-BBwlIevt.js +6 -0
- package/lib/public/assets/session-dashboard-C2k7FFv_.css +1 -0
- package/lib/public/assets/session-dashboard-HPDtwPOZ.js +62 -0
- package/lib/public/assets/settings-DrZsZwdc.js +1 -0
- package/lib/public/assets/trash-2-DQpvzJec.js +6 -0
- package/lib/public/assets/useSocket-Dxsqae2a.js +16 -0
- package/lib/public/assets/webhook-settings-CDPgsgkb.css +1 -0
- package/lib/public/assets/webhook-settings-Cp-B4Nrw.js +1 -0
- package/lib/public/assets/zap-DovP6iow.js +6 -0
- package/lib/public/favicon.ico +0 -0
- package/lib/public/favicon.png +0 -0
- package/lib/public/favicon.svg +9 -0
- package/lib/public/index.html +46 -0
- package/lib/public/logo.svg +17 -0
- package/lib/public/logo192.png +0 -0
- package/lib/public/logo512.png +0 -0
- package/lib/public/manifest.json +25 -0
- package/lib/public/robots.txt +3 -0
- package/lib/schema.json +348 -0
- package/lib/src/InternalHttpClient.js +212 -0
- package/lib/src/PluginContext.js +29 -0
- package/lib/src/XenonCapabilityManager.js +199 -0
- package/lib/src/app/index.js +167 -0
- package/lib/src/app/routers/apps.js +79 -0
- package/lib/src/app/routers/config.js +131 -0
- package/lib/src/app/routers/control.js +835 -0
- package/lib/src/app/routers/dashboard.js +301 -0
- package/lib/src/app/routers/grid.js +352 -0
- package/lib/src/app/routers/reservation.js +190 -0
- package/lib/src/app/routers/webhook.js +83 -0
- package/lib/src/app/swagger-docs.js +203 -0
- package/lib/src/app/swagger.js +366 -0
- package/lib/src/chromeUtils.js +148 -0
- package/lib/src/commands/handle.js +19 -0
- package/lib/src/commands/index.js +8 -0
- package/lib/src/config.js +73 -0
- package/lib/src/dashboard/asset-manager.js +84 -0
- package/lib/src/dashboard/commands.js +284 -0
- package/lib/src/dashboard/event-manager.js +699 -0
- package/lib/src/dashboard/services/app-service.js +134 -0
- package/lib/src/dashboard/services/failure-analysis-service.js +173 -0
- package/lib/src/dashboard/services/session-service.js +113 -0
- package/lib/src/data-service/CircuitBreaker.js +83 -0
- package/lib/src/data-service/config-service.js +155 -0
- package/lib/src/data-service/db.js +122 -0
- package/lib/src/data-service/device-service.js +320 -0
- package/lib/src/data-service/device-store.interface.js +2 -0
- package/lib/src/data-service/device-store.js +345 -0
- package/lib/src/data-service/pending-sessions-service.js +25 -0
- package/lib/src/data-service/pluginArgs.js +25 -0
- package/lib/src/data-service/prisma-service.js +31 -0
- package/lib/src/data-service/prisma-store.js +385 -0
- package/lib/src/data-service/queue-service.js +150 -0
- package/lib/src/data-service/web-config-service.js +130 -0
- package/lib/src/device-managers/AndroidDeviceManager.js +1155 -0
- package/lib/src/device-managers/ChromeDriverManager.js +68 -0
- package/lib/src/device-managers/HealthMonitorService.js +325 -0
- package/lib/src/device-managers/IOSDeviceManager.js +351 -0
- package/lib/src/device-managers/NodeDevices.js +82 -0
- package/lib/src/device-managers/android/AndroidStreamService.js +370 -0
- package/lib/src/device-managers/android/DeviceLockManager.js +45 -0
- package/lib/src/device-managers/cloud/CapabilityManager.js +26 -0
- package/lib/src/device-managers/cloud/Devices.js +86 -0
- package/lib/src/device-managers/iOSTracker.js +44 -0
- package/lib/src/device-managers/index.js +89 -0
- package/lib/src/device-managers/ios/IOSDiscoveryService.js +268 -0
- package/lib/src/device-managers/ios/IOSStreamService.js +893 -0
- package/lib/src/device-managers/ios/WDAClient.js +866 -0
- package/lib/src/device-utils.js +663 -0
- package/lib/src/enums/Capabilities.js +8 -0
- package/lib/src/enums/Cloud.js +11 -0
- package/lib/src/enums/Platform.js +9 -0
- package/lib/src/enums/SessionType.js +9 -0
- package/lib/src/enums/SocketEvents.js +15 -0
- package/lib/src/helpers/UniversalMjpegProxy.js +273 -0
- package/lib/src/helpers/index.js +229 -0
- package/lib/src/index.js +95 -0
- package/lib/src/interceptors/CommandInterceptor.js +524 -0
- package/lib/src/interfaces/ICloudManager.js +2 -0
- package/lib/src/interfaces/IDevice.js +2 -0
- package/lib/src/interfaces/IDeviceFilterOptions.js +2 -0
- package/lib/src/interfaces/IDeviceManager.js +2 -0
- package/lib/src/interfaces/IOptions.js +2 -0
- package/lib/src/interfaces/IPluginArgs.js +55 -0
- package/lib/src/interfaces/ISessionCapability.js +2 -0
- package/lib/src/logger.js +225 -0
- package/lib/src/plugin.js +244 -0
- package/lib/src/prisma.js +12 -0
- package/lib/src/profiling/AndroidAppProfiler.js +213 -0
- package/lib/src/proxy/wd-command-proxy.js +221 -0
- package/lib/src/scripts/generate-database-migration.js +59 -0
- package/lib/src/scripts/initialize-database.js +55 -0
- package/lib/src/scripts/install-go-ios.js +66 -0
- package/lib/src/scripts/prepare-prisma.js +89 -0
- package/lib/src/services/AICommandService.js +143 -0
- package/lib/src/services/AIService.js +466 -0
- package/lib/src/services/CleanupService.js +141 -0
- package/lib/src/services/EventBus.js +74 -0
- package/lib/src/services/InspectorService.js +395 -0
- package/lib/src/services/MetricsService.js +134 -0
- package/lib/src/services/NetworkConditioningService.js +173 -0
- package/lib/src/services/NotificationService.js +163 -0
- package/lib/src/services/RequestLogService.js +252 -0
- package/lib/src/services/ResourceIsolationService.js +122 -0
- package/lib/src/services/SecurityService.js +120 -0
- package/lib/src/services/ServerManager.js +284 -0
- package/lib/src/services/SessionHeartbeatService.js +158 -0
- package/lib/src/services/SessionLifecycleService.js +572 -0
- package/lib/src/services/SocketClient.js +71 -0
- package/lib/src/services/SocketServer.js +87 -0
- package/lib/src/services/TracingService.js +132 -0
- package/lib/src/services/VideoPipelineService.js +220 -0
- package/lib/src/services/healing/FuzzyXmlHealingProvider.js +333 -0
- package/lib/src/services/healing/HealEtalonService.js +98 -0
- package/lib/src/services/healing/HealedLocatorGenerator.js +132 -0
- package/lib/src/services/healing/HealingOrchestrator.js +165 -0
- package/lib/src/services/healing/LlmHealingProvider.js +77 -0
- package/lib/src/services/healing/OcrHealingProvider.js +119 -0
- package/lib/src/services/healing/ResilioTreeHealingProvider.js +100 -0
- package/lib/src/services/healing/VisualAiHealingProvider.js +90 -0
- package/lib/src/services/healing/types.js +12 -0
- package/lib/src/services/omni-vision/OmniVisionService.js +718 -0
- package/lib/src/services/omni-vision/VisionAssertionService.js +68 -0
- package/lib/src/sessions/CloudSession.js +42 -0
- package/lib/src/sessions/LocalSession.js +313 -0
- package/lib/src/sessions/RemoteSession.js +287 -0
- package/lib/src/sessions/SessionManager.js +238 -0
- package/lib/src/sessions/XenonSession.js +44 -0
- package/lib/src/types/CLIArgs.js +2 -0
- package/lib/src/types/CloudArgs.js +2 -0
- package/lib/src/types/CloudSchema.js +131 -0
- package/lib/src/types/DeviceType.js +2 -0
- package/lib/src/types/DeviceUpdate.js +2 -0
- package/lib/src/types/IOSDevice.js +2 -0
- package/lib/src/types/Platform.js +2 -0
- package/lib/src/types/SessionStatus.js +11 -0
- package/lib/src/validators/CapabilityValidator.js +93 -0
- package/lib/test/e2e/android/conf.spec.js +43 -0
- package/lib/test/e2e/android/conf2.spec.js +44 -0
- package/lib/test/e2e/android/conf3.spec.js +44 -0
- package/lib/test/e2e/e2ehelper.js +113 -0
- package/lib/test/e2e/hubnode/forward-request.spec.js +224 -0
- package/lib/test/e2e/hubnode/hubnode.spec.js +214 -0
- package/lib/test/e2e/ios/conf1.spec.js +39 -0
- package/lib/test/e2e/ios/conf2.spec.js +39 -0
- package/lib/test/e2e/plugin-harness.js +236 -0
- package/lib/test/e2e/plugin.spec.js +97 -0
- package/lib/test/e2e/telemetry_verification.spec.js +83 -0
- package/lib/test/e2e/video-recording-test.spec.js +63 -0
- package/lib/test/helpers/test-container.js +112 -0
- package/lib/test/integration/androidDevices.spec.js +137 -0
- package/lib/test/integration/cliArgs.js +73 -0
- package/lib/test/integration/ios/01iOSSimulator.spec.js +291 -0
- package/lib/test/integration/ios/02iOSDevices.spec.js +75 -0
- package/lib/test/integration/testHelpers.js +74 -0
- package/lib/test/unit/AndroidDeviceManager.spec.js +178 -0
- package/lib/test/unit/ChromeDriverManager.spec.js +26 -0
- package/lib/test/unit/CleanupService.spec.js +21 -0
- package/lib/test/unit/DeviceModel.spec.js +157 -0
- package/lib/test/unit/FuzzyXmlHealingProvider.test.js +294 -0
- package/lib/test/unit/GetAdbOriginal.js +42 -0
- package/lib/test/unit/HealingCascade.test.js +128 -0
- package/lib/test/unit/IOSDeviceManager.spec.js +261 -0
- package/lib/test/unit/RemoteIOs.spec.js +78 -0
- package/lib/test/unit/ResilioTreeHealingProvider.test.js +96 -0
- package/lib/test/unit/commands.spec.js +27 -0
- package/lib/test/unit/config.spec.js +27 -0
- package/lib/test/unit/device-service.spec.js +307 -0
- package/lib/test/unit/device-utils.spec.js +313 -0
- package/lib/test/unit/fixtures/device.config.js +4 -0
- package/lib/test/unit/fixtures/devices.js +89 -0
- package/lib/test/unit/helpers.spec.js +62 -0
- package/lib/test/unit/omni-vision.spec.js +100 -0
- package/lib/test/unit/plugin.spec.js +133 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +207 -0
- package/prisma/data.db +0 -0
- package/prisma/dev.db +0 -0
- package/prisma/dev.db-journal +0 -0
- package/prisma/migrations/20231011074725_initial_tables/migration.sql +47 -0
- package/prisma/migrations/20231226115334_update_session_log/migration.sql +2 -0
- package/prisma/migrations/20251204113710_add_video_recording_enabled/migration.sql +29 -0
- package/prisma/migrations/20251204132449_add_log_table/migration.sql +11 -0
- package/prisma/migrations/20251205050111_add_profiling_support/migration.sql +47 -0
- package/prisma/migrations/20251205050947_add_is_error_field/migration.sql +24 -0
- package/prisma/migrations/20260126201337_add_app_model/migration.sql +18 -0
- package/prisma/migrations/20260130115722_add_performance_trace_and_xenon_sync/migration.sql +2 -0
- package/prisma/migrations/20260130135114_add_device_models/migration.sql +57 -0
- package/prisma/migrations/20260130140655_make_systemport_optional/migration.sql +45 -0
- package/prisma/migrations/20260130140932_make_device_fields_optional/migration.sql +45 -0
- package/prisma/migrations/20260130141040_final_schema_fix/migration.sql +45 -0
- package/prisma/migrations/20260130143234_add_device_health_fields/migration.sql +4 -0
- package/prisma/migrations/20260130144921_add_failure_category/migration.sql +2 -0
- package/prisma/migrations/20260131151456_add_webhook_config/migration.sql +10 -0
- package/prisma/migrations/20260201094507_add_device_tags/migration.sql +11 -0
- package/prisma/migrations/20260201103410_add_managed_process/migration.sql +15 -0
- package/prisma/migrations/20260201140637_add_web_config/migration.sql +22 -0
- package/prisma/migrations/20260201162232_add_session_progress/migration.sql +2 -0
- package/prisma/migrations/20260201174231_add_total_healed_count/migration.sql +2 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +210 -0
- package/schema.json +348 -0
- package/scripts/build-xenon.sh +32 -0
- package/scripts/dev/debug-gemini.ts +44 -0
- package/scripts/generate-types-from-schema.js +86 -0
- package/scripts/install-compatible-driver.js +39 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
42
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
43
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
44
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
45
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
46
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
47
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
51
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
52
|
+
};
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.CommandInterceptor = void 0;
|
|
55
|
+
const typedi_1 = require("typedi");
|
|
56
|
+
const TracingService_1 = require("../services/TracingService");
|
|
57
|
+
const event_manager_1 = require("../dashboard/event-manager");
|
|
58
|
+
const SessionManager_1 = require("../sessions/SessionManager");
|
|
59
|
+
const HealingOrchestrator_1 = require("../services/healing/HealingOrchestrator");
|
|
60
|
+
const HealEtalonService_1 = require("../services/healing/HealEtalonService");
|
|
61
|
+
const OmniVisionService_1 = require("../services/omni-vision/OmniVisionService");
|
|
62
|
+
const AICommandService_1 = require("../services/AICommandService");
|
|
63
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
64
|
+
let CommandInterceptor = class CommandInterceptor {
|
|
65
|
+
constructor() {
|
|
66
|
+
this.log = logger_1.default.scope('CommandInterceptor');
|
|
67
|
+
this.learningSessions = new Set();
|
|
68
|
+
}
|
|
69
|
+
handle(next, driver, commandName, args, pluginArgs, isHub) {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const IGNORED_COMMANDS = ['getScreenshot', 'stopRecordingScreen', 'startRecordingScreen'];
|
|
72
|
+
if (IGNORED_COMMANDS.includes(commandName))
|
|
73
|
+
return yield next();
|
|
74
|
+
const sessionId = driver.sessionId || args[args.length - 1];
|
|
75
|
+
const tracingService = typedi_1.Container.get(TracingService_1.TracingService);
|
|
76
|
+
let span;
|
|
77
|
+
if (isHub && sessionId) {
|
|
78
|
+
span = tracingService.startCommandSpan(sessionId, commandName, {
|
|
79
|
+
'xenon.command.args': JSON.stringify(args),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (commandName === 'createSession' || commandName === 'deleteSession') {
|
|
83
|
+
try {
|
|
84
|
+
return yield next();
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
if (span)
|
|
88
|
+
tracingService.endSpan(`${sessionId}:${commandName}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
if (sessionId) {
|
|
93
|
+
const { updateCmdExecutedTime } = yield Promise.resolve().then(() => __importStar(require('../data-service/device-service')));
|
|
94
|
+
yield updateCmdExecutedTime(sessionId);
|
|
95
|
+
if (commandName === 'execute') {
|
|
96
|
+
const script = typeof args[0] === 'string' ? args[0] : '';
|
|
97
|
+
let scriptArgs = args[1];
|
|
98
|
+
// Appium executeScript puts script arguments in args[1] as an array.
|
|
99
|
+
// Dig into it to find our payload object.
|
|
100
|
+
if (Array.isArray(scriptArgs)) {
|
|
101
|
+
if (scriptArgs.length > 0 &&
|
|
102
|
+
typeof scriptArgs[0] === 'object' &&
|
|
103
|
+
scriptArgs[0] !== null) {
|
|
104
|
+
scriptArgs = scriptArgs[0];
|
|
105
|
+
}
|
|
106
|
+
else if (scriptArgs.length === 0) {
|
|
107
|
+
scriptArgs = {};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const aiCommand = script.replace(/^(xe|xenon)\s*:\s*/, '').trim();
|
|
111
|
+
if (aiCommand === 'smartTap' || aiCommand === 'omniClick') {
|
|
112
|
+
this.log.info(`[Interceptor] Routing AI command: ${script} with payload: ${JSON.stringify(scriptArgs)}`);
|
|
113
|
+
const payload = typeof scriptArgs === 'object' && scriptArgs !== null ? scriptArgs : {};
|
|
114
|
+
return yield typedi_1.Container.get(AICommandService_1.AICommandService).smartTap(driver, payload);
|
|
115
|
+
}
|
|
116
|
+
if (aiCommand === 'uiInventory' || aiCommand === 'uiScanExport') {
|
|
117
|
+
this.log.info(`[Interceptor] Routing AI command: ${script} with payload: ${JSON.stringify(scriptArgs)}`);
|
|
118
|
+
const payload = typeof scriptArgs === 'object' && scriptArgs !== null ? scriptArgs : {};
|
|
119
|
+
return yield typedi_1.Container.get(AICommandService_1.AICommandService).uiInventory(driver, payload);
|
|
120
|
+
}
|
|
121
|
+
if (aiCommand === 'analyzeScreen' || aiCommand === 'omniScan') {
|
|
122
|
+
this.log.info(`[Interceptor] Routing AI command: ${script}`);
|
|
123
|
+
return yield typedi_1.Container.get(AICommandService_1.AICommandService).analyzeScreen(driver);
|
|
124
|
+
}
|
|
125
|
+
if (aiCommand === 'visualTap') {
|
|
126
|
+
this.log.info(`[Interceptor] Routing AI command: ${script} with payload: ${JSON.stringify(scriptArgs)}`);
|
|
127
|
+
const payload = typeof scriptArgs === 'object' && scriptArgs !== null ? scriptArgs : {};
|
|
128
|
+
return yield typedi_1.Container.get(AICommandService_1.AICommandService).visualTap(driver, payload);
|
|
129
|
+
}
|
|
130
|
+
if (aiCommand === 'assertVisualState') {
|
|
131
|
+
this.log.info(`[Interceptor] Routing AI command: ${script} with payload: ${JSON.stringify(scriptArgs)}`);
|
|
132
|
+
const instruction = typeof scriptArgs === 'string'
|
|
133
|
+
? scriptArgs
|
|
134
|
+
: typeof scriptArgs === 'object' && (scriptArgs === null || scriptArgs === void 0 ? void 0 : scriptArgs.instruction)
|
|
135
|
+
? scriptArgs.instruction
|
|
136
|
+
: '';
|
|
137
|
+
return yield typedi_1.Container.get(AICommandService_1.AICommandService).assertVisualState(driver, instruction);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const shouldProceed = yield event_manager_1.DASHBORD_EVENT_MANAGER.beforeSessionCommand(sessionId, commandName, { body: { script: args[0], args: args[1] } }, {
|
|
141
|
+
status: () => ({ json: (d) => d }),
|
|
142
|
+
setHeader: () => { },
|
|
143
|
+
getHeader: () => { },
|
|
144
|
+
});
|
|
145
|
+
if (shouldProceed === false) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// --- OMNI-VISION: PROACTIVE SEARCH ---
|
|
150
|
+
const strategy = args[0];
|
|
151
|
+
const selector = args[1];
|
|
152
|
+
if (['findElement', 'findElements'].includes(commandName) &&
|
|
153
|
+
['-custom:ai-icon', '-custom:ai-text'].includes(strategy)) {
|
|
154
|
+
return yield this.handleOmniVisionSearch(sessionId, driver, commandName, strategy, selector);
|
|
155
|
+
}
|
|
156
|
+
// --- OMNI-VISION: VIRTUAL ELEMENT INTERACTION ---
|
|
157
|
+
const elementCommands = [
|
|
158
|
+
'click',
|
|
159
|
+
'getElementRect',
|
|
160
|
+
'getElementLocation',
|
|
161
|
+
'getElementSize',
|
|
162
|
+
'getText',
|
|
163
|
+
'setValue',
|
|
164
|
+
'elementDisplayed',
|
|
165
|
+
'elementEnabled',
|
|
166
|
+
];
|
|
167
|
+
if (elementCommands.includes(commandName)) {
|
|
168
|
+
const elementId = args[0];
|
|
169
|
+
if (typeof elementId === 'string' &&
|
|
170
|
+
(elementId.startsWith('omni_') ||
|
|
171
|
+
elementId.startsWith('healed_ocr') ||
|
|
172
|
+
elementId.startsWith('healed_visual'))) {
|
|
173
|
+
return yield this.handleVirtualElementCommand(sessionId, driver, commandName, elementId, args[1]);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const response = yield next();
|
|
177
|
+
if (isHub && !!pluginArgs.enableDashboard && SessionManager_1.SESSION_MANAGER.isValidSession(sessionId)) {
|
|
178
|
+
try {
|
|
179
|
+
yield event_manager_1.DASHBORD_EVENT_MANAGER.afterSessionCommand(sessionId, commandName, driver, {
|
|
180
|
+
body: args,
|
|
181
|
+
method: 'POST',
|
|
182
|
+
path: `/${commandName}`,
|
|
183
|
+
originalUrl: `/${commandName}`,
|
|
184
|
+
}, {}, JSON.stringify({ value: response, sessionId }));
|
|
185
|
+
if (commandName === 'findElement' &&
|
|
186
|
+
response &&
|
|
187
|
+
pluginArgs.enableSelfHealing !== false) {
|
|
188
|
+
this.triggerLearning(driver, args, response, sessionId);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (postCommandErr) {
|
|
192
|
+
this.log.warn(`[Interceptor] Post-command hooks failed: ${postCommandErr.message}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return response;
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (this.isNoSuchElementError(error) &&
|
|
199
|
+
['findElement', 'findElements'].includes(commandName) &&
|
|
200
|
+
pluginArgs.enableSelfHealing !== false) {
|
|
201
|
+
const healed = yield typedi_1.Container.get(HealingOrchestrator_1.HealingOrchestrator).attemptHealing(sessionId, driver, args[0], args[1]);
|
|
202
|
+
if (healed) {
|
|
203
|
+
yield this.logHealingEvent(sessionId, commandName, driver, args, healed);
|
|
204
|
+
let finalId = healed.id;
|
|
205
|
+
// OCR/Visual AI tiers return virtual IDs with rect coordinates
|
|
206
|
+
// We need to resolve a REAL element that Appium can interact with
|
|
207
|
+
if (healed.id.startsWith('healed_') && healed.rect) {
|
|
208
|
+
// REGISTER the virtual element for subsequent state checks
|
|
209
|
+
typedi_1.Container.get(OmniVisionService_1.OmniVisionService).addVirtualElement({
|
|
210
|
+
id: healed.id,
|
|
211
|
+
rect: healed.rect,
|
|
212
|
+
confidence: healed.confidence,
|
|
213
|
+
text: healed.message,
|
|
214
|
+
});
|
|
215
|
+
this.log.info(`[Interceptor] Visual healing returned coordinates. Resolving real element at (${healed.rect.x}, ${healed.rect.y})...`);
|
|
216
|
+
let resolved = false;
|
|
217
|
+
// Strategy 1: Try to find element at the center of the detected area
|
|
218
|
+
try {
|
|
219
|
+
const cx = Math.round(healed.rect.x + healed.rect.width / 2);
|
|
220
|
+
const cy = Math.round(healed.rect.y + healed.rect.height / 2);
|
|
221
|
+
const touchEl = yield driver.findElement('-ios class chain', `**/XCUIElementTypeAny[\`rect.x <= ${cx} AND rect.x + rect.width >= ${cx} AND rect.y <= ${cy} AND rect.y + rect.height >= ${cy}\`]`);
|
|
222
|
+
if (touchEl) {
|
|
223
|
+
finalId =
|
|
224
|
+
touchEl.ELEMENT || touchEl['element-6066-11e4-a52e-4f735466cecf'] || finalId;
|
|
225
|
+
resolved = !!finalId && !finalId.startsWith('healed_');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
// Strategy 1 failed
|
|
230
|
+
}
|
|
231
|
+
// Strategy 2: Use coordinate tap action (W3C Actions API)
|
|
232
|
+
if (!resolved) {
|
|
233
|
+
this.log.info('[Interceptor] Falling back to coordinate-based tap for visual healing');
|
|
234
|
+
try {
|
|
235
|
+
const cx = Math.round(healed.rect.x + healed.rect.width / 2);
|
|
236
|
+
const cy = Math.round(healed.rect.y + healed.rect.height / 2);
|
|
237
|
+
yield driver.performActions([
|
|
238
|
+
{
|
|
239
|
+
type: 'pointer',
|
|
240
|
+
id: 'xenon-heal-tap',
|
|
241
|
+
parameters: { pointerType: 'touch' },
|
|
242
|
+
actions: [
|
|
243
|
+
{ type: 'pointerMove', duration: 0, x: cx, y: cy },
|
|
244
|
+
{ type: 'pointerDown', button: 0 },
|
|
245
|
+
{ type: 'pause', duration: 100 },
|
|
246
|
+
{ type: 'pointerUp', button: 0 },
|
|
247
|
+
],
|
|
248
|
+
},
|
|
249
|
+
]);
|
|
250
|
+
yield driver.releaseActions();
|
|
251
|
+
this.log.info(`[Interceptor] ✅ Visual healing: tapped at (${cx}, ${cy})`);
|
|
252
|
+
// Return the virtual ID — the tap already happened
|
|
253
|
+
}
|
|
254
|
+
catch (tapErr) {
|
|
255
|
+
this.log.error(`[Interceptor] Coordinate tap failed: ${tapErr.message}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const elementResponse = {
|
|
260
|
+
ELEMENT: finalId,
|
|
261
|
+
'element-6066-11e4-a52e-4f735466cecf': finalId,
|
|
262
|
+
};
|
|
263
|
+
return commandName === 'findElement' ? elementResponse : [elementResponse];
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (isHub && !!pluginArgs.enableDashboard && sessionId) {
|
|
267
|
+
yield event_manager_1.DASHBORD_EVENT_MANAGER.afterSessionCommand(sessionId, commandName, driver, {
|
|
268
|
+
body: args,
|
|
269
|
+
method: 'POST',
|
|
270
|
+
path: `/${commandName}`,
|
|
271
|
+
originalUrl: `/${commandName}`,
|
|
272
|
+
}, {}, JSON.stringify({ value: { error: error.message || error }, sessionId }));
|
|
273
|
+
}
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
if (isHub && sessionId && span)
|
|
278
|
+
tracingService.endSpan(`${sessionId}:${commandName}`);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
isNoSuchElementError(error) {
|
|
283
|
+
var _a;
|
|
284
|
+
return (error.name === 'NoSuchElementError' ||
|
|
285
|
+
((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('NoSuchElement')) ||
|
|
286
|
+
error.status === 7);
|
|
287
|
+
}
|
|
288
|
+
handleOmniVisionSearch(sessionId, driver, commandName, strategy, selector) {
|
|
289
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
290
|
+
const omniService = typedi_1.Container.get(OmniVisionService_1.OmniVisionService);
|
|
291
|
+
let results = [];
|
|
292
|
+
if (strategy === '-custom:ai-text')
|
|
293
|
+
results = yield omniService.findByText(driver, selector);
|
|
294
|
+
else if (strategy === '-custom:ai-icon') {
|
|
295
|
+
const match = yield omniService.findByIcon(driver, selector);
|
|
296
|
+
if (match)
|
|
297
|
+
results = [match];
|
|
298
|
+
}
|
|
299
|
+
const appiumResults = results.map((r) => ({
|
|
300
|
+
ELEMENT: r.id,
|
|
301
|
+
'element-6066-11e4-a52e-4f735466cecf': r.id,
|
|
302
|
+
}));
|
|
303
|
+
if (commandName === 'findElement') {
|
|
304
|
+
if (appiumResults.length === 0)
|
|
305
|
+
throw new Error('NoSuchElement: AI Vision failed to find matching element');
|
|
306
|
+
return appiumResults[0];
|
|
307
|
+
}
|
|
308
|
+
return appiumResults;
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
handleVirtualElementCommand(sessionId, driver, commandName, elementId, value) {
|
|
312
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
313
|
+
const omniService = typedi_1.Container.get(OmniVisionService_1.OmniVisionService);
|
|
314
|
+
const element = omniService.getVirtualElement(elementId);
|
|
315
|
+
if (!element)
|
|
316
|
+
throw new Error(`NoSuchElement: Virtual element ${elementId} not found`);
|
|
317
|
+
const centerX = Math.round(element.rect.x + element.rect.width / 2);
|
|
318
|
+
const centerY = Math.round(element.rect.y + element.rect.height / 2);
|
|
319
|
+
switch (commandName) {
|
|
320
|
+
case 'click':
|
|
321
|
+
yield driver.performActions([
|
|
322
|
+
{
|
|
323
|
+
type: 'pointer',
|
|
324
|
+
id: 'finger1',
|
|
325
|
+
parameters: { pointerType: 'touch' },
|
|
326
|
+
actions: [
|
|
327
|
+
{ type: 'pointerMove', duration: 0, x: centerX, y: centerY },
|
|
328
|
+
{ type: 'pointerDown', button: 0 },
|
|
329
|
+
{ type: 'pause', duration: 100 },
|
|
330
|
+
{ type: 'pointerUp', button: 0 },
|
|
331
|
+
],
|
|
332
|
+
},
|
|
333
|
+
]);
|
|
334
|
+
return null;
|
|
335
|
+
case 'getElementRect':
|
|
336
|
+
return element.rect;
|
|
337
|
+
case 'getElementLocation':
|
|
338
|
+
return { x: element.rect.x, y: element.rect.y };
|
|
339
|
+
case 'getElementSize':
|
|
340
|
+
return { width: element.rect.width, height: element.rect.height };
|
|
341
|
+
case 'elementDisplayed':
|
|
342
|
+
case 'elementEnabled':
|
|
343
|
+
return true;
|
|
344
|
+
case 'getText':
|
|
345
|
+
return element.text || '';
|
|
346
|
+
case 'setValue':
|
|
347
|
+
yield driver.performActions([
|
|
348
|
+
{
|
|
349
|
+
type: 'pointer',
|
|
350
|
+
id: 'finger1',
|
|
351
|
+
parameters: { pointerType: 'touch' },
|
|
352
|
+
actions: [
|
|
353
|
+
{ type: 'pointerMove', duration: 0, x: centerX, y: centerY },
|
|
354
|
+
{ type: 'pointerDown', button: 0 },
|
|
355
|
+
{ type: 'pause', duration: 100 },
|
|
356
|
+
{ type: 'pointerUp', button: 0 },
|
|
357
|
+
],
|
|
358
|
+
},
|
|
359
|
+
]);
|
|
360
|
+
try {
|
|
361
|
+
return yield driver.setValue(elementId, value);
|
|
362
|
+
}
|
|
363
|
+
catch (e) {
|
|
364
|
+
this.log.error(`setValue failed for virtual element ${elementId} on session ${sessionId}: ${e.message}`);
|
|
365
|
+
throw e;
|
|
366
|
+
}
|
|
367
|
+
default:
|
|
368
|
+
throw new Error(`Command ${commandName} not supported for visual elements`);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
triggerLearning(driver, args, response, sessionId) {
|
|
373
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
374
|
+
if (this.learningSessions.has(sessionId))
|
|
375
|
+
return;
|
|
376
|
+
this.learningSessions.add(sessionId);
|
|
377
|
+
const strategy = args[0];
|
|
378
|
+
const selector = args[1];
|
|
379
|
+
const elementId = response.ELEMENT || response['element-6066-11e4-a52e-4f735466cecf'];
|
|
380
|
+
if (!elementId || typeof selector !== 'string') {
|
|
381
|
+
this.learningSessions.delete(sessionId);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
385
|
+
try {
|
|
386
|
+
const etalonService = typedi_1.Container.get(HealEtalonService_1.HealEtalonService);
|
|
387
|
+
// CRITICAL PERFORMANCE OPTIMIZATION:
|
|
388
|
+
// Only trigger the heavy metadata collection if we don't already have an etalon for this selector.
|
|
389
|
+
// Collecting page source and element rects for every single action is too CPU-intensive.
|
|
390
|
+
const existing = yield etalonService.getSignature(selector);
|
|
391
|
+
if (existing) {
|
|
392
|
+
this.log.debug(`[Learning] Etalon already exists for selector: ${selector}. Skipping...`);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const anchors = [
|
|
396
|
+
'content-desc',
|
|
397
|
+
'resource-id',
|
|
398
|
+
'text',
|
|
399
|
+
'name',
|
|
400
|
+
'id',
|
|
401
|
+
'hint',
|
|
402
|
+
'label',
|
|
403
|
+
'value', // iOS-specific identity attributes
|
|
404
|
+
];
|
|
405
|
+
const nodeAttrs = [];
|
|
406
|
+
for (const attr of anchors) {
|
|
407
|
+
try {
|
|
408
|
+
const val = yield driver.getElementAttribute(elementId, attr);
|
|
409
|
+
if (val)
|
|
410
|
+
nodeAttrs.push({ name: attr, value: val });
|
|
411
|
+
}
|
|
412
|
+
catch (e) {
|
|
413
|
+
// Silently ignore: attribute may not exist or be inaccessible
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// Capture spatial coordinates from element rect for position-based healing
|
|
417
|
+
try {
|
|
418
|
+
const rect = yield driver.getElementRect(elementId);
|
|
419
|
+
if (rect) {
|
|
420
|
+
if (rect.x !== undefined)
|
|
421
|
+
nodeAttrs.push({ name: 'x', value: String(Math.round(rect.x)) });
|
|
422
|
+
if (rect.y !== undefined)
|
|
423
|
+
nodeAttrs.push({ name: 'y', value: String(Math.round(rect.y)) });
|
|
424
|
+
if (rect.width !== undefined)
|
|
425
|
+
nodeAttrs.push({ name: 'width', value: String(Math.round(rect.width)) });
|
|
426
|
+
if (rect.height !== undefined)
|
|
427
|
+
nodeAttrs.push({ name: 'height', value: String(Math.round(rect.height)) });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
catch (e) {
|
|
431
|
+
// Silently ignore: rect capture is optional
|
|
432
|
+
}
|
|
433
|
+
let nodeName = 'XCUIElementTypeAny'; // Default for IOS, will be overridden
|
|
434
|
+
try {
|
|
435
|
+
if (typeof driver.getElementTagName === 'function') {
|
|
436
|
+
nodeName = (yield driver.getElementTagName(elementId)) || nodeName;
|
|
437
|
+
}
|
|
438
|
+
else if (typeof driver.getName === 'function') {
|
|
439
|
+
nodeName = (yield driver.getName(elementId)) || nodeName;
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
// Check if it's android or ios to provide a better default
|
|
443
|
+
const caps = yield driver.getCapabilities();
|
|
444
|
+
const platform = (caps.platformName || caps.platform || 'ios').toLowerCase();
|
|
445
|
+
nodeName = platform === 'android' ? 'android.view.View' : 'XCUIElementTypeAny';
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
catch (e) {
|
|
449
|
+
this.log.debug(`[Learning] Failed to get tag name for element ${elementId}. Using default.`);
|
|
450
|
+
}
|
|
451
|
+
// Final safety check to ensure nodeName is a valid string
|
|
452
|
+
if (!nodeName)
|
|
453
|
+
nodeName = 'Unknown';
|
|
454
|
+
// Path capture logic
|
|
455
|
+
let resiliotreePathJson = null;
|
|
456
|
+
try {
|
|
457
|
+
const { JSDOMParser, Path } = yield Promise.resolve().then(() => __importStar(require('resiliotree')));
|
|
458
|
+
const pageSource = yield driver.getPageSource();
|
|
459
|
+
const rootNode = new JSDOMParser().parse(pageSource);
|
|
460
|
+
const foundNode = this.findMatchingNode(rootNode, nodeName, nodeAttrs);
|
|
461
|
+
if (foundNode) {
|
|
462
|
+
const pathNodes = [];
|
|
463
|
+
let curr = foundNode;
|
|
464
|
+
while (curr) {
|
|
465
|
+
pathNodes.unshift(curr);
|
|
466
|
+
curr = curr.parent;
|
|
467
|
+
}
|
|
468
|
+
resiliotreePathJson = new Path(pathNodes).toJSON();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
catch (e) {
|
|
472
|
+
// Silently ignore: path capture is optional for learning
|
|
473
|
+
}
|
|
474
|
+
yield etalonService.saveSignature(strategy, selector, { nodeName, attributes: nodeAttrs }, resiliotreePathJson);
|
|
475
|
+
}
|
|
476
|
+
catch (err) {
|
|
477
|
+
this.log.debug(`[Learning] Failed: ${err.message}`);
|
|
478
|
+
}
|
|
479
|
+
finally {
|
|
480
|
+
this.learningSessions.delete(sessionId);
|
|
481
|
+
}
|
|
482
|
+
}))();
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
findMatchingNode(root, tag, attributes) {
|
|
486
|
+
const attrMap = new Map(attributes.map((a) => [a.name.toLowerCase(), a.value]));
|
|
487
|
+
const queue = [root];
|
|
488
|
+
while (queue.length > 0) {
|
|
489
|
+
const node = queue.shift();
|
|
490
|
+
if (node.tag.toLowerCase() === tag.toLowerCase()) {
|
|
491
|
+
let matchCount = 0;
|
|
492
|
+
for (const [name, value] of attrMap) {
|
|
493
|
+
if (node.otherAttributes.get(name) === value ||
|
|
494
|
+
node.id === value ||
|
|
495
|
+
node.classes.has(value))
|
|
496
|
+
matchCount++;
|
|
497
|
+
}
|
|
498
|
+
if (matchCount > 0)
|
|
499
|
+
return node;
|
|
500
|
+
}
|
|
501
|
+
if (node.children)
|
|
502
|
+
queue.push(...node.children);
|
|
503
|
+
}
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
logHealingEvent(sessionId, commandName, driver, args, healed) {
|
|
507
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
508
|
+
yield event_manager_1.DASHBORD_EVENT_MANAGER.afterSessionCommand(sessionId, commandName, driver, {
|
|
509
|
+
body: args,
|
|
510
|
+
method: 'POST',
|
|
511
|
+
path: `/${commandName}`,
|
|
512
|
+
originalUrl: `/${commandName}`,
|
|
513
|
+
}, {}, JSON.stringify({ value: { ELEMENT: healed.id }, sessionId }), {
|
|
514
|
+
originalSelector: args[1],
|
|
515
|
+
healedSelector: healed.recommendedSelector,
|
|
516
|
+
confidence: healed.confidence,
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
exports.CommandInterceptor = CommandInterceptor;
|
|
522
|
+
exports.CommandInterceptor = CommandInterceptor = __decorate([
|
|
523
|
+
(0, typedi_1.Service)()
|
|
524
|
+
], CommandInterceptor);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DefaultPluginArgs = void 0;
|
|
7
|
+
/* eslint-disable */
|
|
8
|
+
/**
|
|
9
|
+
* This file was automatically generated by json-schema-to-typescript.
|
|
10
|
+
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
|
11
|
+
* and run "npm run build:schema" to regenerate this file.
|
|
12
|
+
*/
|
|
13
|
+
const ip_1 = __importDefault(require("ip"));
|
|
14
|
+
exports.DefaultPluginArgs = {
|
|
15
|
+
platform: 'both',
|
|
16
|
+
iosDeviceType: 'both',
|
|
17
|
+
androidDeviceType: 'both',
|
|
18
|
+
skipChromeDownload: true,
|
|
19
|
+
hub: undefined,
|
|
20
|
+
maxSessions: 8,
|
|
21
|
+
derivedDataPath: undefined,
|
|
22
|
+
adbRemote: [],
|
|
23
|
+
remoteMachineProxyIP: undefined,
|
|
24
|
+
proxy: undefined,
|
|
25
|
+
emulators: [],
|
|
26
|
+
simulators: [],
|
|
27
|
+
deviceAvailabilityTimeoutMs: 300000,
|
|
28
|
+
deviceAvailabilityQueryIntervalMs: 10000,
|
|
29
|
+
sendNodeDevicesToHubIntervalMs: 30000,
|
|
30
|
+
checkStaleDevicesIntervalMs: 30000,
|
|
31
|
+
checkBlockedDevicesIntervalMs: 30000,
|
|
32
|
+
newCommandTimeoutSec: 60,
|
|
33
|
+
cloud: undefined,
|
|
34
|
+
bindHostOrIp: ip_1.default.address(),
|
|
35
|
+
enableDashboard: false,
|
|
36
|
+
bootedSimulators: false,
|
|
37
|
+
healthCheckIntervalMs: 86400000,
|
|
38
|
+
healthCheckSchedule: undefined,
|
|
39
|
+
removeDevicesFromDatabaseBeforeRunningThePlugin: false,
|
|
40
|
+
databaseProvider: undefined,
|
|
41
|
+
databaseUrl: undefined,
|
|
42
|
+
aiProvider: undefined,
|
|
43
|
+
aiModel: undefined,
|
|
44
|
+
aiBaseUrl: undefined,
|
|
45
|
+
geminiApiKey: undefined,
|
|
46
|
+
openaiApiKey: undefined,
|
|
47
|
+
anthropicApiKey: undefined,
|
|
48
|
+
enableSelfHealing: true,
|
|
49
|
+
buildCleanupDays: 30,
|
|
50
|
+
buildCleanupMaxCount: 100,
|
|
51
|
+
buildCleanupSchedule: '0 0 * * *',
|
|
52
|
+
deleteBuildAssets: true,
|
|
53
|
+
sessionHeartbeatIntervalMs: 30000,
|
|
54
|
+
enableJsonLogging: false,
|
|
55
|
+
};
|