@pixelbyte-software/pixcode 1.50.4 → 1.50.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/dist/index.html CHANGED
@@ -35,7 +35,7 @@
35
35
 
36
36
  <!-- Prevent zoom on iOS -->
37
37
  <meta name="format-detection" content="telephone=no" />
38
- <script type="module" crossorigin src="/assets/index-DYQjBZrd.js"></script>
38
+ <script type="module" crossorigin src="/assets/index-BSxc8Vid.js"></script>
39
39
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DB6V5Fl1.js">
40
40
  <link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-CIYNS698.js">
41
41
  <link rel="modulepreload" crossorigin href="/assets/vendor-xterm-C7tpxJl7.js">
@@ -69,6 +69,7 @@ import providerRoutes from './modules/providers/provider.routes.js';
69
69
  import { createHermesTaskRouter, adapterRegistry, ClaudeCodeA2AAdapter, CodexA2AAdapter, CursorA2AAdapter, GeminiA2AAdapter, OpenCodeA2AAdapter, QwenA2AAdapter, createPreviewProxyRouter, createOrchestrationTaskRouter, createHermesRouter, createWorkflowRouter, } from './modules/orchestration/index.js';
70
70
  import networkRoutes from './routes/network.js';
71
71
  import telegramRoutes from './routes/telegram.js';
72
+ import { restoreRequestedTunnel } from './services/external-access.js';
72
73
  import { restoreBotFromConfig } from './services/telegram/bot.js';
73
74
  import { ensurePortOpen } from './utils/port-access.js';
74
75
  import { applyAllStoredCredentialsToEnv, } from './services/provider-credentials.js';
@@ -232,6 +233,7 @@ const app = express();
232
233
  const server = http.createServer(app);
233
234
  const ptySessionsMap = new Map();
234
235
  const PTY_SESSION_TIMEOUT = 30 * 60 * 1000;
236
+ const COMPLETED_PTY_SESSION_TTL = 5 * 60 * 1000;
235
237
  const SHELL_URL_PARSE_BUFFER_LIMIT = 32768;
236
238
  const SHELL_CLI_PROVIDERS = new Set(['claude', 'codex', 'cursor', 'gemini', 'qwen', 'opencode']);
237
239
  import { stripAnsiSequences, normalizeDetectedUrl, extractUrlsFromText, shouldAutoOpenUrlFromOutput } from './utils/url-detection.js';
@@ -319,6 +321,42 @@ function detectProviderTerminalState(provider, output) {
319
321
  terminalStateReason: 'no_known_marker',
320
322
  };
321
323
  }
324
+ function resolveProviderTerminalState(session, provider, output) {
325
+ if (session?.lifecycleState === 'completed' || session?.lifecycleState === 'failed' || session?.lifecycleState === 'exited') {
326
+ const exitCode = typeof session.exitCode === 'number' ? session.exitCode : null;
327
+ const terminalFailed = exitCode !== null ? exitCode !== 0 : Boolean(session.exitSignal);
328
+ return {
329
+ terminalState: terminalFailed ? 'failed' : 'completed',
330
+ lifecycleState: session.lifecycleState,
331
+ isBusy: false,
332
+ terminalFailed,
333
+ exitCode,
334
+ exitSignal: session.exitSignal || null,
335
+ completedAt: session.completedAt || null,
336
+ terminalStateReason: terminalFailed ? 'pty_failed' : 'pty_completed',
337
+ };
338
+ }
339
+ const detected = detectProviderTerminalState(provider, output);
340
+ return {
341
+ ...detected,
342
+ lifecycleState: session?.lifecycleState || 'running',
343
+ terminalFailed: false,
344
+ exitCode: null,
345
+ exitSignal: null,
346
+ completedAt: null,
347
+ };
348
+ }
349
+ function appendPtySessionBuffer(session, data) {
350
+ if (!session)
351
+ return;
352
+ if (session.buffer.length < 5000) {
353
+ session.buffer.push(data);
354
+ }
355
+ else {
356
+ session.buffer.shift();
357
+ session.buffer.push(data);
358
+ }
359
+ }
322
360
  function normalizeShellPermissionMode(value) {
323
361
  return typeof value === 'string' ? value.trim() : '';
324
362
  }
@@ -524,7 +562,7 @@ app.get('/api/shell/sessions/provider-output', authenticateToken, (req, res) =>
524
562
  }
525
563
  const rawOutput = matchedSession.buffer.join('').slice(-maxChars);
526
564
  const output = stripAnsiSequences(rawOutput);
