ai-battery 0.1.5 → 0.1.7
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/bin/ai-battery-run-win.js +62 -6
- package/bin/ai-battery.js +58 -6
- package/package.json +1 -1
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { spawn, spawnSync } from "node:child_process";
|
|
4
4
|
import fs from "node:fs";
|
|
5
|
-
import os from "node:os";
|
|
6
5
|
import path from "node:path";
|
|
7
6
|
import { fileURLToPath } from "node:url";
|
|
8
7
|
|
|
@@ -170,8 +169,34 @@ function quoteCmdArg(value) {
|
|
|
170
169
|
return `"${String(value).replace(/"/g, '""')}"`;
|
|
171
170
|
}
|
|
172
171
|
|
|
172
|
+
function normalizeWindowsCommandPath(value) {
|
|
173
|
+
let text = String(value || "").trim();
|
|
174
|
+
for (let i = 0; i < 6; i += 1) {
|
|
175
|
+
const before = text;
|
|
176
|
+
text = text.replace(/\\"/g, "\"").trim();
|
|
177
|
+
if (
|
|
178
|
+
(text.startsWith("\"") && text.endsWith("\""))
|
|
179
|
+
|| (text.startsWith("'") && text.endsWith("'"))
|
|
180
|
+
) {
|
|
181
|
+
text = text.slice(1, -1).trim();
|
|
182
|
+
}
|
|
183
|
+
if (text === before) break;
|
|
184
|
+
}
|
|
185
|
+
return text;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function resolveWindowsCommandFile(commandPath) {
|
|
189
|
+
commandPath = normalizeWindowsCommandPath(commandPath);
|
|
190
|
+
if (path.extname(commandPath)) return commandPath;
|
|
191
|
+
for (const suffix of [".cmd", ".exe", ".bat", ".ps1"]) {
|
|
192
|
+
const candidate = `${commandPath}${suffix}`;
|
|
193
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
194
|
+
}
|
|
195
|
+
return commandPath;
|
|
196
|
+
}
|
|
197
|
+
|
|
173
198
|
function windowsCommand(command) {
|
|
174
|
-
const exe = command[0];
|
|
199
|
+
const exe = resolveWindowsCommandFile(command[0]);
|
|
175
200
|
const rest = command.slice(1);
|
|
176
201
|
if (/\.(cmd|bat)$/i.test(exe)) {
|
|
177
202
|
return {
|
|
@@ -179,6 +204,18 @@ function windowsCommand(command) {
|
|
|
179
204
|
args: ["/d", "/s", "/c", [exe, ...rest].map(quoteCmdArg).join(" ")]
|
|
180
205
|
};
|
|
181
206
|
}
|
|
207
|
+
if (/\.ps1$/i.test(exe)) {
|
|
208
|
+
return {
|
|
209
|
+
file: "powershell.exe",
|
|
210
|
+
args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", exe, ...rest]
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
if (/\.(js|mjs|cjs)$/i.test(exe)) {
|
|
214
|
+
return {
|
|
215
|
+
file: process.execPath,
|
|
216
|
+
args: [exe, ...rest]
|
|
217
|
+
};
|
|
218
|
+
}
|
|
182
219
|
return { file: exe, args: rest };
|
|
183
220
|
}
|
|
184
221
|
|
|
@@ -294,7 +331,26 @@ async function main() {
|
|
|
294
331
|
process.exit(exitCode);
|
|
295
332
|
}
|
|
296
333
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
334
|
+
export {
|
|
335
|
+
resolveWindowsCommandFile,
|
|
336
|
+
windowsCommand
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
function sameFilePath(leftPath, rightPath) {
|
|
340
|
+
try {
|
|
341
|
+
return fs.realpathSync(leftPath) === fs.realpathSync(rightPath);
|
|
342
|
+
} catch {
|
|
343
|
+
return path.resolve(leftPath) === path.resolve(rightPath);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function isDirectRun() {
|
|
348
|
+
return Boolean(process.argv[1]) && sameFilePath(fileURLToPath(import.meta.url), process.argv[1]);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (isDirectRun()) {
|
|
352
|
+
main().catch((error) => {
|
|
353
|
+
console.error(`ai-battery-run-win: ${error.message}`);
|
|
354
|
+
process.exit(1);
|
|
355
|
+
});
|
|
356
|
+
}
|
package/bin/ai-battery.js
CHANGED
|
@@ -492,6 +492,14 @@ function executableTarget(commandPath) {
|
|
|
492
492
|
}
|
|
493
493
|
}
|
|
494
494
|
|
|
495
|
+
function wrapperCommandTarget(commandPath) {
|
|
496
|
+
// On Windows the PATH command is often an npm-generated .cmd shim. Resolving
|
|
497
|
+
// it can expose the underlying .js file, which is not directly executable by
|
|
498
|
+
// ConPTY/spawn and fails with ERROR_BAD_EXE_FORMAT (193).
|
|
499
|
+
if (process.platform === "win32") return commandPath;
|
|
500
|
+
return executableTarget(commandPath);
|
|
501
|
+
}
|
|
502
|
+
|
|
495
503
|
function findCommand(command, skipPaths = []) {
|
|
496
504
|
const names = commandNames(command);
|
|
497
505
|
const skips = skipPaths.filter(Boolean);
|
|
@@ -508,7 +516,7 @@ function findCommand(command, skipPaths = []) {
|
|
|
508
516
|
|
|
509
517
|
function commandNames(command) {
|
|
510
518
|
return process.platform === "win32"
|
|
511
|
-
? [
|
|
519
|
+
? [`${command}.cmd`, `${command}.exe`, `${command}.bat`, command]
|
|
512
520
|
: [command];
|
|
513
521
|
}
|
|
514
522
|
|
|
@@ -529,16 +537,56 @@ function cmdQuote(value) {
|
|
|
529
537
|
return `"${String(value).replace(/"/g, '""')}"`;
|
|
530
538
|
}
|
|
531
539
|
|
|
540
|
+
function normalizeWindowsCommandPath(value) {
|
|
541
|
+
let text = String(value || "").trim();
|
|
542
|
+
for (let i = 0; i < 6; i += 1) {
|
|
543
|
+
const before = text;
|
|
544
|
+
text = text.replace(/\\"/g, "\"").trim();
|
|
545
|
+
if (
|
|
546
|
+
(text.startsWith("\"") && text.endsWith("\""))
|
|
547
|
+
|| (text.startsWith("'") && text.endsWith("'"))
|
|
548
|
+
) {
|
|
549
|
+
text = text.slice(1, -1).trim();
|
|
550
|
+
}
|
|
551
|
+
if (text === before) break;
|
|
552
|
+
}
|
|
553
|
+
return text;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function windowsCommandSibling(commandPath) {
|
|
557
|
+
commandPath = normalizeWindowsCommandPath(commandPath);
|
|
558
|
+
if (path.extname(commandPath)) return commandPath;
|
|
559
|
+
for (const suffix of [".cmd", ".exe", ".bat", ".ps1"]) {
|
|
560
|
+
const candidate = `${commandPath}${suffix}`;
|
|
561
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
562
|
+
}
|
|
563
|
+
return commandPath;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function windowsCmdFallbackCommand(commandPath) {
|
|
567
|
+
const command = windowsCommandSibling(commandPath);
|
|
568
|
+
const quotedCommand = cmdQuote(command);
|
|
569
|
+
if (/\.(cmd|bat)$/i.test(command)) return ` call ${quotedCommand} %*`;
|
|
570
|
+
if (/\.ps1$/i.test(command)) {
|
|
571
|
+
return ` powershell.exe -NoProfile -ExecutionPolicy Bypass -File ${quotedCommand} %*`;
|
|
572
|
+
}
|
|
573
|
+
if (/\.(js|mjs|cjs)$/i.test(command)) {
|
|
574
|
+
return ` ${cmdQuote(process.execPath)} ${quotedCommand} %*`;
|
|
575
|
+
}
|
|
576
|
+
return ` ${quotedCommand} %*`;
|
|
577
|
+
}
|
|
578
|
+
|
|
532
579
|
function windowsCodexWrapperScript(originalCommand) {
|
|
533
580
|
const runner = path.join(scriptDir(), "ai-battery-run-win.js");
|
|
581
|
+
const command = windowsCommandSibling(originalCommand);
|
|
534
582
|
return `@echo off
|
|
535
583
|
rem ${CODEX_WRAPPER_MARKER}=1
|
|
536
|
-
set "AI_BATTERY_ORIGINAL_CODEX=${
|
|
584
|
+
set "AI_BATTERY_ORIGINAL_CODEX=${command}"
|
|
537
585
|
set "AI_BATTERY_WRAPPED_CODEX=1"
|
|
538
586
|
if exist ${cmdQuote(runner)} (
|
|
539
|
-
${cmdQuote(process.execPath)} ${cmdQuote(runner)} --provider all -- ${cmdQuote(
|
|
587
|
+
${cmdQuote(process.execPath)} ${cmdQuote(runner)} --provider all -- ${cmdQuote(command)} %*
|
|
540
588
|
) else (
|
|
541
|
-
|
|
589
|
+
${windowsCmdFallbackCommand(command)}
|
|
542
590
|
)
|
|
543
591
|
`;
|
|
544
592
|
}
|
|
@@ -957,7 +1005,7 @@ function installCodexWrapper(args) {
|
|
|
957
1005
|
? configuredOriginal
|
|
958
1006
|
: discoveredOriginal;
|
|
959
1007
|
const originalPathForPath = discoveredOriginal || originalCandidate;
|
|
960
|
-
const originalCommand = originalCandidate ?
|
|
1008
|
+
const originalCommand = originalCandidate ? wrapperCommandTarget(originalCandidate) : null;
|
|
961
1009
|
|
|
962
1010
|
if (!originalCommand) {
|
|
963
1011
|
return {
|
|
@@ -1069,7 +1117,11 @@ function diagnoseCodex() {
|
|
|
1069
1117
|
notes.push(`A Codex backup is available for restore: ${activeWrapperBackup}`);
|
|
1070
1118
|
}
|
|
1071
1119
|
if (wrapperInstalled && !activeIsWrapper) {
|
|
1072
|
-
|
|
1120
|
+
const wrapperDir = path.dirname(configuredWrapper);
|
|
1121
|
+
const refreshNote = process.platform === "win32"
|
|
1122
|
+
? `open a new cmd/PowerShell, or run: $env:Path="${wrapperDir};$env:Path"`
|
|
1123
|
+
: "open a new terminal or run \"hash -r\" in the parent shell";
|
|
1124
|
+
notes.push(`Plain "codex" does not resolve to the AI Battery wrapper in this shell. Ensure ${wrapperDir} is before the original codex on PATH, then ${refreshNote}.`);
|
|
1073
1125
|
}
|
|
1074
1126
|
if (!runnerExists) {
|
|
1075
1127
|
notes.push(`AI Battery runner is missing or not executable: ${runnerPath}`);
|