specrails-desktop 2.11.4 → 2.11.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specrails-desktop",
3
- "version": "2.11.4",
3
+ "version": "2.11.5",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -120,6 +120,25 @@ function fastPathDirectories() {
120
120
  }
121
121
  return [];
122
122
  }
123
+ /**
124
+ * Well-known Windows directories that hold globally-installed CLI shims
125
+ * (`claude.cmd` / `codex.cmd` / `gemini.cmd`) which a GUI-launched (Explorer/
126
+ * Tauri) process may not have on its inherited PATH. The per-user npm prefix
127
+ * places `.cmd` shims DIRECTLY in the prefix root (`%APPDATA%\npm`), not a `bin`
128
+ * subdir. `npm prefix -g` is the authoritative location for a custom prefix.
129
+ */
130
+ function windowsGlobalBinDirs() {
131
+ if (process.platform !== 'win32')
132
+ return [];
133
+ const dirs = [];
134
+ // Default per-user npm prefix: shims (`claude.cmd` …) live in the prefix ROOT.
135
+ if (process.env.APPDATA)
136
+ dirs.push(path_1.default.join(process.env.APPDATA, 'npm'));
137
+ // Machine-wide Node install (also where a machine-scope npm prefix points).
138
+ if (process.env.ProgramFiles)
139
+ dirs.push(path_1.default.join(process.env.ProgramFiles, 'nodejs'));
140
+ return dirs;
141
+ }
123
142
  /**
124
143
  * Returns the absolute path to the bundled runtimes directory.
125
144
  * Only valid when SPECRAILS_IS_DESKTOP=1 and SPECRAILS_BUNDLED_RUNTIMES_PATH is set.
@@ -200,9 +219,26 @@ function resolveStartupPath() {
200
219
  const inherited = splitPath(process.env.PATH);
201
220
  const inheritedSet = new Set(inherited);
202
221
  if (process.platform === 'win32') {
222
+ // Prepend well-known global-CLI dirs (npm prefix, Program Files\nodejs) that
223
+ // a GUI-launched process may lack, so provider shims (claude/codex/gemini
224
+ // .cmd) — and any RECURSIVE bare-name invocation by a spawned CLI — resolve.
225
+ // Existence-gated + deduped; no-op when already present (the common case).
226
+ const winPrepend = [];
227
+ for (const dir of windowsGlobalBinDirs()) {
228
+ if (dir && fileExists(dir) && !inheritedSet.has(dir)) {
229
+ winPrepend.push(dir);
230
+ inheritedSet.add(dir);
231
+ }
232
+ }
233
+ const winMerged = [...winPrepend, ...inherited];
234
+ if (winPrepend.length > 0)
235
+ process.env.PATH = joinPath(winMerged);
203
236
  diagnostic = {
204
- pathSegments: inherited,
205
- pathSources: inherited.map(() => 'inherited'),
237
+ pathSegments: winMerged,
238
+ pathSources: [
239
+ ...winPrepend.map(() => 'fast-path'),
240
+ ...inherited.map(() => 'inherited'),
241
+ ],
206
242
  loginShellStatus: 'skipped',
207
243
  };
208
244
  return;
@@ -29,6 +29,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports._GEMINI_MIN_VERSION = exports.geminiAdapter = void 0;
30
30
  exports._compareSemver = compareSemver;
31
31
  const child_process_1 = require("child_process");
32
+ const win_spawn_1 = require("../util/win-spawn");
32
33
  const gemini_agent_ack_1 = require("./gemini-agent-ack");
33
34
  const WHICH_CMD = process.platform === 'win32' ? 'where' : 'which';
34
35
  // Floor where `--output-format stream-json` + headless `--resume` are available.
@@ -180,16 +181,20 @@ function compareSemver(a, b) {
180
181
  }
181
182
  async function detectGeminiInstalled() {
182
183
  try {
183
- (0, child_process_1.execSync)(`${WHICH_CMD} gemini`, { stdio: 'ignore' });
184
+ (0, child_process_1.execSync)(`${WHICH_CMD} gemini`, { stdio: 'ignore', env: (0, win_spawn_1.windowsSpawnEnv)() });
184
185
  }
185
186
  catch {
186
187
  return { installed: false, executable: false };
187
188
  }
188
189
  try {
190
+ // gemini is a Node CLI; its cold `--version` (cmd.exe → node → load bundle,
191
+ // Defender scanning) can exceed a few seconds on Windows. Give it a generous
192
+ // budget + SystemRoot env so it isn't wrongly reported not-executable.
189
193
  const raw = (0, child_process_1.execSync)('gemini --version', {
190
194
  encoding: 'utf-8',
191
195
  stdio: ['pipe', 'pipe', 'ignore'],
192
- timeout: 3000,
196
+ timeout: process.platform === 'win32' ? 20_000 : 8_000,
197
+ env: (0, win_spawn_1.windowsSpawnEnv)(),
193
198
  }).trim();
194
199
  const match = raw.match(/\d+\.\d+\.\d+/);
195
200
  const version = match ? match[0] : raw;
@@ -28,13 +28,19 @@ const WHICH_CMD = process.platform === 'win32' ? 'where' : 'which';
28
28
  * inherited a stripped env) — without it every bundled probe failed with a bogus
29
29
  * "bundle corrupted — reinstall the app".
30
30
  */
31
- function runVersionSpawn(cmd, args) {
32
- const opts = { env: (0, win_spawn_1.windowsSpawnEnv)(), encoding: 'utf-8', timeout: 5_000 };
31
+ function runVersionSpawn(cmd, args, timeoutMs = 5_000) {
32
+ const opts = { env: (0, win_spawn_1.windowsSpawnEnv)(), encoding: 'utf-8', timeout: timeoutMs };
33
33
  if (process.platform === 'win32') {
34
34
  return cross_spawn_1.default.sync(cmd, args, opts);
35
35
  }
36
36
  return (0, child_process_1.spawnSync)(cmd, args, opts);
37
37
  }
38
+ // A bundled/system CLI's `--version` cold start can be slow on Windows: a npm
39
+ // `.cmd` shim goes cmd.exe → node.exe → load the CLI bundle, while Defender
40
+ // scans it. The Node-based gemini CLI in particular exceeded the old 5s cap and
41
+ // was wrongly reported not-executable. Give version probes a generous Windows
42
+ // budget; the `where`/`which` LOCATE probe stays fast (5s) since it's a builtin.
43
+ const VERSION_PROBE_TIMEOUT_MS = process.platform === 'win32' ? 20_000 : 8_000;
38
44
  exports.MIN_VERSIONS = {
39
45
  node: '18.0.0',
40
46
  npm: '9.0.0',
@@ -53,7 +59,7 @@ function locateCommand(command) {
53
59
  }
54
60
  /** Run `<bin> <args>` and interpret the result as a `--version` probe. */
55
61
  function runProbe(bin, args) {
56
- const result = runVersionSpawn(bin, args);
62
+ const result = runVersionSpawn(bin, args, VERSION_PROBE_TIMEOUT_MS);
57
63
  if (result.error) {
58
64
  const err = result.error;
59
65
  return { executed: false, error: `${err.code ?? 'ERR'}: ${err.message}` };