@kisuke/cli 1.1.71-dev.80.1 → 1.1.73-dev.82.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kisuke/cli",
3
- "version": "1.1.71-dev.80.1",
3
+ "version": "1.1.73-dev.82.1",
4
4
  "private": false,
5
5
  "description": "Kisuke CLI (dev) installer",
6
6
  "type": "module",
@@ -21,7 +21,12 @@ export function getPlatformTag() {
21
21
  }
22
22
 
23
23
  if (platform === 'linux') {
24
- if (arch === 'arm64') return 'linux-arm64';
24
+ if (arch === 'arm64') {
25
+ if (isMusl()) {
26
+ throw new Error('Unsupported platform/arch: linux/arm64 (musl)');
27
+ }
28
+ return 'linux-arm64';
29
+ }
25
30
  if (arch === 'x64') return isMusl() ? 'linux-musl-x64' : 'linux-x64';
26
31
  }
27
32
 
@@ -44,25 +44,58 @@ if (fs.existsSync(executable)) {
44
44
 
45
45
  // On Windows, if the daemon is still alive after service removal,
46
46
  // kill it directly so npm can delete the bundled node.exe.
47
+ // We must also wait for Windows to fully release the file lock —
48
+ // the kernel holds it briefly after process termination while
49
+ // cleaning up handles and memory-mapped image sections.
47
50
  if (process.platform === 'win32') {
51
+ const bundledNodeExe = path.join(bundleDir, 'bin', 'node.exe');
52
+
53
+ // Stage 1: Kill daemon by PID from pid file
48
54
  try {
49
55
  const kisukeDir = path.join(process.env.USERPROFILE || '', '.kisuke');
50
56
  const pidFile = path.join(kisukeDir, 'kisuke.pid');
51
57
  const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
52
58
  if (pid > 0) {
53
- try {
54
- process.kill(pid, 'SIGTERM');
55
- } catch {
56
- // Process may already be gone — try taskkill as last resort
57
- spawnSync('taskkill', ['/F', '/PID', String(pid)], {
58
- stdio: 'ignore',
59
- timeout: 5_000,
60
- });
61
- }
59
+ try { process.kill(pid, 'SIGTERM'); } catch {}
60
+ // Always follow up with taskkill /F — process.kill on Windows
61
+ // can succeed without the process fully terminating yet
62
+ spawnSync('taskkill', ['/F', '/PID', String(pid)], {
63
+ stdio: 'ignore',
64
+ timeout: 5_000,
65
+ });
62
66
  try { fs.unlinkSync(pidFile); } catch {}
63
67
  }
64
68
  } catch {
65
69
  // No PID file or process already gone — fine
66
70
  }
71
+
72
+ // Stage 2: Kill any remaining processes using the bundled node.exe.
73
+ // Catches orphaned daemon processes not tracked by the PID file.
74
+ try {
75
+ const escaped = bundledNodeExe.replace(/'/g, "''");
76
+ spawnSync('powershell', [
77
+ '-NoProfile', '-NonInteractive', '-Command',
78
+ `Get-Process | Where-Object { $_.Path -eq '${escaped}' } | Stop-Process -Force -ErrorAction SilentlyContinue`,
79
+ ], { stdio: 'ignore', timeout: 10_000 });
80
+ } catch {}
81
+
82
+ // Stage 3: Wait for Windows to release the file lock on node.exe.
83
+ // Without this, npm's rename-then-delete fails with EBUSY / EPERM.
84
+ if (fs.existsSync(bundledNodeExe)) {
85
+ const deadline = Date.now() + 5_000;
86
+ while (Date.now() < deadline) {
87
+ try {
88
+ // Opening the executable for writing fails while it's mapped
89
+ const fd = fs.openSync(bundledNodeExe, 'r+');
90
+ fs.closeSync(fd);
91
+ break;
92
+ } catch (e) {
93
+ if (e.code !== 'EBUSY' && e.code !== 'EPERM' && e.code !== 'EACCES') {
94
+ break; // Unexpected error — don't keep retrying
95
+ }
96
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 500);
97
+ }
98
+ }
99
+ }
67
100
  }
68
101
  }