527
- const terminalState = detectProviderTerminalState(provider, output);
565
+ const terminalState = resolveProviderTerminalState(matchedSession, provider, output);
528
566
  res.json({
529
567
  active: true,
530
568
  provider,
@@ -2202,24 +2240,29 @@ function handleShellConnection(ws, request) {
2202
2240
  }
2203
2241
  const existingSession = (isLoginCommand || forceNewSession) ? null : ptySessionsMap.get(ptySessionKey);
2204
2242
  if (existingSession) {
2205
- console.log('♻️ Reconnecting to existing PTY session:', ptySessionKey);
2206
- shellProcess = existingSession.pty;
2207
- clearTimeout(existingSession.timeoutId);
2208
- ws.send(JSON.stringify({
2209
- type: 'output',
2210
- data: `\x1b[36m[Reconnected to existing session]\x1b[0m\r\n`
2211
- }));
2212
- if (existingSession.buffer && existingSession.buffer.length > 0) {
2213
- console.log(`📜 Sending ${existingSession.buffer.length} buffered messages`);
2214
- existingSession.buffer.forEach(bufferedData => {
2215
- ws.send(JSON.stringify({
2216
- type: 'output',
2217
- data: bufferedData
2218
- }));
2219
- });
2243
+ if (!existingSession.pty || existingSession.lifecycleState === 'completed' || existingSession.lifecycleState === 'failed') {
2244
+ ptySessionsMap.delete(ptySessionKey);
2245
+ }
2246
+ else {
2247
+ console.log('♻️ Reconnecting to existing PTY session:', ptySessionKey);
2248
+ shellProcess = existingSession.pty;
2249
+ clearTimeout(existingSession.timeoutId);
2250
+ ws.send(JSON.stringify({
2251
+ type: 'output',
2252
+ data: `\x1b[36m[Reconnected to existing session]\x1b[0m\r\n`
2253
+ }));
2254
+ if (existingSession.buffer && existingSession.buffer.length > 0) {
2255
+ console.log(`📜 Sending ${existingSession.buffer.length} buffered messages`);
2256
+ existingSession.buffer.forEach(bufferedData => {
2257
+ ws.send(JSON.stringify({
2258
+ type: 'output',
2259
+ data: bufferedData
2260
+ }));
2261
+ });
2262
+ }
2263
+ existingSession.ws = ws;
2264
+ return;
2220
2265
  }
2221
- existingSession.ws = ws;
2222
- return;
2223
2266
  }
2224
2267
  console.log('[INFO] Starting shell in:', projectPath);
2225
2268
  console.log('📋 Session info:', hasSession ? `Resume session ${sessionId}` : (isPlainShell ? 'Plain shell mode' : 'New session'));
@@ -2425,6 +2468,10 @@ function handleShellConnection(ws, request) {
2425
2468
  hermesLaunchId,
2426
2469
  provider,
2427
2470
  isPlainShell,
2471
+ lifecycleState: 'running',
2472
+ exitCode: null,
2473
+ exitSignal: null,
2474
+ completedAt: null,
2428
2475
  keepAliveUntilExit: false,
2429
2476
  updatedAt: Date.now(),
2430
2477
  });
@@ -2434,13 +2481,7 @@ function handleShellConnection(ws, request) {
2434
2481
  if (!session)
2435
2482
  return;
2436
2483
  session.updatedAt = Date.now();
2437
- if (session.buffer.length < 5000) {
2438
- session.buffer.push(data);
2439
- }
2440
- else {
2441
- session.buffer.shift();
2442
- session.buffer.push(data);
2443
- }
2484
+ appendPtySessionBuffer(session, data);
2444
2485
  if (session.ws && session.ws.readyState === WebSocket.OPEN) {
2445
2486
  let outputData = data;
2446
2487
  const cleanChunk = stripAnsiSequences(data);
@@ -2481,16 +2522,37 @@ function handleShellConnection(ws, request) {
2481
2522
  shellProcess.onExit((exitCode) => {
2482
2523
  console.log('🔚 Shell process exited with code:', exitCode.exitCode, 'signal:', exitCode.signal);
2483
2524
  const session = ptySessionsMap.get(ptySessionKey);
2525
+ const exitMessage = `\r\n\x1b[33mProcess exited with code ${exitCode.exitCode}${exitCode.signal ? ` (${exitCode.signal})` : ''}\x1b[0m\r\n`;
2526
+ if (session) {
2527
+ session.lifecycleState = exitCode.exitCode === 0 && !exitCode.signal ? 'completed' : 'failed';
2528
+ session.exitCode = typeof exitCode.exitCode === 'number' ? exitCode.exitCode : null;
2529
+ session.exitSignal = exitCode.signal || null;
2530
+ session.completedAt = new Date().toISOString();
2531
+ session.updatedAt = Date.now();
2532
+ session.pty = null;
2533
+ appendPtySessionBuffer(session, exitMessage);
2534
+ }
2484
2535
  if (session && session.ws && session.ws.readyState === WebSocket.OPEN) {
2485
2536
  session.ws.send(JSON.stringify({
2486
2537
  type: 'output',
2487
- data: `\r\n\x1b[33mProcess exited with code ${exitCode.exitCode}${exitCode.signal ? ` (${exitCode.signal})` : ''}\x1b[0m\r\n`
2538
+ data: exitMessage
2488
2539
  }));
2489
2540
  }
2490
2541
  if (session && session.timeoutId) {
2491
2542
  clearTimeout(session.timeoutId);
2492
2543
  }
2493
- ptySessionsMap.delete(ptySessionKey);
2544
+ if (session) {
2545
+ session.ws = null;
2546
+ session.timeoutId = setTimeout(() => {
2547
+ const current = ptySessionsMap.get(ptySessionKey);
2548
+ if (current && current.lifecycleState !== 'running') {
2549
+ ptySessionsMap.delete(ptySessionKey);
2550
+ }
2551
+ }, COMPLETED_PTY_SESSION_TTL);
2552
+ }
2553
+ else {
2554
+ ptySessionsMap.delete(ptySessionKey);
2555
+ }
2494
2556
  shellProcess = null;
2495
2557
  });
2496
2558
  }
@@ -3195,6 +3257,9 @@ async function startServer() {
3195
3257
  catch (err) {
3196
3258
  console.log(`${c.dim('[INFO]')} Port-access helper failed: ${err?.message || err}`);
3197
3259
  }
3260
+ restoreRequestedTunnel({ port: Number(SERVER_PORT) }).catch((err) => {
3261
+ console.warn('[external-access] tunnel restore failed:', err?.message || err);
3262
+ });
3198
3263
  console.log(`${c.tip('[TIP]')} Run "pixcode status" for full configuration details`);
3199
3264
  console.log('');
3200
3265
  // Start watching the projects folder for changes