@serviceme/devtools-core 0.4.5 → 0.4.6
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/index.d.mts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +99 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +95 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1325,7 +1325,8 @@ import {
|
|
|
1325
1325
|
|
|
1326
1326
|
// src/process/runCommand.ts
|
|
1327
1327
|
import { spawn } from "child_process";
|
|
1328
|
-
|
|
1328
|
+
var FORCE_KILL_DELAY_MS = 2e3;
|
|
1329
|
+
function terminateCommandProcess(child, signal = "SIGTERM") {
|
|
1329
1330
|
if (child.killed) {
|
|
1330
1331
|
return;
|
|
1331
1332
|
}
|
|
@@ -1339,7 +1340,32 @@ function terminateCommandProcess(child) {
|
|
|
1339
1340
|
});
|
|
1340
1341
|
return;
|
|
1341
1342
|
}
|
|
1342
|
-
child.
|
|
1343
|
+
const pid = child.pid;
|
|
1344
|
+
if (pid) {
|
|
1345
|
+
try {
|
|
1346
|
+
process.kill(-pid, signal);
|
|
1347
|
+
return;
|
|
1348
|
+
} catch {
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
child.kill(signal);
|
|
1352
|
+
}
|
|
1353
|
+
function scheduleForceKill(child) {
|
|
1354
|
+
if (process.platform === "win32") {
|
|
1355
|
+
return void 0;
|
|
1356
|
+
}
|
|
1357
|
+
const pid = child.pid;
|
|
1358
|
+
if (pid === void 0) {
|
|
1359
|
+
return void 0;
|
|
1360
|
+
}
|
|
1361
|
+
const timer = setTimeout(() => {
|
|
1362
|
+
try {
|
|
1363
|
+
process.kill(-pid, "SIGKILL");
|
|
1364
|
+
} catch {
|
|
1365
|
+
}
|
|
1366
|
+
}, FORCE_KILL_DELAY_MS);
|
|
1367
|
+
timer.unref?.();
|
|
1368
|
+
return timer;
|
|
1343
1369
|
}
|
|
1344
1370
|
async function runCommand(command, options = {}) {
|
|
1345
1371
|
return new Promise((resolve3, reject) => {
|
|
@@ -1348,12 +1374,16 @@ async function runCommand(command, options = {}) {
|
|
|
1348
1374
|
env: options.env,
|
|
1349
1375
|
shell: options.shell,
|
|
1350
1376
|
stdio: "pipe",
|
|
1351
|
-
windowsHide: true
|
|
1377
|
+
windowsHide: true,
|
|
1378
|
+
// POSIX: run the command in its own process group so a timeout can
|
|
1379
|
+
// terminate the whole tree (login shell + its children) at once.
|
|
1380
|
+
detached: process.platform !== "win32"
|
|
1352
1381
|
});
|
|
1353
1382
|
let stdout = "";
|
|
1354
1383
|
let stderr = "";
|
|
1355
1384
|
let finished = false;
|
|
1356
1385
|
let timeoutId;
|
|
1386
|
+
let forceKillTimer;
|
|
1357
1387
|
const finish = (handler) => {
|
|
1358
1388
|
if (finished) {
|
|
1359
1389
|
return;
|
|
@@ -1362,6 +1392,9 @@ async function runCommand(command, options = {}) {
|
|
|
1362
1392
|
if (timeoutId) {
|
|
1363
1393
|
clearTimeout(timeoutId);
|
|
1364
1394
|
}
|
|
1395
|
+
if (forceKillTimer) {
|
|
1396
|
+
clearTimeout(forceKillTimer);
|
|
1397
|
+
}
|
|
1365
1398
|
handler();
|
|
1366
1399
|
};
|
|
1367
1400
|
child.stdout?.setEncoding("utf8");
|
|
@@ -1376,6 +1409,10 @@ async function runCommand(command, options = {}) {
|
|
|
1376
1409
|
finish(() => reject(error));
|
|
1377
1410
|
});
|
|
1378
1411
|
child.once("close", (code) => {
|
|
1412
|
+
if (forceKillTimer) {
|
|
1413
|
+
clearTimeout(forceKillTimer);
|
|
1414
|
+
forceKillTimer = void 0;
|
|
1415
|
+
}
|
|
1379
1416
|
finish(() => {
|
|
1380
1417
|
if (code === 0) {
|
|
1381
1418
|
resolve3({
|
|
@@ -1411,6 +1448,7 @@ async function runCommand(command, options = {}) {
|
|
|
1411
1448
|
error.stderr = stderr;
|
|
1412
1449
|
reject(error);
|
|
1413
1450
|
});
|
|
1451
|
+
forceKillTimer = scheduleForceKill(child);
|
|
1414
1452
|
}, options.timeoutMs);
|
|
1415
1453
|
}
|
|
1416
1454
|
});
|
|
@@ -2525,6 +2563,9 @@ async function collectRecursive(absDir, root, out) {
|
|
|
2525
2563
|
}
|
|
2526
2564
|
|
|
2527
2565
|
// src/env/environmentInspector.ts
|
|
2566
|
+
import { existsSync } from "fs";
|
|
2567
|
+
import { homedir as homedir2 } from "os";
|
|
2568
|
+
import { join as join7 } from "path";
|
|
2528
2569
|
import {
|
|
2529
2570
|
createServicemeError as createServicemeError4,
|
|
2530
2571
|
KNOWN_ENVIRONMENT_TOOLS
|
|
@@ -2575,11 +2616,19 @@ function posixLoginShellArgs(platform3, envShell, command) {
|
|
|
2575
2616
|
}
|
|
2576
2617
|
var EnvironmentInspector = class {
|
|
2577
2618
|
constructor(options = {}) {
|
|
2619
|
+
/**
|
|
2620
|
+
* Per-check-cycle result cache. `checkEnvironment()` clears it before
|
|
2621
|
+
* probing so a fresh cycle never returns stale results, while concurrent
|
|
2622
|
+
* or repeated `checkTool()` calls within the same cycle share one probe
|
|
2623
|
+
* (deduplicating expensive login-shell fallbacks).
|
|
2624
|
+
*/
|
|
2625
|
+
this.toolCheckCache = /* @__PURE__ */ new Map();
|
|
2578
2626
|
this.runCommandFn = options.runCommand ?? runCommand;
|
|
2579
2627
|
this.platform = options.platform ?? process.platform;
|
|
2580
2628
|
this.shell = options.shell ?? process.env.SHELL;
|
|
2581
2629
|
}
|
|
2582
2630
|
async checkEnvironment() {
|
|
2631
|
+
this.toolCheckCache.clear();
|
|
2583
2632
|
const results = await Promise.all(
|
|
2584
2633
|
KNOWN_ENVIRONMENT_TOOLS.map(async (tool) => [tool, await this.checkTool(tool)])
|
|
2585
2634
|
);
|
|
@@ -2589,6 +2638,15 @@ var EnvironmentInspector = class {
|
|
|
2589
2638
|
if (!/^[a-zA-Z0-9-]+$/.test(toolName)) {
|
|
2590
2639
|
throw createServicemeError4("invalid_params", "Invalid tool name.");
|
|
2591
2640
|
}
|
|
2641
|
+
const cached = this.toolCheckCache.get(toolName);
|
|
2642
|
+
if (cached) {
|
|
2643
|
+
return cached;
|
|
2644
|
+
}
|
|
2645
|
+
const check = this.performToolCheck(toolName);
|
|
2646
|
+
this.toolCheckCache.set(toolName, check);
|
|
2647
|
+
return check;
|
|
2648
|
+
}
|
|
2649
|
+
async performToolCheck(toolName) {
|
|
2592
2650
|
try {
|
|
2593
2651
|
if (toolName === "nvm") {
|
|
2594
2652
|
return await this.checkNvm();
|
|
@@ -2700,6 +2758,12 @@ var EnvironmentInspector = class {
|
|
|
2700
2758
|
}
|
|
2701
2759
|
}
|
|
2702
2760
|
try {
|
|
2761
|
+
if (!this.resolveNvmShPath()) {
|
|
2762
|
+
return {
|
|
2763
|
+
installed: false,
|
|
2764
|
+
error: "Not installed"
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2703
2767
|
const nvmArgs = posixLoginShellArgs(this.platform, this.shell, "nvm --version");
|
|
2704
2768
|
const result = await this.runCommandFn(nvmArgs.command, {
|
|
2705
2769
|
args: nvmArgs.args,
|
|
@@ -2717,6 +2781,28 @@ var EnvironmentInspector = class {
|
|
|
2717
2781
|
};
|
|
2718
2782
|
}
|
|
2719
2783
|
}
|
|
2784
|
+
/**
|
|
2785
|
+
* Resolve `nvm.sh` from `$NVM_DIR` and the known install roots without
|
|
2786
|
+
* spawning a shell. Returns undefined when nvm is not installed in any
|
|
2787
|
+
* well-known location, letting callers skip a login-shell probe.
|
|
2788
|
+
*/
|
|
2789
|
+
resolveNvmShPath() {
|
|
2790
|
+
const candidates = [];
|
|
2791
|
+
const nvmDir = process.env.NVM_DIR?.trim();
|
|
2792
|
+
if (nvmDir && nvmDir.length > 0) {
|
|
2793
|
+
candidates.push(nvmDir);
|
|
2794
|
+
}
|
|
2795
|
+
for (const dir of NVM_FALLBACK_DIRS) {
|
|
2796
|
+
candidates.push(dir.replace(/^\$HOME/, homedir2()));
|
|
2797
|
+
}
|
|
2798
|
+
for (const candidate of candidates) {
|
|
2799
|
+
const scriptPath = join7(candidate, "nvm.sh");
|
|
2800
|
+
if (existsSync(scriptPath)) {
|
|
2801
|
+
return scriptPath;
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
return void 0;
|
|
2805
|
+
}
|
|
2720
2806
|
async checkNuget() {
|
|
2721
2807
|
const dotnetPath = await this.getToolPath("dotnet");
|
|
2722
2808
|
if (!dotnetPath) {
|
|
@@ -3433,7 +3519,7 @@ import {
|
|
|
3433
3519
|
// src/utils/fileUtils.ts
|
|
3434
3520
|
import { constants, createWriteStream } from "fs";
|
|
3435
3521
|
import { access as access5, copyFile, lstat, mkdir as mkdir5, readdir as readdir3, rename as rename4, rm as rm3 } from "fs/promises";
|
|
3436
|
-
import { dirname as dirname7, join as
|
|
3522
|
+
import { dirname as dirname7, join as join9 } from "path";
|
|
3437
3523
|
import yauzl from "yauzl";
|
|
3438
3524
|
var unzipFile = (zipPath, dest) => {
|
|
3439
3525
|
return new Promise((resolve3, reject) => {
|
|
@@ -3443,11 +3529,11 @@ var unzipFile = (zipPath, dest) => {
|
|
|
3443
3529
|
zipfile.readEntry();
|
|
3444
3530
|
zipfile.on("entry", (entry) => {
|
|
3445
3531
|
if (/\/$/.test(entry.fileName)) {
|
|
3446
|
-
void mkdir5(
|
|
3532
|
+
void mkdir5(join9(dest, entry.fileName), { recursive: true }).then(() => {
|
|
3447
3533
|
zipfile.readEntry();
|
|
3448
3534
|
}).catch(reject);
|
|
3449
3535
|
} else {
|
|
3450
|
-
const outputPath =
|
|
3536
|
+
const outputPath = join9(dest, entry.fileName);
|
|
3451
3537
|
void mkdir5(dirname7(outputPath), { recursive: true }).then(() => {
|
|
3452
3538
|
zipfile.openReadStream(
|
|
3453
3539
|
entry,
|
|
@@ -3496,7 +3582,7 @@ var mergeEntry = async (sourcePath, destPath, overwrite) => {
|
|
|
3496
3582
|
await mkdir5(destPath, { recursive: true });
|
|
3497
3583
|
const children = await readdir3(sourcePath);
|
|
3498
3584
|
for (const child of children) {
|
|
3499
|
-
await mergeEntry(
|
|
3585
|
+
await mergeEntry(join9(sourcePath, child), join9(destPath, child), overwrite);
|
|
3500
3586
|
}
|
|
3501
3587
|
await rm3(sourcePath, { recursive: true, force: true });
|
|
3502
3588
|
return;
|
|
@@ -3519,8 +3605,8 @@ var moveFiles = async (sourceDir, destDir, overwrite = false) => {
|
|
|
3519
3605
|
await mkdir5(destDir, { recursive: true });
|
|
3520
3606
|
const files = await readdir3(sourceDir);
|
|
3521
3607
|
for (const file of files) {
|
|
3522
|
-
const sourceFile =
|
|
3523
|
-
const destFile =
|
|
3608
|
+
const sourceFile = join9(sourceDir, file);
|
|
3609
|
+
const destFile = join9(destDir, file);
|
|
3524
3610
|
if (!overwrite) {
|
|
3525
3611
|
try {
|
|
3526
3612
|
await access5(destFile, constants.F_OK);
|