@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/assets/{index-DYQjBZrd.js → index-BSxc8Vid.js} +1 -1
- package/dist/index.html +1 -1
- package/dist-server/server/index.js +92 -27
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/routes/network.js +2 -2
- package/dist-server/server/routes/network.js.map +1 -1
- package/dist-server/server/services/external-access.js +193 -11
- package/dist-server/server/services/external-access.js.map +1 -1
- package/package.json +1 -1
- package/scripts/hermes/pixcode-mcp-server.mjs +48 -3
- package/scripts/smoke/hermes-mcp-pixcode-roundtrip.mjs +10 -3
- package/scripts/smoke/hermes-settings-commands.mjs +30 -0
- package/scripts/smoke/tunnel-persistence.mjs +56 -0
- package/server/index.js +95 -29
- package/server/routes/network.js +2 -2
- package/server/services/external-access.js +199 -11
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-
|
|
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 =
|
|
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
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
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
|
-
|
|
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:
|
|
2538
|
+
data: exitMessage
|
|
2488
2539
|
}));
|
|
2489
2540
|
}
|
|
2490
2541
|
if (session && session.timeoutId) {
|
|
2491
2542
|
clearTimeout(session.timeoutId);
|
|
2492
2543
|
}
|
|
2493
|
-
|
|
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
|