copilot-hub 0.1.19 → 0.1.21
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/README.md +3 -2
- package/apps/agent-engine/dist/config.js +58 -0
- package/apps/agent-engine/dist/index.js +90 -16
- package/apps/control-plane/dist/channels/codex-quota-cache.js +16 -0
- package/apps/control-plane/dist/channels/hub-model-utils.js +244 -24
- package/apps/control-plane/dist/channels/hub-ops-commands.js +631 -279
- package/apps/control-plane/dist/channels/telegram-channel.js +5 -7
- package/apps/control-plane/dist/config.js +58 -0
- package/apps/control-plane/dist/index.js +16 -0
- package/apps/control-plane/dist/test/hub-model-utils.test.js +110 -13
- package/package.json +3 -2
- package/packages/core/dist/agent-supervisor.d.ts +5 -0
- package/packages/core/dist/agent-supervisor.js +11 -0
- package/packages/core/dist/agent-supervisor.js.map +1 -1
- package/packages/core/dist/bot-manager.js +17 -1
- package/packages/core/dist/bot-manager.js.map +1 -1
- package/packages/core/dist/bot-runtime.d.ts +4 -0
- package/packages/core/dist/bot-runtime.js +5 -1
- package/packages/core/dist/bot-runtime.js.map +1 -1
- package/packages/core/dist/codex-app-client.d.ts +13 -2
- package/packages/core/dist/codex-app-client.js +51 -13
- package/packages/core/dist/codex-app-client.js.map +1 -1
- package/packages/core/dist/codex-app-utils.d.ts +6 -0
- package/packages/core/dist/codex-app-utils.js +49 -0
- package/packages/core/dist/codex-app-utils.js.map +1 -1
- package/packages/core/dist/codex-provider.d.ts +3 -1
- package/packages/core/dist/codex-provider.js +3 -1
- package/packages/core/dist/codex-provider.js.map +1 -1
- package/packages/core/dist/kernel-control-plane.d.ts +1 -0
- package/packages/core/dist/kernel-control-plane.js +132 -13
- package/packages/core/dist/kernel-control-plane.js.map +1 -1
- package/packages/core/dist/provider-factory.d.ts +2 -0
- package/packages/core/dist/provider-factory.js +3 -0
- package/packages/core/dist/provider-factory.js.map +1 -1
- package/packages/core/dist/provider-options.js +24 -17
- package/packages/core/dist/provider-options.js.map +1 -1
- package/packages/core/dist/state-store.d.ts +1 -0
- package/packages/core/dist/state-store.js +28 -2
- package/packages/core/dist/state-store.js.map +1 -1
- package/packages/core/dist/telegram-channel.d.ts +1 -0
- package/packages/core/dist/telegram-channel.js +3 -0
- package/packages/core/dist/telegram-channel.js.map +1 -1
- package/scripts/dist/cli.mjs +132 -203
- package/scripts/dist/codex-runtime.mjs +352 -0
- package/scripts/dist/codex-version.mjs +91 -0
- package/scripts/dist/configure.mjs +26 -49
- package/scripts/dist/daemon.mjs +58 -0
- package/scripts/src/cli.mts +166 -233
- package/scripts/src/codex-runtime.mts +499 -0
- package/scripts/src/codex-version.mts +114 -0
- package/scripts/src/configure.mts +30 -65
- package/scripts/src/daemon.mts +69 -0
- package/scripts/test/codex-version.test.mjs +21 -0
package/scripts/src/cli.mts
CHANGED
|
@@ -5,6 +5,14 @@ import process, { stdin as input, stdout as output } from "node:process";
|
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { codexInstallPackageSpec } from "./codex-version.mjs";
|
|
9
|
+
import {
|
|
10
|
+
buildCodexCompatibilityError,
|
|
11
|
+
buildCodexCompatibilityNotice,
|
|
12
|
+
probeCodexVersion,
|
|
13
|
+
resolveCodexBinForStart,
|
|
14
|
+
resolveCompatibleInstalledCodexBin,
|
|
15
|
+
} from "./codex-runtime.mjs";
|
|
8
16
|
|
|
9
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
18
|
const __dirname = path.dirname(__filename);
|
|
@@ -15,8 +23,7 @@ const servicePromptStatePath = path.join(runtimeDir, "service-onboarding.json");
|
|
|
15
23
|
const nodeBin = process.execPath;
|
|
16
24
|
const agentEngineEnvPath = path.join(repoRoot, "apps", "agent-engine", ".env");
|
|
17
25
|
const controlPlaneEnvPath = path.join(repoRoot, "apps", "control-plane", ".env");
|
|
18
|
-
const
|
|
19
|
-
const codexInstallCommand = `npm install -g ${codexNpmPackage}`;
|
|
26
|
+
const codexInstallCommand = `npm install -g ${codexInstallPackageSpec}`;
|
|
20
27
|
const packageVersion = readPackageVersion();
|
|
21
28
|
|
|
22
29
|
const rawArgs = process.argv
|
|
@@ -66,6 +73,10 @@ async function main() {
|
|
|
66
73
|
}
|
|
67
74
|
case "restart": {
|
|
68
75
|
runNode(["scripts/dist/ensure-shared-build.mjs"]);
|
|
76
|
+
await ensureCompatibleCodexBinary({
|
|
77
|
+
autoInstall: false,
|
|
78
|
+
purpose: "restart",
|
|
79
|
+
});
|
|
69
80
|
if (isServiceAlreadyInstalled()) {
|
|
70
81
|
runNode(["scripts/dist/service.mjs", "stop"]);
|
|
71
82
|
runNode(["scripts/dist/service.mjs", "start"]);
|
|
@@ -91,9 +102,25 @@ async function main() {
|
|
|
91
102
|
return;
|
|
92
103
|
}
|
|
93
104
|
case "service": {
|
|
105
|
+
const serviceAction = String(rawArgs[1] ?? "")
|
|
106
|
+
.trim()
|
|
107
|
+
.toLowerCase();
|
|
108
|
+
if (serviceAction === "install" || serviceAction === "start") {
|
|
109
|
+
await ensureCompatibleCodexBinary({
|
|
110
|
+
autoInstall: false,
|
|
111
|
+
purpose: "service",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
94
114
|
runNode(["scripts/dist/service.mjs", ...rawArgs.slice(1)]);
|
|
95
115
|
return;
|
|
96
116
|
}
|
|
117
|
+
case "_update_resume": {
|
|
118
|
+
await resumeAfterUpdate({
|
|
119
|
+
serviceInstalled: rawArgs.includes("--service-installed"),
|
|
120
|
+
runningBeforeUpdate: rawArgs.includes("--resume-running"),
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
97
124
|
case "update":
|
|
98
125
|
case "upgrade": {
|
|
99
126
|
await runSelfUpdate();
|
|
@@ -139,27 +166,16 @@ function runNodeCapture(scriptArgs, stdioMode = "pipe") {
|
|
|
139
166
|
}
|
|
140
167
|
|
|
141
168
|
async function ensureCodexLogin() {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
169
|
+
const codexBin = await ensureCompatibleCodexBinary({
|
|
170
|
+
autoInstall: false,
|
|
171
|
+
purpose: "start",
|
|
172
|
+
});
|
|
173
|
+
const status = runCodex(codexBin, ["login", "status"], "pipe");
|
|
145
174
|
if (status.ok) {
|
|
146
175
|
console.log("Codex login already configured.");
|
|
147
176
|
return;
|
|
148
177
|
}
|
|
149
178
|
|
|
150
|
-
if (status.errorCode === "ENOENT") {
|
|
151
|
-
codexBin = await recoverCodexBinary({
|
|
152
|
-
resolved,
|
|
153
|
-
status,
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
status = runCodex(codexBin, ["login", "status"], "pipe");
|
|
157
|
-
if (status.ok) {
|
|
158
|
-
console.log("Codex login already configured.");
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
179
|
const reason = status.errorMessage || status.stderr || status.stdout;
|
|
164
180
|
if (!process.stdin.isTTY) {
|
|
165
181
|
throw new Error(
|
|
@@ -296,6 +312,33 @@ async function runSelfUpdate() {
|
|
|
296
312
|
}
|
|
297
313
|
|
|
298
314
|
console.log("copilot-hub updated to latest.");
|
|
315
|
+
const resume = runNodeCapture(
|
|
316
|
+
[
|
|
317
|
+
"scripts/dist/cli.mjs",
|
|
318
|
+
"_update_resume",
|
|
319
|
+
...(serviceInstalled ? ["--service-installed"] : ["--local-mode"]),
|
|
320
|
+
...(runningBeforeUpdate ? ["--resume-running"] : ["--stopped"]),
|
|
321
|
+
],
|
|
322
|
+
"inherit",
|
|
323
|
+
);
|
|
324
|
+
if (!resume.ok) {
|
|
325
|
+
console.log(
|
|
326
|
+
"Update completed, but post-update Codex validation or restart failed. Run 'copilot-hub start' manually.",
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function resumeAfterUpdate({
|
|
332
|
+
serviceInstalled,
|
|
333
|
+
runningBeforeUpdate,
|
|
334
|
+
}: {
|
|
335
|
+
serviceInstalled: boolean;
|
|
336
|
+
runningBeforeUpdate: boolean;
|
|
337
|
+
}) {
|
|
338
|
+
await ensureCompatibleCodexBinary({
|
|
339
|
+
autoInstall: true,
|
|
340
|
+
purpose: "update",
|
|
341
|
+
});
|
|
299
342
|
|
|
300
343
|
if (!runningBeforeUpdate) {
|
|
301
344
|
console.log("Services remain stopped. Run 'copilot-hub start' when ready.");
|
|
@@ -306,7 +349,6 @@ async function runSelfUpdate() {
|
|
|
306
349
|
const startService = runNodeCapture(["scripts/dist/service.mjs", "start"], "inherit");
|
|
307
350
|
if (!startService.ok) {
|
|
308
351
|
console.log("Update completed, but service start failed. Run 'copilot-hub start' manually.");
|
|
309
|
-
return;
|
|
310
352
|
}
|
|
311
353
|
return;
|
|
312
354
|
}
|
|
@@ -382,45 +424,91 @@ function writeServicePromptState(decision) {
|
|
|
382
424
|
}
|
|
383
425
|
}
|
|
384
426
|
|
|
385
|
-
async function
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
427
|
+
async function ensureCompatibleCodexBinary({
|
|
428
|
+
autoInstall,
|
|
429
|
+
purpose,
|
|
430
|
+
}: {
|
|
431
|
+
autoInstall: boolean;
|
|
432
|
+
purpose: "start" | "restart" | "service" | "update";
|
|
433
|
+
}): Promise<string> {
|
|
434
|
+
const resolved = resolveCodexBinForStart({
|
|
435
|
+
repoRoot,
|
|
436
|
+
agentEngineEnvPath,
|
|
437
|
+
controlPlaneEnvPath,
|
|
438
|
+
});
|
|
439
|
+
const currentProbe = probeCodexVersion({
|
|
440
|
+
codexBin: resolved.bin,
|
|
441
|
+
repoRoot,
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
if (currentProbe.ok && currentProbe.compatible) {
|
|
445
|
+
return resolved.bin;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (!resolved.userConfigured) {
|
|
449
|
+
const compatibleInstalled = resolveCompatibleInstalledCodexBin({ repoRoot });
|
|
450
|
+
if (compatibleInstalled) {
|
|
451
|
+
if (compatibleInstalled !== resolved.bin) {
|
|
452
|
+
const probe = probeCodexVersion({
|
|
453
|
+
codexBin: compatibleInstalled,
|
|
454
|
+
repoRoot,
|
|
455
|
+
});
|
|
456
|
+
if (probe.ok) {
|
|
457
|
+
console.log(`Using compatible Codex CLI ${probe.version} from '${compatibleInstalled}'.`);
|
|
458
|
+
} else {
|
|
459
|
+
console.log(`Using compatible Codex CLI from '${compatibleInstalled}'.`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return compatibleInstalled;
|
|
392
463
|
}
|
|
393
464
|
}
|
|
394
465
|
|
|
395
466
|
if (resolved.userConfigured) {
|
|
396
|
-
|
|
467
|
+
throw new Error(
|
|
468
|
+
buildCodexCompatibilityError({
|
|
469
|
+
resolved,
|
|
470
|
+
probe: currentProbe,
|
|
471
|
+
includeInstallHint: false,
|
|
472
|
+
installCommand: codexInstallCommand,
|
|
473
|
+
}),
|
|
474
|
+
);
|
|
397
475
|
}
|
|
398
476
|
|
|
399
|
-
if (!process.stdin.isTTY) {
|
|
477
|
+
if (!autoInstall && !process.stdin.isTTY) {
|
|
400
478
|
throw new Error(
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
479
|
+
buildCodexCompatibilityError({
|
|
480
|
+
resolved,
|
|
481
|
+
probe: currentProbe,
|
|
482
|
+
includeInstallHint: true,
|
|
483
|
+
installCommand: codexInstallCommand,
|
|
484
|
+
}),
|
|
407
485
|
);
|
|
408
486
|
}
|
|
409
487
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
488
|
+
let shouldInstall = autoInstall;
|
|
489
|
+
if (!autoInstall) {
|
|
490
|
+
console.log(buildCodexCompatibilityNotice({ resolved, probe: currentProbe }));
|
|
491
|
+
const rl = createInterface({ input, output });
|
|
492
|
+
try {
|
|
493
|
+
shouldInstall = await askYesNo(
|
|
494
|
+
rl,
|
|
495
|
+
`Install compatible Codex CLI now (${codexInstallCommand})?`,
|
|
496
|
+
true,
|
|
497
|
+
);
|
|
498
|
+
} finally {
|
|
499
|
+
rl.close();
|
|
500
|
+
}
|
|
417
501
|
}
|
|
418
502
|
|
|
419
|
-
if (!
|
|
420
|
-
throw new Error(
|
|
503
|
+
if (!shouldInstall) {
|
|
504
|
+
throw new Error(
|
|
505
|
+
purpose === "update"
|
|
506
|
+
? "Compatible Codex CLI is required before restarting services."
|
|
507
|
+
: "Compatible Codex CLI is required before starting services.",
|
|
508
|
+
);
|
|
421
509
|
}
|
|
422
510
|
|
|
423
|
-
const install = runNpm(["install", "-g",
|
|
511
|
+
const install = runNpm(["install", "-g", codexInstallPackageSpec], "inherit");
|
|
424
512
|
if (!install.ok) {
|
|
425
513
|
throw new Error(
|
|
426
514
|
[
|
|
@@ -435,28 +523,50 @@ async function recoverCodexBinary({ resolved, status }) {
|
|
|
435
523
|
);
|
|
436
524
|
}
|
|
437
525
|
|
|
438
|
-
const installed =
|
|
526
|
+
const installed = resolveCompatibleInstalledCodexBin({ repoRoot });
|
|
439
527
|
if (!installed) {
|
|
440
528
|
throw new Error(
|
|
441
529
|
[
|
|
442
|
-
|
|
443
|
-
"Set CODEX_BIN to
|
|
530
|
+
`Compatible Codex CLI was not detected after installation.`,
|
|
531
|
+
"Set CODEX_BIN to a compatible executable path, then retry.",
|
|
444
532
|
].join("\n"),
|
|
445
533
|
);
|
|
446
534
|
}
|
|
447
535
|
|
|
448
|
-
|
|
536
|
+
const installedProbe = probeCodexVersion({
|
|
537
|
+
codexBin: installed,
|
|
538
|
+
repoRoot,
|
|
539
|
+
});
|
|
540
|
+
if (installedProbe.ok) {
|
|
541
|
+
console.log(`Codex CLI ready: ${installedProbe.version} from '${installed}'.`);
|
|
542
|
+
} else {
|
|
543
|
+
console.log(`Codex CLI ready from '${installed}'.`);
|
|
544
|
+
}
|
|
449
545
|
return installed;
|
|
450
546
|
}
|
|
451
547
|
|
|
452
548
|
function runCodex(codexBin, args, stdioMode) {
|
|
453
549
|
const stdio: any = stdioMode === "inherit" ? "inherit" : ["ignore", "pipe", "pipe"];
|
|
454
|
-
const result =
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
550
|
+
const result =
|
|
551
|
+
process.platform === "win32" && /\.(cmd|bat)$/i.test(String(codexBin ?? ""))
|
|
552
|
+
? spawnSync(
|
|
553
|
+
[
|
|
554
|
+
quoteWindowsShellValue(codexBin),
|
|
555
|
+
...args.map((arg) => quoteWindowsShellValue(arg)),
|
|
556
|
+
].join(" "),
|
|
557
|
+
{
|
|
558
|
+
cwd: repoRoot,
|
|
559
|
+
stdio,
|
|
560
|
+
shell: true,
|
|
561
|
+
encoding: "utf8",
|
|
562
|
+
},
|
|
563
|
+
)
|
|
564
|
+
: spawnSync(codexBin, args, {
|
|
565
|
+
cwd: repoRoot,
|
|
566
|
+
stdio,
|
|
567
|
+
shell: false,
|
|
568
|
+
encoding: "utf8",
|
|
569
|
+
});
|
|
460
570
|
|
|
461
571
|
if (result.error) {
|
|
462
572
|
return {
|
|
@@ -478,144 +588,8 @@ function runCodex(codexBin, args, stdioMode) {
|
|
|
478
588
|
};
|
|
479
589
|
}
|
|
480
590
|
|
|
481
|
-
function
|
|
482
|
-
|
|
483
|
-
if (fromEnv) {
|
|
484
|
-
return {
|
|
485
|
-
bin: fromEnv,
|
|
486
|
-
source: "process_env",
|
|
487
|
-
userConfigured: true,
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
for (const [source, envPath] of [
|
|
492
|
-
["agent_env", agentEngineEnvPath],
|
|
493
|
-
["control_plane_env", controlPlaneEnvPath],
|
|
494
|
-
]) {
|
|
495
|
-
const value = readEnvValue(envPath, "CODEX_BIN");
|
|
496
|
-
if (value) {
|
|
497
|
-
return {
|
|
498
|
-
bin: value,
|
|
499
|
-
source,
|
|
500
|
-
userConfigured: true,
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
const detected = findDetectedCodexBin();
|
|
506
|
-
if (detected) {
|
|
507
|
-
return {
|
|
508
|
-
bin: detected,
|
|
509
|
-
source: "detected",
|
|
510
|
-
userConfigured: false,
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
return {
|
|
515
|
-
bin: "codex",
|
|
516
|
-
source: "default",
|
|
517
|
-
userConfigured: false,
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
function resolveInstalledCodexBin() {
|
|
522
|
-
const candidates = dedupe(
|
|
523
|
-
["codex", findDetectedCodexBin(), findWindowsNpmGlobalCodexBin(), findVscodeCodexExe()].filter(
|
|
524
|
-
Boolean,
|
|
525
|
-
),
|
|
526
|
-
);
|
|
527
|
-
|
|
528
|
-
for (const candidate of candidates) {
|
|
529
|
-
const status = runCodex(candidate, ["--version"], "pipe");
|
|
530
|
-
if (status.ok) {
|
|
531
|
-
return candidate;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
return "";
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
function findDetectedCodexBin() {
|
|
539
|
-
if (process.platform !== "win32") {
|
|
540
|
-
return "";
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
return findVscodeCodexExe() || findWindowsNpmGlobalCodexBin() || "";
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function findVscodeCodexExe() {
|
|
547
|
-
const userProfile = nonEmpty(process.env.USERPROFILE);
|
|
548
|
-
if (!userProfile) {
|
|
549
|
-
return "";
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
const extensionsDir = path.join(userProfile, ".vscode", "extensions");
|
|
553
|
-
if (!fs.existsSync(extensionsDir)) {
|
|
554
|
-
return "";
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
const candidates = fs
|
|
558
|
-
.readdirSync(extensionsDir, { withFileTypes: true })
|
|
559
|
-
.filter((entry) => entry.isDirectory())
|
|
560
|
-
.map((entry) => entry.name)
|
|
561
|
-
.filter((name) => name.startsWith("openai.chatgpt-"))
|
|
562
|
-
.sort()
|
|
563
|
-
.reverse();
|
|
564
|
-
|
|
565
|
-
for (const folder of candidates) {
|
|
566
|
-
const exePath = path.join(extensionsDir, folder, "bin", "windows-x86_64", "codex.exe");
|
|
567
|
-
if (fs.existsSync(exePath)) {
|
|
568
|
-
return exePath;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
return "";
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
function findWindowsNpmGlobalCodexBin() {
|
|
576
|
-
if (process.platform !== "win32") {
|
|
577
|
-
return "";
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
const candidates = [];
|
|
581
|
-
const appData = nonEmpty(process.env.APPDATA);
|
|
582
|
-
if (appData) {
|
|
583
|
-
candidates.push(path.join(appData, "npm", "codex.cmd"));
|
|
584
|
-
candidates.push(path.join(appData, "npm", "codex.exe"));
|
|
585
|
-
candidates.push(path.join(appData, "npm", "codex"));
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
const npmPrefix = readNpmPrefix();
|
|
589
|
-
if (npmPrefix) {
|
|
590
|
-
candidates.push(path.join(npmPrefix, "codex.cmd"));
|
|
591
|
-
candidates.push(path.join(npmPrefix, "codex.exe"));
|
|
592
|
-
candidates.push(path.join(npmPrefix, "codex"));
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
for (const candidate of dedupe(candidates)) {
|
|
596
|
-
if (fs.existsSync(candidate)) {
|
|
597
|
-
return candidate;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
return "";
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function readNpmPrefix() {
|
|
605
|
-
const result = spawnNpm(["config", "get", "prefix"], {
|
|
606
|
-
cwd: repoRoot,
|
|
607
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
608
|
-
encoding: "utf8",
|
|
609
|
-
});
|
|
610
|
-
if (result.error || result.status !== 0) {
|
|
611
|
-
return "";
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
const value = String(result.stdout ?? "").trim();
|
|
615
|
-
if (!value || value.toLowerCase() === "undefined") {
|
|
616
|
-
return "";
|
|
617
|
-
}
|
|
618
|
-
return value;
|
|
591
|
+
function quoteWindowsShellValue(value) {
|
|
592
|
+
return `"${String(value ?? "").replace(/"/g, '\\"')}"`;
|
|
619
593
|
}
|
|
620
594
|
|
|
621
595
|
function runNpm(args, stdioMode) {
|
|
@@ -646,34 +620,6 @@ function runNpm(args, stdioMode) {
|
|
|
646
620
|
};
|
|
647
621
|
}
|
|
648
622
|
|
|
649
|
-
function readEnvValue(filePath, key) {
|
|
650
|
-
if (!fs.existsSync(filePath)) {
|
|
651
|
-
return "";
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
const lines = fs.readFileSync(filePath, "utf8").split(/\r?\n/);
|
|
655
|
-
const pattern = new RegExp(`^\\s*${escapeRegex(key)}\\s*=\\s*(.*)\\s*$`);
|
|
656
|
-
for (const line of lines) {
|
|
657
|
-
const match = line.match(pattern);
|
|
658
|
-
if (!match) {
|
|
659
|
-
continue;
|
|
660
|
-
}
|
|
661
|
-
return unquote(match[1]);
|
|
662
|
-
}
|
|
663
|
-
return "";
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
function unquote(value) {
|
|
667
|
-
const raw = String(value ?? "").trim();
|
|
668
|
-
if (!raw) {
|
|
669
|
-
return "";
|
|
670
|
-
}
|
|
671
|
-
if ((raw.startsWith('"') && raw.endsWith('"')) || (raw.startsWith("'") && raw.endsWith("'"))) {
|
|
672
|
-
return raw.slice(1, -1).trim();
|
|
673
|
-
}
|
|
674
|
-
return raw;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
623
|
async function askYesNo(rl, label, defaultYes) {
|
|
678
624
|
const suffix = defaultYes ? "[Y/n]" : "[y/N]";
|
|
679
625
|
const answer = await rl.question(`${label} ${suffix}: `);
|
|
@@ -692,15 +638,6 @@ async function askYesNo(rl, label, defaultYes) {
|
|
|
692
638
|
return defaultYes;
|
|
693
639
|
}
|
|
694
640
|
|
|
695
|
-
function escapeRegex(value) {
|
|
696
|
-
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
function nonEmpty(value) {
|
|
700
|
-
const normalized = String(value ?? "").trim();
|
|
701
|
-
return normalized || "";
|
|
702
|
-
}
|
|
703
|
-
|
|
704
641
|
function firstLine(value) {
|
|
705
642
|
return (
|
|
706
643
|
String(value ?? "")
|
|
@@ -740,10 +677,6 @@ function normalizeErrorCode(error) {
|
|
|
740
677
|
.toUpperCase();
|
|
741
678
|
}
|
|
742
679
|
|
|
743
|
-
function dedupe(values: string[]) {
|
|
744
|
-
return [...new Set(values.map((value) => String(value).trim()).filter(Boolean))];
|
|
745
|
-
}
|
|
746
|
-
|
|
747
680
|
function spawnNpm(args, options) {
|
|
748
681
|
if (process.platform === "win32") {
|
|
749
682
|
const comspec = process.env.ComSpec || "cmd.exe";
|