@uxmaltech/collab-cli 0.1.4 → 0.1.5

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.
@@ -25,36 +25,56 @@ function requireNpm() {
25
25
  */
26
26
  function npmGlobalInstall(npmPath, version) {
27
27
  const spec = `${NPM_PACKAGE}@${version}`;
28
- // Try without --force first, then retry with --force on EEXIST
29
- for (const force of [false, true]) {
30
- try {
31
- const args = force
32
- ? ['install', '-g', '--force', spec]
33
- : ['install', '-g', spec];
34
- (0, node_child_process_1.execFileSync)(npmPath, args, {
35
- stdio: 'inherit',
36
- timeout: 60_000,
37
- });
38
- return true;
39
- }
40
- catch (error) {
41
- const message = error instanceof Error ? error.message : String(error);
42
- // EEXIST: bin symlink collision — retry once with --force
43
- if (!force && EEXIST_ERROR.test(message)) {
44
- process.stderr.write('Retrying with --force to overwrite existing bin link...\n');
45
- continue;
46
- }
47
- if (PERMISSION_ERROR.test(message)) {
48
- process.stderr.write((0, ansi_1.red)(`${ansi_1.CROSS} Permission denied. Try:\n`) +
49
- ` sudo npm install -g ${spec}\n`);
50
- }
51
- else {
52
- process.stderr.write((0, ansi_1.red)(`${ansi_1.CROSS} Install failed: ${message}\n`));
53
- }
28
+ // First attempt: pipe stderr so we can inspect it for EEXIST
29
+ try {
30
+ (0, node_child_process_1.execFileSync)(npmPath, ['install', '-g', spec], {
31
+ stdio: ['inherit', 'inherit', 'pipe'],
32
+ timeout: 60_000,
33
+ });
34
+ return true;
35
+ }
36
+ catch (firstError) {
37
+ // Extract stderr from the failed child process
38
+ const stderr = extractStderr(firstError);
39
+ if (!EEXIST_ERROR.test(stderr)) {
40
+ // Not an EEXIST error — report and bail
41
+ process.stderr.write(stderr);
42
+ reportInstallError(spec, firstError);
54
43
  return false;
55
44
  }
45
+ // EEXIST: bin symlink collision — retry with --force
46
+ process.stderr.write('Bin link conflict detected, retrying with --force...\n');
47
+ }
48
+ // Second attempt with --force (stdio: inherit for full visibility)
49
+ try {
50
+ (0, node_child_process_1.execFileSync)(npmPath, ['install', '-g', '--force', spec], {
51
+ stdio: 'inherit',
52
+ timeout: 60_000,
53
+ });
54
+ return true;
55
+ }
56
+ catch (error) {
57
+ reportInstallError(spec, error);
58
+ return false;
59
+ }
60
+ }
61
+ function extractStderr(error) {
62
+ if (error && typeof error === 'object' && 'stderr' in error) {
63
+ const buf = error.stderr;
64
+ return typeof buf === 'string' ? buf : buf?.toString('utf8') ?? '';
65
+ }
66
+ return '';
67
+ }
68
+ function reportInstallError(spec, error) {
69
+ const message = error instanceof Error ? error.message : String(error);
70
+ const stderr = extractStderr(error);
71
+ if (PERMISSION_ERROR.test(message) || PERMISSION_ERROR.test(stderr)) {
72
+ process.stderr.write((0, ansi_1.red)(`${ansi_1.CROSS} Permission denied. Try:\n`) +
73
+ ` sudo npm install -g ${spec}\n`);
74
+ }
75
+ else {
76
+ process.stderr.write((0, ansi_1.red)(`${ansi_1.CROSS} Install failed: ${message}\n`));
56
77
  }
57
- return false;
58
78
  }
59
79
  /**
60
80
  * Runs `npm uninstall -g @uxmaltech/collab-cli`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxmaltech/collab-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "CLI for collaborative architecture and delivery workflows.",
5
5
  "private": false,
6
6
  "license": "UNLICENSED",