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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +40 -2
  2. package/dist/commands/artifacts.d.ts +47 -18
  3. package/dist/commands/artifacts.js +68 -60
  4. package/dist/commands/cloud.d.ts +228 -88
  5. package/dist/commands/cloud.js +389 -288
  6. package/dist/commands/list.d.ts +39 -38
  7. package/dist/commands/list.js +122 -127
  8. package/dist/commands/live.d.ts +2 -0
  9. package/dist/commands/live.js +513 -0
  10. package/dist/commands/login.d.ts +17 -0
  11. package/dist/commands/login.js +250 -0
  12. package/dist/commands/logout.d.ts +2 -0
  13. package/dist/commands/logout.js +32 -0
  14. package/dist/commands/status.d.ts +23 -42
  15. package/dist/commands/status.js +162 -173
  16. package/dist/commands/switch-org.d.ts +12 -0
  17. package/dist/commands/switch-org.js +78 -0
  18. package/dist/commands/upgrade.d.ts +2 -0
  19. package/dist/commands/upgrade.js +122 -0
  20. package/dist/commands/upload.d.ts +33 -18
  21. package/dist/commands/upload.js +62 -67
  22. package/dist/commands/whoami.d.ts +2 -0
  23. package/dist/commands/whoami.js +34 -0
  24. package/dist/config/environments.d.ts +31 -0
  25. package/dist/config/environments.js +58 -0
  26. package/dist/config/flags/api.flags.d.ts +10 -2
  27. package/dist/config/flags/api.flags.js +12 -10
  28. package/dist/config/flags/binary.flags.d.ts +17 -4
  29. package/dist/config/flags/binary.flags.js +13 -14
  30. package/dist/config/flags/device.flags.d.ts +49 -11
  31. package/dist/config/flags/device.flags.js +41 -33
  32. package/dist/config/flags/environment.flags.d.ts +27 -6
  33. package/dist/config/flags/environment.flags.js +23 -25
  34. package/dist/config/flags/execution.flags.d.ts +35 -8
  35. package/dist/config/flags/execution.flags.js +30 -37
  36. package/dist/config/flags/github.flags.d.ts +23 -5
  37. package/dist/config/flags/github.flags.js +18 -11
  38. package/dist/config/flags/output.flags.d.ts +57 -13
  39. package/dist/config/flags/output.flags.js +47 -43
  40. package/dist/constants.d.ts +218 -51
  41. package/dist/constants.js +2 -2
  42. package/dist/gateways/api-gateway.d.ts +43 -12
  43. package/dist/gateways/api-gateway.js +240 -100
  44. package/dist/gateways/cli-auth-gateway.d.ts +13 -0
  45. package/dist/gateways/cli-auth-gateway.js +57 -0
  46. package/dist/gateways/supabase-gateway.d.ts +11 -11
  47. package/dist/gateways/supabase-gateway.js +15 -39
  48. package/dist/index.d.ts +2 -1
  49. package/dist/index.js +93 -2
  50. package/dist/methods.d.ts +3 -5
  51. package/dist/methods.js +170 -178
  52. package/dist/services/device-validation.service.d.ts +8 -0
  53. package/dist/services/device-validation.service.js +55 -35
  54. package/dist/services/execution-plan.service.js +27 -15
  55. package/dist/services/execution-plan.utils.d.ts +3 -0
  56. package/dist/services/execution-plan.utils.js +10 -32
  57. package/dist/services/metadata-extractor.service.d.ts +0 -2
  58. package/dist/services/metadata-extractor.service.js +57 -57
  59. package/dist/services/moropo.service.js +25 -24
  60. package/dist/services/report-download.service.d.ts +12 -1
  61. package/dist/services/report-download.service.js +31 -20
  62. package/dist/services/results-polling.service.d.ts +6 -7
  63. package/dist/services/results-polling.service.js +80 -33
  64. package/dist/services/telemetry.service.d.ts +40 -0
  65. package/dist/services/telemetry.service.js +230 -0
  66. package/dist/services/test-submission.service.js +2 -1
  67. package/dist/services/version.service.d.ts +3 -2
  68. package/dist/services/version.service.js +27 -11
  69. package/dist/types/domain/auth.types.d.ts +12 -0
  70. package/dist/types/{schema.types.js → domain/auth.types.js} +0 -1
  71. package/dist/types/domain/live.types.d.ts +76 -0
  72. package/dist/types/domain/live.types.js +4 -0
  73. package/dist/utils/auth.d.ts +13 -0
  74. package/dist/utils/auth.js +142 -0
  75. package/dist/utils/cli.d.ts +35 -0
  76. package/dist/utils/cli.js +127 -0
  77. package/dist/utils/compatibility.d.ts +2 -1
  78. package/dist/utils/compatibility.js +2 -2
  79. package/dist/utils/config-store.d.ts +35 -0
  80. package/dist/utils/config-store.js +125 -0
  81. package/dist/utils/connectivity.js +7 -3
  82. package/dist/utils/expo.js +14 -3
  83. package/dist/utils/orgs.d.ts +11 -0
  84. package/dist/utils/orgs.js +40 -0
  85. package/dist/utils/paths.d.ts +11 -0
  86. package/dist/utils/paths.js +24 -0
  87. package/dist/utils/progress.d.ts +13 -0
  88. package/dist/utils/progress.js +50 -0
  89. package/dist/utils/styling.d.ts +13 -5
  90. package/dist/utils/styling.js +37 -7
  91. package/package.json +26 -38
  92. package/bin/dev.cmd +0 -3
  93. package/bin/dev.js +0 -6
  94. package/bin/run.cmd +0 -3
  95. package/bin/run.js +0 -7
  96. package/dist/types/schema.types.d.ts +0 -2702
  97. package/oclif.manifest.json +0 -884
