@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.
- package/README.md +35 -0
- package/dist/commands/artifacts.d.ts +28 -28
- package/dist/commands/artifacts.js +20 -23
- package/dist/commands/cloud.d.ts +57 -57
- package/dist/commands/cloud.js +173 -186
- package/dist/commands/list.d.ts +22 -22
- package/dist/commands/list.js +36 -38
- package/dist/commands/live.js +134 -127
- package/dist/commands/login.d.ts +11 -11
- package/dist/commands/login.js +46 -44
- package/dist/commands/logout.js +16 -18
- package/dist/commands/status.d.ts +11 -11
- package/dist/commands/status.js +45 -43
- package/dist/commands/switch-org.d.ts +7 -7
- package/dist/commands/switch-org.js +19 -21
- package/dist/commands/upgrade.js +29 -31
- package/dist/commands/upload.d.ts +10 -10
- package/dist/commands/upload.js +42 -43
- package/dist/commands/whoami.js +17 -20
- package/dist/config/environments.js +6 -12
- package/dist/config/flags/api.flags.js +1 -4
- package/dist/config/flags/binary.flags.js +1 -4
- package/dist/config/flags/device.flags.js +6 -9
- package/dist/config/flags/environment.flags.js +1 -4
- package/dist/config/flags/execution.flags.js +1 -4
- package/dist/config/flags/github.flags.js +1 -4
- package/dist/config/flags/output.flags.js +1 -4
- package/dist/constants.js +15 -18
- package/dist/gateways/api-gateway.d.ts +31 -6
- package/dist/gateways/api-gateway.js +70 -16
- package/dist/gateways/cli-auth-gateway.d.ts +1 -1
- package/dist/gateways/cli-auth-gateway.js +3 -6
- package/dist/gateways/realtime-gateway.d.ts +32 -0
- package/dist/gateways/realtime-gateway.js +103 -0
- package/dist/gateways/supabase-gateway.d.ts +1 -1
- package/dist/gateways/supabase-gateway.js +10 -14
- package/dist/index.js +41 -38
- package/dist/mcp/context.d.ts +33 -0
- package/dist/mcp/context.js +33 -0
- package/dist/mcp/helpers.d.ts +16 -0
- package/dist/mcp/helpers.js +34 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +24 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +27 -0
- package/dist/mcp/tools/download-artifacts.d.ts +11 -0
- package/dist/mcp/tools/download-artifacts.js +84 -0
- package/dist/mcp/tools/get-status.d.ts +7 -0
- package/dist/mcp/tools/get-status.js +39 -0
- package/dist/mcp/tools/list-devices.d.ts +7 -0
- package/dist/mcp/tools/list-devices.js +27 -0
- package/dist/mcp/tools/list-runs.d.ts +3 -0
- package/dist/mcp/tools/list-runs.js +60 -0
- package/dist/mcp/tools/run-cloud-test.d.ts +14 -0
- package/dist/mcp/tools/run-cloud-test.js +233 -0
- package/dist/methods.d.ts +32 -1
- package/dist/methods.js +125 -66
- package/dist/services/device-validation.service.d.ts +1 -1
- package/dist/services/device-validation.service.js +1 -5
- package/dist/services/execution-plan.service.js +14 -17
- package/dist/services/execution-plan.utils.js +15 -23
- package/dist/services/flow-paths.d.ts +17 -0
- package/dist/services/flow-paths.js +52 -0
- package/dist/services/metadata-extractor.service.js +22 -25
- package/dist/services/moropo.service.js +18 -20
- package/dist/services/report-download.service.d.ts +1 -1
- package/dist/services/report-download.service.js +5 -9
- package/dist/services/results-polling.service.d.ts +18 -3
- package/dist/services/results-polling.service.js +195 -108
- package/dist/services/telemetry.service.d.ts +10 -1
- package/dist/services/telemetry.service.js +40 -18
- package/dist/services/test-submission.service.d.ts +21 -4
- package/dist/services/test-submission.service.js +51 -34
- package/dist/services/version.service.d.ts +1 -1
- package/dist/services/version.service.js +1 -5
- package/dist/types/domain/auth.types.d.ts +8 -0
- package/dist/types/domain/auth.types.js +1 -2
- package/dist/types/domain/device.types.js +8 -11
- package/dist/types/domain/live.types.js +1 -2
- package/dist/types/generated/schema.types.js +1 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +2 -18
- package/dist/types.js +1 -2
- package/dist/utils/auth.d.ts +1 -1
- package/dist/utils/auth.js +27 -28
- package/dist/utils/ci.d.ts +12 -0
- package/dist/utils/ci.js +39 -0
- package/dist/utils/cli.js +18 -27
- package/dist/utils/compatibility.d.ts +1 -1
- package/dist/utils/compatibility.js +5 -7
- package/dist/utils/config-store.js +33 -43
- package/dist/utils/connectivity.js +1 -4
- package/dist/utils/expo.js +15 -21
- package/dist/utils/orgs.js +8 -12
- package/dist/utils/paths.js +2 -5
- package/dist/utils/progress.js +2 -5
- package/dist/utils/styling.d.ts +35 -37
- package/dist/utils/styling.js +52 -86
- package/dist/utils/ui.d.ts +41 -0
- package/dist/utils/ui.js +95 -0
- package/package.json +27 -24
package/dist/commands/upgrade.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.upgradeCommand = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* `dcd upgrade` — in-place replace of the standalone binary.
|
|
6
3
|
*
|
|
@@ -10,14 +7,15 @@ exports.upgradeCommand = void 0;
|
|
|
10
7
|
* against the published SHA256SUMS, then atomically renames over the running
|
|
11
8
|
* executable (the old inode stays alive until the process exits).
|
|
12
9
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
11
|
+
import { chmodSync, createReadStream, createWriteStream, renameSync, unlinkSync, } from 'node:fs';
|
|
12
|
+
import { Readable } from 'node:stream';
|
|
13
|
+
import { pipeline } from 'node:stream/promises';
|
|
14
|
+
import { defineCommand } from 'citty';
|
|
15
|
+
import { VersionService } from '../services/version.service.js';
|
|
16
|
+
import { CliError, getCliVersion, getInstallMethod, logger, } from '../utils/cli.js';
|
|
17
|
+
import { colors } from '../utils/styling.js';
|
|
18
|
+
import { ui } from '../utils/ui.js';
|
|
21
19
|
const DEFAULT_DOWNLOAD_BASE = 'https://get.devicecloud.dev';
|
|
22
20
|
// Maps `${process.platform}-${process.arch}` to the GitHub Release asset filename
|
|
23
21
|
// produced by scripts/build-binaries.mjs. Keys must stay in sync with that script.
|
|
@@ -28,41 +26,41 @@ const ASSET_BY_PLATFORM = {
|
|
|
28
26
|
'linux-x64': 'dcd-linux-x64',
|
|
29
27
|
'win32-x64': 'dcd-windows-x64.exe',
|
|
30
28
|
};
|
|
31
|
-
|
|
29
|
+
export const upgradeCommand = defineCommand({
|
|
32
30
|
meta: {
|
|
33
31
|
name: 'upgrade',
|
|
34
32
|
description: 'Upgrade dcd in place to the latest released version',
|
|
35
33
|
},
|
|
36
34
|
async run() {
|
|
37
|
-
if (
|
|
38
|
-
throw new
|
|
35
|
+
if (getInstallMethod() !== 'binary') {
|
|
36
|
+
throw new CliError('`dcd upgrade` only applies to the standalone binary install. ' +
|
|
39
37
|
'Run: npm install -g @devicecloud.dev/dcd@latest');
|
|
40
38
|
}
|
|
41
|
-
const current =
|
|
42
|
-
const versionService = new
|
|
39
|
+
const current = getCliVersion();
|
|
40
|
+
const versionService = new VersionService();
|
|
43
41
|
const latest = await versionService.checkLatestCliVersion();
|
|
44
42
|
if (!latest) {
|
|
45
|
-
throw new
|
|
43
|
+
throw new CliError('Could not reach the update manifest. Check your network connection and try again.');
|
|
46
44
|
}
|
|
47
45
|
if (!versionService.isOutdated(current, latest)) {
|
|
48
|
-
|
|
46
|
+
logger.log(ui.success(`Already on the latest version (${colors.highlight(current)})`));
|
|
49
47
|
return;
|
|
50
48
|
}
|
|
51
49
|
const platformKey = `${process.platform}-${process.arch}`;
|
|
52
50
|
const asset = ASSET_BY_PLATFORM[platformKey];
|
|
53
51
|
if (!asset) {
|
|
54
|
-
throw new
|
|
52
|
+
throw new CliError(`No binary published for ${platformKey}. Supported platforms: ${Object.keys(ASSET_BY_PLATFORM).join(', ')}`);
|
|
55
53
|
}
|
|
56
54
|
if (process.platform === 'win32') {
|
|
57
55
|
// Windows can't replace a running .exe; defer to a re-run of the installer.
|
|
58
56
|
const base = process.env.DCD_DOWNLOAD_BASE ?? DEFAULT_DOWNLOAD_BASE;
|
|
59
|
-
throw new
|
|
57
|
+
throw new CliError(`Automatic upgrade on Windows is not yet supported. Re-run the installer:\n irm ${base}/install.ps1 | iex`);
|
|
60
58
|
}
|
|
61
59
|
const base = process.env.DCD_DOWNLOAD_BASE ?? DEFAULT_DOWNLOAD_BASE;
|
|
62
60
|
const binaryUrl = `${base}/download/${latest}/${asset}`;
|
|
63
61
|
const sumsUrl = `${base}/download/${latest}/SHA256SUMS`;
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
logger.log(ui.info(`Upgrading ${colors.highlight(current)} → ${colors.highlight(latest)}`));
|
|
63
|
+
logger.log(ui.note(` ${binaryUrl}`));
|
|
66
64
|
const execPath = process.execPath;
|
|
67
65
|
const tmpPath = `${execPath}.new`;
|
|
68
66
|
try {
|
|
@@ -72,16 +70,16 @@ exports.upgradeCommand = (0, citty_1.defineCommand)({
|
|
|
72
70
|
if (expected !== actual) {
|
|
73
71
|
throw new Error(`Checksum mismatch for ${asset}: expected ${expected}, got ${actual}`);
|
|
74
72
|
}
|
|
75
|
-
|
|
73
|
+
chmodSync(tmpPath, 0o755);
|
|
76
74
|
// rename is atomic on the same filesystem; on POSIX the running process
|
|
77
75
|
// keeps the old inode open until exit, so this is safe to do mid-run.
|
|
78
|
-
|
|
76
|
+
renameSync(tmpPath, execPath);
|
|
79
77
|
}
|
|
80
78
|
catch (e) {
|
|
81
79
|
safeUnlink(tmpPath);
|
|
82
|
-
throw new
|
|
80
|
+
throw new CliError(`Upgrade failed: ${e.message}. The existing binary at ${execPath} was not modified.`);
|
|
83
81
|
}
|
|
84
|
-
|
|
82
|
+
logger.log(ui.success(`Upgraded to ${colors.highlight(latest)}`));
|
|
85
83
|
},
|
|
86
84
|
});
|
|
87
85
|
async function downloadToFile(url, dest) {
|
|
@@ -90,7 +88,7 @@ async function downloadToFile(url, dest) {
|
|
|
90
88
|
throw new Error(`HTTP ${res.status} fetching ${url}`);
|
|
91
89
|
}
|
|
92
90
|
// Node 22 exposes Readable.fromWeb for piping a WHATWG ReadableStream.
|
|
93
|
-
await
|
|
91
|
+
await pipeline(Readable.fromWeb(res.body), createWriteStream(dest));
|
|
94
92
|
}
|
|
95
93
|
async function fetchExpectedChecksum(sumsUrl, asset) {
|
|
96
94
|
const res = await fetch(sumsUrl, { redirect: 'follow' });
|
|
@@ -105,18 +103,18 @@ async function fetchExpectedChecksum(sumsUrl, asset) {
|
|
|
105
103
|
throw new Error(`SHA256SUMS has no entry for ${asset}`);
|
|
106
104
|
}
|
|
107
105
|
async function sha256File(path) {
|
|
108
|
-
const hash =
|
|
109
|
-
for await (const chunk of
|
|
106
|
+
const hash = createHash('sha256');
|
|
107
|
+
for await (const chunk of createReadStream(path)) {
|
|
110
108
|
hash.update(chunk);
|
|
111
109
|
}
|
|
112
110
|
return hash.digest('hex');
|
|
113
111
|
}
|
|
114
112
|
function safeUnlink(path) {
|
|
115
113
|
try {
|
|
116
|
-
|
|
114
|
+
unlinkSync(path);
|
|
117
115
|
}
|
|
118
116
|
catch {
|
|
119
117
|
// Best-effort cleanup; failure here would only leave a .new file.
|
|
120
118
|
}
|
|
121
119
|
}
|
|
122
|
-
|
|
120
|
+
export default upgradeCommand;
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
export declare const uploadCommand: import("citty").CommandDef<{
|
|
2
|
-
'app-url': {
|
|
2
|
+
readonly 'app-url': {
|
|
3
3
|
readonly type: "string";
|
|
4
4
|
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.";
|
|
5
5
|
};
|
|
6
|
-
'ignore-sha-check': {
|
|
6
|
+
readonly 'ignore-sha-check': {
|
|
7
7
|
readonly type: "boolean";
|
|
8
8
|
readonly description: "Ignore the sha hash check and upload the binary regardless of whether it already exists (not recommended)";
|
|
9
9
|
};
|
|
10
|
-
debug: {
|
|
10
|
+
readonly debug: {
|
|
11
11
|
readonly type: "boolean";
|
|
12
12
|
readonly default: false;
|
|
13
13
|
readonly description: "Enable detailed debug logging for troubleshooting issues";
|
|
14
14
|
};
|
|
15
|
-
json: {
|
|
15
|
+
readonly json: {
|
|
16
16
|
readonly type: "boolean";
|
|
17
17
|
readonly description: "Output results in JSON format. Exit codes: 0 on success, 2 if the test run fails, 1 on CLI/infrastructure errors";
|
|
18
18
|
};
|
|
19
|
-
appFile: {
|
|
20
|
-
type: "positional";
|
|
21
|
-
required: false;
|
|
22
|
-
description:
|
|
19
|
+
readonly appFile: {
|
|
20
|
+
readonly type: "positional";
|
|
21
|
+
readonly required: false;
|
|
22
|
+
readonly 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/...)";
|
|
23
23
|
};
|
|
24
|
-
'api-key': {
|
|
24
|
+
readonly 'api-key': {
|
|
25
25
|
readonly type: "string";
|
|
26
26
|
readonly alias: ["apiKey"];
|
|
27
27
|
readonly description: "API key for devicecloud.dev (find this in the console UI). You can also set the DEVICE_CLOUD_API_KEY environment variable.";
|
|
28
28
|
};
|
|
29
|
-
'api-url': {
|
|
29
|
+
readonly 'api-url': {
|
|
30
30
|
readonly type: "string";
|
|
31
31
|
readonly alias: ["apiURL", "apiUrl"];
|
|
32
32
|
readonly description: "API base URL (defaults to the URL stored by `dcd login`, else prod)";
|
package/dist/commands/upload.js
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const styling_1 = require("../utils/styling");
|
|
15
|
-
exports.uploadCommand = (0, citty_1.defineCommand)({
|
|
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({
|
|
16
14
|
meta: {
|
|
17
15
|
name: 'upload',
|
|
18
16
|
description: 'Upload an app binary to devicecloud.dev',
|
|
19
17
|
},
|
|
20
18
|
args: {
|
|
21
|
-
...
|
|
22
|
-
'app-url':
|
|
23
|
-
'ignore-sha-check':
|
|
24
|
-
debug:
|
|
25
|
-
json:
|
|
19
|
+
...apiFlags,
|
|
20
|
+
'app-url': binaryFlags['app-url'],
|
|
21
|
+
'ignore-sha-check': binaryFlags['ignore-sha-check'],
|
|
22
|
+
debug: outputFlags.debug,
|
|
23
|
+
json: outputFlags.json,
|
|
26
24
|
appFile: {
|
|
27
25
|
type: 'positional',
|
|
28
26
|
required: false,
|
|
@@ -35,45 +33,44 @@ exports.uploadCommand = (0, citty_1.defineCommand)({
|
|
|
35
33
|
// When --json is set, suppress chatty progress logs so stdout stays parseable.
|
|
36
34
|
const out = (m) => {
|
|
37
35
|
if (!json)
|
|
38
|
-
|
|
36
|
+
logger.log(m);
|
|
39
37
|
};
|
|
40
38
|
try {
|
|
41
39
|
const apiKeyFlag = args['api-key'];
|
|
42
|
-
const apiUrl =
|
|
40
|
+
const apiUrl = resolveApiUrl(args['api-url']);
|
|
43
41
|
const appUrl = args['app-url'];
|
|
44
42
|
const ignoreShaCheck = Boolean(args['ignore-sha-check']);
|
|
45
43
|
const debug = Boolean(args.debug);
|
|
46
44
|
const positional = args.appFile;
|
|
47
|
-
const auth = await
|
|
45
|
+
const auth = await resolveAuth({ apiKeyFlag });
|
|
48
46
|
let resolvedFile = appUrl ?? positional;
|
|
49
47
|
if (!resolvedFile) {
|
|
50
|
-
throw new
|
|
48
|
+
throw new CliError('You must provide an app file path or a signed Expo URL via --app-url');
|
|
51
49
|
}
|
|
52
|
-
if (
|
|
53
|
-
out(
|
|
54
|
-
const tarPath = await
|
|
50
|
+
if (isUrl(resolvedFile)) {
|
|
51
|
+
out(ui.running('Downloading Expo build from URL…'));
|
|
52
|
+
const tarPath = await downloadExpoUrl(resolvedFile, debug);
|
|
55
53
|
tempFiles.push(tarPath);
|
|
56
54
|
resolvedFile = tarPath;
|
|
57
55
|
}
|
|
58
56
|
if (resolvedFile.endsWith('.tar.gz')) {
|
|
59
|
-
out(
|
|
60
|
-
const extractDir = await
|
|
57
|
+
out(ui.running('Extracting Expo archive…'));
|
|
58
|
+
const extractDir = await extractTarGz(resolvedFile, debug);
|
|
61
59
|
tempFiles.push(extractDir);
|
|
62
|
-
resolvedFile = await
|
|
60
|
+
resolvedFile = await findAppBundle(extractDir);
|
|
63
61
|
if (debug) {
|
|
64
62
|
out(`[DEBUG] Found .app bundle at: ${resolvedFile}`);
|
|
65
63
|
}
|
|
66
64
|
}
|
|
67
65
|
if (!['.apk', '.app', '.zip', '.tar.gz'].some((ext) => resolvedFile.endsWith(ext))) {
|
|
68
|
-
throw new
|
|
66
|
+
throw new CliError('App file must be a .apk for Android, .app/.zip for iOS, or .tar.gz (Expo iOS build)');
|
|
69
67
|
}
|
|
70
68
|
if (resolvedFile.endsWith('.zip')) {
|
|
71
|
-
await
|
|
69
|
+
await verifyAppZip(resolvedFile);
|
|
72
70
|
}
|
|
73
|
-
out(
|
|
74
|
-
out(
|
|
75
|
-
|
|
76
|
-
const appBinaryId = await (0, methods_1.uploadBinary)({
|
|
71
|
+
out(ui.section('Uploading app binary'));
|
|
72
|
+
out(ui.branch(ui.fields([['file', colors.highlight(resolvedFile)]])));
|
|
73
|
+
const appBinaryId = await uploadBinary({
|
|
77
74
|
auth,
|
|
78
75
|
apiUrl,
|
|
79
76
|
debug,
|
|
@@ -86,19 +83,21 @@ exports.uploadCommand = (0, citty_1.defineCommand)({
|
|
|
86
83
|
console.log(JSON.stringify({ appBinaryId }, null, 2));
|
|
87
84
|
return;
|
|
88
85
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
+
]));
|
|
93
92
|
}
|
|
94
93
|
catch (error) {
|
|
95
|
-
|
|
94
|
+
logger.error(error, { exit: 1, json });
|
|
96
95
|
}
|
|
97
96
|
finally {
|
|
98
97
|
for (const p of tempFiles) {
|
|
99
|
-
await
|
|
98
|
+
await rm(p, { recursive: true, force: true }).catch(() => { });
|
|
100
99
|
}
|
|
101
100
|
}
|
|
102
101
|
},
|
|
103
102
|
});
|
|
104
|
-
|
|
103
|
+
export default uploadCommand;
|
package/dist/commands/whoami.js
CHANGED
|
@@ -1,34 +1,31 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.whoamiCommand = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* `dcd whoami` — prints the logged-in user + active org from stored config.
|
|
6
3
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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({
|
|
12
10
|
meta: {
|
|
13
11
|
name: 'whoami',
|
|
14
12
|
description: 'Show the logged-in user and active organization',
|
|
15
13
|
},
|
|
16
14
|
run() {
|
|
17
|
-
const config =
|
|
15
|
+
const config = readConfig();
|
|
18
16
|
if (!config?.session) {
|
|
19
|
-
|
|
17
|
+
logger.log(ui.info(`Not logged in. Run ${colors.highlight('dcd login')} or set ${colors.highlight('DEVICE_CLOUD_API_KEY')}.`));
|
|
20
18
|
return;
|
|
21
19
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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)]);
|
|
26
25
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
cli_1.logger.log(` ${styling_1.colors.dim('Env:')} ${config.env}`);
|
|
26
|
+
fields.push(['env', config.env]);
|
|
27
|
+
logger.log(ui.section('devicecloud.dev'));
|
|
28
|
+
logger.log(ui.branch(ui.fields(fields)));
|
|
32
29
|
},
|
|
33
30
|
});
|
|
34
|
-
|
|
31
|
+
export default whoamiCommand;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Per-env devicecloud.dev coordinates — single source of truth for the URLs
|
|
4
3
|
* and Supabase credentials the CLI uses.
|
|
@@ -9,12 +8,7 @@
|
|
|
9
8
|
* them here — every Supabase-based product ships its anon key the same way.
|
|
10
9
|
* https://supabase.com/docs/guides/api/api-keys
|
|
11
10
|
*/
|
|
12
|
-
|
|
13
|
-
exports.ENVIRONMENTS = void 0;
|
|
14
|
-
exports.findEnvByApiUrl = findEnvByApiUrl;
|
|
15
|
-
exports.inferEnvFromApiUrl = inferEnvFromApiUrl;
|
|
16
|
-
exports.resolveFrontendUrl = resolveFrontendUrl;
|
|
17
|
-
exports.ENVIRONMENTS = {
|
|
11
|
+
export const ENVIRONMENTS = {
|
|
18
12
|
prod: {
|
|
19
13
|
apiUrl: 'https://api.devicecloud.dev',
|
|
20
14
|
frontendUrl: 'https://console.devicecloud.dev',
|
|
@@ -35,13 +29,13 @@ exports.ENVIRONMENTS = {
|
|
|
35
29
|
},
|
|
36
30
|
};
|
|
37
31
|
/** Exact-match lookup of a known environment by API URL (trailing slashes ignored). */
|
|
38
|
-
function findEnvByApiUrl(apiUrl) {
|
|
32
|
+
export function findEnvByApiUrl(apiUrl) {
|
|
39
33
|
const normalize = (u) => u.replace(/\/+$/, '');
|
|
40
34
|
const needle = normalize(apiUrl);
|
|
41
|
-
return Object.values(
|
|
35
|
+
return Object.values(ENVIRONMENTS).find((env) => normalize(env.apiUrl) === needle);
|
|
42
36
|
}
|
|
43
37
|
/** Map a caller-supplied API URL to one of the known environments. */
|
|
44
|
-
function inferEnvFromApiUrl(apiUrl) {
|
|
38
|
+
export function inferEnvFromApiUrl(apiUrl) {
|
|
45
39
|
if (apiUrl.includes('api.dev.') || apiUrl.includes('localhost'))
|
|
46
40
|
return 'dev';
|
|
47
41
|
return 'prod';
|
|
@@ -51,8 +45,8 @@ function inferEnvFromApiUrl(apiUrl) {
|
|
|
51
45
|
* `ENVIRONMENTS[inferEnvFromApiUrl(apiUrl)].frontendUrl`, except that when the
|
|
52
46
|
* API is running on localhost we assume the frontend is too (vite dev server).
|
|
53
47
|
*/
|
|
54
|
-
function resolveFrontendUrl(apiUrl) {
|
|
48
|
+
export function resolveFrontendUrl(apiUrl) {
|
|
55
49
|
if (apiUrl.includes('localhost'))
|
|
56
50
|
return 'http://localhost:5173';
|
|
57
|
-
return
|
|
51
|
+
return ENVIRONMENTS[inferEnvFromApiUrl(apiUrl)].frontendUrl;
|
|
58
52
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.apiFlags = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* API authentication and configuration flags
|
|
6
3
|
*/
|
|
7
|
-
|
|
4
|
+
export const apiFlags = {
|
|
8
5
|
'api-key': {
|
|
9
6
|
type: 'string',
|
|
10
7
|
alias: ['apiKey'],
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.binaryFlags = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* Binary upload and management flags
|
|
6
3
|
*/
|
|
7
|
-
|
|
4
|
+
export const binaryFlags = {
|
|
8
5
|
'app-binary-id': {
|
|
9
6
|
type: 'string',
|
|
10
7
|
description: 'The ID of the app binary previously uploaded to devicecloud.dev',
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
Object.
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
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(', ');
|
|
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(', ');
|
|
9
6
|
/**
|
|
10
7
|
* Device-specific flags for Android and iOS configuration
|
|
11
8
|
*/
|
|
12
|
-
|
|
9
|
+
export const deviceFlags = {
|
|
13
10
|
'android-api-level': {
|
|
14
11
|
type: 'string',
|
|
15
12
|
description: `[Android only] Android API level to run your flow against (options: ${androidApiLevels})`,
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.environmentFlags = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* Environment configuration and metadata flags
|
|
6
3
|
*/
|
|
7
|
-
|
|
4
|
+
export const environmentFlags = {
|
|
8
5
|
env: {
|
|
9
6
|
type: 'string',
|
|
10
7
|
alias: ['e'],
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.executionFlags = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* Test execution and flow management flags
|
|
6
3
|
*/
|
|
7
|
-
|
|
4
|
+
export const executionFlags = {
|
|
8
5
|
config: {
|
|
9
6
|
type: 'string',
|
|
10
7
|
description: 'Path to custom config.yaml file. If not provided, defaults to config.yaml in root flows folders.',
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.githubFlags = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* GitHub context flags — stored as gh_* metadata alongside the run
|
|
6
3
|
*/
|
|
7
|
-
|
|
4
|
+
export const githubFlags = {
|
|
8
5
|
branch: {
|
|
9
6
|
type: 'string',
|
|
10
7
|
description: 'Git branch name for this run (stored as gh_branch metadata)',
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.outputFlags = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* Output format and artifact download flags
|
|
6
3
|
*/
|
|
7
|
-
|
|
4
|
+
export const outputFlags = {
|
|
8
5
|
'artifacts-path': {
|
|
9
6
|
type: 'string',
|
|
10
7
|
description: 'Custom file path for downloaded artifacts (default: ./artifacts.zip). Requires --download-artifacts.',
|
package/dist/constants.js
CHANGED
|
@@ -1,27 +1,24 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Consolidated flag definitions.
|
|
4
3
|
* Flags are organized by domain in src/config/flags/
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const github_flags_1 = require("./config/flags/github.flags");
|
|
14
|
-
const output_flags_1 = require("./config/flags/output.flags");
|
|
5
|
+
import { apiFlags } from './config/flags/api.flags.js';
|
|
6
|
+
import { binaryFlags } from './config/flags/binary.flags.js';
|
|
7
|
+
import { deviceFlags } from './config/flags/device.flags.js';
|
|
8
|
+
import { environmentFlags } from './config/flags/environment.flags.js';
|
|
9
|
+
import { executionFlags } from './config/flags/execution.flags.js';
|
|
10
|
+
import { githubFlags } from './config/flags/github.flags.js';
|
|
11
|
+
import { outputFlags } from './config/flags/output.flags.js';
|
|
15
12
|
/**
|
|
16
13
|
* All flag definitions consolidated from domain-specific flag modules.
|
|
17
14
|
* Spread into a command's `args` to expose the full cloud-command surface.
|
|
18
15
|
*/
|
|
19
|
-
|
|
20
|
-
...
|
|
21
|
-
...
|
|
22
|
-
...
|
|
23
|
-
...
|
|
24
|
-
...
|
|
25
|
-
...
|
|
26
|
-
...
|
|
16
|
+
export const flags = {
|
|
17
|
+
...apiFlags,
|
|
18
|
+
...binaryFlags,
|
|
19
|
+
...deviceFlags,
|
|
20
|
+
...environmentFlags,
|
|
21
|
+
...executionFlags,
|
|
22
|
+
...githubFlags,
|
|
23
|
+
...outputFlags,
|
|
27
24
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { TAppMetadata } from '../types';
|
|
2
|
-
import type { AuthContext } from '../types/domain/auth.types';
|
|
3
|
-
import type { LiveCommandStatus, LiveExecResult, LiveSession, LiveSessionSummary } from '../types/domain/live.types';
|
|
1
|
+
import { TAppMetadata } from '../types.js';
|
|
2
|
+
import type { AuthContext } from '../types/domain/auth.types.js';
|
|
3
|
+
import type { LiveCommandStatus, LiveExecResult, LiveSession, LiveSessionSummary } from '../types/domain/live.types.js';
|
|
4
|
+
import { components } from '../types/generated/schema.types.js';
|
|
4
5
|
/**
|
|
5
6
|
* Error thrown for non-OK API responses, carrying the HTTP status so callers
|
|
6
7
|
* can branch on auth failures etc. without string matching.
|
|
@@ -51,13 +52,13 @@ export declare const ApiGateway: {
|
|
|
51
52
|
tempPath: string;
|
|
52
53
|
finalPath: string;
|
|
53
54
|
id: string;
|
|
54
|
-
b2?:
|
|
55
|
+
b2?: components["schemas"]["B2UploadStrategy"];
|
|
55
56
|
token?: string;
|
|
56
57
|
}>;
|
|
57
58
|
finishLargeFile(baseUrl: string, auth: AuthContext, fileId: string, partSha1Array: string[]): Promise<unknown>;
|
|
58
59
|
getResultsForUpload(baseUrl: string, auth: AuthContext, uploadId: string): Promise<{
|
|
59
60
|
statusCode?: number;
|
|
60
|
-
results?:
|
|
61
|
+
results?: components["schemas"]["TResultResponse"][];
|
|
61
62
|
}>;
|
|
62
63
|
getUploadStatus(baseUrl: string, auth: AuthContext, options: {
|
|
63
64
|
name?: string;
|
|
@@ -93,7 +94,31 @@ export declare const ApiGateway: {
|
|
|
93
94
|
}>;
|
|
94
95
|
uploadFlow(baseUrl: string, auth: AuthContext, testFormData: FormData): Promise<{
|
|
95
96
|
message?: string;
|
|
96
|
-
results?:
|
|
97
|
+
results?: components["schemas"]["IDBResult"][];
|
|
98
|
+
}>;
|
|
99
|
+
/**
|
|
100
|
+
* Requests a storage URL for a client-direct flow zip upload. Mirrors
|
|
101
|
+
* `getBinaryUploadUrl` (same response shape) but stages the zip under
|
|
102
|
+
* `<orgId>/tests/` instead of the app-binary path. A 404 here means the API
|
|
103
|
+
* predates the client-direct flow path — callers fall back to `uploadFlow`.
|
|
104
|
+
*/
|
|
105
|
+
getFlowUploadUrl(baseUrl: string, auth: AuthContext, fileSize: number): Promise<{
|
|
106
|
+
path: string;
|
|
107
|
+
tempPath: string;
|
|
108
|
+
finalPath: string;
|
|
109
|
+
id: string;
|
|
110
|
+
b2?: components["schemas"]["B2UploadStrategy"];
|
|
111
|
+
token?: string;
|
|
112
|
+
}>;
|
|
113
|
+
/**
|
|
114
|
+
* Submits a flow test that references an already-uploaded zip (JSON body, no
|
|
115
|
+
* multipart). The response is identical to the legacy `POST /uploads/flow`.
|
|
116
|
+
* A 404 means the API predates this endpoint — callers fall back to
|
|
117
|
+
* `uploadFlow`.
|
|
118
|
+
*/
|
|
119
|
+
submitFlowTest(baseUrl: string, auth: AuthContext, body: Record<string, unknown>): Promise<{
|
|
120
|
+
message?: string;
|
|
121
|
+
results?: components["schemas"]["IDBResult"][];
|
|
97
122
|
}>;
|
|
98
123
|
/**
|
|
99
124
|
* Generic report download method that handles both junit and allure reports
|