@devicecloud.dev/dcd 5.0.0-beta.0 → 5.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +35 -0
  2. package/dist/commands/artifacts.d.ts +28 -28
  3. package/dist/commands/artifacts.js +20 -23
  4. package/dist/commands/cloud.d.ts +57 -57
  5. package/dist/commands/cloud.js +173 -186
  6. package/dist/commands/list.d.ts +22 -22
  7. package/dist/commands/list.js +36 -38
  8. package/dist/commands/live.js +134 -127
  9. package/dist/commands/login.d.ts +11 -11
  10. package/dist/commands/login.js +46 -44
  11. package/dist/commands/logout.js +16 -18
  12. package/dist/commands/status.d.ts +11 -11
  13. package/dist/commands/status.js +45 -43
  14. package/dist/commands/switch-org.d.ts +7 -7
  15. package/dist/commands/switch-org.js +19 -21
  16. package/dist/commands/upgrade.js +29 -31
  17. package/dist/commands/upload.d.ts +10 -10
  18. package/dist/commands/upload.js +42 -43
  19. package/dist/commands/whoami.js +17 -20
  20. package/dist/config/environments.js +6 -12
  21. package/dist/config/flags/api.flags.js +1 -4
  22. package/dist/config/flags/binary.flags.js +1 -4
  23. package/dist/config/flags/device.flags.js +6 -9
  24. package/dist/config/flags/environment.flags.js +1 -4
  25. package/dist/config/flags/execution.flags.js +1 -4
  26. package/dist/config/flags/github.flags.js +1 -4
  27. package/dist/config/flags/output.flags.js +1 -4
  28. package/dist/constants.js +15 -18
  29. package/dist/gateways/api-gateway.d.ts +31 -6
  30. package/dist/gateways/api-gateway.js +70 -16
  31. package/dist/gateways/cli-auth-gateway.d.ts +1 -1
  32. package/dist/gateways/cli-auth-gateway.js +3 -6
  33. package/dist/gateways/realtime-gateway.d.ts +32 -0
  34. package/dist/gateways/realtime-gateway.js +103 -0
  35. package/dist/gateways/supabase-gateway.d.ts +1 -1
  36. package/dist/gateways/supabase-gateway.js +10 -14
  37. package/dist/index.js +41 -38
  38. package/dist/mcp/context.d.ts +33 -0
  39. package/dist/mcp/context.js +33 -0
  40. package/dist/mcp/helpers.d.ts +16 -0
  41. package/dist/mcp/helpers.js +34 -0
  42. package/dist/mcp/index.d.ts +2 -0
  43. package/dist/mcp/index.js +24 -0
  44. package/dist/mcp/server.d.ts +7 -0
  45. package/dist/mcp/server.js +27 -0
  46. package/dist/mcp/tools/download-artifacts.d.ts +11 -0
  47. package/dist/mcp/tools/download-artifacts.js +84 -0
  48. package/dist/mcp/tools/get-status.d.ts +7 -0
  49. package/dist/mcp/tools/get-status.js +39 -0
  50. package/dist/mcp/tools/list-devices.d.ts +7 -0
  51. package/dist/mcp/tools/list-devices.js +27 -0
  52. package/dist/mcp/tools/list-runs.d.ts +3 -0
  53. package/dist/mcp/tools/list-runs.js +60 -0
  54. package/dist/mcp/tools/run-cloud-test.d.ts +14 -0
  55. package/dist/mcp/tools/run-cloud-test.js +233 -0
  56. package/dist/methods.d.ts +32 -1
  57. package/dist/methods.js +125 -66
  58. package/dist/services/device-validation.service.d.ts +1 -1
  59. package/dist/services/device-validation.service.js +1 -5
  60. package/dist/services/execution-plan.service.js +14 -17
  61. package/dist/services/execution-plan.utils.js +15 -23
  62. package/dist/services/flow-paths.d.ts +17 -0
  63. package/dist/services/flow-paths.js +52 -0
  64. package/dist/services/metadata-extractor.service.js +22 -25
  65. package/dist/services/moropo.service.js +18 -20
  66. package/dist/services/report-download.service.d.ts +1 -1
  67. package/dist/services/report-download.service.js +5 -9
  68. package/dist/services/results-polling.service.d.ts +18 -3
  69. package/dist/services/results-polling.service.js +195 -108
  70. package/dist/services/telemetry.service.d.ts +10 -1
  71. package/dist/services/telemetry.service.js +40 -18
  72. package/dist/services/test-submission.service.d.ts +21 -4
  73. package/dist/services/test-submission.service.js +51 -34
  74. package/dist/services/version.service.d.ts +1 -1
  75. package/dist/services/version.service.js +1 -5
  76. package/dist/types/domain/auth.types.d.ts +8 -0
  77. package/dist/types/domain/auth.types.js +1 -2
  78. package/dist/types/domain/device.types.js +8 -11
  79. package/dist/types/domain/live.types.js +1 -2
  80. package/dist/types/generated/schema.types.js +1 -2
  81. package/dist/types/index.d.ts +2 -2
  82. package/dist/types/index.js +2 -18
  83. package/dist/types.js +1 -2
  84. package/dist/utils/auth.d.ts +1 -1
  85. package/dist/utils/auth.js +27 -28
  86. package/dist/utils/ci.d.ts +12 -0
  87. package/dist/utils/ci.js +39 -0
  88. package/dist/utils/cli.js +18 -27
  89. package/dist/utils/compatibility.d.ts +1 -1
  90. package/dist/utils/compatibility.js +5 -7
  91. package/dist/utils/config-store.js +33 -43
  92. package/dist/utils/connectivity.js +1 -4
  93. package/dist/utils/expo.js +15 -21
  94. package/dist/utils/orgs.js +8 -12
  95. package/dist/utils/paths.js +2 -5
  96. package/dist/utils/progress.js +2 -5
  97. package/dist/utils/styling.d.ts +35 -37
  98. package/dist/utils/styling.js +52 -86
  99. package/dist/utils/ui.d.ts +41 -0
  100. package/dist/utils/ui.js +95 -0
  101. package/package.json +27 -24
