@jetstart/cli 1.1.4 → 1.4.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/dist/cli.js +18 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/android-emulator.d.ts +8 -0
- package/dist/commands/android-emulator.d.ts.map +1 -0
- package/dist/commands/android-emulator.js +280 -0
- package/dist/commands/android-emulator.js.map +1 -0
- package/dist/commands/create.d.ts +1 -6
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +119 -0
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/dev.d.ts +1 -7
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +69 -10
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +5 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/install-audit.d.ts +9 -0
- package/dist/commands/install-audit.d.ts.map +1 -0
- package/dist/commands/install-audit.js +185 -0
- package/dist/commands/install-audit.js.map +1 -0
- package/dist/types/index.d.ts +22 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/android-sdk.d.ts +81 -0
- package/dist/utils/android-sdk.d.ts.map +1 -0
- package/dist/utils/android-sdk.js +432 -0
- package/dist/utils/android-sdk.js.map +1 -0
- package/dist/utils/downloader.d.ts +35 -0
- package/dist/utils/downloader.d.ts.map +1 -0
- package/dist/utils/downloader.js +214 -0
- package/dist/utils/downloader.js.map +1 -0
- package/dist/utils/emulator-deployer.d.ts +29 -0
- package/dist/utils/emulator-deployer.d.ts.map +1 -0
- package/dist/utils/emulator-deployer.js +224 -0
- package/dist/utils/emulator-deployer.js.map +1 -0
- package/dist/utils/emulator.d.ts +101 -0
- package/dist/utils/emulator.d.ts.map +1 -0
- package/dist/utils/emulator.js +410 -0
- package/dist/utils/emulator.js.map +1 -0
- package/dist/utils/java.d.ts +25 -0
- package/dist/utils/java.d.ts.map +1 -0
- package/dist/utils/java.js +363 -0
- package/dist/utils/java.js.map +1 -0
- package/dist/utils/system-tools.d.ts +93 -0
- package/dist/utils/system-tools.d.ts.map +1 -0
- package/dist/utils/system-tools.js +599 -0
- package/dist/utils/system-tools.js.map +1 -0
- package/dist/utils/template.d.ts.map +1 -1
- package/dist/utils/template.js +777 -748
- package/dist/utils/template.js.map +1 -1
- package/package.json +7 -3
- package/src/cli.ts +20 -2
- package/src/commands/android-emulator.ts +304 -0
- package/src/commands/create.ts +128 -5
- package/src/commands/dev.ts +71 -18
- package/src/commands/index.ts +3 -1
- package/src/commands/install-audit.ts +227 -0
- package/src/types/index.ts +30 -0
- package/src/utils/android-sdk.ts +478 -0
- package/src/utils/downloader.ts +201 -0
- package/src/utils/emulator-deployer.ts +210 -0
- package/src/utils/emulator.ts +463 -0
- package/src/utils/java.ts +369 -0
- package/src/utils/system-tools.ts +648 -0
- package/src/utils/template.ts +875 -867
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System tool detection utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
|
+
import * as fs from 'fs-extra';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as os from 'os';
|
|
10
|
+
import { compareVersions, isVersionCompatible } from '@jetstart/shared';
|
|
11
|
+
|
|
12
|
+
const execAsync = promisify(exec);
|
|
13
|
+
const which = require('which');
|
|
14
|
+
|
|
15
|
+
export interface ToolInfo {
|
|
16
|
+
name: string;
|
|
17
|
+
installed: boolean;
|
|
18
|
+
version?: string;
|
|
19
|
+
path?: string;
|
|
20
|
+
status: 'ok' | 'warning' | 'error';
|
|
21
|
+
message?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Version requirements for different tools
|
|
26
|
+
*/
|
|
27
|
+
export const VERSION_REQUIREMENTS = {
|
|
28
|
+
node: { min: '18.0.0', recommended: '20.0.0' },
|
|
29
|
+
npm: { min: '9.0.0', recommended: '10.0.0' },
|
|
30
|
+
java: { min: '17.0.0', recommended: '17.0.0' },
|
|
31
|
+
gradle: { min: '8.0.0', recommended: '8.5.0' },
|
|
32
|
+
buildTools: { min: '33.0.0', recommended: '34.0.0' },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check version compatibility status
|
|
37
|
+
*/
|
|
38
|
+
function checkVersionCompatibility(
|
|
39
|
+
tool: keyof typeof VERSION_REQUIREMENTS,
|
|
40
|
+
version: string
|
|
41
|
+
): { status: 'ok' | 'warning' | 'error'; message?: string } {
|
|
42
|
+
const req = VERSION_REQUIREMENTS[tool];
|
|
43
|
+
if (!req) return { status: 'ok' };
|
|
44
|
+
|
|
45
|
+
if (compareVersions(version, req.min) < 0) {
|
|
46
|
+
return {
|
|
47
|
+
status: 'error',
|
|
48
|
+
message: `Version ${version} is below minimum ${req.min}`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (compareVersions(version, req.recommended) < 0) {
|
|
53
|
+
return {
|
|
54
|
+
status: 'warning',
|
|
55
|
+
message: `Version ${version} is outdated (${req.recommended} recommended)`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { status: 'ok' };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Detect Node.js installation
|
|
64
|
+
*/
|
|
65
|
+
export async function detectNode(): Promise<ToolInfo> {
|
|
66
|
+
try {
|
|
67
|
+
const { stdout } = await execAsync('node --version');
|
|
68
|
+
const version = stdout.trim().replace('v', '');
|
|
69
|
+
const toolPath = await which('node').catch(() => undefined);
|
|
70
|
+
|
|
71
|
+
const compat = checkVersionCompatibility('node', version);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
name: 'Node.js',
|
|
75
|
+
installed: true,
|
|
76
|
+
version,
|
|
77
|
+
path: toolPath,
|
|
78
|
+
status: compat.status,
|
|
79
|
+
message: compat.message,
|
|
80
|
+
};
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return {
|
|
83
|
+
name: 'Node.js',
|
|
84
|
+
installed: false,
|
|
85
|
+
status: 'error',
|
|
86
|
+
message: 'Node.js not found. Install from https://nodejs.org',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Detect npm installation
|
|
93
|
+
*/
|
|
94
|
+
export async function detectNpm(): Promise<ToolInfo> {
|
|
95
|
+
try {
|
|
96
|
+
const { stdout } = await execAsync('npm --version');
|
|
97
|
+
const version = stdout.trim();
|
|
98
|
+
const toolPath = await which('npm').catch(() => undefined);
|
|
99
|
+
|
|
100
|
+
const compat = checkVersionCompatibility('npm', version);
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
name: 'npm',
|
|
104
|
+
installed: true,
|
|
105
|
+
version,
|
|
106
|
+
path: toolPath,
|
|
107
|
+
status: compat.status,
|
|
108
|
+
message: compat.message,
|
|
109
|
+
};
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
name: 'npm',
|
|
113
|
+
installed: false,
|
|
114
|
+
status: 'error',
|
|
115
|
+
message: 'npm not found. Install from https://nodejs.org',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Parse Java version from command output
|
|
122
|
+
*/
|
|
123
|
+
function parseJavaVersion(output: string): string | null {
|
|
124
|
+
// Handles various formats:
|
|
125
|
+
// openjdk version "17.0.9" 2023-10-17
|
|
126
|
+
// java version "1.8.0_291"
|
|
127
|
+
// openjdk 11.0.12 2021-07-20
|
|
128
|
+
const match = output.match(/version\s+"?(\d+)\.(\d+)\.(\d+)/i) ||
|
|
129
|
+
output.match(/openjdk\s+(\d+)\.(\d+)\.(\d+)/i);
|
|
130
|
+
|
|
131
|
+
if (match) {
|
|
132
|
+
// Handle old Java versioning (1.8 = Java 8)
|
|
133
|
+
if (match[1] === '1') {
|
|
134
|
+
return match[2]; // Return 8 from 1.8
|
|
135
|
+
}
|
|
136
|
+
return `${match[1]}.${match[2]}.${match[3]}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Detect Java/JDK installation
|
|
144
|
+
*/
|
|
145
|
+
export async function detectJava(): Promise<ToolInfo> {
|
|
146
|
+
try {
|
|
147
|
+
const { stdout } = await execAsync('java -version 2>&1');
|
|
148
|
+
const version = parseJavaVersion(stdout);
|
|
149
|
+
|
|
150
|
+
if (!version) {
|
|
151
|
+
return {
|
|
152
|
+
name: 'Java/JDK',
|
|
153
|
+
installed: true,
|
|
154
|
+
status: 'warning',
|
|
155
|
+
message: 'Java found but version could not be determined',
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const toolPath = process.env.JAVA_HOME || (await which('java').catch(() => undefined));
|
|
160
|
+
const compat = checkVersionCompatibility('java', version);
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
name: 'Java/JDK',
|
|
164
|
+
installed: true,
|
|
165
|
+
version,
|
|
166
|
+
path: toolPath,
|
|
167
|
+
status: compat.status,
|
|
168
|
+
message: compat.message,
|
|
169
|
+
};
|
|
170
|
+
} catch (error) {
|
|
171
|
+
return {
|
|
172
|
+
name: 'Java/JDK',
|
|
173
|
+
installed: false,
|
|
174
|
+
status: 'error',
|
|
175
|
+
message: 'Java not found. Install JDK 17+ from https://adoptium.net',
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Detect Gradle installation
|
|
182
|
+
*/
|
|
183
|
+
export async function detectGradle(): Promise<ToolInfo> {
|
|
184
|
+
try {
|
|
185
|
+
const { stdout } = await execAsync('gradle --version');
|
|
186
|
+
const match = stdout.match(/Gradle\s+(\d+\.\d+(?:\.\d+)?)/);
|
|
187
|
+
const version = match ? match[1] : undefined;
|
|
188
|
+
|
|
189
|
+
if (!version) {
|
|
190
|
+
return {
|
|
191
|
+
name: 'Gradle',
|
|
192
|
+
installed: true,
|
|
193
|
+
status: 'warning',
|
|
194
|
+
message: 'Gradle found but version could not be determined',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const toolPath = await which('gradle').catch(() => undefined);
|
|
199
|
+
const compat = checkVersionCompatibility('gradle', version);
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
name: 'Gradle',
|
|
203
|
+
installed: true,
|
|
204
|
+
version,
|
|
205
|
+
path: toolPath,
|
|
206
|
+
status: compat.status,
|
|
207
|
+
message: compat.message,
|
|
208
|
+
};
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return {
|
|
211
|
+
name: 'Gradle',
|
|
212
|
+
installed: false,
|
|
213
|
+
status: 'warning',
|
|
214
|
+
message: 'Gradle not found (Gradle wrapper will be used)',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get default Android SDK path based on platform
|
|
221
|
+
*/
|
|
222
|
+
export function getDefaultSDKPath(): string {
|
|
223
|
+
const homeDir = os.homedir();
|
|
224
|
+
const platform = os.platform();
|
|
225
|
+
|
|
226
|
+
switch (platform) {
|
|
227
|
+
case 'win32':
|
|
228
|
+
return path.join(homeDir, 'AppData', 'Local', 'Android', 'Sdk');
|
|
229
|
+
case 'darwin':
|
|
230
|
+
return path.join(homeDir, 'Library', 'Android', 'sdk');
|
|
231
|
+
default: // Linux
|
|
232
|
+
return path.join(homeDir, 'Android', 'Sdk');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Find Android SDK location
|
|
238
|
+
*/
|
|
239
|
+
export async function findAndroidSDK(): Promise<string | null> {
|
|
240
|
+
// Check environment variables first
|
|
241
|
+
const envPaths = [
|
|
242
|
+
process.env.ANDROID_HOME,
|
|
243
|
+
process.env.ANDROID_SDK_ROOT,
|
|
244
|
+
].filter(Boolean);
|
|
245
|
+
|
|
246
|
+
for (const envPath of envPaths) {
|
|
247
|
+
if (envPath && await fs.pathExists(envPath)) {
|
|
248
|
+
return envPath;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Check default path
|
|
253
|
+
const defaultPath = getDefaultSDKPath();
|
|
254
|
+
if (await fs.pathExists(defaultPath)) {
|
|
255
|
+
return defaultPath;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Check alternative paths
|
|
259
|
+
const platform = os.platform();
|
|
260
|
+
const homeDir = os.homedir();
|
|
261
|
+
const altPaths: string[] = [];
|
|
262
|
+
|
|
263
|
+
if (platform === 'win32') {
|
|
264
|
+
altPaths.push(
|
|
265
|
+
'C:\\Android\\Sdk',
|
|
266
|
+
'C:\\Android',
|
|
267
|
+
path.join(homeDir, 'AppData', 'Local', 'Android', 'Sdk'),
|
|
268
|
+
'C:\\Program Files (x86)\\Android\\android-sdk'
|
|
269
|
+
);
|
|
270
|
+
} else if (platform === 'darwin') {
|
|
271
|
+
altPaths.push(
|
|
272
|
+
path.join(homeDir, 'Library', 'Android', 'sdk'),
|
|
273
|
+
'/opt/android-sdk'
|
|
274
|
+
);
|
|
275
|
+
} else {
|
|
276
|
+
altPaths.push(
|
|
277
|
+
path.join(homeDir, 'Android', 'Sdk'),
|
|
278
|
+
'/opt/android-sdk'
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
for (const altPath of altPaths) {
|
|
283
|
+
if (await fs.pathExists(altPath)) {
|
|
284
|
+
return altPath;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Detect Android SDK installation
|
|
293
|
+
*/
|
|
294
|
+
export async function detectAndroidSDK(): Promise<ToolInfo> {
|
|
295
|
+
const sdkPath = await findAndroidSDK();
|
|
296
|
+
|
|
297
|
+
if (!sdkPath) {
|
|
298
|
+
return {
|
|
299
|
+
name: 'Android SDK',
|
|
300
|
+
installed: false,
|
|
301
|
+
status: 'error',
|
|
302
|
+
message: 'Android SDK not found. Run "jetstart create --full-install" to install',
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Verify it's a valid SDK
|
|
307
|
+
const platformsPath = path.join(sdkPath, 'platforms');
|
|
308
|
+
const buildToolsPath = path.join(sdkPath, 'build-tools');
|
|
309
|
+
|
|
310
|
+
const isValid = (await fs.pathExists(platformsPath)) || (await fs.pathExists(buildToolsPath));
|
|
311
|
+
|
|
312
|
+
if (!isValid) {
|
|
313
|
+
return {
|
|
314
|
+
name: 'Android SDK',
|
|
315
|
+
installed: true,
|
|
316
|
+
path: sdkPath,
|
|
317
|
+
status: 'warning',
|
|
318
|
+
message: 'SDK found but appears incomplete',
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
name: 'Android SDK',
|
|
324
|
+
installed: true,
|
|
325
|
+
path: sdkPath,
|
|
326
|
+
status: 'ok',
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Detect Android cmdline-tools
|
|
332
|
+
*/
|
|
333
|
+
export async function detectAndroidCmdlineTools(): Promise<ToolInfo> {
|
|
334
|
+
const sdkPath = await findAndroidSDK();
|
|
335
|
+
|
|
336
|
+
if (!sdkPath) {
|
|
337
|
+
return {
|
|
338
|
+
name: 'cmdline-tools',
|
|
339
|
+
installed: false,
|
|
340
|
+
status: 'error',
|
|
341
|
+
message: 'Android SDK not found',
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const cmdlineToolsPath = path.join(sdkPath, 'cmdline-tools', 'latest');
|
|
346
|
+
const exists = await fs.pathExists(cmdlineToolsPath);
|
|
347
|
+
|
|
348
|
+
if (!exists) {
|
|
349
|
+
return {
|
|
350
|
+
name: 'cmdline-tools',
|
|
351
|
+
installed: false,
|
|
352
|
+
status: 'error',
|
|
353
|
+
message: 'Android cmdline-tools not installed',
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Try to get version
|
|
358
|
+
try {
|
|
359
|
+
const sdkmanagerPath = path.join(
|
|
360
|
+
cmdlineToolsPath,
|
|
361
|
+
'bin',
|
|
362
|
+
os.platform() === 'win32' ? 'sdkmanager.bat' : 'sdkmanager'
|
|
363
|
+
);
|
|
364
|
+
const { stdout } = await execAsync(`"${sdkmanagerPath}" --version`);
|
|
365
|
+
const version = stdout.trim();
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
name: 'cmdline-tools',
|
|
369
|
+
installed: true,
|
|
370
|
+
version,
|
|
371
|
+
path: cmdlineToolsPath,
|
|
372
|
+
status: 'ok',
|
|
373
|
+
};
|
|
374
|
+
} catch {
|
|
375
|
+
return {
|
|
376
|
+
name: 'cmdline-tools',
|
|
377
|
+
installed: true,
|
|
378
|
+
path: cmdlineToolsPath,
|
|
379
|
+
status: 'ok',
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Detect Android build-tools
|
|
386
|
+
*/
|
|
387
|
+
export async function detectAndroidBuildTools(): Promise<ToolInfo> {
|
|
388
|
+
const sdkPath = await findAndroidSDK();
|
|
389
|
+
|
|
390
|
+
if (!sdkPath) {
|
|
391
|
+
return {
|
|
392
|
+
name: 'build-tools',
|
|
393
|
+
installed: false,
|
|
394
|
+
status: 'error',
|
|
395
|
+
message: 'Android SDK not found',
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const buildToolsPath = path.join(sdkPath, 'build-tools');
|
|
400
|
+
const exists = await fs.pathExists(buildToolsPath);
|
|
401
|
+
|
|
402
|
+
if (!exists) {
|
|
403
|
+
return {
|
|
404
|
+
name: 'build-tools',
|
|
405
|
+
installed: false,
|
|
406
|
+
status: 'error',
|
|
407
|
+
message: 'Android build-tools not installed',
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Find latest version
|
|
412
|
+
try {
|
|
413
|
+
const versions = await fs.readdir(buildToolsPath);
|
|
414
|
+
if (versions.length === 0) {
|
|
415
|
+
return {
|
|
416
|
+
name: 'build-tools',
|
|
417
|
+
installed: false,
|
|
418
|
+
status: 'error',
|
|
419
|
+
message: 'No build-tools versions found',
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Sort versions and get latest
|
|
424
|
+
const latest = versions.sort((a, b) => compareVersions(b, a))[0];
|
|
425
|
+
const compat = checkVersionCompatibility('buildTools', latest);
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
name: 'build-tools',
|
|
429
|
+
installed: true,
|
|
430
|
+
version: latest,
|
|
431
|
+
path: path.join(buildToolsPath, latest),
|
|
432
|
+
status: compat.status,
|
|
433
|
+
message: compat.message,
|
|
434
|
+
};
|
|
435
|
+
} catch {
|
|
436
|
+
return {
|
|
437
|
+
name: 'build-tools',
|
|
438
|
+
installed: true,
|
|
439
|
+
path: buildToolsPath,
|
|
440
|
+
status: 'ok',
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Detect Android platform-tools (adb, fastboot)
|
|
447
|
+
*/
|
|
448
|
+
export async function detectAndroidPlatformTools(): Promise<ToolInfo> {
|
|
449
|
+
const sdkPath = await findAndroidSDK();
|
|
450
|
+
|
|
451
|
+
if (!sdkPath) {
|
|
452
|
+
return {
|
|
453
|
+
name: 'platform-tools',
|
|
454
|
+
installed: false,
|
|
455
|
+
status: 'error',
|
|
456
|
+
message: 'Android SDK not found',
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const platformToolsPath = path.join(sdkPath, 'platform-tools');
|
|
461
|
+
const exists = await fs.pathExists(platformToolsPath);
|
|
462
|
+
|
|
463
|
+
if (!exists) {
|
|
464
|
+
return {
|
|
465
|
+
name: 'platform-tools',
|
|
466
|
+
installed: false,
|
|
467
|
+
status: 'error',
|
|
468
|
+
message: 'Android platform-tools not installed',
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Try to get adb version
|
|
473
|
+
try {
|
|
474
|
+
const adbPath = path.join(
|
|
475
|
+
platformToolsPath,
|
|
476
|
+
os.platform() === 'win32' ? 'adb.exe' : 'adb'
|
|
477
|
+
);
|
|
478
|
+
const { stdout } = await execAsync(`"${adbPath}" version`);
|
|
479
|
+
const match = stdout.match(/version\s+(\d+\.\d+\.\d+)/);
|
|
480
|
+
const version = match ? match[1] : undefined;
|
|
481
|
+
|
|
482
|
+
return {
|
|
483
|
+
name: 'platform-tools',
|
|
484
|
+
installed: true,
|
|
485
|
+
version,
|
|
486
|
+
path: platformToolsPath,
|
|
487
|
+
status: 'ok',
|
|
488
|
+
};
|
|
489
|
+
} catch {
|
|
490
|
+
return {
|
|
491
|
+
name: 'platform-tools',
|
|
492
|
+
installed: true,
|
|
493
|
+
path: platformToolsPath,
|
|
494
|
+
status: 'ok',
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Detect Android emulator
|
|
501
|
+
*/
|
|
502
|
+
export async function detectAndroidEmulator(): Promise<ToolInfo> {
|
|
503
|
+
const sdkPath = await findAndroidSDK();
|
|
504
|
+
|
|
505
|
+
if (!sdkPath) {
|
|
506
|
+
return {
|
|
507
|
+
name: 'emulator',
|
|
508
|
+
installed: false,
|
|
509
|
+
status: 'error',
|
|
510
|
+
message: 'Android SDK not found',
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const emulatorPath = path.join(sdkPath, 'emulator');
|
|
515
|
+
const exists = await fs.pathExists(emulatorPath);
|
|
516
|
+
|
|
517
|
+
if (!exists) {
|
|
518
|
+
return {
|
|
519
|
+
name: 'emulator',
|
|
520
|
+
installed: false,
|
|
521
|
+
status: 'warning',
|
|
522
|
+
message: 'Android emulator not installed',
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Try to get emulator version
|
|
527
|
+
try {
|
|
528
|
+
const emulatorBin = path.join(
|
|
529
|
+
emulatorPath,
|
|
530
|
+
os.platform() === 'win32' ? 'emulator.exe' : 'emulator'
|
|
531
|
+
);
|
|
532
|
+
const { stdout } = await execAsync(`"${emulatorBin}" -version`);
|
|
533
|
+
const match = stdout.match(/version\s+(\d+\.\d+\.\d+)/);
|
|
534
|
+
const version = match ? match[1] : undefined;
|
|
535
|
+
|
|
536
|
+
return {
|
|
537
|
+
name: 'emulator',
|
|
538
|
+
installed: true,
|
|
539
|
+
version,
|
|
540
|
+
path: emulatorPath,
|
|
541
|
+
status: 'ok',
|
|
542
|
+
};
|
|
543
|
+
} catch {
|
|
544
|
+
return {
|
|
545
|
+
name: 'emulator',
|
|
546
|
+
installed: true,
|
|
547
|
+
path: emulatorPath,
|
|
548
|
+
status: 'ok',
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Detect specific Android platform API level
|
|
555
|
+
*/
|
|
556
|
+
export async function detectAndroidPlatform(apiLevel: number): Promise<ToolInfo> {
|
|
557
|
+
const sdkPath = await findAndroidSDK();
|
|
558
|
+
|
|
559
|
+
if (!sdkPath) {
|
|
560
|
+
return {
|
|
561
|
+
name: `API ${apiLevel}`,
|
|
562
|
+
installed: false,
|
|
563
|
+
status: 'error',
|
|
564
|
+
message: 'Android SDK not found',
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const platformPath = path.join(sdkPath, 'platforms', `android-${apiLevel}`);
|
|
569
|
+
const exists = await fs.pathExists(platformPath);
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
name: `API ${apiLevel}`,
|
|
573
|
+
installed: exists,
|
|
574
|
+
path: exists ? platformPath : undefined,
|
|
575
|
+
status: exists ? 'ok' : 'error',
|
|
576
|
+
message: exists ? undefined : `Install with: sdkmanager "platforms;android-${apiLevel}"`,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Check JAVA_HOME environment variable
|
|
582
|
+
*/
|
|
583
|
+
export async function checkJavaHome(): Promise<ToolInfo> {
|
|
584
|
+
const javaHome = process.env.JAVA_HOME;
|
|
585
|
+
|
|
586
|
+
if (!javaHome) {
|
|
587
|
+
return {
|
|
588
|
+
name: 'JAVA_HOME',
|
|
589
|
+
installed: false,
|
|
590
|
+
status: 'warning',
|
|
591
|
+
message: 'JAVA_HOME environment variable not set',
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const exists = await fs.pathExists(javaHome);
|
|
596
|
+
|
|
597
|
+
if (!exists) {
|
|
598
|
+
return {
|
|
599
|
+
name: 'JAVA_HOME',
|
|
600
|
+
installed: false,
|
|
601
|
+
path: javaHome,
|
|
602
|
+
status: 'error',
|
|
603
|
+
message: `JAVA_HOME points to non-existent path: ${javaHome}`,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return {
|
|
608
|
+
name: 'JAVA_HOME',
|
|
609
|
+
installed: true,
|
|
610
|
+
path: javaHome,
|
|
611
|
+
status: 'ok',
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Check ANDROID_HOME environment variable
|
|
617
|
+
*/
|
|
618
|
+
export async function checkAndroidHome(): Promise<ToolInfo> {
|
|
619
|
+
const androidHome = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
|
|
620
|
+
|
|
621
|
+
if (!androidHome) {
|
|
622
|
+
return {
|
|
623
|
+
name: 'ANDROID_HOME',
|
|
624
|
+
installed: false,
|
|
625
|
+
status: 'warning',
|
|
626
|
+
message: 'ANDROID_HOME environment variable not set',
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const exists = await fs.pathExists(androidHome);
|
|
631
|
+
|
|
632
|
+
if (!exists) {
|
|
633
|
+
return {
|
|
634
|
+
name: 'ANDROID_HOME',
|
|
635
|
+
installed: false,
|
|
636
|
+
path: androidHome,
|
|
637
|
+
status: 'error',
|
|
638
|
+
message: `ANDROID_HOME points to non-existent path: ${androidHome}`,
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
name: 'ANDROID_HOME',
|
|
644
|
+
installed: true,
|
|
645
|
+
path: androidHome,
|
|
646
|
+
status: 'ok',
|
|
647
|
+
};
|
|
648
|
+
}
|