ai-battery 0.1.5 → 0.1.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.
@@ -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,17 @@ function quoteCmdArg(value) {
170
169
  return `"${String(value).replace(/"/g, '""')}"`;
171
170
  }
172
171
 
172
+ function resolveWindowsCommandFile(commandPath) {
173
+ if (path.extname(commandPath)) return commandPath;
174
+ for (const suffix of [".cmd", ".exe", ".bat", ".ps1"]) {
175
+ const candidate = `${commandPath}${suffix}`;
176
+ if (fs.existsSync(candidate)) return candidate;
177
+ }
178
+ return commandPath;
179
+ }
180
+
173
181
  function windowsCommand(command) {
174
- const exe = command[0];
182
+ const exe = resolveWindowsCommandFile(command[0]);
175
183
  const rest = command.slice(1);
176
184
  if (/\.(cmd|bat)$/i.test(exe)) {
177
185
  return {
@@ -179,6 +187,18 @@ function windowsCommand(command) {
179
187
  args: ["/d", "/s", "/c", [exe, ...rest].map(quoteCmdArg).join(" ")]
180
188
  };
181
189
  }
190
+ if (/\.ps1$/i.test(exe)) {
191
+ return {
192
+ file: "powershell.exe",
193
+ args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", exe, ...rest]
194
+ };
195
+ }
196
+ if (/\.(js|mjs|cjs)$/i.test(exe)) {
197
+ return {
198
+ file: process.execPath,
199
+ args: [exe, ...rest]
200
+ };
201
+ }
182
202
  return { file: exe, args: rest };
183
203
  }
184
204
 
@@ -294,7 +314,26 @@ async function main() {
294
314
  process.exit(exitCode);
295
315
  }
296
316
 
297
- main().catch((error) => {
298
- console.error(`ai-battery-run-win: ${error.message}`);
299
- process.exit(1);
300
- });
317
+ export {
318
+ resolveWindowsCommandFile,
319
+ windowsCommand
320
+ };
321
+
322
+ function sameFilePath(leftPath, rightPath) {
323
+ try {
324
+ return fs.realpathSync(leftPath) === fs.realpathSync(rightPath);
325
+ } catch {
326
+ return path.resolve(leftPath) === path.resolve(rightPath);
327
+ }
328
+ }
329
+
330
+ function isDirectRun() {
331
+ return Boolean(process.argv[1]) && sameFilePath(fileURLToPath(import.meta.url), process.argv[1]);
332
+ }
333
+
334
+ if (isDirectRun()) {
335
+ main().catch((error) => {
336
+ console.error(`ai-battery-run-win: ${error.message}`);
337
+ process.exit(1);
338
+ });
339
+ }
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
- ? [command, `${command}.cmd`, `${command}.exe`, `${command}.bat`]
519
+ ? [`${command}.cmd`, `${command}.exe`, `${command}.bat`, command]
512
520
  : [command];
513
521
  }
514
522
 
@@ -529,16 +537,39 @@ function cmdQuote(value) {
529
537
  return `"${String(value).replace(/"/g, '""')}"`;
530
538
  }
531
539
 
540
+ function windowsCommandSibling(commandPath) {
541
+ if (path.extname(commandPath)) return commandPath;
542
+ for (const suffix of [".cmd", ".exe", ".bat", ".ps1"]) {
543
+ const candidate = `${commandPath}${suffix}`;
544
+ if (fs.existsSync(candidate)) return candidate;
545
+ }
546
+ return commandPath;
547
+ }
548
+
549
+ function windowsCmdFallbackCommand(commandPath) {
550
+ const command = windowsCommandSibling(commandPath);
551
+ const quotedCommand = cmdQuote(command);
552
+ if (/\.(cmd|bat)$/i.test(command)) return ` call ${quotedCommand} %*`;
553
+ if (/\.ps1$/i.test(command)) {
554
+ return ` powershell.exe -NoProfile -ExecutionPolicy Bypass -File ${quotedCommand} %*`;
555
+ }
556
+ if (/\.(js|mjs|cjs)$/i.test(command)) {
557
+ return ` ${cmdQuote(process.execPath)} ${quotedCommand} %*`;
558
+ }
559
+ return ` ${quotedCommand} %*`;
560
+ }
561
+
532
562
  function windowsCodexWrapperScript(originalCommand) {
533
563
  const runner = path.join(scriptDir(), "ai-battery-run-win.js");
564
+ const command = windowsCommandSibling(originalCommand);
534
565
  return `@echo off
535
566
  rem ${CODEX_WRAPPER_MARKER}=1
536
- set "AI_BATTERY_ORIGINAL_CODEX=${originalCommand}"
567
+ set "AI_BATTERY_ORIGINAL_CODEX=${command}"
537
568
  set "AI_BATTERY_WRAPPED_CODEX=1"
538
569
  if exist ${cmdQuote(runner)} (
539
- ${cmdQuote(process.execPath)} ${cmdQuote(runner)} --provider all -- ${cmdQuote(originalCommand)} %*
570
+ ${cmdQuote(process.execPath)} ${cmdQuote(runner)} --provider all -- ${cmdQuote(command)} %*
540
571
  ) else (
541
- ${cmdQuote(originalCommand)} %*
572
+ ${windowsCmdFallbackCommand(command)}
542
573
  )
543
574
  `;
544
575
  }
@@ -957,7 +988,7 @@ function installCodexWrapper(args) {
957
988
  ? configuredOriginal
958
989
  : discoveredOriginal;
959
990
  const originalPathForPath = discoveredOriginal || originalCandidate;
960
- const originalCommand = originalCandidate ? executableTarget(originalCandidate) : null;
991
+ const originalCommand = originalCandidate ? wrapperCommandTarget(originalCandidate) : null;
961
992
 
962
993
  if (!originalCommand) {
963
994
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-battery",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Tiny terminal battery meter for local Codex and Claude Code usage.",
5
5
  "type": "module",
6
6
  "keywords": [