@pixelbyte-software/pixcode 1.38.2 → 1.38.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.
Files changed (34) hide show
  1. package/dist/assets/index-BzgMq98S.css +32 -0
  2. package/dist/assets/{index-BJiAbLzU.js → index-Cc0uLPZw.js} +140 -140
  3. package/dist/index.html +2 -2
  4. package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +76 -1
  5. package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -1
  6. package/dist-server/server/modules/orchestration/workflows/workspace-target.js +2 -0
  7. package/dist-server/server/modules/orchestration/workflows/workspace-target.js.map +1 -1
  8. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +2 -1
  9. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -1
  10. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +5 -2
  11. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -1
  12. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +1 -1
  13. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -1
  14. package/dist-server/server/modules/providers/list/qwen/qwen-auth.provider.js +1 -1
  15. package/dist-server/server/modules/providers/list/qwen/qwen-auth.provider.js.map +1 -1
  16. package/dist-server/server/routes/taskmaster.js +31 -52
  17. package/dist-server/server/routes/taskmaster.js.map +1 -1
  18. package/dist-server/server/services/install-jobs.js +152 -17
  19. package/dist-server/server/services/install-jobs.js.map +1 -1
  20. package/package.json +1 -1
  21. package/scripts/smoke/chat-session-provider-pools.mjs +35 -0
  22. package/scripts/smoke/desktop-tray-icon.mjs +33 -0
  23. package/scripts/smoke/mac-desktop-runtime.mjs +43 -0
  24. package/scripts/smoke/multi-project-ui.mjs +45 -0
  25. package/scripts/smoke/orchestration-permission-fallback.mjs +34 -0
  26. package/server/modules/orchestration/workflows/workflow-runner.ts +105 -1
  27. package/server/modules/orchestration/workflows/workspace-target.ts +2 -0
  28. package/server/modules/providers/list/codex/codex-auth.provider.ts +2 -1
  29. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +6 -2
  30. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +1 -1
  31. package/server/modules/providers/list/qwen/qwen-auth.provider.ts +1 -1
  32. package/server/routes/taskmaster.js +36 -57
  33. package/server/services/install-jobs.js +159 -16
  34. package/dist/assets/index-BzL2G4Sw.css +0 -32
@@ -48,6 +48,11 @@ import spawn from 'cross-spawn';
48
48
  const jobs = new Map();
49
49
  const FINISHED_TTL_MS = 10 * 60 * 1000;
50
50
  const HARD_TIMEOUT_MS = 10 * 60 * 1000;
51
+ const USER_SHELL_PATH_CACHE_TTL_MS = 5 * 60 * 1000;
52
+ const userShellPathCache = {
53
+ value: null,
54
+ readAt: 0,
55
+ };
51
56
 
52
57
  export const CLI_HOME = path.join(os.homedir(), '.pixcode', 'cli-bin');
53
58
  export const CLI_BIN_DIR = path.join(CLI_HOME, 'node_modules', '.bin');
