nunmai 0.1.2 → 0.1.3

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 CHANGED
@@ -15,10 +15,13 @@ engine; the first run opens the AI-account wizard (Claude, ChatGPT, Kimi,
15
15
  Gemini, OpenRouter).
16
16
 
17
17
  - macOS, Linux, Windows 10/11 (PowerShell), Android (Termux).
18
- - **Local install** (`npm install nunmai`, no `-g`): run it with `npx nunmai`.
18
+ - **Local install** (`npm install nunmai`, no `-g`): run it once with `npx nunmai`.
19
19
  npm ≥ 11.19 blocks dependency install scripts by default, so the engine is
20
- installed on the first `npx nunmai` run. To get a plain `nunmai` command from
21
- a local install, allow the script: `npm install nunmai --allow-scripts=nunmai`.
20
+ installed on that first run. The launcher then puts a plain `nunmai` command
21
+ on your PATH itself `~/.local/bin` on macOS/Linux, `%LOCALAPPDATA%\nunmai\bin`
22
+ (registered on the user PATH) on Windows — so after opening a new terminal
23
+ `nunmai` just works. To skip even that first `npx`, allow the script:
24
+ `npm install nunmai --allow-scripts=nunmai`.
22
25
  - If npm skipped the install script (`--ignore-scripts`, `CI` set, or the
23
26
  allow-scripts policy above), the install simply happens on the first run.
24
27
  - After `nunmai uninstall`, the `nunmai` command falls back to this launcher —
package/bin/nunmai.js CHANGED
@@ -33,11 +33,27 @@ const IS_WIN = process.platform === "win32";
33
33
  const SELF = fs.realpathSync(__filename);
34
34
  const DRY = process.env.NUNMAI_BOOTSTRAP_DRY_RUN === "1";
35
35
 
