@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
@@ -1,76 +1,77 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const core_1 = require("@oclif/core");
4
- const promises_1 = require("node:fs/promises");
5
- const constants_1 = require("../constants");
6
- const methods_1 = require("../methods");
7
- const expo_1 = require("../utils/expo");
8
- const styling_1 = require("../utils/styling");
9
- class Upload extends core_1.Command {
10
- static args = {
11
- appFile: core_1.Args.string({
12
- description: 'The binary file or Expo signed URL to upload (e.g. test.apk, test.app, test.zip, build.tar.gz, or https://expo.dev/...)',
13
- name: 'App file',
1
+ import { defineCommand } from 'citty';
2
+ import { rm } from 'node:fs/promises';
3
+ import { apiFlags } from '../config/flags/api.flags.js';
4
+ import { binaryFlags } from '../config/flags/binary.flags.js';
5
+ import { outputFlags } from '../config/flags/output.flags.js';
6
+ import { uploadBinary, verifyAppZip } from '../methods.js';
7
+ import { resolveAuth } from '../utils/auth.js';
8
+ import { CliError, logger } from '../utils/cli.js';
9
+ import { resolveApiUrl } from '../utils/config-store.js';
10
+ import { downloadExpoUrl, extractTarGz, findAppBundle, isUrl } from '../utils/expo.js';
11
+ import { colors, formatId } from '../utils/styling.js';
12
+ import { ui } from '../utils/ui.js';
13
+ export const uploadCommand = defineCommand({
14
+ meta: {
15
+ name: 'upload',
16
+ description: 'Upload an app binary to devicecloud.dev',
17
+ },
18
+ args: {
19
+ ...apiFlags,
20
+ 'app-url': binaryFlags['app-url'],
21
+ 'ignore-sha-check': binaryFlags['ignore-sha-check'],
22
+ debug: outputFlags.debug,
23
+ json: outputFlags.json,
24
+ appFile: {
25
+ type: 'positional',
14
26
  required: false,
15
- }),
16
- };
17
- static description = 'Upload an app binary to devicecloud.dev';
18
- static enableJsonFlag = true;
19
- static examples = [
20
- '<%= config.bin %> <%= command.id %> path/to/app.apk',
21
- '<%= config.bin %> <%= command.id %> path/to/app.zip --api-key YOUR_API_KEY',
22
- '<%= config.bin %> <%= command.id %> path/to/build.tar.gz',
23
- '<%= config.bin %> <%= command.id %> --app-url https://expo.dev/artifacts/...',
24
- ];
25
- static flags = {
26
- apiKey: constants_1.flags.apiKey,
27
- apiUrl: constants_1.flags.apiUrl,
28
- 'app-url': constants_1.flags['app-url'],
29
- debug: constants_1.flags.debug,
30
- 'ignore-sha-check': constants_1.flags['ignore-sha-check'],
31
- };
32
- async run() {
27
+ description: 'The binary file or Expo signed URL to upload (e.g. test.apk, test.app, test.zip, build.tar.gz, or https://expo.dev/...)',
28
+ },
29
+ },
30
+ async run({ args }) {
33
31
  const tempFiles = [];
32
+ const json = Boolean(args.json);
33
+ // When --json is set, suppress chatty progress logs so stdout stays parseable.
34
+ const out = (m) => {
35
+ if (!json)
36
+ logger.log(m);
37
+ };
34
38
  try {
35
- const { args, flags } = await this.parse(Upload);
36
- const { apiKey: apiKeyFlag, apiUrl, 'app-url': appUrl, debug, 'ignore-sha-check': ignoreShaCheck, json, } = flags;
37
- const apiKey = apiKeyFlag || process.env.DEVICE_CLOUD_API_KEY;
38
- if (!apiKey) {
39
- throw new Error('You must provide an API key via --api-key flag or DEVICE_CLOUD_API_KEY environment variable');
40
- }
41
- // Resolve source: --app-url flag takes precedence, then positional arg
42
- let resolvedFile = appUrl ?? args.appFile;
39
+ const apiKeyFlag = args['api-key'];
40
+ const apiUrl = resolveApiUrl(args['api-url']);
41
+ const appUrl = args['app-url'];
42
+ const ignoreShaCheck = Boolean(args['ignore-sha-check']);
43
+ const debug = Boolean(args.debug);
44
+ const positional = args.appFile;
45
+ const auth = await resolveAuth({ apiKeyFlag });
46
+ let resolvedFile = appUrl ?? positional;
43
47
  if (!resolvedFile) {
44
- throw new Error('You must provide an app file path or a signed Expo URL via --app-url');
48
+ throw new CliError('You must provide an app file path or a signed Expo URL via --app-url');
45
49
  }
46
- // Download from URL if needed
47
- if ((0, expo_1.isUrl)(resolvedFile)) {
48
- this.log(` ${styling_1.colors.dim('→ Downloading Expo build from URL...')}`);
49
- const tarPath = await (0, expo_1.downloadExpoUrl)(resolvedFile, debug ?? false);
50
+ if (isUrl(resolvedFile)) {
51
+ out(ui.running('Downloading Expo build from URL…'));
52
+ const tarPath = await downloadExpoUrl(resolvedFile, debug);
50
53
  tempFiles.push(tarPath);
51
54
  resolvedFile = tarPath;
52
55
  }
53
- // Extract .tar.gz if needed
54
56
  if (resolvedFile.endsWith('.tar.gz')) {
55
- this.log(` ${styling_1.colors.dim('Extracting Expo archive...')}`);
56
- const extractDir = await (0, expo_1.extractTarGz)(resolvedFile, debug ?? false);
57
+ out(ui.running('Extracting Expo archive'));
58
+ const extractDir = await extractTarGz(resolvedFile, debug);
57
59
  tempFiles.push(extractDir);
58
- resolvedFile = await (0, expo_1.findAppBundle)(extractDir);
60
+ resolvedFile = await findAppBundle(extractDir);
59
61
  if (debug) {
60
- this.log(`[DEBUG] Found .app bundle at: ${resolvedFile}`);
62
+ out(`[DEBUG] Found .app bundle at: ${resolvedFile}`);
61
63
  }
62
64
  }
63
- if (!['apk', '.app', '.zip', '.tar.gz'].some((ext) => resolvedFile.endsWith(ext))) {
64
- throw new Error('App file must be a .apk for Android, .app/.zip for iOS, or .tar.gz (Expo iOS build)');
65
+ if (!['.apk', '.app', '.zip', '.tar.gz'].some((ext) => resolvedFile.endsWith(ext))) {
66
+ throw new CliError('App file must be a .apk for Android, .app/.zip for iOS, or .tar.gz (Expo iOS build)');
65
67
  }
66
68
  if (resolvedFile.endsWith('.zip')) {
67
- await (0, methods_1.verifyAppZip)(resolvedFile);
69
+ await verifyAppZip(resolvedFile);
68
70
  }
69
- this.log((0, styling_1.sectionHeader)('Uploading app binary'));
70
- this.log(` ${styling_1.colors.dim('→ File:')} ${styling_1.colors.highlight(resolvedFile)}`);
71
- this.log('');
72
- const appBinaryId = await (0, methods_1.uploadBinary)({
73
- apiKey,
71
+ out(ui.section('Uploading app binary'));
72
+ out(ui.branch(ui.fields([['file', colors.highlight(resolvedFile)]])));
73
+ const appBinaryId = await uploadBinary({
74
+ auth,
74
75
  apiUrl,
75
76
  debug,
76
77
  filePath: resolvedFile,
@@ -78,32 +79,25 @@ class Upload extends core_1.Command {
78
79
  log: !json,
79
80
  });
80
81
  if (json) {
81
- return { appBinaryId };
82
+ // eslint-disable-next-line no-console
83
+ console.log(JSON.stringify({ appBinaryId }, null, 2));
84
+ return;
82
85
  }
83
- this.log(`\n${styling_1.colors.success('✓')} ${styling_1.colors.bold('Upload complete')}`);
84
- this.log(` ${styling_1.colors.dim('Binary ID:')} ${(0, styling_1.formatId)(appBinaryId)}\n`);
85
- this.log(styling_1.colors.dim('You can use this Binary ID in subsequent test runs with:'));
86
- this.log(styling_1.colors.info(`dcd cloud --app-binary-id ${appBinaryId} path/to/flow.yaml\n`));
86
+ logger.log(`\n${ui.success('Upload complete')}`);
87
+ logger.log(ui.branch([
88
+ ...ui.fields([['binary id', formatId(appBinaryId)]]),
89
+ ui.note('Use this Binary ID in subsequent test runs with:'),
90
+ colors.info(`dcd cloud --app-binary-id ${appBinaryId} path/to/flow.yaml`),
91
+ ]));
87
92
  }
88
93
  catch (error) {
89
- this.error(error, { exit: 1 });
94
+ logger.error(error, { exit: 1, json });
90
95
  }
91
96
  finally {
92
97
  for (const p of tempFiles) {
93
- await (0, promises_1.rm)(p, { recursive: true, force: true }).catch(() => { });
98
+ await rm(p, { recursive: true, force: true }).catch(() => { });
94
99
  }
95
100
  }
96
- }
97
- toErrorJson(err) {
98
- if (err instanceof Error) {
99
- return {
100
- error: {
101
- message: err.message,
102
- ...(err.code ? { code: err.code } : {}),
103
- },
104
- };
105
- }
106
- return { error: { message: String(err) } };
107
- }
108
- }
109
- exports.default = Upload;
101
+ },
102
+ });
103
+ export default uploadCommand;
@@ -0,0 +1,2 @@
1
+ export declare const whoamiCommand: import("citty").CommandDef<import("citty").ArgsDef>;
2
+ export default whoamiCommand;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `dcd whoami` — prints the logged-in user + active org from stored config.
3
+ */
4
+ import { defineCommand } from 'citty';
5
+ import { logger } from '../utils/cli.js';
6
+ import { readConfig } from '../utils/config-store.js';
7
+ import { colors, formatId } from '../utils/styling.js';
8
+ import { ui } from '../utils/ui.js';
9
+ export const whoamiCommand = defineCommand({
10
+ meta: {
11
+ name: 'whoami',
12
+ description: 'Show the logged-in user and active organization',
13
+ },
14
+ run() {
15
+ const config = readConfig();
16
+ if (!config?.session) {
17
+ logger.log(ui.info(`Not logged in. Run ${colors.highlight('dcd login')} or set ${colors.highlight('DEVICE_CLOUD_API_KEY')}.`));
18
+ return;
19
+ }
20
+ // Fallback for sessions stored before we persisted org name alongside id.
21
+ const org = config.current_org_name ?? config.current_org_id;
22
+ const fields = [['user', formatId(config.session.user_email)]];
23
+ if (org) {
24
+ fields.push(['org', formatId(org)]);
25
+ }
26
+ fields.push(['env', config.env]);
27
+ logger.log(ui.section('devicecloud.dev'));
28
+ logger.log(ui.branch(ui.fields(fields)));
29
+ },
30
+ });
31
+ export default whoamiCommand;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Per-env devicecloud.dev coordinates — single source of truth for the URLs
3
+ * and Supabase credentials the CLI uses.
4
+ *
5
+ * **These `anonKey` values are public.** Supabase anon keys are JWTs designed
6
+ * to be embedded in client code (web apps, mobile apps, CLIs). They only
7
+ * grant whatever the project's RLS policies expose. Don't worry about seeing
8
+ * them here — every Supabase-based product ships its anon key the same way.
9
+ * https://supabase.com/docs/guides/api/api-keys
10
+ */
11
+ export type DcdEnvName = 'dev' | 'prod';
12
+ export interface DcdEnvironment {
13
+ apiUrl: string;
14
+ frontendUrl: string;
15
+ supabase: {
16
+ url: string;
17
+ projectRef: string;
18
+ anonKey: string;
19
+ };
20
+ }
21
+ export declare const ENVIRONMENTS: Record<DcdEnvName, DcdEnvironment>;
22
+ /** Exact-match lookup of a known environment by API URL (trailing slashes ignored). */
23
+ export declare function findEnvByApiUrl(apiUrl: string): DcdEnvironment | undefined;
24
+ /** Map a caller-supplied API URL to one of the known environments. */
25
+ export declare function inferEnvFromApiUrl(apiUrl: string): DcdEnvName;
26
+ /**
27
+ * Frontend URL to open for a given API URL. Same as
28
+ * `ENVIRONMENTS[inferEnvFromApiUrl(apiUrl)].frontendUrl`, except that when the
29
+ * API is running on localhost we assume the frontend is too (vite dev server).
30
+ */
31
+ export declare function resolveFrontendUrl(apiUrl: string): string;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Per-env devicecloud.dev coordinates — single source of truth for the URLs
3
+ * and Supabase credentials the CLI uses.
4
+ *
5
+ * **These `anonKey` values are public.** Supabase anon keys are JWTs designed
6
+ * to be embedded in client code (web apps, mobile apps, CLIs). They only
7
+ * grant whatever the project's RLS policies expose. Don't worry about seeing
8
+ * them here — every Supabase-based product ships its anon key the same way.
9
+ * https://supabase.com/docs/guides/api/api-keys
10
+ */
11
+ export const ENVIRONMENTS = {
12
+ prod: {
13
+ apiUrl: 'https://api.devicecloud.dev',
14
+ frontendUrl: 'https://console.devicecloud.dev',
15
+ supabase: {
16
+ url: 'https://cloud.devicecloud.dev',
17
+ projectRef: 'pgydnphbimetinsgfkbo',
18
+ anonKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneWRucGhiaW1ldGluc2dma2JvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDc1OTQzNDYsImV4cCI6MjAyMzE3MDM0Nn0.hAYOMFxxwX1exkQkY9xyQJGC_GhGnyogkj2N-kBkMI8',
19
+ },
20
+ },
21
+ dev: {
22
+ apiUrl: 'https://api.dev.devicecloud.dev',
23
+ frontendUrl: 'https://dev.console.devicecloud.dev',
24
+ supabase: {
25
+ url: 'https://lbmsowehtjwnqlurpemb.supabase.co',
26
+ projectRef: 'lbmsowehtjwnqlurpemb',
27
+ anonKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxibXNvd2VodGp3bnFsdXJwZW1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDkyMTg0ODcsImV4cCI6MjAyNDc5NDQ4N30.zeLTMAuZ_WwYvGdeP0kdvL_Zrs-RQee5APPyxmWq7qQ',
28
+ },
29
+ },
30
+ };
31
+ /** Exact-match lookup of a known environment by API URL (trailing slashes ignored). */
32
+ export function findEnvByApiUrl(apiUrl) {
33
+ const normalize = (u) => u.replace(/\/+$/, '');
34
+ const needle = normalize(apiUrl);
35
+ return Object.values(ENVIRONMENTS).find((env) => normalize(env.apiUrl) === needle);
36
+ }
37
+ /** Map a caller-supplied API URL to one of the known environments. */
38
+ export function inferEnvFromApiUrl(apiUrl) {
39
+ if (apiUrl.includes('api.dev.') || apiUrl.includes('localhost'))
40
+ return 'dev';
41
+ return 'prod';
42
+ }
43
+ /**
44
+ * Frontend URL to open for a given API URL. Same as
45
+ * `ENVIRONMENTS[inferEnvFromApiUrl(apiUrl)].frontendUrl`, except that when the
46
+ * API is running on localhost we assume the frontend is too (vite dev server).
47
+ */
48
+ export function resolveFrontendUrl(apiUrl) {
49
+ if (apiUrl.includes('localhost'))
50
+ return 'http://localhost:5173';
51
+ return ENVIRONMENTS[inferEnvFromApiUrl(apiUrl)].frontendUrl;
52
+ }
@@ -2,6 +2,14 @@
2
2
  * API authentication and configuration flags
3
3
  */
4
4
  export declare const apiFlags: {
5
- apiKey: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
6
- apiUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
5
+ readonly 'api-key': {
6
+ readonly type: "string";
7
+ readonly alias: ["apiKey"];
8
+ readonly description: "API key for devicecloud.dev (find this in the console UI). You can also set the DEVICE_CLOUD_API_KEY environment variable.";
9
+ };
10
+ readonly 'api-url': {
11
+ readonly type: "string";
12
+ readonly alias: ["apiURL", "apiUrl"];
13
+ readonly description: "API base URL (defaults to the URL stored by `dcd login`, else prod)";
14
+ };
7
15
  };
@@ -1,19 +1,18 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.apiFlags = void 0;
4
- const core_1 = require("@oclif/core");
5
1
  /**
6
2
  * API authentication and configuration flags
7
3
  */
8
- exports.apiFlags = {
9
- apiKey: core_1.Flags.string({
10
- aliases: ['api-key'],
4
+ export const apiFlags = {
5
+ 'api-key': {
6
+ type: 'string',
7
+ alias: ['apiKey'],
11
8
  description: 'API key for devicecloud.dev (find this in the console UI). You can also set the DEVICE_CLOUD_API_KEY environment variable.',
12
- }),
13
- apiUrl: core_1.Flags.string({
14
- aliases: ['api-url', 'apiURL'],
15
- default: 'https://api.devicecloud.dev',
16
- description: 'API base URL',
17
- hidden: true,
18
- }),
9
+ },
10
+ 'api-url': {
11
+ type: 'string',
12
+ alias: ['apiURL', 'apiUrl'],
13
+ // No citty `default` here: commands resolve the effective URL via
14
+ // resolveApiUrl() so a value stored by `dcd login` (e.g. dev/staging) is
15
+ // honored instead of always defaulting to prod. See utils/config-store.ts.
16
+ description: 'API base URL (defaults to the URL stored by `dcd login`, else prod)',
17
+ },
19
18
  };
@@ -2,8 +2,21 @@
2
2
  * Binary upload and management flags
3
3
  */
4
4
  export declare const binaryFlags: {
5
- 'app-binary-id': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
6
- 'app-file': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
7
- 'app-url': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
- 'ignore-sha-check': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
5
+ readonly 'app-binary-id': {
6
+ readonly type: "string";
7
+ readonly description: "The ID of the app binary previously uploaded to devicecloud.dev";
8
+ };
9
+ readonly 'app-file': {
10
+ readonly type: "string";
11
+ readonly description: "App binary to run your flows against";
12
+ readonly valueHint: "path";
13
+ };
14
+ readonly 'app-url': {
15
+ readonly type: "string";
16
+ 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.";
17
+ };
18
+ readonly 'ignore-sha-check': {
19
+ readonly type: "boolean";
20
+ readonly description: "Ignore the sha hash check and upload the binary regardless of whether it already exists (not recommended)";
21
+ };
9
22
  };
@@ -1,26 +1,22 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.binaryFlags = void 0;
4
- const core_1 = require("@oclif/core");
5
1
  /**
6
2
  * Binary upload and management flags
7
3
  */
8
- exports.binaryFlags = {
9
- 'app-binary-id': core_1.Flags.string({
10
- aliases: ['app-binary-id'],
4
+ export const binaryFlags = {
5
+ 'app-binary-id': {
6
+ type: 'string',
11
7
  description: 'The ID of the app binary previously uploaded to devicecloud.dev',
12
- }),
13
- 'app-file': core_1.Flags.file({
14
- aliases: ['app-file'],
8
+ },
9
+ 'app-file': {
10
+ type: 'string',
15
11
  description: 'App binary to run your flows against',
16
- exclusive: ['app-url'],
17
- }),
18
- 'app-url': core_1.Flags.string({
19
- aliases: ['app-url'],
12
+ valueHint: 'path',
13
+ },
14
+ 'app-url': {
15
+ type: 'string',
20
16
  description: 'Signed URL to an Expo iOS build (.tar.gz). The archive is downloaded and extracted automatically. Expo signed URLs expire after ~1 hour.',
21
- exclusive: ['app-file'],
22
- }),
23
- 'ignore-sha-check': core_1.Flags.boolean({
17
+ },
18
+ 'ignore-sha-check': {
19
+ type: 'boolean',
24
20
  description: 'Ignore the sha hash check and upload the binary regardless of whether it already exists (not recommended)',
25
- }),
21
+ },
26
22
  };
@@ -2,15 +2,53 @@
2
2
  * Device-specific flags for Android and iOS configuration
3
3
  */
4
4
  export declare const deviceFlags: {
5
- 'android-api-level': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
6
- 'android-device': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
7
- 'device-locale': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
- 'google-play': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
- 'ios-device': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
- 'ios-version': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
11
- orientation: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
12
- 'show-crosshairs': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
- 'maestro-chrome-onboarding': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
14
- 'android-no-snapshot': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
15
- 'disable-animations': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
5
+ readonly 'android-api-level': {
6
+ readonly type: "string";
7
+ readonly description: `[Android only] Android API level to run your flow against (options: ${string})`;
8
+ };
9
+ readonly 'android-device': {
10
+ readonly type: "string";
11
+ readonly description: `[Android only] Android device to run your flow against (options: ${string})`;
12
+ };
13
+ readonly 'device-locale': {
14
+ readonly type: "string";
15
+ 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";
16
+ };
17
+ readonly 'google-play': {
18
+ readonly type: "boolean";
19
+ readonly default: false;
20
+ readonly description: "[Android only] Run your flow against Google Play devices";
21
+ };
22
+ readonly 'ios-device': {
23
+ readonly type: "string";
24
+ readonly description: `[iOS only] iOS device to run your flow against (options: ${string})`;
25
+ };
26
+ readonly 'ios-version': {
27
+ readonly type: "string";
28
+ readonly description: `[iOS only] iOS version to run your flow against (options: ${string})`;
29
+ };
30
+ readonly orientation: {
31
+ readonly type: "string";
32
+ readonly description: "[Android only] The orientation of the device to run your flow against (0 = portrait, 90 = landscape)";
33
+ };
34
+ readonly 'show-crosshairs': {
35
+ readonly type: "boolean";
36
+ readonly default: false;
37
+ readonly description: "[Android only] Display crosshairs for screen interactions during test execution";
38
+ };
39
+ readonly 'maestro-chrome-onboarding': {
40
+ readonly type: "boolean";
41
+ readonly default: false;
42
+ 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.";
43
+ };
44
+ readonly 'android-no-snapshot': {
45
+ readonly type: "boolean";
46
+ readonly default: false;
47
+ 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.";
48
+ };
49
+ readonly 'disable-animations': {
50
+ readonly type: "boolean";
51
+ readonly default: false;
52
+ 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.";
53
+ };
16
54
  };
@@ -1,54 +1,59 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.deviceFlags = void 0;
4
- const core_1 = require("@oclif/core");
5
- const device_types_1 = require("../../types/domain/device.types");
1
+ import { EAndroidApiLevels, EAndroidDevices, EiOSDevices, EiOSVersions, } from '../../types/domain/device.types.js';
2
+ const androidApiLevels = Object.values(EAndroidApiLevels).join(', ');
3
+ const androidDevices = Object.values(EAndroidDevices).join(', ');
4
+ const iosDevices = Object.values(EiOSDevices).join(', ');
5
+ const iosVersions = Object.values(EiOSVersions).join(', ');
6
6
  /**
7
7
  * Device-specific flags for Android and iOS configuration
8
8
  */
9
- exports.deviceFlags = {
10
- 'android-api-level': core_1.Flags.string({
11
- description: '[Android only] Android API level to run your flow against',
12
- options: Object.values(device_types_1.EAndroidApiLevels),
13
- }),
14
- 'android-device': core_1.Flags.string({
15
- description: '[Android only] Android device to run your flow against',
16
- options: Object.values(device_types_1.EAndroidDevices),
17
- }),
18
- 'device-locale': core_1.Flags.string({
9
+ export const deviceFlags = {
10
+ 'android-api-level': {
11
+ type: 'string',
12
+ description: `[Android only] Android API level to run your flow against (options: ${androidApiLevels})`,
13
+ },
14
+ 'android-device': {
15
+ type: 'string',
16
+ description: `[Android only] Android device to run your flow against (options: ${androidDevices})`,
17
+ },
18
+ 'device-locale': {
19
+ type: 'string',
19
20
  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',
20
- }),
21
- 'google-play': core_1.Flags.boolean({
22
- aliases: ['google-play'],
21
+ },
22
+ 'google-play': {
23
+ type: 'boolean',
23
24
  default: false,
24
25
  description: '[Android only] Run your flow against Google Play devices',
25
- }),
26
- 'ios-device': core_1.Flags.string({
27
- description: '[iOS only] iOS device to run your flow against',
28
- options: Object.values(device_types_1.EiOSDevices),
29
- }),
30
- 'ios-version': core_1.Flags.string({
31
- description: '[iOS only] iOS version to run your flow against',
32
- options: Object.values(device_types_1.EiOSVersions),
33
- }),
34
- orientation: core_1.Flags.string({
26
+ },
27
+ 'ios-device': {
28
+ type: 'string',
29
+ description: `[iOS only] iOS device to run your flow against (options: ${iosDevices})`,
30
+ },
31
+ 'ios-version': {
32
+ type: 'string',
33
+ description: `[iOS only] iOS version to run your flow against (options: ${iosVersions})`,
34
+ },
35
+ orientation: {
36
+ type: 'string',
35
37
  description: '[Android only] The orientation of the device to run your flow against (0 = portrait, 90 = landscape)',
36
- options: ['0', '90'],
37
- }),
38
- 'show-crosshairs': core_1.Flags.boolean({
38
+ },
39
+ 'show-crosshairs': {
40
+ type: 'boolean',
39
41
  default: false,
40
42
  description: '[Android only] Display crosshairs for screen interactions during test execution',
41
- }),
42
- 'maestro-chrome-onboarding': core_1.Flags.boolean({
43
+ },
44
+ 'maestro-chrome-onboarding': {
45
+ type: 'boolean',
43
46
  default: false,
44
47
  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.',
45
- }),
46
- 'android-no-snapshot': core_1.Flags.boolean({
48
+ },
49
+ 'android-no-snapshot': {
50
+ type: 'boolean',
47
51
  default: false,
48
52
  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.',
49
- }),
50
- 'disable-animations': core_1.Flags.boolean({
53
+ },
54
+ 'disable-animations': {
55
+ type: 'boolean',
51
56
  default: false,
52
57
  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
- }),
58
+ },
54
59
  };
@@ -2,10 +2,31 @@
2
2
  * Environment configuration and metadata flags
3
3
  */
4
4
  export declare const environmentFlags: {
5
- env: import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
6
- metadata: import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
7
- mitmHost: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
- mitmPath: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
9
- 'moropo-v1-api-key': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
- name: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
5
+ readonly env: {
6
+ readonly type: "string";
7
+ readonly alias: ["e"];
8
+ readonly description: "One or more environment variables to inject into your flows (format: KEY=VALUE, may be repeated)";
9
+ readonly valueHint: "KEY=VALUE";
10
+ };
11
+ readonly metadata: {
12
+ readonly type: "string";
13
+ readonly alias: ["m"];
14
+ readonly description: "Arbitrary key-value metadata to include with your test run (format: key=value, may be repeated)";
15
+ };
16
+ readonly mitmHost: {
17
+ readonly type: "string";
18
+ readonly description: "used for mitmproxy support, enterprise only, contact support if interested";
19
+ };
20
+ readonly mitmPath: {
21
+ readonly type: "string";
22
+ readonly description: "used for mitmproxy support, enterprise only, contact support if interested";
23
+ };
24
+ readonly 'moropo-v1-api-key': {
25
+ readonly type: "string";
26
+ readonly description: "API key for Moropo v1 integration";
27
+ };
28
+ readonly name: {
29
+ readonly type: "string";
30
+ readonly description: "A custom name for your upload (useful for tagging commits etc)";
31
+ };
11
32
  };