@@ -94,13 +99,9 @@ function ensureCliHome() {
94
99
  */
95
100
  export function primeCliBinPath(env = process.env) {
96
101
  ensureCliHome();
97
- const sep = process.platform === 'win32' ? ';' : ':';
98
- const current = env.PATH || env.Path || '';
99
- if (!current.split(sep).some((entry) => path.resolve(entry || '') === path.resolve(CLI_BIN_DIR))) {
100
- const next = current ? `${CLI_BIN_DIR}${sep}${current}` : CLI_BIN_DIR;
101
- env.PATH = next;
102
- if ('Path' in env) env.Path = next;
103
- }
102
+ const augmentedEnv = buildCliSpawnEnv(env);
103
+ env.PATH = augmentedEnv.PATH;
104
+ if ('Path' in env || augmentedEnv.Path) env.Path = augmentedEnv.Path || augmentedEnv.PATH;
104
105
  // Once PATH is ready, resolve any well-known provider binaries to absolute
105
106
  // paths and export them as *_CLI_PATH env vars. This side-steps a Windows
106
107
  // gotcha: `child_process.spawn('claude', …)` does NOT auto-resolve .cmd /
@@ -141,6 +142,135 @@ export function resolveProviderExecutables(env = process.env) {
141
142
  }
142
143
  }
143
144
 
145
+ function pathSeparator() {
146
+ return process.platform === 'win32' ? ';' : ':';
147
+ }
148
+
149
+ function splitPathList(value) {
150
+ return String(value || '').split(pathSeparator()).map((entry) => entry.trim()).filter(Boolean);
151
+ }
152
+
153
+ function collectNvmNodeBins(home) {
154
+ const versionsDir = path.join(home, '.nvm', 'versions', 'node');
155
+ try {
156
+ return fs.readdirSync(versionsDir)
157
+ .map((version) => path.join(versionsDir, version, 'bin'))
158
+ .filter((candidate) => {
159
+ try {
160
+ return fs.statSync(candidate).isDirectory();
161
+ } catch {
162
+ return false;
163
+ }
164
+ })
165
+ .sort()
166
+ .reverse();
167
+ } catch {
168
+ return [];
169
+ }
170
+ }
171
+
172
+ function collectKnownUserBinDirs(env = process.env) {
173
+ const home = os.homedir();
174
+ if (process.platform === 'win32') {
175
+ return [
176
+ path.join(env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'npm'),
177
+ path.join(env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'Programs', 'nodejs'),
178
+ ];
179
+ }
180
+
181
+ return [
182
+ CLI_BIN_DIR,
183
+ path.dirname(process.execPath),
184
+ ...collectNvmNodeBins(home),
185
+ path.join(home, '.volta', 'bin'),
186
+ path.join(home, '.asdf', 'shims'),
187
+ path.join(home, '.bun', 'bin'),
188
+ path.join(home, '.local', 'bin'),
189
+ path.join(home, '.npm-global', 'bin'),
190
+ '/opt/homebrew/bin',
191
+ '/opt/homebrew/sbin',
192
+ '/usr/local/bin',
193
+ '/usr/local/sbin',
194
+ '/usr/bin',
195
+ '/bin',
196
+ ];
197
+ }
198
+
199
+ export function collectUserShellPath(env = process.env) {
200
+ if (process.platform === 'win32') return [];
201
+
202
+ const now = Date.now();
203
+ if (userShellPathCache.value && now - userShellPathCache.readAt < USER_SHELL_PATH_CACHE_TTL_MS) {
204
+ return userShellPathCache.value;
205
+ }
206
+
207
+ const shells = [env.SHELL, '/bin/zsh', '/bin/bash']
208
+ .filter(Boolean)
209
+ .filter((candidate, index, list) => list.indexOf(candidate) === index)
210
+ .filter((candidate) => {
211
+ try {
212
+ return fs.existsSync(candidate);
213
+ } catch {
214
+ return false;
215
+ }
216
+ });
217
+
218
+ const marker = '__PIXCODE_LOGIN_PATH__=';
219
+ for (const shell of shells) {
220
+ try {
221
+ const output = execFileSync(shell, ['-lc', `printf '\\n${marker}%s\\n' "$PATH"`], {
222
+ encoding: 'utf8',
223
+ env,
224
+ timeout: 2500,
225
+ stdio: ['ignore', 'pipe', 'ignore'],
226
+ });
227
+ const line = output.split(/\r?\n/).reverse().find((part) => part.startsWith(marker));
228
+ const shellPath = line?.slice(marker.length);
229
+ if (shellPath) {
230
+ const entries = splitPathList(shellPath);
231
+ userShellPathCache.value = entries;
232
+ userShellPathCache.readAt = now;
233
+ return entries;
234
+ }
235
+ } catch {
236
+ // GUI-launched macOS apps often have a tiny PATH. If the user's
237
+ // shell startup files are noisy or slow, fall back to known bins.
238
+ }
239
+ }
240
+
241
+ userShellPathCache.value = [];
242
+ userShellPathCache.readAt = now;
243
+ return [];
244
+ }
245
+
246
+ function mergePathEntries(env, preferredEntries) {
247
+ const existing = splitPathList(env.PATH || env.Path || '');
248
+ const seen = new Set();
249
+ const merged = [];
250
+
251
+ for (const entry of [...preferredEntries, ...existing]) {
252
+ if (!entry) continue;
253
+ const key = path.resolve(entry);
254
+ if (seen.has(key)) continue;
255
+ seen.add(key);
256
+ merged.push(entry);
257
+ }
258
+
259
+ const nextPath = merged.join(pathSeparator());
260
+ env.PATH = nextPath;
261
+ if ('Path' in env) env.Path = nextPath;
262
+ return env;
263
+ }
264
+
265
+ export function buildCliSpawnEnv(baseEnv = process.env) {
266
+ const env = { ...baseEnv };
267
+ return mergePathEntries(env, [
268
+ CLI_BIN_DIR,
269
+ ...collectUserShellPath(baseEnv),
270
+ ...collectKnownUserBinDirs(baseEnv),
271
+ ]);
272
+ }
273
+
144
274
  /**
145
275
  * Cross-platform lookup for the Claude Code CLI executable. The
146
276
  * @anthropic-ai/claude-agent-sdk SDK spawns its target with plain
@@ -301,10 +431,8 @@ export function findExecutableOnPath(name, env = process.env) {
301
431
  paths.push(path.join(env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'Programs', `${name}-code`));
302
432
  paths.push(path.join(env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'AnthropicClaude'));
303
433
  } else {
304
- paths.push(path.join(home, '.local', 'bin'));
305
- paths.push(path.join(home, '.npm-global', 'bin'));
306
- paths.push('/usr/local/bin');
307
- paths.push('/opt/homebrew/bin');
434
+ paths.push(...collectUserShellPath(env));
435
+ paths.push(...collectKnownUserBinDirs(env));
308
436
  }
309
437
 
310
438
  const exts = isWindows
@@ -331,7 +459,7 @@ export function findExecutableOnPath(name, env = process.env) {
331
459
  * more reliable than trusting PATH — when Pixcode runs as a daemon, PATH
332
460
  * is often minimal and doesn't include the user's node install.
333
461
  */
334
- function resolveNpmCommand() {
462
+ function resolveNpmCommand(env = process.env) {
335
463
  const nodeDir = path.dirname(process.execPath);
336
464
  const isWindows = process.platform === 'win32';
337
465
  const candidates = isWindows
@@ -348,8 +476,11 @@ function resolveNpmCommand() {
348
476
  return siblingNpm; // we'll invoke `node <npm-cli.js>`
349
477
  }
350
478
  }
351
- // Fall back to bare name and let the shell resolve.
352
- return isWindows ? 'npm.cmd' : 'npm';
479
+
480
+ const resolvedFromPath = findExecutableOnPath('npm', env);
481
+ if (resolvedFromPath) return resolvedFromPath;
482
+
483
+ return null;
353
484
  }
354
485
 
355
486
  function packageFromCommand(installCmd) {
@@ -406,7 +537,19 @@ export function createInstallJob({ provider, installCmd, packageName }) {
406
537
  appendLog('meta', `Installing ${pkg} into ${CLI_HOME}\n`);
407
538
  appendLog('meta', `(sandboxed — no sudo / admin required)\n`);
408
539
 
409
- const npmCmd = resolveNpmCommand();
540
+ const installEnv = buildCliSpawnEnv(process.env);
541
+ const npmCmd = resolveNpmCommand(installEnv);
542
+ if (!npmCmd) {
543
+ job.status = 'error';
544
+ job.error = 'npm was not found. Install Node.js/npm or add it to your macOS login shell PATH, then click Refresh.';
545
+ job.finishedAt = new Date().toISOString();
546
+ appendLog('stderr', job.error + '\n');
547
+ emitter.emit('done', buildDonePayload(job));
548
+ scheduleCleanup(job);
549
+ jobs.set(id, job);
550
+ return job;
551
+ }
552
+
410
553
  const useNodeRunner = npmCmd.endsWith('.js');
411
554
 
412
555
  const cmd = useNodeRunner ? process.execPath : npmCmd;
@@ -420,7 +563,7 @@ export function createInstallJob({ provider, installCmd, packageName }) {
420
563
  try {
421
564
  child = spawn(cmd, args, {
422
565
  cwd: CLI_HOME,
423
- env: { ...process.env, npm_config_yes: 'true' },
566
+ env: { ...installEnv, npm_config_yes: 'true' },
424
567
  stdio: ['ignore', 'pipe', 'pipe'],
425
568
  windowsHide: true,
426
569
  // cross-spawn handles .cmd/.bat resolution itself — no shell