feishu-devops 0.1.0 → 26.627.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 +208 -25
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Command } from "commander";
|
|
|
4
4
|
// package.json
|
|
5
5
|
var package_default = {
|
|
6
6
|
name: "feishu-devops",
|
|
7
|
-
version: "
|
|
7
|
+
version: "26.627.0",
|
|
8
8
|
description: "\u98DE\u4E66\u6D88\u606F bot\uFF1A\u626B\u7801\u521B\u5EFA\u5E94\u7528\uFF0C\u901A\u8FC7 Claude Code / Codex \u5904\u7406\u6D88\u606F",
|
|
9
9
|
type: "module",
|
|
10
10
|
bin: {
|
|
@@ -144,6 +144,12 @@ function daemonStdoutPath() {
|
|
|
144
144
|
function daemonStderrPath() {
|
|
145
145
|
return join2(daemonLogDir(), "daemon-stderr.log");
|
|
146
146
|
}
|
|
147
|
+
function spawnDaemonPidPath() {
|
|
148
|
+
return join2(daemonLogDir(), "bot.pid");
|
|
149
|
+
}
|
|
150
|
+
function spawnDaemonMarkerPath() {
|
|
151
|
+
return join2(daemonLogDir(), ".spawn-daemon");
|
|
152
|
+
}
|
|
147
153
|
|
|
148
154
|
// src/daemon/launchd.ts
|
|
149
155
|
import { spawnSync } from "child_process";
|
|
@@ -393,10 +399,131 @@ async function deleteTask() {
|
|
|
393
399
|
return r;
|
|
394
400
|
}
|
|
395
401
|
|
|
402
|
+
// src/daemon/spawn-daemon.ts
|
|
403
|
+
import { spawn } from "child_process";
|
|
404
|
+
import { closeSync, existsSync as existsSync4, openSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
405
|
+
import { mkdir as mkdir3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
406
|
+
function readPid() {
|
|
407
|
+
if (!existsSync4(spawnDaemonPidPath())) return null;
|
|
408
|
+
const raw = readFileSync(spawnDaemonPidPath(), "utf8").trim();
|
|
409
|
+
const pid = Number.parseInt(raw, 10);
|
|
410
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
411
|
+
}
|
|
412
|
+
function isProcessAlive(pid) {
|
|
413
|
+
try {
|
|
414
|
+
process.kill(pid, 0);
|
|
415
|
+
return true;
|
|
416
|
+
} catch {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function markerExists() {
|
|
421
|
+
return existsSync4(spawnDaemonMarkerPath());
|
|
422
|
+
}
|
|
423
|
+
function isRunning() {
|
|
424
|
+
const pid = readPid();
|
|
425
|
+
if (pid === null) return false;
|
|
426
|
+
if (!isProcessAlive(pid)) {
|
|
427
|
+
try {
|
|
428
|
+
unlinkSync(spawnDaemonPidPath());
|
|
429
|
+
} catch {
|
|
430
|
+
}
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
async function install(extraRunArgs = []) {
|
|
436
|
+
void extraRunArgs;
|
|
437
|
+
await mkdir3(daemonLogDir(), { recursive: true });
|
|
438
|
+
await writeFile3(spawnDaemonMarkerPath(), "spawn-daemon\n", "utf8");
|
|
439
|
+
}
|
|
440
|
+
function start(extraRunArgs = []) {
|
|
441
|
+
if (isRunning()) {
|
|
442
|
+
return { ok: true, stderr: "" };
|
|
443
|
+
}
|
|
444
|
+
const invocation = resolveDaemonRunInvocation(extraRunArgs);
|
|
445
|
+
let outFd;
|
|
446
|
+
let errFd;
|
|
447
|
+
try {
|
|
448
|
+
outFd = openSync(daemonStdoutPath(), "a");
|
|
449
|
+
errFd = openSync(daemonStderrPath(), "a");
|
|
450
|
+
} catch (err) {
|
|
451
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
452
|
+
return { ok: false, stderr: message };
|
|
453
|
+
}
|
|
454
|
+
const child = spawn(invocation.program, invocation.runArgs, {
|
|
455
|
+
detached: true,
|
|
456
|
+
stdio: ["ignore", outFd, errFd],
|
|
457
|
+
cwd: invocation.cwd,
|
|
458
|
+
env: {
|
|
459
|
+
...process.env,
|
|
460
|
+
PATH: process.env.PATH ?? "",
|
|
461
|
+
FEISHU_DEVOPS_HOME: paths.rootDir
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
closeSync(outFd);
|
|
465
|
+
closeSync(errFd);
|
|
466
|
+
if (!child.pid) {
|
|
467
|
+
return { ok: false, stderr: "failed to spawn daemon process" };
|
|
468
|
+
}
|
|
469
|
+
try {
|
|
470
|
+
writeFileSync(spawnDaemonPidPath(), `${child.pid}
|
|
471
|
+
`, "utf8");
|
|
472
|
+
} catch (err) {
|
|
473
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
474
|
+
return { ok: false, stderr: message };
|
|
475
|
+
}
|
|
476
|
+
child.unref();
|
|
477
|
+
return { ok: true, stderr: "" };
|
|
478
|
+
}
|
|
479
|
+
function stop() {
|
|
480
|
+
const pid = readPid();
|
|
481
|
+
if (pid === null || !isProcessAlive(pid)) {
|
|
482
|
+
return { ok: true, stderr: "" };
|
|
483
|
+
}
|
|
484
|
+
try {
|
|
485
|
+
process.kill(pid, "SIGTERM");
|
|
486
|
+
} catch (err) {
|
|
487
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
488
|
+
return { ok: false, stderr: message };
|
|
489
|
+
}
|
|
490
|
+
return { ok: true, stderr: "" };
|
|
491
|
+
}
|
|
492
|
+
function stopAndDisableAutostart() {
|
|
493
|
+
return stop();
|
|
494
|
+
}
|
|
495
|
+
function restart(extraRunArgs = []) {
|
|
496
|
+
const r = stop();
|
|
497
|
+
if (!r.ok) return r;
|
|
498
|
+
return start(extraRunArgs);
|
|
499
|
+
}
|
|
500
|
+
async function waitUntilInactive(timeoutMs = 5e3) {
|
|
501
|
+
const deadline = Date.now() + timeoutMs;
|
|
502
|
+
while (Date.now() < deadline) {
|
|
503
|
+
if (!isRunning()) return true;
|
|
504
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
505
|
+
}
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
async function deleteMarker() {
|
|
509
|
+
await rm3(spawnDaemonMarkerPath(), { force: true });
|
|
510
|
+
await rm3(spawnDaemonPidPath(), { force: true });
|
|
511
|
+
}
|
|
512
|
+
function describeService2() {
|
|
513
|
+
const pid = readPid();
|
|
514
|
+
if (pid !== null && isProcessAlive(pid)) {
|
|
515
|
+
return `bot (detached): running (pid ${pid})`;
|
|
516
|
+
}
|
|
517
|
+
if (pid !== null) {
|
|
518
|
+
return `bot (detached): not running (stale pid ${pid})`;
|
|
519
|
+
}
|
|
520
|
+
return "bot (detached): not running";
|
|
521
|
+
}
|
|
522
|
+
|
|
396
523
|
// src/daemon/systemd.ts
|
|
397
524
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
398
|
-
import { existsSync as
|
|
399
|
-
import { mkdir as
|
|
525
|
+
import { existsSync as existsSync5 } from "fs";
|
|
526
|
+
import { mkdir as mkdir4, rm as rm4, writeFile as writeFile4 } from "fs/promises";
|
|
400
527
|
import { dirname as dirname4 } from "path";
|
|
401
528
|
function buildUnit(inputs) {
|
|
402
529
|
const escape = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
@@ -431,12 +558,12 @@ async function writeUnit(extraRunArgs = []) {
|
|
|
431
558
|
cwd: invocation.cwd
|
|
432
559
|
});
|
|
433
560
|
const unitPath = systemdUnitPath();
|
|
434
|
-
await
|
|
435
|
-
await
|
|
436
|
-
await
|
|
561
|
+
await mkdir4(dirname4(unitPath), { recursive: true });
|
|
562
|
+
await mkdir4(daemonLogDir(), { recursive: true });
|
|
563
|
+
await writeFile4(unitPath, content, "utf8");
|
|
437
564
|
}
|
|
438
565
|
function unitExists() {
|
|
439
|
-
return
|
|
566
|
+
return existsSync5(systemdUnitPath());
|
|
440
567
|
}
|
|
441
568
|
function runSystemctl(args) {
|
|
442
569
|
const r = spawnSync3("systemctl", ["--user", ...args], { encoding: "utf8" });
|
|
@@ -446,19 +573,31 @@ function runSystemctl(args) {
|
|
|
446
573
|
stdout: r.stdout ?? ""
|
|
447
574
|
};
|
|
448
575
|
}
|
|
576
|
+
function isUserBusAvailable() {
|
|
577
|
+
const r = spawnSync3("systemctl", ["--user", "is-system-running"], {
|
|
578
|
+
encoding: "utf8",
|
|
579
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
580
|
+
});
|
|
581
|
+
const combined = `${r.stderr ?? ""}
|
|
582
|
+
${r.stdout ?? ""}`;
|
|
583
|
+
if (/Failed to connect to bus|Couldn't connect to bus|No medium found|找不到介质/i.test(combined)) {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
return r.status !== null;
|
|
587
|
+
}
|
|
449
588
|
function daemonReload() {
|
|
450
589
|
return runSystemctl(["daemon-reload"]);
|
|
451
590
|
}
|
|
452
591
|
function enableAndStart() {
|
|
453
592
|
return runSystemctl(["enable", "--now", systemdUnitName()]);
|
|
454
593
|
}
|
|
455
|
-
function
|
|
594
|
+
function stop2() {
|
|
456
595
|
return runSystemctl(["stop", systemdUnitName()]);
|
|
457
596
|
}
|
|
458
597
|
function disableAndStop() {
|
|
459
598
|
return runSystemctl(["disable", "--now", systemdUnitName()]);
|
|
460
599
|
}
|
|
461
|
-
function
|
|
600
|
+
function restart2() {
|
|
462
601
|
return runSystemctl(["restart", systemdUnitName()]);
|
|
463
602
|
}
|
|
464
603
|
function isActive() {
|
|
@@ -467,11 +606,11 @@ function isActive() {
|
|
|
467
606
|
});
|
|
468
607
|
return r.status === 0;
|
|
469
608
|
}
|
|
470
|
-
function
|
|
609
|
+
function describeService3() {
|
|
471
610
|
const r = runSystemctl(["status", systemdUnitName(), "--no-pager"]);
|
|
472
611
|
return r.stdout || r.stderr || "";
|
|
473
612
|
}
|
|
474
|
-
async function
|
|
613
|
+
async function waitUntilInactive2(timeoutMs = 5e3) {
|
|
475
614
|
const deadline = Date.now() + timeoutMs;
|
|
476
615
|
while (Date.now() < deadline) {
|
|
477
616
|
if (!isActive()) return true;
|
|
@@ -480,7 +619,7 @@ async function waitUntilInactive(timeoutMs = 5e3) {
|
|
|
480
619
|
return false;
|
|
481
620
|
}
|
|
482
621
|
async function deleteUnit() {
|
|
483
|
-
await
|
|
622
|
+
await rm4(systemdUnitPath(), { force: true });
|
|
484
623
|
}
|
|
485
624
|
|
|
486
625
|
// src/daemon/service-adapter.ts
|
|
@@ -504,6 +643,26 @@ function makeLaunchdAdapter(extraRunArgs) {
|
|
|
504
643
|
})
|
|
505
644
|
};
|
|
506
645
|
}
|
|
646
|
+
function makeSpawnDaemonAdapter(extraRunArgs) {
|
|
647
|
+
return {
|
|
648
|
+
platformName: "detached process (Linux fallback)",
|
|
649
|
+
fileExists: () => markerExists(),
|
|
650
|
+
isRunning: () => isRunning(),
|
|
651
|
+
servicePath: () => spawnDaemonPidPath(),
|
|
652
|
+
install: () => install(extraRunArgs),
|
|
653
|
+
start: () => start(extraRunArgs),
|
|
654
|
+
stop: () => stop(),
|
|
655
|
+
stopAndDisableAutostart: () => stopAndDisableAutostart(),
|
|
656
|
+
restart: () => restart(extraRunArgs),
|
|
657
|
+
waitUntilStopped: (timeoutMs) => waitUntilInactive(timeoutMs),
|
|
658
|
+
deleteFile: () => deleteMarker(),
|
|
659
|
+
describeStatus: () => describeService2(),
|
|
660
|
+
parseStatus: (text) => ({
|
|
661
|
+
pid: text.match(/pid[:\s]+(\d+)/i)?.[1],
|
|
662
|
+
lastExit: void 0
|
|
663
|
+
})
|
|
664
|
+
};
|
|
665
|
+
}
|
|
507
666
|
function makeSystemdAdapter(extraRunArgs) {
|
|
508
667
|
return {
|
|
509
668
|
platformName: "systemd (Linux user)",
|
|
@@ -515,15 +674,15 @@ function makeSystemdAdapter(extraRunArgs) {
|
|
|
515
674
|
daemonReload();
|
|
516
675
|
},
|
|
517
676
|
start: () => enableAndStart(),
|
|
518
|
-
stop: () =>
|
|
677
|
+
stop: () => stop2(),
|
|
519
678
|
stopAndDisableAutostart: () => disableAndStop(),
|
|
520
|
-
restart: () =>
|
|
521
|
-
waitUntilStopped: (timeoutMs) =>
|
|
679
|
+
restart: () => restart2(),
|
|
680
|
+
waitUntilStopped: (timeoutMs) => waitUntilInactive2(timeoutMs),
|
|
522
681
|
deleteFile: async () => {
|
|
523
682
|
await deleteUnit();
|
|
524
683
|
daemonReload();
|
|
525
684
|
},
|
|
526
|
-
describeStatus: () =>
|
|
685
|
+
describeStatus: () => describeService3(),
|
|
527
686
|
parseStatus: (text) => ({
|
|
528
687
|
pid: text.match(/Main PID:\s*(\d+)/)?.[1],
|
|
529
688
|
lastExit: text.match(/Process:\s+\d+\s+ExecStart=.*status=(\d+)/)?.[1]
|
|
@@ -557,7 +716,9 @@ function makeSchtasksAdapter(extraRunArgs) {
|
|
|
557
716
|
}
|
|
558
717
|
function getServiceAdapter(extraRunArgs = []) {
|
|
559
718
|
if (process.platform === "darwin") return makeLaunchdAdapter(extraRunArgs);
|
|
560
|
-
if (process.platform === "linux")
|
|
719
|
+
if (process.platform === "linux") {
|
|
720
|
+
return isUserBusAvailable() ? makeSystemdAdapter(extraRunArgs) : makeSpawnDaemonAdapter(extraRunArgs);
|
|
721
|
+
}
|
|
561
722
|
if (process.platform === "win32") return makeSchtasksAdapter(extraRunArgs);
|
|
562
723
|
return null;
|
|
563
724
|
}
|
|
@@ -619,7 +780,7 @@ import { readFile } from "fs/promises";
|
|
|
619
780
|
|
|
620
781
|
// src/platform/atomic-write.ts
|
|
621
782
|
import { randomBytes } from "crypto";
|
|
622
|
-
import { chmod, mkdir as
|
|
783
|
+
import { chmod, mkdir as mkdir5, open, rm as rm5 } from "fs/promises";
|
|
623
784
|
import { basename, dirname as dirname5, join as join4 } from "path";
|
|
624
785
|
import { promisify } from "util";
|
|
625
786
|
import gracefulFs from "graceful-fs";
|
|
@@ -627,7 +788,7 @@ var gracefulRename = promisify(gracefulFs.rename);
|
|
|
627
788
|
var DEFAULT_RENAME_ATTEMPTS = 5;
|
|
628
789
|
var DEFAULT_RETRY_DELAY_MS = 25;
|
|
629
790
|
async function writeFileAtomic(path, data, opts = {}) {
|
|
630
|
-
await
|
|
791
|
+
await mkdir5(dirname5(path), { recursive: true });
|
|
631
792
|
const tmp = join4(
|
|
632
793
|
dirname5(path),
|
|
633
794
|
`.${basename(path)}.tmp-${process.pid}-${Date.now()}-${randomBytes(3).toString("hex")}`
|
|
@@ -647,7 +808,7 @@ async function writeFileAtomic(path, data, opts = {}) {
|
|
|
647
808
|
await chmod(tmp, opts.mode ?? 384);
|
|
648
809
|
await renameWithRetry(tmp, path, opts);
|
|
649
810
|
} catch (err) {
|
|
650
|
-
await
|
|
811
|
+
await rm5(tmp, { force: true }).catch(() => {
|
|
651
812
|
});
|
|
652
813
|
throw err;
|
|
653
814
|
}
|
|
@@ -835,6 +996,22 @@ function printServiceFailure(stderr) {
|
|
|
835
996
|
console.error(` ${cleaned}`);
|
|
836
997
|
return;
|
|
837
998
|
}
|
|
999
|
+
if (/Failed to connect to bus|Couldn't connect to bus|No medium found|找不到介质/i.test(cleaned)) {
|
|
1000
|
+
console.error("\u2717 bot \u542F\u52A8\u5931\u8D25\u3002");
|
|
1001
|
+
console.error("");
|
|
1002
|
+
console.error("\u5F53\u524D\u73AF\u5883\u65E0\u6CD5\u8FDE\u63A5 systemd \u7528\u6237 D-Bus\uFF08\u5E38\u89C1\u4E8E SSH\u3001Docker\u3001\u65E0\u56FE\u5F62\u4F1A\u8BDD\uFF09\u3002");
|
|
1003
|
+
console.error("");
|
|
1004
|
+
console.error("\u53EF\u9009\u65B9\u6848\uFF1A");
|
|
1005
|
+
console.error(" 1. \u524D\u53F0\u8FD0\u884C: feishu-devops run");
|
|
1006
|
+
console.error(" 2. \u542F\u7528 systemd \u7528\u6237\u4F1A\u8BDD\u540E\u91CD\u8BD5:");
|
|
1007
|
+
console.error(" export XDG_RUNTIME_DIR=/run/user/$(id -u)");
|
|
1008
|
+
console.error(" sudo loginctl enable-linger $USER");
|
|
1009
|
+
console.error(" npm start");
|
|
1010
|
+
console.error("");
|
|
1011
|
+
console.error("\u539F\u59CB\u9519\u8BEF:");
|
|
1012
|
+
console.error(` ${cleaned}`);
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
838
1015
|
console.error("\u2717 bot \u542F\u52A8\u5931\u8D25:");
|
|
839
1016
|
console.error(cleaned);
|
|
840
1017
|
}
|
|
@@ -880,6 +1057,9 @@ ${formatServiceStderr(r2.stderr)}`);
|
|
|
880
1057
|
process.exit(1);
|
|
881
1058
|
}
|
|
882
1059
|
console.log("\u2713 bot \u5DF2\u5728\u540E\u53F0\u542F\u52A8");
|
|
1060
|
+
if (adapter.platformName.includes("fallback")) {
|
|
1061
|
+
console.log(" \u540E\u53F0\u65B9\u5F0F: detached \u8FDB\u7A0B\uFF08systemd \u7528\u6237\u4F1A\u8BDD\u4E0D\u53EF\u7528\uFF09");
|
|
1062
|
+
}
|
|
883
1063
|
console.log(" \u65E5\u5FD7:");
|
|
884
1064
|
console.log(` ${daemonStdoutPath()}`);
|
|
885
1065
|
console.log(` ${daemonStderrPath()}`);
|
|
@@ -934,6 +1114,9 @@ async function runServiceRestart(opts = {}) {
|
|
|
934
1114
|
process.exit(1);
|
|
935
1115
|
}
|
|
936
1116
|
console.log("\u2713 bot \u5DF2\u5728\u540E\u53F0\u542F\u52A8");
|
|
1117
|
+
if (adapter.platformName.includes("fallback")) {
|
|
1118
|
+
console.log(" \u540E\u53F0\u65B9\u5F0F: detached \u8FDB\u7A0B\uFF08systemd \u7528\u6237\u4F1A\u8BDD\u4E0D\u53EF\u7528\uFF09");
|
|
1119
|
+
}
|
|
937
1120
|
console.log(" \u65E5\u5FD7:");
|
|
938
1121
|
console.log(` ${daemonStdoutPath()}`);
|
|
939
1122
|
console.log(` ${daemonStderrPath()}`);
|
|
@@ -970,7 +1153,7 @@ import { createInterface as createInterface2 } from "readline";
|
|
|
970
1153
|
// src/core/logger.ts
|
|
971
1154
|
import { AsyncLocalStorage } from "async_hooks";
|
|
972
1155
|
import { createWriteStream, mkdirSync } from "fs";
|
|
973
|
-
import { open as open2, readdir, rm as
|
|
1156
|
+
import { open as open2, readdir, rm as rm6, stat } from "fs/promises";
|
|
974
1157
|
import { join as join5 } from "path";
|
|
975
1158
|
|
|
976
1159
|
// src/core/telemetry.ts
|
|
@@ -3674,7 +3857,7 @@ function isTerminalEvent(event) {
|
|
|
3674
3857
|
|
|
3675
3858
|
// src/session/catalog.ts
|
|
3676
3859
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3677
|
-
import { open as open3, readFile as readFile2, rename, mkdir as
|
|
3860
|
+
import { open as open3, readFile as readFile2, rename, mkdir as mkdir6 } from "fs/promises";
|
|
3678
3861
|
import { dirname as dirname6 } from "path";
|
|
3679
3862
|
var DEFAULT_MAX_ARCHIVED_AGE_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
3680
3863
|
var DEFAULT_MAX_ENTRIES_PER_SCOPE = 20;
|
|
@@ -3797,7 +3980,7 @@ var SessionCatalog = class {
|
|
|
3797
3980
|
});
|
|
3798
3981
|
}
|
|
3799
3982
|
async persist() {
|
|
3800
|
-
await
|
|
3983
|
+
await mkdir6(dirname6(this.path), { recursive: true });
|
|
3801
3984
|
const tmp = `${this.path}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
|
|
3802
3985
|
const payload = `${JSON.stringify(this.entries(), null, 2)}
|
|
3803
3986
|
`;
|
|
@@ -5419,14 +5602,14 @@ function normalizeCommandInput(msg) {
|
|
|
5419
5602
|
}
|
|
5420
5603
|
|
|
5421
5604
|
// src/bot/run-shell.ts
|
|
5422
|
-
import { spawn } from "child_process";
|
|
5605
|
+
import { spawn as spawn2 } from "child_process";
|
|
5423
5606
|
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
5424
5607
|
var MAX_OUTPUT_CHARS = 3500;
|
|
5425
5608
|
async function runShellCommand(command, opts = {}) {
|
|
5426
5609
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
5427
5610
|
const cwd = opts.cwd ?? process.cwd();
|
|
5428
5611
|
return new Promise((resolve3) => {
|
|
5429
|
-
const child =
|
|
5612
|
+
const child = spawn2(command, {
|
|
5430
5613
|
shell: true,
|
|
5431
5614
|
cwd,
|
|
5432
5615
|
env: process.env,
|