36
+ // Marker stamped into the Windows fallback shim (a .cmd that re-enters this
37
+ // file). findLauncher() must never treat that shim as the engine launcher or
38
+ // `nunmai` would recurse into itself forever.
39
+ const WIN_SHIM_MARKER = "rem nunmai-npm-shim";
40
+
41
+ function winBinDir() {
42
+ // Same managed dir install.ps1 uses for the real launchers (Set-PathVariable):
43
+ // %NUNMAI_HOME%\bin, defaulting to %LOCALAPPDATA%\nunmai\bin.
44
+ const home = os.homedir();
45
+ const nunmaiHome = process.env.NUNMAI_HOME || path.join(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "nunmai");
46
+ return path.join(nunmaiHome, "bin");
47
+ }
48
+
49
+ function isWinShim(p) {
50
+ try { return p.toLowerCase().endsWith(".cmd") && fs.readFileSync(p, "utf8").includes(WIN_SHIM_MARKER); } catch (_) { return false; }
51
+ }
52
+
36
53
  function candidates() {
37
54
  const home = os.homedir();
38
55
  if (IS_WIN) {
39
- const nunmaiHome = process.env.NUNMAI_HOME || path.join(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "nunmai");
40
- const bin = path.join(nunmaiHome, "bin");
56
+ const bin = winBinDir();
41
57
  return [path.join(bin, "nunmai.exe"), path.join(bin, "nunmai.cmd")];
42
58
  }
43
59
  const list = [];
@@ -52,6 +68,7 @@ function findLauncher() {
52
68
  for (const p of candidates()) {
53
69
  try {
54
70
  if (fs.realpathSync(p) === SELF) continue; // never recurse into this shim
71
+ if (IS_WIN && isWinShim(p)) continue; // ...nor into the Windows .cmd shim
55
72
  fs.accessSync(p, fs.constants.X_OK);
56
73
  return p;
57
74
  } catch (_) { /* not there */ }
@@ -64,7 +81,7 @@ function ensureFallbackLauncher() {
64
81
  // ./node_modules/.bin, which is not on PATH. Make sure `nunmai` resolves
65
82
  // anyway by linking this shim into ~/.local/bin (or $PREFIX/bin on Termux).
66
83
  // The real installer replaces the link with the engine launcher (ln -sf).
67
- if (IS_WIN) return null;
84
+ if (IS_WIN) return ensureWinFallbackLauncher();
68
85
  const binDir = process.env.PREFIX && fs.existsSync(path.join(process.env.PREFIX, "bin"))
69
86
  ? path.join(process.env.PREFIX, "bin")
70
87
  : path.join(os.homedir(), ".local", "bin");
@@ -83,6 +100,51 @@ function ensureFallbackLauncher() {
83
100
  }
84
101
  }
85
102
 
103
+ function ensureWinFallbackLauncher() {
104
+ // Windows twin of the symlink above: drop a nunmai.cmd shim into the managed
105
+ // bin dir and register that dir on the User PATH, so a plain `nunmai` works
106
+ // after a local `npm install nunmai` (where npm >= 11.19 skips postinstall
107
+ // and node_modules\.bin is not on PATH). install.ps1 later overwrites the
108
+ // shim with the real launcher (Install-NunmaiCommandLaunchers) and keeps the
109
+ // same PATH entry, so nothing here has to be undone.
110
+ const binDir = winBinDir();
111
+ const target = path.join(binDir, "nunmai.cmd");
112
+ try {
113
+ fs.mkdirSync(binDir, { recursive: true });
114
+ // Never clobber a real engine launcher.
115
+ if (fs.existsSync(path.join(binDir, "nunmai.exe"))) return null;
116
+ if (fs.existsSync(target) && !isWinShim(target)) return null;
117
+ const body = `@echo off\r\n${WIN_SHIM_MARKER}: ${SELF}\r\n"${process.execPath}" "${SELF}" %*\r\n`;
118
+ fs.writeFileSync(target, body);
119
+ ensureWinUserPath(binDir);
120
+ return target;
121
+ } catch (e) {
122
+ console.error(`nunmai: could not stage launcher into ${binDir}: ${e.message}`);
123
+ return null;
124
+ }
125
+ }
126
+
127
+ function ensureWinUserPath(binDir) {
128
+ // Persist binDir at the front of the User PATH (registry), the same way
129
+ // install.ps1's Set-PathVariable does. Idempotent; best effort.
130
+ const norm = (p) => p.trim().replace(/[\\/]+$/, "").toLowerCase();
131
+ const onPath = (process.env.PATH || "").split(";").some((p) => norm(p) === norm(binDir));
132
+ if (onPath || DRY) return;
133
+ const ps = [
134
+ `$d='${binDir.replace(/'/g, "''")}'`,
135
+ "$cur=[Environment]::GetEnvironmentVariable('Path','User')",
136
+ "$items=@(); if ($cur) { $items=@($cur -split ';') }",
137
+ "if (-not ($items | Where-Object { $_.TrimEnd('\\') -ieq $d })) { [Environment]::SetEnvironmentVariable('Path', ((@($d) + $items) -join ';'), 'User'); exit 0 }",
138
+ "exit 3",
139
+ ].join("; ");
140
+ const r = spawnSync("powershell", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", ps], { stdio: "ignore" });
141
+ if (r.status === 0) {
142
+ console.log(`nunmai: added ${binDir} to your user PATH (open a new terminal, then run \`nunmai\`).`);
143
+ } else if (r.status !== 3) {
144
+ console.error(`nunmai: could not update the user PATH automatically. Add this folder to PATH to use \`nunmai\` directly: ${binDir}`);
145
+ }
146
+ }
147
+
86
148
  function ensurePathLine(binDir) {
87
149
  // Add ~/.local/bin to PATH in the user's shell rc if it is not there already.
88
150
  if (binDir !== path.join(os.homedir(), ".local", "bin")) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nunmai",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Nunmai Engine — `npm i nunmai` (global or local) installs the engine with all features and dependencies; run `nunmai` to start.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://nunmai.in",