@devicecloud.dev/dcd 4.4.2 → 4.4.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/dist/commands/live.d.ts +22 -0
- package/dist/commands/live.js +207 -0
- package/dist/config/flags/device.flags.js +1 -1
- package/dist/methods.js +9 -15
- package/dist/services/execution-plan.service.d.ts +8 -0
- package/dist/services/execution-plan.service.js +12 -2
- package/dist/services/test-submission.service.js +6 -1
- package/oclif.manifest.json +88 -2
- package/package.json +2 -2
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Live extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
apiKey: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
apiUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
'app-binary-id': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
platform: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
'session-id': import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
yaml: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
};
|
|
13
|
+
static hidden: boolean;
|
|
14
|
+
static strict: boolean;
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
private execTest;
|
|
17
|
+
private getFrontendUrl;
|
|
18
|
+
private getStatus;
|
|
19
|
+
private installBinary;
|
|
20
|
+
private startSession;
|
|
21
|
+
private stopSession;
|
|
22
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
const api_gateway_1 = require("../gateways/api-gateway");
|
|
6
|
+
const styling_1 = require("../utils/styling");
|
|
7
|
+
class Live extends core_1.Command {
|
|
8
|
+
static description = 'Start and interact with a live device session';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> <%= command.id %> start',
|
|
11
|
+
'<%= config.bin %> <%= command.id %> start --platform android --app-binary-id abc-123',
|
|
12
|
+
'<%= config.bin %> <%= command.id %> install --session-id 42 --app-binary-id abc-123',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> exec --session-id 42 --yaml "- launchApp"',
|
|
14
|
+
'<%= config.bin %> <%= command.id %> stop --session-id 42',
|
|
15
|
+
'<%= config.bin %> <%= command.id %> status --session-id 42',
|
|
16
|
+
];
|
|
17
|
+
static flags = {
|
|
18
|
+
apiKey: constants_1.flags.apiKey,
|
|
19
|
+
apiUrl: constants_1.flags.apiUrl,
|
|
20
|
+
'app-binary-id': core_1.Flags.string({
|
|
21
|
+
description: 'Binary upload ID to install on the device',
|
|
22
|
+
}),
|
|
23
|
+
platform: core_1.Flags.string({
|
|
24
|
+
default: 'android',
|
|
25
|
+
description: 'Device platform',
|
|
26
|
+
options: ['android', 'ios'],
|
|
27
|
+
}),
|
|
28
|
+
'session-id': core_1.Flags.integer({
|
|
29
|
+
description: 'Live session ID (required for install, exec, stop, status)',
|
|
30
|
+
}),
|
|
31
|
+
yaml: core_1.Flags.string({
|
|
32
|
+
description: 'Maestro YAML commands to execute (for exec subcommand)',
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
static hidden = true;
|
|
36
|
+
static strict = false;
|
|
37
|
+
async run() {
|
|
38
|
+
const { argv, flags } = await this.parse(Live);
|
|
39
|
+
const { apiKey: apiKeyFlag, apiUrl, 'app-binary-id': binaryId, platform, 'session-id': sessionId, yaml, } = flags;
|
|
40
|
+
const subcommand = argv[0];
|
|
41
|
+
if (!subcommand || !['exec', 'install', 'start', 'status', 'stop'].includes(subcommand)) {
|
|
42
|
+
this.log((0, styling_1.sectionHeader)('Live Session Commands'));
|
|
43
|
+
this.log(` ${styling_1.colors.bold('start')} Start a new live device session`);
|
|
44
|
+
this.log(` ${styling_1.colors.bold('install')} Install a binary on the device`);
|
|
45
|
+
this.log(` ${styling_1.colors.bold('exec')} Execute Maestro YAML commands`);
|
|
46
|
+
this.log(` ${styling_1.colors.bold('stop')} Stop a live session`);
|
|
47
|
+
this.log(` ${styling_1.colors.bold('status')} Get session status`);
|
|
48
|
+
this.log('');
|
|
49
|
+
this.log(` Run ${styling_1.colors.highlight('dcd live <command> --help')} for details`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const apiKey = apiKeyFlag || process.env.DEVICE_CLOUD_API_KEY;
|
|
53
|
+
if (!apiKey) {
|
|
54
|
+
this.error('API key is required. Provide via --api-key flag or DEVICE_CLOUD_API_KEY environment variable.');
|
|
55
|
+
}
|
|
56
|
+
switch (subcommand) {
|
|
57
|
+
case 'start': {
|
|
58
|
+
await this.startSession(apiUrl, apiKey, platform, binaryId);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case 'install': {
|
|
62
|
+
if (!sessionId)
|
|
63
|
+
this.error('--session-id is required for install');
|
|
64
|
+
if (!binaryId)
|
|
65
|
+
this.error('--app-binary-id is required for install');
|
|
66
|
+
await this.installBinary(apiUrl, apiKey, sessionId, binaryId);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case 'exec': {
|
|
70
|
+
if (!sessionId)
|
|
71
|
+
this.error('--session-id is required for exec');
|
|
72
|
+
if (!yaml)
|
|
73
|
+
this.error('--yaml is required for exec');
|
|
74
|
+
await this.execTest(apiUrl, apiKey, sessionId, yaml);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case 'stop': {
|
|
78
|
+
if (!sessionId)
|
|
79
|
+
this.error('--session-id is required for stop');
|
|
80
|
+
await this.stopSession(apiUrl, apiKey, sessionId);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case 'status': {
|
|
84
|
+
if (!sessionId)
|
|
85
|
+
this.error('--session-id is required for status');
|
|
86
|
+
await this.getStatus(apiUrl, apiKey, sessionId);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
default: {
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async execTest(apiUrl, apiKey, sessionId, yaml) {
|
|
95
|
+
this.log(`${styling_1.symbols.running} Executing commands on session ${sessionId}...`);
|
|
96
|
+
const res = await fetch(`${apiUrl}/live/${sessionId}/exec`, {
|
|
97
|
+
body: JSON.stringify({ yaml }),
|
|
98
|
+
headers: {
|
|
99
|
+
'content-type': 'application/json',
|
|
100
|
+
'x-app-api-key': apiKey,
|
|
101
|
+
},
|
|
102
|
+
method: 'POST',
|
|
103
|
+
});
|
|
104
|
+
if (!res.ok) {
|
|
105
|
+
await api_gateway_1.ApiGateway.handleApiError(res, 'Failed to execute test');
|
|
106
|
+
}
|
|
107
|
+
const result = (await res.json());
|
|
108
|
+
if (result.success) {
|
|
109
|
+
this.log(`${styling_1.symbols.success} Command executed successfully`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.log(`${styling_1.symbols.error} Command failed`);
|
|
113
|
+
}
|
|
114
|
+
if (result.output) {
|
|
115
|
+
this.log((0, styling_1.sectionHeader)('Output'));
|
|
116
|
+
this.log(result.output);
|
|
117
|
+
}
|
|
118
|
+
if (result.error) {
|
|
119
|
+
this.log((0, styling_1.sectionHeader)('Error'));
|
|
120
|
+
this.log(styling_1.colors.error(result.error));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
getFrontendUrl(apiUrl) {
|
|
124
|
+
if (apiUrl.includes('localhost:8000'))
|
|
125
|
+
return 'http://localhost:5173';
|
|
126
|
+
if (apiUrl.includes('api.dev.'))
|
|
127
|
+
return 'https://dev.console.devicecloud.dev';
|
|
128
|
+
return 'https://console.devicecloud.dev';
|
|
129
|
+
}
|
|
130
|
+
async getStatus(apiUrl, apiKey, sessionId) {
|
|
131
|
+
const res = await fetch(`${apiUrl}/live/${sessionId}`, {
|
|
132
|
+
headers: {
|
|
133
|
+
'x-app-api-key': apiKey,
|
|
134
|
+
},
|
|
135
|
+
method: 'GET',
|
|
136
|
+
});
|
|
137
|
+
if (!res.ok) {
|
|
138
|
+
await api_gateway_1.ApiGateway.handleApiError(res, 'Failed to get session status');
|
|
139
|
+
}
|
|
140
|
+
const session = (await res.json());
|
|
141
|
+
this.log((0, styling_1.sectionHeader)('Live Session'));
|
|
142
|
+
this.log(` ${styling_1.colors.dim('Session ID:')} ${styling_1.colors.highlight(String(session.id))}`);
|
|
143
|
+
this.log(` ${styling_1.colors.dim('Platform:')} ${session.platform}`);
|
|
144
|
+
this.log(` ${styling_1.colors.dim('Status:')} ${session.status}`);
|
|
145
|
+
if (session.binary_upload_id) {
|
|
146
|
+
this.log(` ${styling_1.colors.dim('Binary:')} ${session.binary_upload_id}`);
|
|
147
|
+
}
|
|
148
|
+
this.log(` ${styling_1.colors.dim('Created:')} ${new Date(session.created_at).toLocaleString()}`);
|
|
149
|
+
}
|
|
150
|
+
async installBinary(apiUrl, apiKey, sessionId, binaryId) {
|
|
151
|
+
this.log(`${styling_1.symbols.running} Installing binary ${styling_1.colors.highlight(binaryId)} on session ${sessionId}...`);
|
|
152
|
+
const res = await fetch(`${apiUrl}/live/${sessionId}/install`, {
|
|
153
|
+
body: JSON.stringify({ binaryUploadId: binaryId }),
|
|
154
|
+
headers: {
|
|
155
|
+
'content-type': 'application/json',
|
|
156
|
+
'x-app-api-key': apiKey,
|
|
157
|
+
},
|
|
158
|
+
method: 'POST',
|
|
159
|
+
});
|
|
160
|
+
if (!res.ok) {
|
|
161
|
+
await api_gateway_1.ApiGateway.handleApiError(res, 'Failed to install binary');
|
|
162
|
+
}
|
|
163
|
+
this.log(`${styling_1.symbols.success} Binary installed successfully`);
|
|
164
|
+
}
|
|
165
|
+
async startSession(apiUrl, apiKey, platform, binaryId) {
|
|
166
|
+
this.log(`${styling_1.symbols.running} Starting ${platform} live session...`);
|
|
167
|
+
const res = await fetch(`${apiUrl}/live`, {
|
|
168
|
+
body: JSON.stringify({
|
|
169
|
+
binaryUploadId: binaryId,
|
|
170
|
+
platform,
|
|
171
|
+
}),
|
|
172
|
+
headers: {
|
|
173
|
+
'content-type': 'application/json',
|
|
174
|
+
'x-app-api-key': apiKey,
|
|
175
|
+
},
|
|
176
|
+
method: 'POST',
|
|
177
|
+
});
|
|
178
|
+
if (!res.ok) {
|
|
179
|
+
await api_gateway_1.ApiGateway.handleApiError(res, 'Failed to start live session');
|
|
180
|
+
}
|
|
181
|
+
const session = (await res.json());
|
|
182
|
+
const frontendUrl = this.getFrontendUrl(apiUrl);
|
|
183
|
+
this.log(`${styling_1.symbols.success} Live session started`);
|
|
184
|
+
this.log(` ${styling_1.colors.dim('Session ID:')} ${styling_1.colors.highlight(String(session.id))}`);
|
|
185
|
+
this.log(` ${styling_1.colors.dim('Platform:')} ${session.platform}`);
|
|
186
|
+
this.log(` ${styling_1.colors.dim('Status:')} ${session.status}`);
|
|
187
|
+
this.log(` ${styling_1.colors.dim('Console:')} ${styling_1.colors.highlight(`${frontendUrl}/live?session=${session.id}`)}`);
|
|
188
|
+
this.log('');
|
|
189
|
+
this.log(` ${styling_1.colors.dim('Install a binary:')} ${styling_1.colors.highlight(`dcd live install --session-id ${session.id} --app-binary-id <id>`)}`);
|
|
190
|
+
this.log(` ${styling_1.colors.dim('Run a command:')} ${styling_1.colors.highlight(`dcd live exec --session-id ${session.id} --yaml "- launchApp"`)}`);
|
|
191
|
+
this.log(` ${styling_1.colors.dim('Stop session:')} ${styling_1.colors.highlight(`dcd live stop --session-id ${session.id}`)}`);
|
|
192
|
+
}
|
|
193
|
+
async stopSession(apiUrl, apiKey, sessionId) {
|
|
194
|
+
this.log(`${styling_1.symbols.running} Stopping session ${sessionId}...`);
|
|
195
|
+
const res = await fetch(`${apiUrl}/live/${sessionId}`, {
|
|
196
|
+
headers: {
|
|
197
|
+
'x-app-api-key': apiKey,
|
|
198
|
+
},
|
|
199
|
+
method: 'DELETE',
|
|
200
|
+
});
|
|
201
|
+
if (!res.ok) {
|
|
202
|
+
await api_gateway_1.ApiGateway.handleApiError(res, 'Failed to stop session');
|
|
203
|
+
}
|
|
204
|
+
this.log(`${styling_1.symbols.success} Session stopped`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.default = Live;
|
|
@@ -49,6 +49,6 @@ exports.deviceFlags = {
|
|
|
49
49
|
}),
|
|
50
50
|
'disable-animations': core_1.Flags.boolean({
|
|
51
51
|
default: false,
|
|
52
|
-
description: '
|
|
52
|
+
description: 'Disable device animations during test execution. On Android, disables system animation scales. On iOS, enables Reduce Motion. Reduces CPU load and may improve test reliability.',
|
|
53
53
|
}),
|
|
54
54
|
};
|
package/dist/methods.js
CHANGED
|
@@ -495,33 +495,27 @@ async function performUpload(config) {
|
|
|
495
495
|
finalPath,
|
|
496
496
|
});
|
|
497
497
|
let lastError = backblazeResult.error;
|
|
498
|
-
//
|
|
498
|
+
// Always upload to Supabase (re-enabled as always-on alongside Backblaze)
|
|
499
499
|
let supabaseResult = { error: null, success: false };
|
|
500
|
-
if (
|
|
501
|
-
|
|
502
|
-
console.log('[DEBUG] Backblaze upload failed, falling back to Supabase...');
|
|
503
|
-
}
|
|
504
|
-
supabaseResult = await uploadToSupabase(env, tempPath, file, debug);
|
|
505
|
-
if (!supabaseResult.success && supabaseResult.error) {
|
|
506
|
-
lastError = supabaseResult.error;
|
|
507
|
-
}
|
|
500
|
+
if (debug) {
|
|
501
|
+
console.log('[DEBUG] Uploading to Supabase...');
|
|
508
502
|
}
|
|
509
|
-
|
|
510
|
-
|
|
503
|
+
supabaseResult = await uploadToSupabase(env, tempPath, file, debug);
|
|
504
|
+
if (!supabaseResult.success && supabaseResult.error) {
|
|
505
|
+
lastError = supabaseResult.error;
|
|
511
506
|
}
|
|
512
507
|
// Validate results
|
|
513
508
|
validateUploadResults(supabaseResult.success, backblazeResult.success, lastError, b2, debug);
|
|
514
509
|
// Log upload summary
|
|
515
510
|
if (debug) {
|
|
516
|
-
|
|
517
|
-
console.log(`[DEBUG] Upload summary - Backblaze: ${backblazeResult.success ? '✓' : '✗'}, Supabase: ${supabaseResult.success ? '✓' : '✗ (fallback ' + (backblazeResult.success ? 'not needed' : 'attempted') + ')'}`);
|
|
511
|
+
console.log(`[DEBUG] Upload summary - Backblaze: ${backblazeResult.success ? '✓' : '✗'}, Supabase: ${supabaseResult.success ? '✓' : '✗'}`);
|
|
518
512
|
console.log('[DEBUG] Finalizing upload...');
|
|
519
513
|
console.log(`[DEBUG] Target endpoint: ${apiUrl}/uploads/finaliseUpload`);
|
|
520
514
|
console.log(`[DEBUG] Uploaded to staging path: ${tempPath}`);
|
|
521
515
|
console.log(`[DEBUG] API will move to final path: ${finalPath}`);
|
|
522
516
|
console.log(`[DEBUG] Backblaze upload status: ${backblazeResult.success ? 'SUCCESS' : 'FAILED'}`);
|
|
523
|
-
console.log(`[DEBUG] Supabase upload status: ${supabaseResult.success ? 'SUCCESS
|
|
524
|
-
if (
|
|
517
|
+
console.log(`[DEBUG] Supabase upload status: ${supabaseResult.success ? 'SUCCESS' : 'FAILED'}`);
|
|
518
|
+
if (!backblazeResult.success && supabaseResult.success)
|
|
525
519
|
console.log('[DEBUG] ⚠ Warning: File only exists in Supabase (Backblaze failed)');
|
|
526
520
|
}
|
|
527
521
|
// Finalize upload
|
|
@@ -14,6 +14,14 @@ interface IWorkspaceConfig {
|
|
|
14
14
|
includeTags?: null | string[];
|
|
15
15
|
local?: ILocal | null;
|
|
16
16
|
notifications?: INotificationsConfig;
|
|
17
|
+
platform?: {
|
|
18
|
+
android?: {
|
|
19
|
+
disableAnimations?: boolean;
|
|
20
|
+
};
|
|
21
|
+
ios?: {
|
|
22
|
+
disableAnimations?: boolean;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
17
25
|
}
|
|
18
26
|
/** Local execution configuration */
|
|
19
27
|
interface ILocal {
|
|
@@ -92,9 +92,10 @@ function extractDeviceCloudOverrides(config) {
|
|
|
92
92
|
/**
|
|
93
93
|
* Generate execution plan for a single flow file
|
|
94
94
|
* @param normalizedInput - Normalized path to the flow file
|
|
95
|
+
* @param configFile - Optional custom config file path
|
|
95
96
|
* @returns Execution plan for the single file with dependencies
|
|
96
97
|
*/
|
|
97
|
-
async function planSingleFile(normalizedInput) {
|
|
98
|
+
async function planSingleFile(normalizedInput, configFile) {
|
|
98
99
|
if (normalizedInput.endsWith('config.yaml') ||
|
|
99
100
|
normalizedInput.endsWith('config.yml')) {
|
|
100
101
|
throw new Error('If using config.yaml, pass the workspace folder path, not the config file or a custom path via --config');
|
|
@@ -106,6 +107,14 @@ async function planSingleFile(normalizedInput) {
|
|
|
106
107
|
flowMetadata[normalizedInput] = config;
|
|
107
108
|
flowOverrides[normalizedInput] = extractDeviceCloudOverrides(config);
|
|
108
109
|
}
|
|
110
|
+
let workspaceConfig;
|
|
111
|
+
if (configFile) {
|
|
112
|
+
const configFilePath = path.resolve(process.cwd(), configFile);
|
|
113
|
+
if (!fs.existsSync(configFilePath)) {
|
|
114
|
+
throw new Error(`Config file does not exist: ${configFilePath}`);
|
|
115
|
+
}
|
|
116
|
+
workspaceConfig = (0, execution_plan_utils_1.readYamlFileAsJson)(configFilePath);
|
|
117
|
+
}
|
|
109
118
|
const checkedDependancies = await checkDependencies(normalizedInput);
|
|
110
119
|
return {
|
|
111
120
|
flowMetadata,
|
|
@@ -113,6 +122,7 @@ async function planSingleFile(normalizedInput) {
|
|
|
113
122
|
flowsToRun: [normalizedInput],
|
|
114
123
|
referencedFiles: [...new Set(checkedDependancies)],
|
|
115
124
|
totalFlowFiles: 1,
|
|
125
|
+
workspaceConfig,
|
|
116
126
|
};
|
|
117
127
|
}
|
|
118
128
|
/**
|
|
@@ -211,7 +221,7 @@ async function plan(options) {
|
|
|
211
221
|
throw new Error(`Flow path does not exist: ${path.resolve(normalizedInput)}`);
|
|
212
222
|
}
|
|
213
223
|
if (fs.lstatSync(normalizedInput).isFile()) {
|
|
214
|
-
return planSingleFile(normalizedInput);
|
|
224
|
+
return planSingleFile(normalizedInput, configFile);
|
|
215
225
|
}
|
|
216
226
|
let unfilteredFlowFiles = await (0, execution_plan_utils_1.readDirectory)(normalizedInput, execution_plan_utils_1.isFlowFile);
|
|
217
227
|
if (unfilteredFlowFiles.length === 0) {
|
|
@@ -67,6 +67,11 @@ class TestSubmissionService {
|
|
|
67
67
|
testFormData.set('env', JSON.stringify(envObject));
|
|
68
68
|
// Note: googlePlay is now included in configPayload below instead of as a separate field
|
|
69
69
|
// to work around a FormData parsing issue in the API
|
|
70
|
+
const targetPlatform = iOSDevice || iOSVersion ? 'ios' : 'android';
|
|
71
|
+
const configYamlDisableAnimations = targetPlatform === 'ios'
|
|
72
|
+
? Boolean(workspaceConfig?.platform?.ios?.disableAnimations)
|
|
73
|
+
: Boolean(workspaceConfig?.platform?.android?.disableAnimations);
|
|
74
|
+
const effectiveDisableAnimations = disableAnimations || configYamlDisableAnimations;
|
|
70
75
|
const configPayload = {
|
|
71
76
|
allExcludeTags,
|
|
72
77
|
allIncludeTags,
|
|
@@ -83,7 +88,7 @@ class TestSubmissionService {
|
|
|
83
88
|
report,
|
|
84
89
|
showCrosshairs,
|
|
85
90
|
maestroChromeOnboarding,
|
|
86
|
-
disableAnimations,
|
|
91
|
+
disableAnimations: effectiveDisableAnimations,
|
|
87
92
|
version: cliVersion,
|
|
88
93
|
};
|
|
89
94
|
testFormData.set('config', JSON.stringify(configPayload));
|
package/oclif.manifest.json
CHANGED
|
@@ -332,7 +332,7 @@
|
|
|
332
332
|
"type": "boolean"
|
|
333
333
|
},
|
|
334
334
|
"disable-animations": {
|
|
335
|
-
"description": "
|
|
335
|
+
"description": "Disable device animations during test execution. On Android, disables system animation scales. On iOS, enables Reduce Motion. Reduces CPU load and may improve test reliability.",
|
|
336
336
|
"name": "disable-animations",
|
|
337
337
|
"allowNo": false,
|
|
338
338
|
"type": "boolean"
|
|
@@ -717,6 +717,92 @@
|
|
|
717
717
|
"list.js"
|
|
718
718
|
]
|
|
719
719
|
},
|
|
720
|
+
"live": {
|
|
721
|
+
"aliases": [],
|
|
722
|
+
"args": {},
|
|
723
|
+
"description": "Start and interact with a live device session",
|
|
724
|
+
"examples": [
|
|
725
|
+
"<%= config.bin %> <%= command.id %> start",
|
|
726
|
+
"<%= config.bin %> <%= command.id %> start --platform android --app-binary-id abc-123",
|
|
727
|
+
"<%= config.bin %> <%= command.id %> install --session-id 42 --app-binary-id abc-123",
|
|
728
|
+
"<%= config.bin %> <%= command.id %> exec --session-id 42 --yaml \"- launchApp\"",
|
|
729
|
+
"<%= config.bin %> <%= command.id %> stop --session-id 42",
|
|
730
|
+
"<%= config.bin %> <%= command.id %> status --session-id 42"
|
|
731
|
+
],
|
|
732
|
+
"flags": {
|
|
733
|
+
"apiKey": {
|
|
734
|
+
"aliases": [
|
|
735
|
+
"api-key"
|
|
736
|
+
],
|
|
737
|
+
"description": "API key for devicecloud.dev (find this in the console UI). You can also set the DEVICE_CLOUD_API_KEY environment variable.",
|
|
738
|
+
"name": "apiKey",
|
|
739
|
+
"hasDynamicHelp": false,
|
|
740
|
+
"multiple": false,
|
|
741
|
+
"type": "option"
|
|
742
|
+
},
|
|
743
|
+
"apiUrl": {
|
|
744
|
+
"aliases": [
|
|
745
|
+
"api-url",
|
|
746
|
+
"apiURL"
|
|
747
|
+
],
|
|
748
|
+
"description": "API base URL",
|
|
749
|
+
"hidden": true,
|
|
750
|
+
"name": "apiUrl",
|
|
751
|
+
"default": "https://api.devicecloud.dev",
|
|
752
|
+
"hasDynamicHelp": false,
|
|
753
|
+
"multiple": false,
|
|
754
|
+
"type": "option"
|
|
755
|
+
},
|
|
756
|
+
"app-binary-id": {
|
|
757
|
+
"description": "Binary upload ID to install on the device",
|
|
758
|
+
"name": "app-binary-id",
|
|
759
|
+
"hasDynamicHelp": false,
|
|
760
|
+
"multiple": false,
|
|
761
|
+
"type": "option"
|
|
762
|
+
},
|
|
763
|
+
"platform": {
|
|
764
|
+
"description": "Device platform",
|
|
765
|
+
"name": "platform",
|
|
766
|
+
"default": "android",
|
|
767
|
+
"hasDynamicHelp": false,
|
|
768
|
+
"multiple": false,
|
|
769
|
+
"options": [
|
|
770
|
+
"android",
|
|
771
|
+
"ios"
|
|
772
|
+
],
|
|
773
|
+
"type": "option"
|
|
774
|
+
},
|
|
775
|
+
"session-id": {
|
|
776
|
+
"description": "Live session ID (required for install, exec, stop, status)",
|
|
777
|
+
"name": "session-id",
|
|
778
|
+
"hasDynamicHelp": false,
|
|
779
|
+
"multiple": false,
|
|
780
|
+
"type": "option"
|
|
781
|
+
},
|
|
782
|
+
"yaml": {
|
|
783
|
+
"description": "Maestro YAML commands to execute (for exec subcommand)",
|
|
784
|
+
"name": "yaml",
|
|
785
|
+
"hasDynamicHelp": false,
|
|
786
|
+
"multiple": false,
|
|
787
|
+
"type": "option"
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
"hasDynamicHelp": false,
|
|
791
|
+
"hidden": true,
|
|
792
|
+
"hiddenAliases": [],
|
|
793
|
+
"id": "live",
|
|
794
|
+
"pluginAlias": "@devicecloud.dev/dcd",
|
|
795
|
+
"pluginName": "@devicecloud.dev/dcd",
|
|
796
|
+
"pluginType": "core",
|
|
797
|
+
"strict": false,
|
|
798
|
+
"enableJsonFlag": false,
|
|
799
|
+
"isESM": false,
|
|
800
|
+
"relativePath": [
|
|
801
|
+
"dist",
|
|
802
|
+
"commands",
|
|
803
|
+
"live.js"
|
|
804
|
+
]
|
|
805
|
+
},
|
|
720
806
|
"status": {
|
|
721
807
|
"aliases": [],
|
|
722
808
|
"args": {},
|
|
@@ -880,5 +966,5 @@
|
|
|
880
966
|
]
|
|
881
967
|
}
|
|
882
968
|
},
|
|
883
|
-
"version": "4.4.
|
|
969
|
+
"version": "4.4.4"
|
|
884
970
|
}
|
package/package.json
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"eslint-config-oclif-typescript": "^3.1.14",
|
|
37
37
|
"eslint-config-prettier": "^10.1.8",
|
|
38
38
|
"mocha": "^11.7.5",
|
|
39
|
-
"oclif": "^4.22.
|
|
39
|
+
"oclif": "^4.22.96",
|
|
40
40
|
"shx": "^0.4.0",
|
|
41
41
|
"ts-node": "^10.9.2",
|
|
42
42
|
"typescript": "^5.9.3"
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"type": "git",
|
|
70
70
|
"url": "https://devicecloud.dev"
|
|
71
71
|
},
|
|
72
|
-
"version": "4.4.
|
|
72
|
+
"version": "4.4.4",
|
|
73
73
|
"bugs": {
|
|
74
74
|
"url": "https://discord.gg/gm3mJwcNw8"
|
|
75
75
|
},
|