jvm-manager-cli 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/bin/jvm.js ADDED
@@ -0,0 +1,796 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from 'child_process';
4
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, symlinkSync, writeFileSync } from 'fs';
5
+ import { dirname, join, resolve } from 'path';
6
+ import { homedir } from 'os';
7
+
8
+ const APP_NAME = 'jvm-manager';
9
+ const HOME = homedir();
10
+ const APP_DIR = join(HOME, '.local', 'share', APP_NAME);
11
+ const JDKS_DIR = join(APP_DIR, 'jdks');
12
+ const CURRENT_GLOBAL = join(APP_DIR, 'current');
13
+ const PROJECT_FILE = '.jvmrc';
14
+
15
+ function ensureDirs() {
16
+ if (!existsSync(APP_DIR)) {
17
+ mkdirSync(APP_DIR, { recursive: true });
18
+ }
19
+ if (!existsSync(JDKS_DIR)) {
20
+ mkdirSync(JDKS_DIR, { recursive: true });
21
+ }
22
+ }
23
+
24
+ function run(cmd, options = {}) {
25
+ return execSync(cmd, {
26
+ stdio: ['ignore', 'pipe', 'pipe'],
27
+ encoding: 'utf8',
28
+ ...options
29
+ }).trim();
30
+ }
31
+
32
+ function fail(msg) {
33
+ console.error(`Error: ${msg}`);
34
+ process.exit(1);
35
+ }
36
+
37
+ function usage() {
38
+ console.log(`jvm - Linux JDK manager
39
+
40
+ Usage:
41
+ jvm install <version> Install JDK to local store
42
+ jvm list List installed JDKs (including system)
43
+ jvm use <version> [--global] Switch JDK globally (default)
44
+ jvm use <version> --project Write .jvmrc in current project
45
+ jvm current Show current global and project JDK
46
+ jvm env [--global|--project] Print export lines for shell
47
+ jvm doctor Check system for compatibility
48
+ jvm remove <version> Remove installed JDK
49
+ jvm link <system-path> <version> Link system JDK to local store
50
+
51
+ Examples:
52
+ jvm install 17
53
+ jvm use 21 --global
54
+ jvm use 17 --project
55
+ jvm link /usr/lib/jvm/java-17-openjdk-amd64 17
56
+ eval "$(jvm env --project)"`);
57
+ }
58
+
59
+ function detectArch() {
60
+ const arch = process.arch;
61
+ if (arch === 'x64') return 'x64';
62
+ if (arch === 'arm64') return 'aarch64';
63
+ fail(`Unsupported architecture: ${arch}`);
64
+ }
65
+
66
+ function getOSRelease() {
67
+ const releaseFile = '/etc/os-release';
68
+ if (!existsSync(releaseFile)) {
69
+ return {
70
+ ID: 'unknown',
71
+ PRETTY_NAME: 'Unknown'
72
+ };
73
+ }
74
+
75
+ const content = readFileSync(releaseFile, 'utf8');
76
+ const releaseInfo = {};
77
+
78
+ content.split('\n').forEach(line => {
79
+ const trimmed = line.trim();
80
+ if (trimmed && !trimmed.startsWith('#')) {
81
+ const [key, value] = trimmed.split('=');
82
+ releaseInfo[key] = value ? value.replace(/['"]/g, '') : '';
83
+ }
84
+ });
85
+
86
+ return releaseInfo;
87
+ }
88
+
89
+ function install(version) {
90
+ ensureDirs();
91
+ if (!/^\d+$/.test(version)) {
92
+ fail('Version should be a major number, e.g. 8, 11, 17, 21');
93
+ }
94
+ const target = join(JDKS_DIR, version);
95
+ if (existsSync(target)) {
96
+ console.log(`JDK ${version} already installed: ${target}`);
97
+ return;
98
+ }
99
+
100
+ const osRelease = getOSRelease();
101
+ console.log(`Detected OS: ${osRelease.PRETTY_NAME}`);
102
+
103
+ // 检查用户是否有 sudo 权限
104
+ try {
105
+ run('sudo -n true');
106
+ } catch (err) {
107
+ console.log('This command requires sudo privileges. Please enter your password when prompted.');
108
+ }
109
+
110
+ if (osRelease.ID === 'ubuntu' || osRelease.ID === 'debian') {
111
+ installUbuntuDebian(version, target);
112
+ } else if (osRelease.ID === 'fedora' || osRelease.ID === 'centos' || osRelease.ID === 'rhel') {
113
+ installFedoraCentOSRHEL(version, target);
114
+ } else if (osRelease.ID === 'arch') {
115
+ installArchLinux(version, target);
116
+ } else {
117
+ installFromDownload(version, target);
118
+ }
119
+ }
120
+
121
+ function installUbuntuDebian(version, target) {
122
+ const packageName = `openjdk-${version}-jdk`;
123
+
124
+ console.log(`Installing OpenJDK ${version} using apt...`);
125
+
126
+ try {
127
+ // Try to update package list
128
+ try {
129
+ run('sudo apt update -qq');
130
+ } catch (err) {
131
+ console.error('Failed to update package list. Please try running `sudo apt update` manually first.');
132
+ console.error(err);
133
+ fail('Failed to install OpenJDK');
134
+ }
135
+
136
+ // Try to install package without interactive prompts
137
+ try {
138
+ run(`sudo apt install -y -qq ${packageName}`);
139
+ } catch (err) {
140
+ console.error('Failed to install package. Please try running the following command manually:');
141
+ console.error(` sudo apt install -y ${packageName}`);
142
+ console.error(err);
143
+ fail('Failed to install OpenJDK');
144
+ }
145
+
146
+ // Find the installed JDK path
147
+ let jdkPath;
148
+ if (existsSync('/usr/lib/jvm')) {
149
+ const entries = readdirSync('/usr/lib/jvm', { withFileTypes: true });
150
+ for (const entry of entries) {
151
+ if (entry.isDirectory() && entry.name.includes(`java-${version}`)) {
152
+ jdkPath = join('/usr/lib/jvm', entry.name);
153
+ break;
154
+ }
155
+ }
156
+ }
157
+
158
+ if (!jdkPath) {
159
+ fail(`Could not find installed OpenJDK ${version} in /usr/lib/jvm`);
160
+ }
161
+
162
+ symlinkSync(jdkPath, target, 'dir');
163
+ console.log(`Installed OpenJDK ${version} at ${target}`);
164
+ } catch (err) {
165
+ console.error(`Error: ${err}`);
166
+ console.error('Please ensure you have sudo privileges and try again.');
167
+ fail('Installation failed');
168
+ }
169
+ }
170
+
171
+ function installFedoraCentOSRHEL(version, target) {
172
+ const packageName = `java-${version}-openjdk-devel`;
173
+
174
+ console.log(`Installing OpenJDK ${version} using dnf...`);
175
+
176
+ try {
177
+ // Try to update package list
178
+ try {
179
+ run('sudo dnf check-update -q');
180
+ } catch (err) {
181
+ console.error('Failed to check for updates. Please try running `sudo dnf check-update` manually first.');
182
+ console.error(err);
183
+ fail('Failed to install OpenJDK');
184
+ }
185
+
186
+ // Try to install package without interactive prompts
187
+ try {
188
+ run(`sudo dnf install -y -q ${packageName}`);
189
+ } catch (err) {
190
+ console.error('Failed to install package. Please try running the following command manually:');
191
+ console.error(` sudo dnf install -y ${packageName}`);
192
+ console.error(err);
193
+ fail('Failed to install OpenJDK');
194
+ }
195
+
196
+ // Find the installed JDK path
197
+ let jdkPath;
198
+ if (existsSync('/usr/lib/jvm')) {
199
+ const entries = readdirSync('/usr/lib/jvm', { withFileTypes: true });
200
+ for (const entry of entries) {
201
+ if (entry.isDirectory() && entry.name.includes(`java-${version}`)) {
202
+ jdkPath = join('/usr/lib/jvm', entry.name);
203
+ break;
204
+ }
205
+ }
206
+ }
207
+
208
+ if (!jdkPath) {
209
+ fail(`Could not find installed OpenJDK ${version} in /usr/lib/jvm`);
210
+ }
211
+
212
+ symlinkSync(jdkPath, target, 'dir');
213
+ console.log(`Installed OpenJDK ${version} at ${target}`);
214
+ } catch (err) {
215
+ console.error(`Error: ${err}`);
216
+ console.error('Please ensure you have sudo privileges and try again.');
217
+ fail('Installation failed');
218
+ }
219
+ }
220
+
221
+ function installArchLinux(version, target) {
222
+ const packageName = `jdk${version}-openjdk`;
223
+
224
+ console.log(`Installing OpenJDK ${version} using pacman...`);
225
+
226
+ try {
227
+ // Try to install package without interactive prompts
228
+ try {
229
+ run(`sudo pacman -Syu --noconfirm ${packageName}`);
230
+ } catch (err) {
231
+ console.error('Failed to install package. Please try running the following command manually:');
232
+ console.error(` sudo pacman -Syu --noconfirm ${packageName}`);
233
+ console.error(err);
234
+ fail('Failed to install OpenJDK');
235
+ }
236
+
237
+ // Find the installed JDK path
238
+ let jdkPath;
239
+ if (existsSync('/usr/lib/jvm')) {
240
+ const entries = readdirSync('/usr/lib/jvm', { withFileTypes: true });
241
+ for (const entry of entries) {
242
+ if (entry.isDirectory() && entry.name.includes(`java-${version}`)) {
243
+ jdkPath = join('/usr/lib/jvm', entry.name);
244
+ break;
245
+ }
246
+ }
247
+ }
248
+
249
+ if (!jdkPath) {
250
+ fail(`Could not find installed OpenJDK ${version} in /usr/lib/jvm`);
251
+ }
252
+
253
+ symlinkSync(jdkPath, target, 'dir');
254
+ console.log(`Installed OpenJDK ${version} at ${target}`);
255
+ } catch (err) {
256
+ console.error(`Error: ${err}`);
257
+ console.error('Please ensure you have sudo privileges and try again.');
258
+ fail('Installation failed');
259
+ }
260
+ }
261
+
262
+ function installFromDownload(version, target) {
263
+ const { link, filename } = detectLatestBuild(version);
264
+ const tmpDir = run('mktemp -d');
265
+ const tarPath = join(tmpDir, filename);
266
+
267
+ try {
268
+ console.log(`Downloading JDK ${version}...`);
269
+ run('curl -fL "' + link + '" -o "' + tarPath + '"', { stdio: 'inherit' });
270
+ mkdirSync(target, { recursive: true });
271
+ run('tar -xzf "' + tarPath + '" -C "' + target + '" --strip-components=1', { stdio: 'inherit' });
272
+ } catch (error) {
273
+ rmSync(target, { recursive: true, force: true });
274
+ throw error;
275
+ } finally {
276
+ rmSync(tmpDir, { recursive: true, force: true });
277
+ }
278
+ console.log(`Installed JDK ${version} at ${target}`);
279
+ }
280
+
281
+ function detectLatestBuild(version) {
282
+ const arch = detectArch();
283
+
284
+ let osArch = 'x64';
285
+ if (arch === 'arm64') {
286
+ osArch = 'aarch64';
287
+ }
288
+
289
+ if (version >= 17) {
290
+ const url = 'https://download.java.net/java/GA/jdk' + version + '/latest/openjdk-' + version + '_linux-' + osArch + '_bin.tar.gz';
291
+ const filename = 'openjdk-' + version + '_linux-' + osArch + '_bin.tar.gz';
292
+ return { link: url, filename: filename };
293
+ } else if (version === 11) {
294
+ const url = 'https://download.java.net/java/GA/jdk11/9/GPL/openjdk-11.0.2_linux-' + osArch + '_bin.tar.gz';
295
+ const filename = 'openjdk-11.0.2_linux-' + osArch + '_bin.tar.gz';
296
+ return { link: url, filename: filename };
297
+ } else if (version === 8) {
298
+ const url = 'https://download.java.net/java/GA/jdk8u402/40265a1938f34f94a9e96e1708e6c9f3/21/GPL/openjdk-8u402-linux-' + osArch + '_bin.tar.gz';
299
+ const filename = 'openjdk-8u402-linux-' + osArch + '_bin.tar.gz';
300
+ return { link: url, filename: filename };
301
+ } else {
302
+ fail(`Unsupported OpenJDK version: ${version}`);
303
+ }
304
+ }
305
+
306
+ function listInstalled() {
307
+ ensureDirs();
308
+
309
+ const versions = new Map();
310
+
311
+ if (existsSync(JDKS_DIR)) {
312
+ const localEntries = readdirSync(JDKS_DIR, { withFileTypes: true })
313
+ .filter(d => d.isDirectory() || d.isSymbolicLink())
314
+ .map(d => {
315
+ let jdkPath = join(JDKS_DIR, d.name);
316
+
317
+ if (d.isSymbolicLink()) {
318
+ try {
319
+ const realPath = run('readlink -f "' + jdkPath + '"');
320
+ return {
321
+ name: d.name,
322
+ path: realPath
323
+ };
324
+ } catch (err) {
325
+ return {
326
+ name: d.name,
327
+ path: jdkPath
328
+ };
329
+ }
330
+ }
331
+
332
+ return {
333
+ name: d.name,
334
+ path: jdkPath
335
+ };
336
+ });
337
+
338
+ for (const entry of localEntries) {
339
+ versions.set(entry.name, entry.path);
340
+ }
341
+ }
342
+
343
+ if (existsSync('/usr/lib/jvm')) {
344
+ const systemEntries = readdirSync('/usr/lib/jvm', { withFileTypes: true })
345
+ .filter(d => d.isDirectory() && (d.name.includes('-amd64') || d.name.includes('-aarch64')))
346
+ .map(d => {
347
+ const match = d.name.match(/java-(\d+)/);
348
+ if (match) {
349
+ const version = match[1];
350
+ return {
351
+ name: version,
352
+ path: join('/usr/lib/jvm', d.name)
353
+ };
354
+ }
355
+ return null;
356
+ })
357
+ .filter(Boolean);
358
+
359
+ for (const entry of systemEntries) {
360
+ if (!versions.has(entry.name)) {
361
+ versions.set(entry.name, entry.path);
362
+ }
363
+ }
364
+ }
365
+
366
+ if (versions.size === 0) {
367
+ console.log('No JDK installed. Try: jvm install 17');
368
+ return;
369
+ }
370
+
371
+ let current = null;
372
+ try {
373
+ current = existsSync(CURRENT_GLOBAL) ? readlinkSafe(CURRENT_GLOBAL) : null;
374
+ } catch {
375
+ current = null;
376
+ }
377
+
378
+ const sortedVersions = Array.from(versions.entries()).sort((a, b) => Number(a[0]) - Number(b[0]));
379
+
380
+ console.log('Installed JDK versions:');
381
+ for (const [version, path] of sortedVersions) {
382
+ let isCurrent = false;
383
+ if (current) {
384
+ isCurrent = path === current || (typeof path === 'string' && typeof current === 'string' && (path.includes(current) || current.includes(path)));
385
+ }
386
+
387
+ const marker = isCurrent ? '*' : ' ';
388
+ console.log(`${marker} ${version} - ${path}`);
389
+ }
390
+ }
391
+
392
+ function detectSystemJdks() {
393
+ const versions = new Set();
394
+
395
+ try {
396
+ if (existsSync('/usr/lib/jvm')) {
397
+ const entries = readdirSync('/usr/lib/jvm', { withFileTypes: true });
398
+ for (const entry of entries) {
399
+ if (entry.isDirectory() && (entry.name.includes('-amd64') || entry.name.includes('-aarch64'))) {
400
+ const match = entry.name.match(/java-(\d+)/);
401
+ if (match) {
402
+ versions.add(match[1]);
403
+ }
404
+ }
405
+ }
406
+ }
407
+ } catch (err) {
408
+ console.log(err);
409
+ }
410
+
411
+ try {
412
+ const versionStr = run('java -version 2>&1 | head -1');
413
+ const match = versionStr.match(/version "(.*)"/);
414
+ if (match) {
415
+ const javaVersion = match[1];
416
+ if (javaVersion.startsWith('1.')) {
417
+ const oldVersion = javaVersion.match(/1\.(\d+)\.0/);
418
+ if (oldVersion) {
419
+ versions.add(oldVersion[1]);
420
+ }
421
+ } else {
422
+ const newVersion = javaVersion.match(/(\d+)/);
423
+ if (newVersion) {
424
+ versions.add(newVersion[1]);
425
+ }
426
+ }
427
+ }
428
+ } catch (err) {
429
+ console.log(err);
430
+ }
431
+
432
+ return Array.from(versions).sort((a, b) => Number(a) - Number(b));
433
+ }
434
+
435
+ function readlinkSafe(path) {
436
+ return run('readlink -f "' + path + '"');
437
+ }
438
+
439
+ function findSystemJdkPath(version) {
440
+ if (!/^\d+$/.test(version)) {
441
+ fail('Version should be a major number, e.g. 8, 11, 17, 21');
442
+ }
443
+
444
+ try {
445
+ if (existsSync('/usr/lib/jvm')) {
446
+ const entries = readdirSync('/usr/lib/jvm', { withFileTypes: true });
447
+ for (const entry of entries) {
448
+ const match = entry.name.match(new RegExp('java-' + version, 'i'));
449
+ if (match) {
450
+ const candidate = join('/usr/lib/jvm', entry.name);
451
+ if (existsSync(join(candidate, 'bin', 'java'))) {
452
+ return candidate;
453
+ }
454
+ }
455
+ }
456
+ }
457
+
458
+ const javaHome = process.env.JAVA_HOME;
459
+ if (javaHome && existsSync(javaHome)) {
460
+ const currentVersion = getJavaVersionFromPath(javaHome);
461
+ if (currentVersion === version) {
462
+ return javaHome;
463
+ }
464
+ }
465
+
466
+ const whichJava = run('which java');
467
+ if (whichJava) {
468
+ const realPath = run('readlink -f "' + whichJava + '"');
469
+ const candidate = resolve(dirname(dirname(realPath)));
470
+ const currentVersion = getJavaVersionFromPath(candidate);
471
+ if (currentVersion === version) {
472
+ return candidate;
473
+ }
474
+ }
475
+ } catch (err) {
476
+ console.log(err);
477
+ }
478
+
479
+ fail('Could not find system JDK with version ' + version);
480
+ }
481
+
482
+ function getJavaVersionFromPath(path) {
483
+ try {
484
+ const releaseFile = join(path, 'release');
485
+ if (existsSync(releaseFile)) {
486
+ const content = readFileSync(releaseFile, 'utf8');
487
+ const match = content.match(/JAVA_VERSION="(\d+)/);
488
+ if (match) {
489
+ return match[1];
490
+ }
491
+ }
492
+ const versionStr = run('"' + join(path, 'bin', 'java') + '" -version 2>&1 | head -1');
493
+ const match = versionStr.match(/version "(.*)"/);
494
+ if (match) {
495
+ const javaVersion = match[1];
496
+ if (javaVersion.startsWith('1.')) {
497
+ const oldVersion = javaVersion.match(/1\.(\d+)\.0/);
498
+ if (oldVersion) {
499
+ return oldVersion[1];
500
+ }
501
+ } else {
502
+ const newVersion = javaVersion.match(/(\d+)/);
503
+ if (newVersion) {
504
+ return newVersion[1];
505
+ }
506
+ }
507
+ }
508
+ } catch (err) {
509
+ console.log(err);
510
+ }
511
+ return null;
512
+ }
513
+
514
+ function linkSystemJdk(systemPath, version) {
515
+ ensureDirs();
516
+
517
+ if (!existsSync(systemPath)) {
518
+ fail('System path ' + systemPath + ' does not exist');
519
+ }
520
+
521
+ const binPath = join(systemPath, 'bin', 'java');
522
+ if (!existsSync(binPath)) {
523
+ fail('Path ' + systemPath + ' does not contain a valid JDK (missing bin/java)');
524
+ }
525
+
526
+ const target = join(JDKS_DIR, version);
527
+ if (existsSync(target)) {
528
+ fail('JDK ' + version + ' already installed: ' + target);
529
+ }
530
+
531
+ symlinkSync(systemPath, target, 'dir');
532
+ console.log('Linked system JDK ' + version + ' to ' + target);
533
+ }
534
+
535
+ function setGlobal(version) {
536
+ ensureDirs();
537
+ let target;
538
+
539
+ const localTarget = join(JDKS_DIR, version);
540
+ if (existsSync(localTarget)) {
541
+ target = localTarget;
542
+ } else {
543
+ target = findSystemJdkPath(version);
544
+ }
545
+
546
+ if (!existsSync(target)) {
547
+ fail('JDK ' + version + ' is not installed. Run: jvm install ' + version);
548
+ }
549
+
550
+ rmSync(CURRENT_GLOBAL, { recursive: true, force: true });
551
+ symlinkSync(target, CURRENT_GLOBAL, 'dir');
552
+ console.log('Global JDK set to ' + version);
553
+ console.log('Run: eval "$(jvm env --global)"');
554
+ }
555
+
556
+ function setProject(version) {
557
+ ensureDirs();
558
+ const localTarget = join(JDKS_DIR, version);
559
+ let target = localTarget;
560
+
561
+ if (!existsSync(localTarget)) {
562
+ try {
563
+ target = findSystemJdkPath(version);
564
+ } catch (err) {
565
+ fail('JDK ' + version + ' is not installed. Run: jvm install ' + version + ' or jvm link <path> ' + version);
566
+ }
567
+ }
568
+
569
+ if (!existsSync(target)) {
570
+ fail('JDK ' + version + ' is not installed. Run: jvm install ' + version + ' or jvm link <path> ' + version);
571
+ }
572
+
573
+ const cwd = process.cwd();
574
+ const file = join(cwd, PROJECT_FILE);
575
+ writeFileSync(file, version + '\n', 'utf8');
576
+ console.log('Project JDK set to ' + version + ' via ' + PROJECT_FILE);
577
+ console.log('Run: eval "$(jvm env --project)"');
578
+ }
579
+
580
+ function findProjectVersion(startDir) {
581
+ let dir = resolve(startDir);
582
+ while (true) {
583
+ const file = join(dir, PROJECT_FILE);
584
+ if (existsSync(file)) {
585
+ const version = readFileSync(file, 'utf8').trim();
586
+ return { version, file };
587
+ }
588
+ const parent = dirname(dir);
589
+ if (parent === dir) break;
590
+ dir = parent;
591
+ }
592
+ return null;
593
+ }
594
+
595
+ function printEnv(mode) {
596
+ ensureDirs();
597
+ if (mode === 'project') {
598
+ const found = findProjectVersion(process.cwd());
599
+ if (!found) {
600
+ fail('No ' + PROJECT_FILE + ' found in current path.');
601
+ }
602
+ let target = join(JDKS_DIR, found.version);
603
+
604
+ if (!existsSync(target)) {
605
+ try {
606
+ target = findSystemJdkPath(found.version);
607
+ } catch (err) {
608
+ fail('Project JDK ' + found.version + ' not installed. Run: jvm install ' + found.version + ' or jvm link <path> ' + found.version);
609
+ }
610
+ }
611
+
612
+ console.log('export JAVA_HOME="' + target + '"');
613
+ console.log('export PATH="$JAVA_HOME/bin:$PATH"');
614
+ return;
615
+ }
616
+
617
+ if (!existsSync(CURRENT_GLOBAL)) {
618
+ fail('Global JDK is not set. Run: jvm use <version> --global');
619
+ }
620
+ const target = readlinkSafe(CURRENT_GLOBAL);
621
+ console.log('export JAVA_HOME="' + target + '"');
622
+ console.log('export PATH="$JAVA_HOME/bin:$PATH"');
623
+ }
624
+
625
+ function showCurrent() {
626
+ ensureDirs();
627
+ const project = findProjectVersion(process.cwd());
628
+ if (project) {
629
+ console.log('Project: ' + project.version + ' (' + project.file + ')');
630
+ } else {
631
+ console.log('Project: <none>');
632
+ }
633
+ if (existsSync(CURRENT_GLOBAL)) {
634
+ const g = readlinkSafe(CURRENT_GLOBAL);
635
+ if (g.startsWith('/usr')) {
636
+ console.log('Global: ' + g + ' (system)');
637
+ } else {
638
+ console.log('Global: ' + g);
639
+ }
640
+ } else {
641
+ console.log('Global: <none>');
642
+ }
643
+ }
644
+
645
+ function removeJdk(version) {
646
+ ensureDirs();
647
+ const target = join(JDKS_DIR, version);
648
+ if (!existsSync(target)) {
649
+ fail('JDK ' + version + ' not installed');
650
+ }
651
+
652
+ if (existsSync(target)) {
653
+ if (existsSync(join(target, 'release'))) {
654
+ try {
655
+ const content = readFileSync(join(target, 'release'), 'utf8');
656
+ if (!content.includes('JAVA_VERSION')) {
657
+ console.log('Warning: ' + target + " doesn't contain typical JDK release file.");
658
+ }
659
+ } catch (err) {
660
+ console.log(err);
661
+ }
662
+ }
663
+
664
+ const current = existsSync(CURRENT_GLOBAL) ? readlinkSafe(CURRENT_GLOBAL) : null;
665
+
666
+ if (existsSync(target) && !run('test -L "' + target + '"')) {
667
+ rmSync(target, { recursive: true, force: true });
668
+ } else if (existsSync(target) && run('test -L "' + target + '"')) {
669
+ rmSync(target, { recursive: true, force: true });
670
+ } else {
671
+ fail('Failed to remove ' + target);
672
+ }
673
+
674
+ if (current && current === target) {
675
+ rmSync(CURRENT_GLOBAL, { recursive: true, force: true });
676
+ console.log('Removed JDK ' + version + ' and cleared global selection');
677
+ } else {
678
+ console.log('Removed JDK ' + version);
679
+ }
680
+ } else {
681
+ fail('JDK ' + version + ' not found');
682
+ }
683
+ }
684
+
685
+ function doctor() {
686
+ const checks = [
687
+ { name: 'Node.js', fn: () => process.versions.node },
688
+ { name: 'Architecture', fn: () => process.arch },
689
+ { name: 'OS Platform', fn: () => process.platform },
690
+ { name: 'curl', fn: () => run('curl --version | head -1') },
691
+ { name: 'tar', fn: () => run('tar --version | head -1') },
692
+ { name: 'mktemp', fn: () => run('which mktemp') },
693
+ { name: 'readlink', fn: () => run('readlink --version | head -1') },
694
+ { name: 'App Dir Permissions', fn: () => run('test -w "' + APP_DIR + '" || echo "Not writable: ' + APP_DIR + '"') },
695
+ { name: 'JDK Store Permissions', fn: () => run('test -w "' + JDKS_DIR + '" || echo "Not writable: ' + JDKS_DIR + '"') },
696
+ { name: 'System Java', fn: () => run('which java') },
697
+ { name: 'System JDKs', fn: () => {
698
+ const versions = detectSystemJdks();
699
+ return versions.length > 0 ? versions.join(', ') : 'Not detected';
700
+ }}
701
+ ];
702
+
703
+ console.log('jvm doctor checks:');
704
+ for (const check of checks) {
705
+ try {
706
+ const result = check.fn();
707
+ console.log(' ' + check.name + ': ' + result);
708
+ } catch (err) {
709
+ console.log(' ' + check.name + ': Not available');
710
+ }
711
+ }
712
+ }
713
+
714
+ function parseFlags(args) {
715
+ const flags = new Set(args.filter(a => a.startsWith('--')));
716
+ return {
717
+ global: flags.has('--global'),
718
+ project: flags.has('--project')
719
+ };
720
+ }
721
+
722
+ function main() {
723
+ const args = process.argv.slice(2);
724
+ const cmd = args[0];
725
+ if (!cmd || cmd === '-h' || cmd === '--help' || cmd === 'help') {
726
+ usage();
727
+ return;
728
+ }
729
+
730
+ try {
731
+ if (cmd === 'install') {
732
+ const version = args[1];
733
+ if (!version) fail('Missing version. Example: jvm install 17');
734
+ install(version);
735
+ return;
736
+ }
737
+ if (cmd === 'link') {
738
+ const systemPath = args[1];
739
+ const version = args[2];
740
+ if (!systemPath || !version) fail('Usage: jvm link <system-path> <version>');
741
+ linkSystemJdk(systemPath, version);
742
+ return;
743
+ }
744
+ if (cmd === 'list') {
745
+ listInstalled();
746
+ return;
747
+ }
748
+ if (cmd === 'use') {
749
+ const version = args[1];
750
+ if (!version) fail('Missing version. Example: jvm use 21 --global');
751
+ const flags = parseFlags(args.slice(2));
752
+ if (flags.global && flags.project) {
753
+ fail('Cannot use --global and --project together');
754
+ }
755
+ if (flags.project) {
756
+ setProject(version);
757
+ } else {
758
+ setGlobal(version);
759
+ }
760
+ return;
761
+ }
762
+ if (cmd === 'env') {
763
+ const flags = parseFlags(args.slice(1));
764
+ if (flags.global && flags.project) {
765
+ fail('Cannot use --global and --project together');
766
+ }
767
+ if (flags.project) {
768
+ printEnv('project');
769
+ } else {
770
+ printEnv('global');
771
+ }
772
+ return;
773
+ }
774
+ if (cmd === 'current') {
775
+ showCurrent();
776
+ return;
777
+ }
778
+ if (cmd === 'doctor') {
779
+ doctor();
780
+ return;
781
+ }
782
+ if (cmd === 'remove') {
783
+ const version = args[1];
784
+ if (!version) fail('Missing version. Example: jvm remove 17');
785
+ removeJdk(version);
786
+ return;
787
+ }
788
+ } catch (error) {
789
+ const message = error?.stderr || error?.message || String(error);
790
+ fail(message);
791
+ }
792
+
793
+ fail('Unknown command: ' + cmd);
794
+ }
795
+
796
+ main();