@devicecloud.dev/dcd 4.4.9 → 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 (130) hide show
  1. package/README.md +75 -2
  2. package/dist/commands/artifacts.d.ts +47 -18
  3. package/dist/commands/artifacts.js +69 -64
  4. package/dist/commands/cloud.d.ts +228 -88
  5. package/dist/commands/cloud.js +430 -342
  6. package/dist/commands/list.d.ts +39 -38
  7. package/dist/commands/list.js +124 -131
  8. package/dist/commands/live.d.ts +2 -0
  9. package/dist/commands/live.js +520 -0
  10. package/dist/commands/login.d.ts +17 -0
  11. package/dist/commands/login.js +252 -0
  12. package/dist/commands/logout.d.ts +2 -0
  13. package/dist/commands/logout.js +30 -0
  14. package/dist/commands/status.d.ts +23 -42
  15. package/dist/commands/status.js +170 -179
  16. package/dist/commands/switch-org.d.ts +12 -0
  17. package/dist/commands/switch-org.js +76 -0
  18. package/dist/commands/upgrade.d.ts +2 -0
  19. package/dist/commands/upgrade.js +120 -0
  20. package/dist/commands/upload.d.ts +33 -18
  21. package/dist/commands/upload.js +72 -78
  22. package/dist/commands/whoami.d.ts +2 -0
  23. package/dist/commands/whoami.js +31 -0
  24. package/dist/config/environments.d.ts +31 -0
  25. package/dist/config/environments.js +52 -0
  26. package/dist/config/flags/api.flags.d.ts +10 -2
  27. package/dist/config/flags/api.flags.js +13 -14
  28. package/dist/config/flags/binary.flags.d.ts +17 -4
  29. package/dist/config/flags/binary.flags.js +14 -18
  30. package/dist/config/flags/device.flags.d.ts +49 -11
  31. package/dist/config/flags/device.flags.js +43 -38
  32. package/dist/config/flags/environment.flags.d.ts +27 -6
  33. package/dist/config/flags/environment.flags.js +24 -29
  34. package/dist/config/flags/execution.flags.d.ts +35 -8
  35. package/dist/config/flags/execution.flags.js +31 -41
  36. package/dist/config/flags/github.flags.d.ts +23 -5
  37. package/dist/config/flags/github.flags.js +19 -15
  38. package/dist/config/flags/output.flags.d.ts +57 -13
  39. package/dist/config/flags/output.flags.js +48 -47
  40. package/dist/constants.d.ts +218 -51
  41. package/dist/constants.js +17 -20
  42. package/dist/gateways/api-gateway.d.ts +72 -16
  43. package/dist/gateways/api-gateway.js +298 -104
  44. package/dist/gateways/cli-auth-gateway.d.ts +13 -0
  45. package/dist/gateways/cli-auth-gateway.js +54 -0
  46. package/dist/gateways/realtime-gateway.d.ts +32 -0
  47. package/dist/gateways/realtime-gateway.js +103 -0
  48. package/dist/gateways/supabase-gateway.d.ts +11 -11
  49. package/dist/gateways/supabase-gateway.js +20 -48
  50. package/dist/index.d.ts +2 -1
  51. package/dist/index.js +98 -4
  52. package/dist/mcp/context.d.ts +33 -0
  53. package/dist/mcp/context.js +33 -0
  54. package/dist/mcp/helpers.d.ts +16 -0
  55. package/dist/mcp/helpers.js +34 -0
  56. package/dist/mcp/index.d.ts +2 -0
  57. package/dist/mcp/index.js +24 -0
  58. package/dist/mcp/server.d.ts +7 -0
  59. package/dist/mcp/server.js +27 -0
  60. package/dist/mcp/tools/download-artifacts.d.ts +11 -0
  61. package/dist/mcp/tools/download-artifacts.js +84 -0
  62. package/dist/mcp/tools/get-status.d.ts +7 -0
  63. package/dist/mcp/tools/get-status.js +39 -0
  64. package/dist/mcp/tools/list-devices.d.ts +7 -0
  65. package/dist/mcp/tools/list-devices.js +27 -0
  66. package/dist/mcp/tools/list-runs.d.ts +3 -0
  67. package/dist/mcp/tools/list-runs.js +60 -0
  68. package/dist/mcp/tools/run-cloud-test.d.ts +14 -0
  69. package/dist/mcp/tools/run-cloud-test.js +233 -0
  70. package/dist/methods.d.ts +34 -5
  71. package/dist/methods.js +266 -215
  72. package/dist/services/device-validation.service.d.ts +9 -1
  73. package/dist/services/device-validation.service.js +56 -40
  74. package/dist/services/execution-plan.service.js +40 -31
  75. package/dist/services/execution-plan.utils.d.ts +3 -0
  76. package/dist/services/execution-plan.utils.js +25 -55
  77. package/dist/services/flow-paths.d.ts +17 -0
  78. package/dist/services/flow-paths.js +52 -0
  79. package/dist/services/metadata-extractor.service.d.ts +0 -2
  80. package/dist/services/metadata-extractor.service.js +75 -78
  81. package/dist/services/moropo.service.js +33 -34
  82. package/dist/services/report-download.service.d.ts +12 -1
  83. package/dist/services/report-download.service.js +34 -27
  84. package/dist/services/results-polling.service.d.ts +23 -9
  85. package/dist/services/results-polling.service.js +257 -123
  86. package/dist/services/telemetry.service.d.ts +49 -0
  87. package/dist/services/telemetry.service.js +252 -0
  88. package/dist/services/test-submission.service.d.ts +21 -4
  89. package/dist/services/test-submission.service.js +51 -33
  90. package/dist/services/version.service.d.ts +4 -3
  91. package/dist/services/version.service.js +28 -16
  92. package/dist/types/domain/auth.types.d.ts +20 -0
  93. package/dist/types/domain/auth.types.js +1 -0
  94. package/dist/types/domain/device.types.js +8 -11
  95. package/dist/types/domain/live.types.d.ts +76 -0
  96. package/dist/types/domain/live.types.js +3 -0
  97. package/dist/types/generated/schema.types.js +1 -2
  98. package/dist/types/index.d.ts +2 -2
  99. package/dist/types/index.js +2 -18
  100. package/dist/types.js +1 -2
  101. package/dist/utils/auth.d.ts +13 -0
  102. package/dist/utils/auth.js +141 -0
  103. package/dist/utils/ci.d.ts +12 -0
  104. package/dist/utils/ci.js +39 -0
  105. package/dist/utils/cli.d.ts +35 -0
  106. package/dist/utils/cli.js +118 -0
  107. package/dist/utils/compatibility.d.ts +2 -1
  108. package/dist/utils/compatibility.js +6 -8
  109. package/dist/utils/config-store.d.ts +35 -0
  110. package/dist/utils/config-store.js +115 -0
  111. package/dist/utils/connectivity.js +8 -7
  112. package/dist/utils/expo.js +29 -24
  113. package/dist/utils/orgs.d.ts +11 -0
  114. package/dist/utils/orgs.js +36 -0
  115. package/dist/utils/paths.d.ts +11 -0
  116. package/dist/utils/paths.js +21 -0
  117. package/dist/utils/progress.d.ts +13 -0
  118. package/dist/utils/progress.js +47 -0
  119. package/dist/utils/styling.d.ts +42 -36
  120. package/dist/utils/styling.js +78 -82
  121. package/dist/utils/ui.d.ts +41 -0
  122. package/dist/utils/ui.js +95 -0
  123. package/package.json +36 -45
  124. package/bin/dev.cmd +0 -3
  125. package/bin/dev.js +0 -6
  126. package/bin/run.cmd +0 -3
  127. package/bin/run.js +0 -7
  128. package/dist/types/schema.types.d.ts +0 -2702
  129. package/dist/types/schema.types.js +0 -3
  130. package/oclif.manifest.json +0 -884