@@ -1,76 +1,80 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const core_1 = require("@oclif/core");
3
+ exports.uploadCommand = void 0;
4
+ const citty_1 = require("citty");
4
5
  const promises_1 = require("node:fs/promises");
5
- const constants_1 = require("../constants");
6
+ const api_flags_1 = require("../config/flags/api.flags");
7
+ const binary_flags_1 = require("../config/flags/binary.flags");
8
+ const output_flags_1 = require("../config/flags/output.flags");
6
9
  const methods_1 = require("../methods");
10
+ const auth_1 = require("../utils/auth");
11
+ const cli_1 = require("../utils/cli");
12
+ const config_store_1 = require("../utils/config-store");
7
13
  const expo_1 = require("../utils/expo");
8
14
  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',
15
+ exports.uploadCommand = (0, citty_1.defineCommand)({
16
+ meta: {
17
+ name: 'upload',
18
+ description: 'Upload an app binary to devicecloud.dev',
19
+ },
20
+ args: {
21
+ ...api_flags_1.apiFlags,
22
+ 'app-url': binary_flags_1.binaryFlags['app-url'],
23
+ 'ignore-sha-check': binary_flags_1.binaryFlags['ignore-sha-check'],
24
+ debug: output_flags_1.outputFlags.debug,
25
+ json: output_flags_1.outputFlags.json,
26
+ appFile: {
27
+ type: 'positional',
14
28
  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() {
29
+ 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/...)',
30
+ },
31
+ },
32
+ async run({ args }) {
33
33
  const tempFiles = [];
34
+ const json = Boolean(args.json);
35
+ // When --json is set, suppress chatty progress logs so stdout stays parseable.
36
+ const out = (m) => {
37
+ if (!json)
38
+ cli_1.logger.log(m);
39
+ };
34
40
  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;
41
+ const apiKeyFlag = args['api-key'];
42
+ const apiUrl = (0, config_store_1.resolveApiUrl)(args['api-url']);
43
+ const appUrl = args['app-url'];
44
+ const ignoreShaCheck = Boolean(args['ignore-sha-check']);
45
+ const debug = Boolean(args.debug);
46
+ const positional = args.appFile;
47
+ const auth = await (0, auth_1.resolveAuth)({ apiKeyFlag });
48
+ let resolvedFile = appUrl ?? positional;
43
49
  if (!resolvedFile) {
44
- throw new Error('You must provide an app file path or a signed Expo URL via --app-url');
50
+ throw new cli_1.CliError('You must provide an app file path or a signed Expo URL via --app-url');
45
51
  }
46
- // Download from URL if needed
47
52
  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);
53
+ out(` ${styling_1.colors.dim('→ Downloading Expo build from URL...')}`);
54
+ const tarPath = await (0, expo_1.downloadExpoUrl)(resolvedFile, debug);
50
55
  tempFiles.push(tarPath);
51
56
  resolvedFile = tarPath;
52
57
  }
53
- // Extract .tar.gz if needed
54
58
  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);