package/README.md CHANGED
@@ -34,6 +34,41 @@ $ dcd cloud --apiKey <apiKey> <appFile> .myFlows/
34
34
  See full documentation: [Docs](https://docs.devicecloud.dev)
35
35
 
36
36
 
37
+ ## MCP server
38
+
39
+ The same npm package ships an [MCP](https://modelcontextprotocol.io) server (`dcd-mcp` bin) so AI agents — Claude, Cursor, VS Code — can drive devicecloud.dev directly.
40
+
41
+ Add it to your MCP client config:
42
+
43
+ ```jsonc
44
+ {
45
+ "mcpServers": {
46
+ "devicecloud": {
47
+ "command": "npx",
48
+ "args": ["-y", "@devicecloud.dev/dcd", "dcd-mcp"],
49
+ "env": { "DEVICE_CLOUD_API_KEY": "<your-api-key>" }
50
+ }
51
+ }
52
+ }
53
+ ```
54
+
55
+ Auth is inherited from the CLI: set `DEVICE_CLOUD_API_KEY` as above, or run `dcd login` once and the server picks up the stored session. Point it at a non-prod environment with `DCD_API_URL`.
56
+
57
+ **Tools**
58
+
59
+ | Tool | What it does |
60
+ | --- | --- |
61
+ | `dcd_list_devices` | Discover available devices, OS versions, and Maestro versions |
62
+ | `dcd_list_runs` | List recent test runs (filter by name/date, paginated) |
63
+ | `dcd_get_status` | Get the status + per-test results of a run |
64
+ | `dcd_download_artifacts` | Download a run's artifacts/report to disk |
65
+ | `dcd_run_cloud_test` | Submit a flow to run on the cloud (**billable**) |
66
+
67
+ **Read-only mode.** `dcd_run_cloud_test` consumes test minutes, so it is annotated as non-read-only/destructive (clients can prompt before calling it). To hide it entirely — recommended for autonomous or untrusted agents — pass `--read-only` in `args`, or set `DCD_MCP_READONLY=1` in `env`.
68
+
69
+ By default `dcd_run_cloud_test` is async: it returns an `uploadId` immediately, which you poll with `dcd_get_status`. Pass `wait: true` (bounded by `waitTimeoutSeconds`) to block until completion, or `dryRun: true` to preview the flows without submitting.
70
+
71
+
37
72
  ## Development
38
73
 
39
74
  Requires Node 22+ and [pnpm](https://pnpm.io). `pnpm install` builds the CLI and installs the git hooks automatically.
@@ -1,44 +1,44 @@
1
1
  export declare const artifactsCommand: import("citty").CommandDef<{
2
- debug: {
3
- type: "boolean";
4
- default: false;
5
- description: string;
2
+ readonly debug: {
3
+ readonly type: "boolean";
4
+ readonly default: false;
5
+ readonly description: "Enable detailed debug logging for troubleshooting issues";
6
6
  };
7
- 'upload-id': {
8
- type: "string";
9
- required: true;
10
- description: string;
7
+ readonly 'upload-id': {
8
+ readonly type: "string";
9
+ readonly required: true;
10
+ readonly description: "UUID of the completed upload to download artifacts for";
11
11
  };
12
- 'download-artifacts': {
13
- type: "string";
14
- description: string;
12
+ readonly 'download-artifacts': {
13
+ readonly type: "string";
14
+ readonly description: "Download a zip containing the logs, screenshots and videos for this run (options: ALL, FAILED)";
15
15
  };
16
- 'artifacts-path': {
17
- type: "string";
18
- description: string;
16
+ readonly 'artifacts-path': {
17
+ readonly type: "string";
18
+ readonly description: "Custom file path for downloaded artifacts (default: ./artifacts.zip). Requires --download-artifacts.";
19
19
  };
20
- report: {
21
- type: "string";
22
- description: string;
20
+ readonly report: {
21
+ readonly type: "string";
22
+ readonly description: "Download a test report in the specified format (options: allure, html, html-detailed, junit)";
23
23
  };
24
- 'allure-path': {
25
- type: "string";
26
- description: string;
24
+ readonly 'allure-path': {
25
+ readonly type: "string";
26
+ readonly description: "Custom file path for downloaded Allure report (default: ./report.html)";
27
27
  };
28
- 'html-path': {
29
- type: "string";
30
- description: string;
28
+ readonly 'html-path': {
29
+ readonly type: "string";
30
+ readonly description: "Custom file path for downloaded HTML report (default: ./report.html)";
31
31
  };
32
- 'junit-path': {
33
- type: "string";
34
- description: string;
32
+ readonly 'junit-path': {
33
+ readonly type: "string";
34
+ readonly description: "Custom file path for downloaded JUnit report (default: ./report.xml)";
35
35
  };
36
- 'api-key': {
36
+ readonly 'api-key': {
37
37
  readonly type: "string";
38
38
  readonly alias: ["apiKey"];
39
39
  readonly description: "API key for devicecloud.dev (find this in the console UI). You can also set the DEVICE_CLOUD_API_KEY environment variable.";
40
40
  };
41
- 'api-url': {
41
+ readonly 'api-url': {
42
42
  readonly type: "string";
43
43
  readonly alias: ["apiURL", "apiUrl"];
44
44
  readonly description: "API base URL (defaults to the URL stored by `dcd login`, else prod)";
@@ -1,21 +1,18 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.artifactsCommand = void 0;
4
- const citty_1 = require("citty");
5
- const api_flags_1 = require("../config/flags/api.flags");
6
- const report_download_service_1 = require("../services/report-download.service");
7
- const auth_1 = require("../utils/auth");
8
- const cli_1 = require("../utils/cli");
9
- const config_store_1 = require("../utils/config-store");
1
+ import { defineCommand } from 'citty';
2
+ import { apiFlags } from '../config/flags/api.flags.js';
3
+ import { ReportDownloadService } from '../services/report-download.service.js';
4
+ import { resolveAuth } from '../utils/auth.js';
5
+ import { CliError, logger, validateEnum } from '../utils/cli.js';
6
+ import { resolveApiUrl } from '../utils/config-store.js';
10
7
  const DOWNLOAD_OPTIONS = ['ALL', 'FAILED'];
11
8
  const REPORT_OPTIONS = ['allure', 'html', 'html-detailed', 'junit'];
12
- exports.artifactsCommand = (0, citty_1.defineCommand)({
9
+ export const artifactsCommand = defineCommand({
13
10
  meta: {
14
11
  name: 'artifacts',
15
12
  description: 'Download artifacts or reports for a completed test run',
16
13
  },
17
14
  args: {
18
- ...api_flags_1.apiFlags,
15
+ ...apiFlags,
19
16
  debug: {
20
17
  type: 'boolean',
21
18
  default: false,
@@ -53,23 +50,23 @@ exports.artifactsCommand = (0, citty_1.defineCommand)({
53
50
  },
54
51
  async run({ args }) {
55
52
  const apiKeyFlag = args['api-key'];
56
- const apiUrl = (0, config_store_1.resolveApiUrl)(args['api-url']);
53
+ const apiUrl = resolveApiUrl(args['api-url']);
57
54
  const debug = Boolean(args.debug);
58
55
  const uploadId = args['upload-id'];
59
- const downloadArtifacts = (0, cli_1.validateEnum)(args['download-artifacts'], DOWNLOAD_OPTIONS, 'download-artifacts');
56
+ const downloadArtifacts = validateEnum(args['download-artifacts'], DOWNLOAD_OPTIONS, 'download-artifacts');
60
57
  const artifactsPath = args['artifacts-path'];
61
- const report = (0, cli_1.validateEnum)(args.report, REPORT_OPTIONS, 'report');
58
+ const report = validateEnum(args.report, REPORT_OPTIONS, 'report');
62
59
  const allurePath = args['allure-path'];
63
60
  const htmlPath = args['html-path'];
64
61
  const junitPath = args['junit-path'];
65
- const auth = await (0, auth_1.resolveAuth)({ apiKeyFlag });
62
+ const auth = await resolveAuth({ apiKeyFlag });
66
63
  if (downloadArtifacts && report) {
67
- throw new cli_1.CliError('--download-artifacts cannot also be provided when using --report (flags are mutually exclusive).');
64
+ throw new CliError('--download-artifacts cannot also be provided when using --report (flags are mutually exclusive).');
68
65
  }
69
66
  if (!downloadArtifacts && !report) {
70
- throw new cli_1.CliError('Either --download-artifacts or --report must be specified.');
67
+ throw new CliError('Either --download-artifacts or --report must be specified.');
71
68
  }
72
- const service = new report_download_service_1.ReportDownloadService();
69
+ const service = new ReportDownloadService();
73
70
  if (downloadArtifacts) {
74
71
  await service.downloadArtifacts({
75
72
  auth,
@@ -77,9 +74,9 @@ exports.artifactsCommand = (0, citty_1.defineCommand)({
77
74
  artifactsPath: artifactsPath ?? './artifacts.zip',
78
75
  debug,
79
76
  downloadType: downloadArtifacts,
80
- logger: (m) => cli_1.logger.log(m),
77
+ logger: (m) => logger.log(m),
81
78
  uploadId,
82
- warnLogger: (m) => cli_1.logger.warn(m),
79
+ warnLogger: (m) => logger.warn(m),
83
80
  });
84
81
  }
85
82
  if (report) {
@@ -90,12 +87,12 @@ exports.artifactsCommand = (0, citty_1.defineCommand)({
90
87
  debug,
91
88
  htmlPath,
92
89
  junitPath,
93
- logger: (m) => cli_1.logger.log(m),
90
+ logger: (m) => logger.log(m),
94
91
  reportType: report,
95
92
  uploadId,
96
- warnLogger: (m) => cli_1.logger.warn(m),
93
+ warnLogger: (m) => logger.warn(m),
97
94
  });
98
95
  }
99
96
  },
100
97
  });
101
- exports.default = exports.artifactsCommand;
98
+ export default artifactsCommand;
@@ -11,227 +11,227 @@
11
11
  * Replaces `maestro cloud` with DeviceCloud-specific functionality.
12
12
  */
13
13
  export declare const cloudCommand: import("citty").CommandDef<{
14
- firstFile: {
15
- type: "positional";
16
- required: false;
17
- description: string;
14
+ readonly firstFile: {
15
+ readonly type: "positional";
16
+ readonly required: false;
17
+ readonly description: "The binary file of the app to run your flow against, e.g. test.apk for android or test.app/.zip for ios";
18
18
  };
19
- secondFile: {
20
- type: "positional";
21
- required: false;
22
- description: string;
19
+ readonly secondFile: {
20
+ readonly type: "positional";
21
+ readonly required: false;
22
+ readonly description: "The flow file to run against the app, e.g. test.yaml";
23
23
  };
24
- 'artifacts-path': {
24
+ readonly 'artifacts-path': {
25
25
  readonly type: "string";
26
26
  readonly description: "Custom file path for downloaded artifacts (default: ./artifacts.zip). Requires --download-artifacts.";
27
27
  };
28
- 'junit-path': {
28
+ readonly 'junit-path': {
29
29
  readonly type: "string";
30
30
  readonly description: "Custom file path for downloaded JUnit report (requires --report junit, default: ./report.xml)";
31
31
  };
32
- 'allure-path': {
32
+ readonly 'allure-path': {
33
33
  readonly type: "string";
34
34
  readonly description: "Custom file path for downloaded Allure report (requires --report allure, default: ./report.html)";
35
35
  };
36
- 'html-path': {
36
+ readonly 'html-path': {
37
37
  readonly type: "string";
38
38
  readonly description: "Custom file path for downloaded HTML report (requires --report html, default: ./report.html)";
39
39
  };
40
- async: {
40
+ readonly async: {
41
41
  readonly type: "boolean";
42
42
  readonly description: "Immediately return (exit code 0) from the command without waiting for the results of the run (useful for saving CI minutes)";
43
43
  };
44
- debug: {
44
+ readonly debug: {
45
45
  readonly type: "boolean";
46
46
  readonly default: false;
47
47
  readonly description: "Enable detailed debug logging for troubleshooting issues";
48
48
  };
49
- 'download-artifacts': {
49
+ readonly 'download-artifacts': {
50
50
  readonly type: "string";
51
51
  readonly description: "Download a zip containing the logs, screenshots and videos for each result in this run (options: ALL, FAILED)";
52
52
  };
53
- 'dry-run': {
53
+ readonly 'dry-run': {
54
54
  readonly type: "boolean";
55
55
  readonly default: false;
56
56
  readonly description: "Simulate the run without actually triggering the upload/test, useful for debugging workflow issues.";
57
57
  };
58
- json: {
58
+ readonly json: {
59
59
  readonly type: "boolean";
60
60
  readonly description: "Output results in JSON format. Exit codes: 0 on success, 2 if the test run fails, 1 on CLI/infrastructure errors";
61
61
  };
62
- 'json-file': {
62
+ readonly 'json-file': {
63
63
  readonly type: "boolean";
64
64
  readonly description: "Write JSON output to a file. File will be called <upload_id>_dcd.json unless you supply the --json-file-name flag - note: exits with code 0 even if the test run fails (CLI/infrastructure errors still exit 1)";
65
65
  };
66
- 'json-file-name': {
66
+ readonly 'json-file-name': {
67
67
  readonly type: "string";
68
68
  readonly description: "A custom name for the JSON file (can also include relative path). Requires --json-file.";
69
69
  };
70
- quiet: {
70
+ readonly quiet: {
71
71
  readonly type: "boolean";
72
72
  readonly alias: ["q"];
73
73
  readonly default: false;
74
74
  readonly description: "Quieter console output that won't provide progress updates";
75
75
  };
76
- report: {
76
+ readonly report: {
77
77
  readonly type: "string";
78
78
  readonly alias: ["format"];
79
79
  readonly description: "Generate and download test reports in the specified format (options: allure, junit, html, html-detailed). Use \"allure\" for a complete HTML report.";
80
80
  };
81
- branch: {
81
+ readonly branch: {
82
82
  readonly type: "string";
83
83
  readonly description: "Git branch name for this run (stored as gh_branch metadata)";
84
84
  };
85
- 'commit-sha': {
85
+ readonly 'commit-sha': {
86
86
  readonly type: "string";
87
87
  readonly description: "Git commit SHA for this run (stored as gh_sha metadata)";
88
88
  };
89
- 'repo-name': {
89
+ readonly 'repo-name': {
90
90
  readonly type: "string";
91
91
  readonly description: "Repository in owner/repo format (stored as gh_repo metadata, e.g. \"acme/my-app\")";
92
92
  };
93
- 'pr-number': {
93
+ readonly 'pr-number': {
94
94
  readonly type: "string";
95
95
  readonly description: "Pull request number for this run (stored as gh_pr_number metadata)";
96
96
  };
97
- 'pr-url': {
97
+ readonly 'pr-url': {
98
98
  readonly type: "string";
99
99
  readonly description: "Pull request URL for this run (stored as gh_pr_url metadata)";
100
100
  };
101
- config: {
101
+ readonly config: {
102
102
  readonly type: "string";
103
103
  readonly description: "Path to custom config.yaml file. If not provided, defaults to config.yaml in root flows folders.";
104
104
  readonly valueHint: "path";
105
105
  };
106
- 'exclude-flows': {
106
+ readonly 'exclude-flows': {
107
107
  readonly type: "string";
108
108
  readonly description: "Sub directories to ignore when building the flow file list (comma-separated, may be repeated)";
109
109
  };
110
- 'exclude-tags': {
110
+ readonly 'exclude-tags': {
111
111
  readonly type: "string";
112
112
  readonly description: "Flows which have these tags will be excluded from the run (comma-separated, may be repeated)";
113
113
  };
114
- flows: {
114
+ readonly flows: {
115
115
  readonly type: "string";
116
116
  readonly description: "The path to the flow file or folder containing your flows";
117
117
  };
118
- 'include-tags': {
118
+ readonly 'include-tags': {
119
119
  readonly type: "string";
120
120
  readonly description: "Only flows which have these tags will be included in the run (comma-separated, may be repeated)";
121
121
  };
122
- 'maestro-version': {
122
+ readonly 'maestro-version': {
123
123
  readonly type: "string";
124
124
  readonly alias: ["maestroVersion"];
125
125
  readonly description: "Maestro version to run your flow against. Use \"latest\" for the most recent version. See https://docs.devicecloud.dev/configuration/maestro-versions for supported versions.";
126
126
  };
127
- retry: {
127
+ readonly retry: {
128
128
  readonly type: "string";
129
129
  readonly description: "Automatically retry the run up to the number of times specified (same as pressing retry in the UI) - this is free of charge";
130
130
  };
131
- 'runner-type': {
131
+ readonly 'runner-type': {
132
132
  readonly type: "string";
133
133
  readonly default: "default";
134
134
  readonly description: "[experimental] The type of runner to use (options: default, m4, m1, gpu1, cpu1) - note: anything other than default or cpu1 will incur premium pricing tiers, see https://docs.devicecloud.dev/configuration/runner-type for more information.";
135
135
  };
136
- env: {
136
+ readonly env: {
137
137
  readonly type: "string";
138
138
  readonly alias: ["e"];
139
139
  readonly description: "One or more environment variables to inject into your flows (format: KEY=VALUE, may be repeated)";
140
140
  readonly valueHint: "KEY=VALUE";
141
141
  };
142
- metadata: {
142
+ readonly metadata: {
143
143
  readonly type: "string";
144
144
  readonly alias: ["m"];
145
145
  readonly description: "Arbitrary key-value metadata to include with your test run (format: key=value, may be repeated)";
146
146
  };
147
- mitmHost: {
147
+ readonly mitmHost: {
148
148
  readonly type: "string";
149
149
  readonly description: "used for mitmproxy support, enterprise only, contact support if interested";
150
150
  };
151
- mitmPath: {
151
+ readonly mitmPath: {
152
152
  readonly type: "string";
153
153
  readonly description: "used for mitmproxy support, enterprise only, contact support if interested";
154
154
  };
155
- 'moropo-v1-api-key': {
155
+ readonly 'moropo-v1-api-key': {
156
156
  readonly type: "string";
157
157
  readonly description: "API key for Moropo v1 integration";
158
158
  };
159
- name: {
159
+ readonly name: {
160
160
  readonly type: "string";
161
161
  readonly description: "A custom name for your upload (useful for tagging commits etc)";
162
162
  };
163
- 'android-api-level': {
163
+ readonly 'android-api-level': {
164
164
  readonly type: "string";
165
165
  readonly description: `[Android only] Android API level to run your flow against (options: ${string})`;
166
166
  };
167
- 'android-device': {
167
+ readonly 'android-device': {
168
168
  readonly type: "string";
169
169
  readonly description: `[Android only] Android device to run your flow against (options: ${string})`;
170
170
  };
171
- 'device-locale': {
171
+ readonly 'device-locale': {
172
172
  readonly type: "string";
173
173
  readonly description: "Locale that will be set to a device, ISO-639-1 code and uppercase ISO-3166-1 code e.g. \"de_DE\" for Germany";
174
174
  };
175
- 'google-play': {
175
+ readonly 'google-play': {
176
176
  readonly type: "boolean";
177
177
  readonly default: false;
178
178
  readonly description: "[Android only] Run your flow against Google Play devices";
179
179
  };
180
- 'ios-device': {
180
+ readonly 'ios-device': {
181
181
  readonly type: "string";
182
182
  readonly description: `[iOS only] iOS device to run your flow against (options: ${string})`;
183
183
  };
184
- 'ios-version': {
184
+ readonly 'ios-version': {
185
185
  readonly type: "string";
186
186
  readonly description: `[iOS only] iOS version to run your flow against (options: ${string})`;
187
187
  };
188
- orientation: {
188
+ readonly orientation: {
189
189
  readonly type: "string";
190
190
  readonly description: "[Android only] The orientation of the device to run your flow against (0 = portrait, 90 = landscape)";
191
191
  };
192
- 'show-crosshairs': {
192
+ readonly 'show-crosshairs': {
193
193
  readonly type: "boolean";
194
194
  readonly default: false;
195
195
  readonly description: "[Android only] Display crosshairs for screen interactions during test execution";
196
196
  };
197
- 'maestro-chrome-onboarding': {
197
+ readonly 'maestro-chrome-onboarding': {
198
198
  readonly type: "boolean";
199
199
  readonly default: false;
200
200
  readonly description: "[Android only] Force Maestro-based Chrome onboarding - note: this will slow your tests but can fix browser related crashes. See https://docs.devicecloud.dev/advanced/chrome-onboarding for more information.";
201
201
  };
202
- 'android-no-snapshot': {
202
+ readonly 'android-no-snapshot': {
203
203
  readonly type: "boolean";
204
204
  readonly default: false;
205
205
  readonly description: "[Android only] Force cold boot instead of using snapshot boot. This is automatically enabled for API 35+ but can be used to force cold boot on older API levels.";
206
206
  };
207
- 'disable-animations': {
207
+ readonly 'disable-animations': {
208
208
  readonly type: "boolean";
209
209
  readonly default: false;
210
210
  readonly 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.";
211
211
  };
212
- 'app-binary-id': {
212
+ readonly 'app-binary-id': {
213
213
  readonly type: "string";
214
214
  readonly description: "The ID of the app binary previously uploaded to devicecloud.dev";
215
215
  };
216
- 'app-file': {
216
+ readonly 'app-file': {
217
217
  readonly type: "string";
218
218
  readonly description: "App binary to run your flows against";
219
219
  readonly valueHint: "path";
220
220
  };
221
- 'app-url': {
221
+ readonly 'app-url': {
222
222
  readonly type: "string";
223
223
  readonly description: "Signed URL to an Expo iOS build (.tar.gz). The archive is downloaded and extracted automatically. Expo signed URLs expire after ~1 hour.";
224
224
  };
225
- 'ignore-sha-check': {
225
+ readonly 'ignore-sha-check': {
226
226
  readonly type: "boolean";
227
227
  readonly description: "Ignore the sha hash check and upload the binary regardless of whether it already exists (not recommended)";
228
228
  };
229
- 'api-key': {
229
+ readonly 'api-key': {
230
230
  readonly type: "string";
231
231
  readonly alias: ["apiKey"];
232
232
  readonly description: "API key for devicecloud.dev (find this in the console UI). You can also set the DEVICE_CLOUD_API_KEY environment variable.";
233
233
  };
234
- 'api-url': {
234
+ readonly 'api-url': {
235
235
  readonly type: "string";
236
236
  readonly alias: ["apiURL", "apiUrl"];
237
237
  readonly description: "API base URL (defaults to the URL stored by `dcd login`, else prod)";