package/README.md CHANGED
@@ -1,16 +1,29 @@
1
1
  # devicecloud.dev CLI
2
2
 
3
-
4
3
  ---
5
4
 
6
5
  One line swap out for Maestro Cloud
7
6
 
8
- Install:
7
+ Install (no Node required):
8
+
9
+ ```sh-session
10
+ $ curl -fsSL https://get.devicecloud.dev/install.sh | sh
11
+ ```
12
+
13
+ On Windows:
14
+
15
+ ```powershell
16
+ irm https://get.devicecloud.dev/install.ps1 | iex
17
+ ```
18
+
19
+ Or via npm if you already have Node 22+:
9
20
 
10
21
  ```sh-session
11
22
  $ npm install -g @devicecloud.dev/dcd
12
23
  ```
13
24
 
25
+ Upgrade later with `dcd upgrade` (binary install) or `npm install -g @devicecloud.dev/dcd@latest` (npm install).
26
+
14
27
  Use:
15
28
 
16
29
  ```sh-session
@@ -19,3 +32,63 @@ $ dcd cloud --apiKey <apiKey> <appFile> .myFlows/
19
32
  ```
20
33
 
21
34
  See full documentation: [Docs](https://docs.devicecloud.dev)
35
+
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
+
72
+ ## Development
73
+
74
+ Requires Node 22+ and [pnpm](https://pnpm.io). `pnpm install` builds the CLI and installs the git hooks automatically.
75
+
76
+ ```sh-session
77
+ $ pnpm install # install deps, build, set up git hooks
78
+ $ pnpm dcd <args> # run the CLI from source
79
+ $ pnpm lint # ESLint
80
+ $ pnpm typecheck # strict tsc, no emit
81
+ $ pnpm test # build + boot mock API + integration/unit tests
82
+ ```
83
+
84
+ ### Secret scanning
85
+
86
+ A [gitleaks](https://github.com/gitleaks/gitleaks) scan runs in two places, both sharing the allowlist in `.gitleaks.toml`:
87
+
88
+ - **pre-commit hook** (via husky) — scans your staged changes and blocks the commit if a secret is found. Install the binary so it can run; without it the hook skips with a warning:
89
+ ```sh-session
90
+ $ brew install gitleaks # or see github.com/gitleaks/gitleaks#installing
91
+ ```
92
+ - **CI** — the `secret-scan` job scans the full history on every push and pull request, and is the enforced backstop regardless of local setup.
93
+
94
+
@@ -1,18 +1,47 @@
1
- import { Command } from '@oclif/core';
2
- export default class Artifacts 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
- debug: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
- 'upload-id': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
- 'download-artifacts': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
11
- 'artifacts-path': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
12
- report: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
13
- 'allure-path': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
14
- 'html-path': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
15
- 'junit-path': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
16
- };
17
- run(): Promise<void>;
18
- }
1
+ export declare const artifactsCommand: import("citty").CommandDef<{
2
+ readonly debug: {
3
+ readonly type: "boolean";
4
+ readonly default: false;
5
+ readonly description: "Enable detailed debug logging for troubleshooting issues";
6
+ };
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
+ };
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
+ };
16
+ readonly 'artifacts-path': {
17
+ readonly type: "string";
18
+ readonly description: "Custom file path for downloaded artifacts (default: ./artifacts.zip). Requires --download-artifacts.";
19
+ };
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
+ };
24
+ readonly 'allure-path': {
25
+ readonly type: "string";
26
+ readonly description: "Custom file path for downloaded Allure report (default: ./report.html)";
27
+ };
28
+ readonly 'html-path': {
29
+ readonly type: "string";
30
+ readonly description: "Custom file path for downloaded HTML report (default: ./report.html)";
31
+ };
32
+ readonly 'junit-path': {
33
+ readonly type: "string";
34
+ readonly description: "Custom file path for downloaded JUnit report (default: ./report.xml)";
35
+ };
36
+ readonly 'api-key': {
37
+ readonly type: "string";
38
+ readonly alias: ["apiKey"];
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
+ };
41
+ readonly 'api-url': {
42
+ readonly type: "string";
43
+ readonly alias: ["apiURL", "apiUrl"];
44
+ readonly description: "API base URL (defaults to the URL stored by `dcd login`, else prod)";
45
+ };
46
+ }>;
47
+ export default artifactsCommand;
@@ -1,93 +1,98 @@
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 report_download_service_1 = require("../services/report-download.service");
6
- class Artifacts extends core_1.Command {
7
- static description = 'Download artifacts or reports for a completed test run';
8
- static examples = [
9
- '<%= config.bin %> <%= command.id %> --upload-id 123e4567-e89b-12d3-a456-426614174000 --download-artifacts FAILED',
10
- '<%= config.bin %> <%= command.id %> --upload-id 123e4567-e89b-12d3-a456-426614174000 --download-artifacts ALL --artifacts-path ./my-artifacts.zip',
11
- '<%= config.bin %> <%= command.id %> --upload-id 123e4567-e89b-12d3-a456-426614174000 --report junit',
12
- '<%= config.bin %> <%= command.id %> --upload-id 123e4567-e89b-12d3-a456-426614174000 --report allure --allure-path ./report.html',
13
- ];
14
- static flags = {
15
- apiKey: constants_1.flags.apiKey,
16
- apiUrl: constants_1.flags.apiUrl,
17
- debug: core_1.Flags.boolean({
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';
7
+ const DOWNLOAD_OPTIONS = ['ALL', 'FAILED'];
8
+ const REPORT_OPTIONS = ['allure', 'html', 'html-detailed', 'junit'];
9
+ export const artifactsCommand = defineCommand({
10
+ meta: {
11
+ name: 'artifacts',
12
+ description: 'Download artifacts or reports for a completed test run',
13
+ },
14
+ args: {
15
+ ...apiFlags,
16
+ debug: {
17
+ type: 'boolean',
18
18
  default: false,
19
19
  description: 'Enable detailed debug logging for troubleshooting issues',
20
- }),
21
- 'upload-id': core_1.Flags.string({
22
- description: 'UUID of the completed upload to download artifacts for',
20
+ },
21
+ 'upload-id': {
22
+ type: 'string',
23
23
  required: true,
24
- }),
25
- 'download-artifacts': core_1.Flags.string({
26
- description: 'Download a zip containing the logs, screenshots and videos for this run. Options: ALL (everything), FAILED (failures only).',
27
- exclusive: ['report'],
28
- options: ['ALL', 'FAILED'],
29
- }),
30
- 'artifacts-path': core_1.Flags.string({
31
- dependsOn: ['download-artifacts'],
32
- description: 'Custom file path for downloaded artifacts (default: ./artifacts.zip)',
33
- }),
34
- report: core_1.Flags.string({
35
- description: 'Download a test report in the specified format.',
36
- exclusive: ['download-artifacts'],
37
- options: ['allure', 'html', 'html-detailed', 'junit'],
38
- }),
39
- 'allure-path': core_1.Flags.string({
40
- dependsOn: ['report'],
24
+ description: 'UUID of the completed upload to download artifacts for',
25
+ },
26
+ 'download-artifacts': {
27
+ type: 'string',
28
+ description: 'Download a zip containing the logs, screenshots and videos for this run (options: ALL, FAILED)',
29
+ },
30
+ 'artifacts-path': {
31
+ type: 'string',
32
+ description: 'Custom file path for downloaded artifacts (default: ./artifacts.zip). Requires --download-artifacts.',
33
+ },
34
+ report: {
35
+ type: 'string',
36
+ description: 'Download a test report in the specified format (options: allure, html, html-detailed, junit)',
37
+ },
38
+ 'allure-path': {
39
+ type: 'string',
41
40
  description: 'Custom file path for downloaded Allure report (default: ./report.html)',
42
- }),
43
- 'html-path': core_1.Flags.string({
44
- dependsOn: ['report'],
41
+ },
42
+ 'html-path': {
43
+ type: 'string',
45
44
  description: 'Custom file path for downloaded HTML report (default: ./report.html)',
46
- }),
47
- 'junit-path': core_1.Flags.string({
48
- dependsOn: ['report'],
45
+ },
46
+ 'junit-path': {
47
+ type: 'string',
49
48
  description: 'Custom file path for downloaded JUnit report (default: ./report.xml)',
50
- }),
51
- };
52
- async run() {
53
- const { flags } = await this.parse(Artifacts);
54
- const { apiKey: apiKeyFlag, apiUrl, debug, 'upload-id': uploadId, 'download-artifacts': downloadArtifacts, 'artifacts-path': artifactsPath, report, 'allure-path': allurePath, 'html-path': htmlPath, 'junit-path': junitPath, } = flags;
55
- const apiKey = apiKeyFlag || process.env.DEVICE_CLOUD_API_KEY;
56
- if (!apiKey) {
57
- this.error('API key is required. Please provide it via --api-key flag or DEVICE_CLOUD_API_KEY environment variable.');
58
- return;
49
+ },
50
+ },
51
+ async run({ args }) {
52
+ const apiKeyFlag = args['api-key'];
53
+ const apiUrl = resolveApiUrl(args['api-url']);
54
+ const debug = Boolean(args.debug);
55
+ const uploadId = args['upload-id'];
56
+ const downloadArtifacts = validateEnum(args['download-artifacts'], DOWNLOAD_OPTIONS, 'download-artifacts');
57
+ const artifactsPath = args['artifacts-path'];
58
+ const report = validateEnum(args.report, REPORT_OPTIONS, 'report');
59
+ const allurePath = args['allure-path'];
60
+ const htmlPath = args['html-path'];
61
+ const junitPath = args['junit-path'];
62
+ const auth = await resolveAuth({ apiKeyFlag });
63
+ if (downloadArtifacts && report) {
64
+ throw new CliError('--download-artifacts cannot also be provided when using --report (flags are mutually exclusive).');
59
65
  }
60
66
  if (!downloadArtifacts && !report) {
61
- this.error('Either --download-artifacts or --report must be specified.');
62
- return;
67
+ throw new CliError('Either --download-artifacts or --report must be specified.');
63
68
  }
64
- const service = new report_download_service_1.ReportDownloadService();
69
+ const service = new ReportDownloadService();
65
70
  if (downloadArtifacts) {
66
71
  await service.downloadArtifacts({
67
- apiKey,
72
+ auth,
68
73
  apiUrl,
69
74
  artifactsPath: artifactsPath ?? './artifacts.zip',
70
75
  debug,
71
76
  downloadType: downloadArtifacts,
72
- logger: this.log.bind(this),
77
+ logger: (m) => logger.log(m),
73
78
  uploadId,
74
- warnLogger: this.warn.bind(this),
79
+ warnLogger: (m) => logger.warn(m),
75
80
  });
76
81
  }
77
82
  if (report) {
78
83
  await service.downloadReports({
79
84
  allurePath,
80
- apiKey,
85
+ auth,
81
86
  apiUrl,
82
87
  debug,
83
88
  htmlPath,
84
89
  junitPath,
85
- logger: this.log.bind(this),
90
+ logger: (m) => logger.log(m),
86
91
  reportType: report,
87
92
  uploadId,
88
- warnLogger: this.warn.bind(this),
93
+ warnLogger: (m) => logger.warn(m),
89
94
  });
90
95
  }
91
- }
92
- }
93
- exports.default = Artifacts;
96
+ },
97
+ });
98
+ export default artifactsCommand;