59
+ out(` ${styling_1.colors.dim('→ Extracting Expo archive...')}`);
60
+ const extractDir = await (0, expo_1.extractTarGz)(resolvedFile, debug);
57
61
  tempFiles.push(extractDir);
58
62
  resolvedFile = await (0, expo_1.findAppBundle)(extractDir);
59
63
  if (debug) {
60
- this.log(`[DEBUG] Found .app bundle at: ${resolvedFile}`);
64
+ out(`[DEBUG] Found .app bundle at: ${resolvedFile}`);
61
65
  }
62
66
  }
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)');
67
+ if (!['.apk', '.app', '.zip', '.tar.gz'].some((ext) => resolvedFile.endsWith(ext))) {
68
+ throw new cli_1.CliError('App file must be a .apk for Android, .app/.zip for iOS, or .tar.gz (Expo iOS build)');
65
69
  }
66
70
  if (resolvedFile.endsWith('.zip')) {
67
71
  await (0, methods_1.verifyAppZip)(resolvedFile);
68
72
  }
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('');
73
+ out((0, styling_1.sectionHeader)('Uploading app binary'));
74
+ out(` ${styling_1.colors.dim('→ File:')} ${styling_1.colors.highlight(resolvedFile)}`);
75
+ out('');
72
76
  const appBinaryId = await (0, methods_1.uploadBinary)({
73
- apiKey,
77
+ auth,
74
78
  apiUrl,
75
79
  debug,
76
80
  filePath: resolvedFile,
@@ -78,32 +82,23 @@ class Upload extends core_1.Command {
78
82
  log: !json,
79
83
  });
80
84
  if (json) {
81
- return { appBinaryId };
85
+ // eslint-disable-next-line no-console
86
+ console.log(JSON.stringify({ appBinaryId }, null, 2));
87
+ return;
82
88
  }
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`));
89
+ cli_1.logger.log(`\n${styling_1.symbols.success} ${styling_1.colors.bold('Upload complete')}`);
90
+ cli_1.logger.log(` ${styling_1.colors.dim('Binary ID:')} ${(0, styling_1.formatId)(appBinaryId)}\n`);
91
+ cli_1.logger.log(styling_1.colors.dim('You can use this Binary ID in subsequent test runs with:'));
92
+ cli_1.logger.log(styling_1.colors.info(`dcd cloud --app-binary-id ${appBinaryId} path/to/flow.yaml\n`));
87
93
  }
88
94
  catch (error) {
89
- this.error(error, { exit: 1 });
95
+ cli_1.logger.error(error, { exit: 1, json });
90
96
  }
91
97
  finally {
92
98
  for (const p of tempFiles) {
93
99
  await (0, promises_1.rm)(p, { recursive: true, force: true }).catch(() => { });
94
100
  }
95
101
  }
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;
102
+ },
103
+ });
104
+ exports.default = exports.uploadCommand;
@@ -0,0 +1,2 @@
1
+ export declare const whoamiCommand: import("citty").CommandDef<import("citty").ArgsDef>;
2
+ export default whoamiCommand;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.whoamiCommand = void 0;
4
+ /**
5
+ * `dcd whoami` — prints the logged-in user + active org from stored config.
6
+ */
7
+ const citty_1 = require("citty");
8
+ const cli_1 = require("../utils/cli");
9
+ const config_store_1 = require("../utils/config-store");
10
+ const styling_1 = require("../utils/styling");
11
+ exports.whoamiCommand = (0, citty_1.defineCommand)({
12
+ meta: {
13
+ name: 'whoami',
14
+ description: 'Show the logged-in user and active organization',
15
+ },
16
+ run() {
17
+ const config = (0, config_store_1.readConfig)();
18
+ if (!config?.session) {
19
+ cli_1.logger.log(`${styling_1.symbols.info} Not logged in. Run ${styling_1.colors.highlight('dcd login')} or set ${styling_1.colors.highlight('DEVICE_CLOUD_API_KEY')}.`);
20
+ return;
21
+ }
22
+ cli_1.logger.log((0, styling_1.sectionHeader)('devicecloud.dev'));
23
+ cli_1.logger.log(` ${styling_1.colors.dim('User:')} ${styling_1.colors.highlight(config.session.user_email)}`);
24
+ if (config.current_org_name) {
25
+ cli_1.logger.log(` ${styling_1.colors.dim('Org:')} ${styling_1.colors.highlight(config.current_org_name)}`);
26
+ }
27
+ else if (config.current_org_id) {
28
+ // Fallback for sessions stored before we persisted org name alongside id.
29
+ cli_1.logger.log(` ${styling_1.colors.dim('Org:')} ${styling_1.colors.highlight(config.current_org_id)}`);
30
+ }
31
+ cli_1.logger.log(` ${styling_1.colors.dim('Env:')} ${config.env}`);
32
+ },
33
+ });
34
+ exports.default = exports.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,58 @@
1
+ "use strict";
2
+ /**
3
+ * Per-env devicecloud.dev coordinates — single source of truth for the URLs
4
+ * and Supabase credentials the CLI uses.
5
+ *
6
+ * **These `anonKey` values are public.** Supabase anon keys are JWTs designed
7
+ * to be embedded in client code (web apps, mobile apps, CLIs). They only
8
+ * grant whatever the project's RLS policies expose. Don't worry about seeing
9
+ * them here — every Supabase-based product ships its anon key the same way.
10
+ * https://supabase.com/docs/guides/api/api-keys
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ENVIRONMENTS = void 0;
14
+ exports.findEnvByApiUrl = findEnvByApiUrl;
15
+ exports.inferEnvFromApiUrl = inferEnvFromApiUrl;
16
+ exports.resolveFrontendUrl = resolveFrontendUrl;
17
+ exports.ENVIRONMENTS = {
18
+ prod: {
19
+ apiUrl: 'https://api.devicecloud.dev',
20
+ frontendUrl: 'https://console.devicecloud.dev',
21
+ supabase: {
22
+ url: 'https://cloud.devicecloud.dev',
23
+ projectRef: 'pgydnphbimetinsgfkbo',
24
+ anonKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneWRucGhiaW1ldGluc2dma2JvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDc1OTQzNDYsImV4cCI6MjAyMzE3MDM0Nn0.hAYOMFxxwX1exkQkY9xyQJGC_GhGnyogkj2N-kBkMI8',
25
+ },
26
+ },
27
+ dev: {
28
+ apiUrl: 'https://api.dev.devicecloud.dev',
29
+ frontendUrl: 'https://dev.console.devicecloud.dev',
30
+ supabase: {
31
+ url: 'https://lbmsowehtjwnqlurpemb.supabase.co',
32
+ projectRef: 'lbmsowehtjwnqlurpemb',
33
+ anonKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxibXNvd2VodGp3bnFsdXJwZW1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDkyMTg0ODcsImV4cCI6MjAyNDc5NDQ4N30.zeLTMAuZ_WwYvGdeP0kdvL_Zrs-RQee5APPyxmWq7qQ',
34
+ },
35
+ },
36
+ };
37
+ /** Exact-match lookup of a known environment by API URL (trailing slashes ignored). */
38
+ function findEnvByApiUrl(apiUrl) {
39
+ const normalize = (u) => u.replace(/\/+$/, '');
40
+ const needle = normalize(apiUrl);
41
+ return Object.values(exports.ENVIRONMENTS).find((env) => normalize(env.apiUrl) === needle);
42
+ }
43
+ /** Map a caller-supplied API URL to one of the known environments. */
44
+ function inferEnvFromApiUrl(apiUrl) {
45
+ if (apiUrl.includes('api.dev.') || apiUrl.includes('localhost'))
46
+ return 'dev';
47
+ return 'prod';
48
+ }
49
+ /**
50
+ * Frontend URL to open for a given API URL. Same as
51
+ * `ENVIRONMENTS[inferEnvFromApiUrl(apiUrl)].frontendUrl`, except that when the
52
+ * API is running on localhost we assume the frontend is too (vite dev server).
53
+ */
54
+ function resolveFrontendUrl(apiUrl) {
55
+ if (apiUrl.includes('localhost'))
56
+ return 'http://localhost:5173';
57
+ return exports.ENVIRONMENTS[inferEnvFromApiUrl(apiUrl)].frontendUrl;
58
+ }
@@ -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,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.apiFlags = void 0;
4
- const core_1 = require("@oclif/core");
5
4
  /**
6
5
  * API authentication and configuration flags
7
6
  */
8
7
  exports.apiFlags = {
9
- apiKey: core_1.Flags.string({
10
- aliases: ['api-key'],
8
+ 'api-key': {
9
+ type: 'string',
10
+ alias: ['apiKey'],
11
11
  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
- }),
12
+ },
13
+ 'api-url': {
14
+ type: 'string',
15
+ alias: ['apiURL', 'apiUrl'],
16
+ // No citty `default` here: commands resolve the effective URL via
17
+ // resolveApiUrl() so a value stored by `dcd login` (e.g. dev/staging) is
18
+ // honored instead of always defaulting to prod. See utils/config-store.ts.
19
+ description: 'API base URL (defaults to the URL stored by `dcd login`, else prod)',
20
+ },
19
21
  };
@@ -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,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.binaryFlags = void 0;
4
- const core_1 = require("@oclif/core");
5
4
  /**
6
5
  * Binary upload and management flags
7
6
  */
8
7
  exports.binaryFlags = {
9
- 'app-binary-id': core_1.Flags.string({
10
- aliases: ['app-binary-id'],
8
+ 'app-binary-id': {
9
+ type: 'string',
11
10
  description: 'The ID of the app binary previously uploaded to devicecloud.dev',
12
- }),
13
- 'app-file': core_1.Flags.file({
14
- aliases: ['app-file'],
11
+ },
12
+ 'app-file': {
13
+ type: 'string',
15
14
  description: 'App binary to run your flows against',
16
- exclusive: ['app-url'],
17
- }),
18
- 'app-url': core_1.Flags.string({
19
- aliases: ['app-url'],
15
+ valueHint: 'path',
16
+ },
17
+ 'app-url': {
18
+ type: 'string',
20
19
  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({
20
+ },
21
+ 'ignore-sha-check': {
22
+ type: 'boolean',
24
23
  description: 'Ignore the sha hash check and upload the binary regardless of whether it already exists (not recommended)',
25
- }),
24
+ },
26
25
  };
@@ -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,62 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.deviceFlags = void 0;
4
- const core_1 = require("@oclif/core");
5
4
  const device_types_1 = require("../../types/domain/device.types");
5
+ const androidApiLevels = Object.values(device_types_1.EAndroidApiLevels).join(', ');
6
+ const androidDevices = Object.values(device_types_1.EAndroidDevices).join(', ');
7
+ const iosDevices = Object.values(device_types_1.EiOSDevices).join(', ');
8
+ const iosVersions = Object.values(device_types_1.EiOSVersions).join(', ');
6
9
  /**
7
10
  * Device-specific flags for Android and iOS configuration
8
11
  */
9
12
  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({
13
+ 'android-api-level': {
14
+ type: 'string',
15
+ description: `[Android only] Android API level to run your flow against (options: ${androidApiLevels})`,
16
+ },
17
+ 'android-device': {
18
+ type: 'string',
19
+ description: `[Android only] Android device to run your flow against (options: ${androidDevices})`,
20
+ },
21
+ 'device-locale': {
22
+ type: 'string',
19
23
  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'],
24
+ },
25
+ 'google-play': {
26
+ type: 'boolean',
23
27
  default: false,
24
28
  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({
29
+ },
30
+ 'ios-device': {
31
+ type: 'string',
32
+ description: `[iOS only] iOS device to run your flow against (options: ${iosDevices})`,
33
+ },
34
+ 'ios-version': {
35
+ type: 'string',
36
+ description: `[iOS only] iOS version to run your flow against (options: ${iosVersions})`,
37
+ },
38
+ orientation: {
39
+ type: 'string',
35
40
  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({
41
+ },
42
+ 'show-crosshairs': {
43
+ type: 'boolean',
39
44
  default: false,
40
45
  description: '[Android only] Display crosshairs for screen interactions during test execution',
41
- }),
42
- 'maestro-chrome-onboarding': core_1.Flags.boolean({
46
+ },
47
+ 'maestro-chrome-onboarding': {
48
+ type: 'boolean',
43
49
  default: false,
44
50
  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({
51
+ },
52
+ 'android-no-snapshot': {
53
+ type: 'boolean',
47
54
  default: false,
48
55
  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({
56
+ },
57
+ 'disable-animations': {
58
+ type: 'boolean',
51
59
  default: false,
52
60
  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
- }),
61
+ },
54
62
  };
@@ -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
  };