replicant-mcp 1.0.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.
- package/LICENSE +21 -0
- package/README.md +386 -0
- package/dist/adapters/adb.d.ts +21 -0
- package/dist/adapters/adb.js +75 -0
- package/dist/adapters/emulator.d.ts +19 -0
- package/dist/adapters/emulator.js +72 -0
- package/dist/adapters/gradle.d.ts +20 -0
- package/dist/adapters/gradle.js +80 -0
- package/dist/adapters/index.d.ts +4 -0
- package/dist/adapters/index.js +4 -0
- package/dist/adapters/ui-automator.d.ts +23 -0
- package/dist/adapters/ui-automator.js +53 -0
- package/dist/cli/adb.d.ts +2 -0
- package/dist/cli/adb.js +256 -0
- package/dist/cli/cache.d.ts +2 -0
- package/dist/cli/cache.js +115 -0
- package/dist/cli/emulator.d.ts +2 -0
- package/dist/cli/emulator.js +181 -0
- package/dist/cli/formatter.d.ts +52 -0
- package/dist/cli/formatter.js +68 -0
- package/dist/cli/gradle.d.ts +2 -0
- package/dist/cli/gradle.js +192 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +6 -0
- package/dist/cli/ui.d.ts +2 -0
- package/dist/cli/ui.js +218 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +14 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/parsers/adb-output.d.ts +4 -0
- package/dist/parsers/adb-output.js +32 -0
- package/dist/parsers/emulator-output.d.ts +9 -0
- package/dist/parsers/emulator-output.js +33 -0
- package/dist/parsers/gradle-output.d.ts +30 -0
- package/dist/parsers/gradle-output.js +80 -0
- package/dist/parsers/index.d.ts +4 -0
- package/dist/parsers/index.js +4 -0
- package/dist/parsers/ui-dump.d.ts +27 -0
- package/dist/parsers/ui-dump.js +142 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +113 -0
- package/dist/services/cache-manager.d.ts +22 -0
- package/dist/services/cache-manager.js +90 -0
- package/dist/services/device-state.d.ts +9 -0
- package/dist/services/device-state.js +26 -0
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.js +3 -0
- package/dist/services/process-runner.d.ts +15 -0
- package/dist/services/process-runner.js +62 -0
- package/dist/tools/adb-app.d.ts +38 -0
- package/dist/tools/adb-app.js +68 -0
- package/dist/tools/adb-device.d.ts +31 -0
- package/dist/tools/adb-device.js +71 -0
- package/dist/tools/adb-logcat.d.ts +54 -0
- package/dist/tools/adb-logcat.js +70 -0
- package/dist/tools/adb-shell.d.ts +26 -0
- package/dist/tools/adb-shell.js +27 -0
- package/dist/tools/cache.d.ts +50 -0
- package/dist/tools/cache.js +57 -0
- package/dist/tools/emulator-device.d.ts +56 -0
- package/dist/tools/emulator-device.js +132 -0
- package/dist/tools/gradle-build.d.ts +35 -0
- package/dist/tools/gradle-build.js +40 -0
- package/dist/tools/gradle-get-details.d.ts +32 -0
- package/dist/tools/gradle-get-details.js +72 -0
- package/dist/tools/gradle-list.d.ts +30 -0
- package/dist/tools/gradle-list.js +55 -0
- package/dist/tools/gradle-test.d.ts +34 -0
- package/dist/tools/gradle-test.js +40 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.js +12 -0
- package/dist/tools/rtfm.d.ts +26 -0
- package/dist/tools/rtfm.js +70 -0
- package/dist/tools/ui.d.ts +77 -0
- package/dist/tools/ui.js +131 -0
- package/dist/types/cache.d.ts +24 -0
- package/dist/types/cache.js +14 -0
- package/dist/types/device.d.ts +11 -0
- package/dist/types/device.js +1 -0
- package/dist/types/errors.d.ts +31 -0
- package/dist/types/errors.js +43 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +3 -0
- package/package.json +64 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { EmulatorAdapter } from "../adapters/index.js";
|
|
3
|
+
const adapter = new EmulatorAdapter();
|
|
4
|
+
export function createEmulatorCommand() {
|
|
5
|
+
const emulator = new Command("emulator").description("Android emulator management");
|
|
6
|
+
// List subcommand
|
|
7
|
+
emulator
|
|
8
|
+
.command("list")
|
|
9
|
+
.description("List available AVDs and running emulators")
|
|
10
|
+
.option("--json", "Output as JSON")
|
|
11
|
+
.action(async (options) => {
|
|
12
|
+
try {
|
|
13
|
+
const result = await adapter.list();
|
|
14
|
+
if (options.json) {
|
|
15
|
+
console.log(JSON.stringify(result, null, 2));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
console.log("Available AVDs:");
|
|
19
|
+
if (result.available.length === 0) {
|
|
20
|
+
console.log(" (none)");
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
result.available.forEach((avd) => {
|
|
24
|
+
const status = result.running.includes(avd.name) ? " [running]" : "";
|
|
25
|
+
console.log(` ${avd.name}${status}`);
|
|
26
|
+
if (avd.skin) {
|
|
27
|
+
console.log(` Skin: ${avd.skin}`);
|
|
28
|
+
}
|
|
29
|
+
if (avd.target) {
|
|
30
|
+
console.log(` Target: ${avd.target}`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
console.log("\nRunning emulators:");
|
|
35
|
+
if (result.running.length === 0) {
|
|
36
|
+
console.log(" (none)");
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
result.running.forEach((id) => {
|
|
40
|
+
console.log(` ${id}`);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
47
|
+
console.error(`Error: ${errorMessage}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// Start subcommand
|
|
52
|
+
emulator
|
|
53
|
+
.command("start <avdName>")
|
|
54
|
+
.description("Start an emulator")
|
|
55
|
+
.option("--cold-boot", "Perform a cold boot (ignore snapshots)")
|
|
56
|
+
.option("--wipe-data", "Wipe user data before starting")
|
|
57
|
+
.option("--json", "Output as JSON")
|
|
58
|
+
.action(async (avdName, options) => {
|
|
59
|
+
try {
|
|
60
|
+
if (options.wipeData) {
|
|
61
|
+
console.log(`Wiping data for ${avdName}...`);
|
|
62
|
+
await adapter.wipe(avdName);
|
|
63
|
+
}
|
|
64
|
+
console.log(`Starting emulator ${avdName}...`);
|
|
65
|
+
const deviceId = await adapter.start(avdName);
|
|
66
|
+
if (options.json) {
|
|
67
|
+
console.log(JSON.stringify({ avdName, deviceId, status: "started" }, null, 2));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
console.log(`Emulator started: ${deviceId}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
75
|
+
console.error(`Error: ${errorMessage}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
// Stop subcommand
|
|
80
|
+
emulator
|
|
81
|
+
.command("stop <deviceId>")
|
|
82
|
+
.description("Stop a running emulator")
|
|
83
|
+
.option("--json", "Output as JSON")
|
|
84
|
+
.action(async (deviceId, options) => {
|
|
85
|
+
try {
|
|
86
|
+
await adapter.kill(deviceId);
|
|
87
|
+
if (options.json) {
|
|
88
|
+
console.log(JSON.stringify({ deviceId, status: "stopped" }, null, 2));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.log(`Emulator stopped: ${deviceId}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
96
|
+
console.error(`Error: ${errorMessage}`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// Snapshot subcommand
|
|
101
|
+
emulator
|
|
102
|
+
.command("snapshot <action>")
|
|
103
|
+
.description("Manage emulator snapshots (save, load, list, delete)")
|
|
104
|
+
.requiredOption("-d, --device <deviceId>", "Target emulator device ID")
|
|
105
|
+
.option("-n, --name <name>", "Snapshot name (required for save, load, delete)")
|
|
106
|
+
.option("--json", "Output as JSON")
|
|
107
|
+
.action(async (action, options) => {
|
|
108
|
+
try {
|
|
109
|
+
const deviceId = options.device;
|
|
110
|
+
const snapshotName = options.name;
|
|
111
|
+
switch (action) {
|
|
112
|
+
case "save":
|
|
113
|
+
if (!snapshotName) {
|
|
114
|
+
console.error("Error: --name is required for save action");
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
await adapter.snapshotSave(deviceId, snapshotName);
|
|
118
|
+
if (options.json) {
|
|
119
|
+
console.log(JSON.stringify({ action: "save", deviceId, name: snapshotName, status: "saved" }, null, 2));
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
console.log(`Snapshot saved: ${snapshotName}`);
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
case "load":
|
|
126
|
+
if (!snapshotName) {
|
|
127
|
+
console.error("Error: --name is required for load action");
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
await adapter.snapshotLoad(deviceId, snapshotName);
|
|
131
|
+
if (options.json) {
|
|
132
|
+
console.log(JSON.stringify({ action: "load", deviceId, name: snapshotName, status: "loaded" }, null, 2));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
console.log(`Snapshot loaded: ${snapshotName}`);
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
case "list":
|
|
139
|
+
const snapshots = await adapter.snapshotList(deviceId);
|
|
140
|
+
if (options.json) {
|
|
141
|
+
console.log(JSON.stringify({ deviceId, snapshots }, null, 2));
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
console.log(`Snapshots for ${deviceId}:`);
|
|
145
|
+
if (snapshots.length === 0) {
|
|
146
|
+
console.log(" (none)");
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
snapshots.forEach((name) => {
|
|
150
|
+
console.log(` ${name}`);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
case "delete":
|
|
156
|
+
if (!snapshotName) {
|
|
157
|
+
console.error("Error: --name is required for delete action");
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
await adapter.snapshotDelete(deviceId, snapshotName);
|
|
161
|
+
if (options.json) {
|
|
162
|
+
console.log(JSON.stringify({ action: "delete", deviceId, name: snapshotName, status: "deleted" }, null, 2));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
console.log(`Snapshot deleted: ${snapshotName}`);
|
|
166
|
+
}
|
|
167
|
+
break;
|
|
168
|
+
default:
|
|
169
|
+
console.error(`Unknown action: ${action}`);
|
|
170
|
+
console.error("Valid actions: save, load, list, delete");
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
176
|
+
console.error(`Error: ${errorMessage}`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return emulator;
|
|
181
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface BuildSuccessData {
|
|
2
|
+
duration: string;
|
|
3
|
+
apkPath?: string;
|
|
4
|
+
apkSize?: string;
|
|
5
|
+
warnings: number;
|
|
6
|
+
cacheId: string;
|
|
7
|
+
}
|
|
8
|
+
export interface BuildFailureData {
|
|
9
|
+
duration: string;
|
|
10
|
+
error: string;
|
|
11
|
+
cacheId: string;
|
|
12
|
+
}
|
|
13
|
+
export interface TestResultsData {
|
|
14
|
+
passed: number;
|
|
15
|
+
failed: number;
|
|
16
|
+
skipped: number;
|
|
17
|
+
duration: string;
|
|
18
|
+
failures: string[];
|
|
19
|
+
cacheId: string;
|
|
20
|
+
}
|
|
21
|
+
export interface UiElement {
|
|
22
|
+
index: number;
|
|
23
|
+
type: string;
|
|
24
|
+
text?: string;
|
|
25
|
+
hint?: string;
|
|
26
|
+
focused?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface UiDumpData {
|
|
29
|
+
screenName: string;
|
|
30
|
+
elements: UiElement[];
|
|
31
|
+
}
|
|
32
|
+
export interface LogcatData {
|
|
33
|
+
level: string;
|
|
34
|
+
count: number;
|
|
35
|
+
lines: string[];
|
|
36
|
+
cacheId: string;
|
|
37
|
+
}
|
|
38
|
+
export interface DeviceInfo {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
state: string;
|
|
42
|
+
selected: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface DeviceListData {
|
|
45
|
+
devices: DeviceInfo[];
|
|
46
|
+
}
|
|
47
|
+
export declare function formatBuildSuccess(data: BuildSuccessData): string;
|
|
48
|
+
export declare function formatBuildFailure(data: BuildFailureData): string;
|
|
49
|
+
export declare function formatTestResults(data: TestResultsData): string;
|
|
50
|
+
export declare function formatUiDump(data: UiDumpData): string;
|
|
51
|
+
export declare function formatLogcat(data: LogcatData): string;
|
|
52
|
+
export declare function formatDeviceList(data: DeviceListData): string;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export function formatBuildSuccess(data) {
|
|
2
|
+
const lines = [`✓ Build successful (${data.duration})`];
|
|
3
|
+
if (data.apkPath) {
|
|
4
|
+
const filename = data.apkPath.split("/").pop();
|
|
5
|
+
lines.push(` APK: ${filename}${data.apkSize ? ` (${data.apkSize})` : ""}`);
|
|
6
|
+
}
|
|
7
|
+
if (data.warnings > 0) {
|
|
8
|
+
lines.push(` Warnings: ${data.warnings}`);
|
|
9
|
+
}
|
|
10
|
+
lines.push(` Cache ID: ${data.cacheId}`);
|
|
11
|
+
return lines.join("\n");
|
|
12
|
+
}
|
|
13
|
+
export function formatBuildFailure(data) {
|
|
14
|
+
const lines = [
|
|
15
|
+
`✗ Build failed (${data.duration})`,
|
|
16
|
+
"",
|
|
17
|
+
` Error: ${data.error}`,
|
|
18
|
+
"",
|
|
19
|
+
` Cache ID: ${data.cacheId}`,
|
|
20
|
+
` Run: replicant gradle details ${data.cacheId} --errors`,
|
|
21
|
+
];
|
|
22
|
+
return lines.join("\n");
|
|
23
|
+
}
|
|
24
|
+
export function formatTestResults(data) {
|
|
25
|
+
const status = data.failed === 0 ? "✓" : "✗";
|
|
26
|
+
const lines = [
|
|
27
|
+
`${status} ${data.passed} passed, ${data.failed} failed, ${data.skipped} skipped (${data.duration})`,
|
|
28
|
+
];
|
|
29
|
+
if (data.failures.length > 0) {
|
|
30
|
+
lines.push("");
|
|
31
|
+
lines.push("Failed:");
|
|
32
|
+
data.failures.forEach(f => lines.push(` • ${f}`));
|
|
33
|
+
}
|
|
34
|
+
lines.push("");
|
|
35
|
+
lines.push(`Cache ID: ${data.cacheId}`);
|
|
36
|
+
return lines.join("\n");
|
|
37
|
+
}
|
|
38
|
+
export function formatUiDump(data) {
|
|
39
|
+
const lines = [`Screen: ${data.screenName}`];
|
|
40
|
+
data.elements.forEach((el, i) => {
|
|
41
|
+
const prefix = i === data.elements.length - 1 ? "└─" : "├─";
|
|
42
|
+
let desc = el.text || el.hint || "";
|
|
43
|
+
if (el.focused)
|
|
44
|
+
desc += " (focused)";
|
|
45
|
+
lines.push(`${prefix} [${el.index}] ${el.type}${desc ? ` "${desc}"` : ""}`);
|
|
46
|
+
});
|
|
47
|
+
lines.push("");
|
|
48
|
+
lines.push(`${data.elements.length} interactive elements`);
|
|
49
|
+
return lines.join("\n");
|
|
50
|
+
}
|
|
51
|
+
export function formatLogcat(data) {
|
|
52
|
+
const lines = [`${data.count} ${data.level}s in recent logs:`, ""];
|
|
53
|
+
data.lines.forEach(line => lines.push(line));
|
|
54
|
+
lines.push("");
|
|
55
|
+
lines.push(`Cache ID: ${data.cacheId}`);
|
|
56
|
+
return lines.join("\n");
|
|
57
|
+
}
|
|
58
|
+
export function formatDeviceList(data) {
|
|
59
|
+
if (data.devices.length === 0) {
|
|
60
|
+
return "No devices connected";
|
|
61
|
+
}
|
|
62
|
+
const lines = ["Devices:"];
|
|
63
|
+
data.devices.forEach(device => {
|
|
64
|
+
const indicator = device.selected ? "→ " : " ";
|
|
65
|
+
lines.push(`${indicator}${device.id} (${device.name}) [${device.state}]`);
|
|
66
|
+
});
|
|
67
|
+
return lines.join("\n");
|
|
68
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { GradleAdapter } from "../adapters/index.js";
|
|
3
|
+
import { CacheManager } from "../services/index.js";
|
|
4
|
+
import { formatBuildSuccess, formatBuildFailure, formatTestResults, } from "./formatter.js";
|
|
5
|
+
import { CACHE_TTLS, ReplicantError } from "../types/index.js";
|
|
6
|
+
const adapter = new GradleAdapter();
|
|
7
|
+
const cache = new CacheManager();
|
|
8
|
+
export function createGradleCommand() {
|
|
9
|
+
const gradle = new Command("gradle").description("Build and test Android apps");
|
|
10
|
+
// Build subcommand
|
|
11
|
+
gradle
|
|
12
|
+
.command("build")
|
|
13
|
+
.description("Build the Android project")
|
|
14
|
+
.option("-o, --operation <type>", "Build operation (assembleDebug, assembleRelease, bundle)", "assembleDebug")
|
|
15
|
+
.option("-m, --module <name>", "Target module")
|
|
16
|
+
.option("-f, --flavor <name>", "Build flavor")
|
|
17
|
+
.option("--json", "Output full JSON result")
|
|
18
|
+
.action(async (options) => {
|
|
19
|
+
try {
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
const { result, fullOutput } = await adapter.build(options.operation, options.module, options.flavor);
|
|
22
|
+
const duration = `${((Date.now() - startTime) / 1000).toFixed(1)}s`;
|
|
23
|
+
const cacheId = cache.generateId("build");
|
|
24
|
+
cache.set(cacheId, { result, fullOutput }, "build", CACHE_TTLS.BUILD_OUTPUT);
|
|
25
|
+
if (options.json) {
|
|
26
|
+
console.log(JSON.stringify({ result, cacheId }, null, 2));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
console.log(formatBuildSuccess({
|
|
30
|
+
duration,
|
|
31
|
+
apkPath: result.apkPath,
|
|
32
|
+
warnings: result.warnings,
|
|
33
|
+
cacheId,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
const duration = "0s";
|
|
39
|
+
const cacheId = cache.generateId("build");
|
|
40
|
+
if (error instanceof ReplicantError && error.details?.buildResult) {
|
|
41
|
+
cache.set(cacheId, { result: error.details.buildResult, fullOutput: "" }, "build", CACHE_TTLS.BUILD_OUTPUT);
|
|
42
|
+
if (options.json) {
|
|
43
|
+
console.log(JSON.stringify({ error: error.message, result: error.details.buildResult, cacheId }, null, 2));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
console.log(formatBuildFailure({
|
|
47
|
+
duration,
|
|
48
|
+
error: error.message,
|
|
49
|
+
cacheId,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
55
|
+
console.error(`Error: ${errorMessage}`);
|
|
56
|
+
}
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// Test subcommand
|
|
61
|
+
gradle
|
|
62
|
+
.command("test")
|
|
63
|
+
.description("Run tests")
|
|
64
|
+
.option("-o, --operation <type>", "Test type (unitTest, connectedTest)", "unitTest")
|
|
65
|
+
.option("-m, --module <name>", "Target module")
|
|
66
|
+
.option("-f, --filter <pattern>", "Test filter pattern")
|
|
67
|
+
.option("--json", "Output full JSON result")
|
|
68
|
+
.action(async (options) => {
|
|
69
|
+
try {
|
|
70
|
+
const startTime = Date.now();
|
|
71
|
+
const { result, fullOutput } = await adapter.test(options.operation, options.module, options.filter);
|
|
72
|
+
const duration = `${((Date.now() - startTime) / 1000).toFixed(1)}s`;
|
|
73
|
+
const cacheId = cache.generateId("test");
|
|
74
|
+
cache.set(cacheId, { result, fullOutput }, "test", CACHE_TTLS.TEST_RESULTS);
|
|
75
|
+
if (options.json) {
|
|
76
|
+
console.log(JSON.stringify({ result, cacheId }, null, 2));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log(formatTestResults({
|
|
80
|
+
passed: result.passed,
|
|
81
|
+
failed: result.failed,
|
|
82
|
+
skipped: result.skipped,
|
|
83
|
+
duration,
|
|
84
|
+
failures: result.failures.map((f) => `${f.test}: ${f.message}`),
|
|
85
|
+
cacheId,
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
if (result.failed > 0) {
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
94
|
+
console.error(`Error: ${errorMessage}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
// List subcommand
|
|
99
|
+
gradle
|
|
100
|
+
.command("list")
|
|
101
|
+
.description("List modules, variants, or tasks")
|
|
102
|
+
.option("-t, --type <type>", "What to list (modules, variants, tasks)", "modules")
|
|
103
|
+
.option("-m, --module <name>", "Target module (for variants/tasks)")
|
|
104
|
+
.option("--json", "Output as JSON")
|
|
105
|
+
.action(async (options) => {
|
|
106
|
+
try {
|
|
107
|
+
let result;
|
|
108
|
+
switch (options.type) {
|
|
109
|
+
case "modules":
|
|
110
|
+
result = await adapter.listModules();
|
|
111
|
+
break;
|
|
112
|
+
case "variants":
|
|
113
|
+
result = await adapter.listVariants(options.module);
|
|
114
|
+
break;
|
|
115
|
+
case "tasks":
|
|
116
|
+
result = await adapter.listTasks(options.module);
|
|
117
|
+
break;
|
|
118
|
+
default:
|
|
119
|
+
console.error(`Unknown list type: ${options.type}`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
if (options.json) {
|
|
123
|
+
console.log(JSON.stringify(result, null, 2));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
if (options.type === "variants") {
|
|
127
|
+
const variants = result;
|
|
128
|
+
console.log("Variants:");
|
|
129
|
+
variants.forEach((v) => {
|
|
130
|
+
const flavors = v.flavors.length > 0 ? ` (flavors: ${v.flavors.join(", ")})` : "";
|
|
131
|
+
console.log(` ${v.name} [${v.buildType}]${flavors}`);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const items = result;
|
|
136
|
+
console.log(`${options.type.charAt(0).toUpperCase() + options.type.slice(1)}:`);
|
|
137
|
+
items.forEach((item) => console.log(` ${item}`));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
143
|
+
console.error(`Error: ${errorMessage}`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
// Details subcommand
|
|
148
|
+
gradle
|
|
149
|
+
.command("details <cacheId>")
|
|
150
|
+
.description("Get details from a cached build/test result")
|
|
151
|
+
.option("--errors", "Show only errors")
|
|
152
|
+
.option("--warnings", "Show only warnings")
|
|
153
|
+
.option("--json", "Output as JSON")
|
|
154
|
+
.action((cacheId, options) => {
|
|
155
|
+
try {
|
|
156
|
+
const entry = cache.get(cacheId);
|
|
157
|
+
if (!entry) {
|
|
158
|
+
console.error(`Cache entry not found: ${cacheId}`);
|
|
159
|
+
console.error("Cache entries expire after 30 minutes.");
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
if (options.json) {
|
|
163
|
+
console.log(JSON.stringify(entry.data, null, 2));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const output = entry.data.fullOutput;
|
|
167
|
+
if (options.errors) {
|
|
168
|
+
const errorLines = output
|
|
169
|
+
.split("\n")
|
|
170
|
+
.filter((line) => /error:|FAILED|Exception/i.test(line));
|
|
171
|
+
console.log("Errors:");
|
|
172
|
+
errorLines.forEach((line) => console.log(line));
|
|
173
|
+
}
|
|
174
|
+
else if (options.warnings) {
|
|
175
|
+
const warningLines = output
|
|
176
|
+
.split("\n")
|
|
177
|
+
.filter((line) => /warning:|warn:/i.test(line));
|
|
178
|
+
console.log("Warnings:");
|
|
179
|
+
warningLines.forEach((line) => console.log(line));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
console.log(output);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
187
|
+
console.error(`Error: ${errorMessage}`);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
return gradle;
|
|
192
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createGradleCommand } from "./gradle.js";
|
|
2
|
+
export { createAdbCommand } from "./adb.js";
|
|
3
|
+
export { createEmulatorCommand } from "./emulator.js";
|
|
4
|
+
export { createUiCommand } from "./ui.js";
|
|
5
|
+
export { createCacheCommand } from "./cache.js";
|
|
6
|
+
export * from "./formatter.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createGradleCommand } from "./gradle.js";
|
|
2
|
+
export { createAdbCommand } from "./adb.js";
|
|
3
|
+
export { createEmulatorCommand } from "./emulator.js";
|
|
4
|
+
export { createUiCommand } from "./ui.js";
|
|
5
|
+
export { createCacheCommand } from "./cache.js";
|
|
6
|
+
export * from "./formatter.js";
|
package/dist/cli/ui.d.ts
ADDED