@pixelbyte-software/pixcode 1.50.4 → 1.50.6
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-DVEXTVKy.js} +3 -3
- package/dist/index.html +1 -1
- package/dist-server/server/index.js +106 -35
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js +2 -0
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.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/configure-pixcode-mcp.mjs +8 -5
- package/scripts/hermes/pixcode-mcp-server.mjs +85 -14
- package/scripts/smoke/hermes-api-install.mjs +2 -1
- package/scripts/smoke/hermes-mcp-pixcode-roundtrip.mjs +10 -3
- package/scripts/smoke/hermes-rest-codex-launch.mjs +4 -2
- package/scripts/smoke/hermes-settings-commands.mjs +71 -6
- package/scripts/smoke/pixcode-workbench-1-48.mjs +3 -1
- package/scripts/smoke/tunnel-persistence.mjs +56 -0
- package/scripts/smoke/vscode-workbench-polish.mjs +8 -2
- package/server/index.js +110 -38
- package/server/modules/orchestration/hermes/hermes.routes.ts +3 -0
- 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-DVEXTVKy.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';
|
|
@@ -287,22 +289,24 @@ function detectProviderTerminalState(provider, output) {
|
|
|
287
289
|
terminalStateReason: 'process_exit',
|
|
288
290
|
};
|
|
289
291
|
}
|
|
290
|
-
const
|
|
292
|
+
const lastWeakBusy = getLastRegexMatchIndex(cleanOutput, /(?:^|\n)\s*[•*]\s*(?:Working|Running|Thinking)\b/giu);
|
|
293
|
+
const lastStrongBusy = Math.max(getLastRegexMatchIndex(cleanOutput, /\bWorking\s*\([^)]*esc to interrupt[^)]*\)/giu), getLastRegexMatchIndex(cleanOutput, /\bmsg=interrupt\b/giu));
|
|
294
|
+
const lastBusy = Math.max(lastWeakBusy, lastStrongBusy);
|
|
291
295
|
if (provider === 'codex') {
|
|
292
296
|
const lastPrompt = Math.max(getLastRegexMatchIndex(cleanOutput, /(?:^|\n)\s*›(?:\s|$)/gu), getLastRegexMatchIndex(cleanOutput, /(?:^|\n)\s*❯(?:\s|$)/gu));
|
|
293
|
-
if (
|
|
294
|
-
const isBusy =
|
|
297
|
+
if (lastPrompt >= 0) {
|
|
298
|
+
const isBusy = lastStrongBusy > lastPrompt;
|
|
295
299
|
return {
|
|
296
300
|
terminalState: isBusy ? 'busy' : 'idle',
|
|
297
301
|
isBusy,
|
|
298
|
-
terminalStateReason: isBusy ? '
|
|
302
|
+
terminalStateReason: isBusy ? 'codex_strong_busy_marker_after_prompt' : 'codex_prompt_after_busy_marker',
|
|
299
303
|
};
|
|
300
304
|
}
|
|
301
|
-
if (
|
|
305
|
+
if (lastBusy >= 0) {
|
|
302
306
|
return {
|
|
303
|
-
terminalState: '
|
|
304
|
-
isBusy:
|
|
305
|
-
terminalStateReason: '
|
|
307
|
+
terminalState: 'busy',
|
|
308
|
+
isBusy: true,
|
|
309
|
+
terminalStateReason: 'codex_busy_marker_without_prompt',
|
|
306
310
|
};
|
|
307
311
|
}
|
|
308
312
|
}
|
|
@@ -319,6 +323,42 @@ function detectProviderTerminalState(provider, output) {
|
|
|
319
323
|
terminalStateReason: 'no_known_marker',
|
|
320
324
|
};
|
|
321
325
|
}
|
|
326
|
+
function resolveProviderTerminalState(session, provider, output) {
|
|
327
|
+
if (session?.lifecycleState === 'completed' || session?.lifecycleState === 'failed' || session?.lifecycleState === 'exited') {
|
|
328
|
+
const exitCode = typeof session.exitCode === 'number' ? session.exitCode : null;
|
|
329
|
+
const terminalFailed = exitCode !== null ? exitCode !== 0 : Boolean(session.exitSignal);
|
|
330
|
+
return {
|
|
331
|
+
terminalState: terminalFailed ? 'failed' : 'completed',
|
|
332
|
+
lifecycleState: session.lifecycleState,
|
|
333
|
+
isBusy: false,
|
|
334
|
+
terminalFailed,
|
|
335
|
+
exitCode,
|
|
336
|
+
exitSignal: session.exitSignal || null,
|
|
337
|
+
completedAt: session.completedAt || null,
|
|
338
|
+
terminalStateReason: terminalFailed ? 'pty_failed' : 'pty_completed',
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
const detected = detectProviderTerminalState(provider, output);
|
|
342
|
+
return {
|
|
343
|
+
...detected,
|
|
344
|
+
lifecycleState: session?.lifecycleState || 'running',
|
|
345
|
+
terminalFailed: false,
|
|
346
|
+
exitCode: null,
|
|
347
|
+
exitSignal: null,
|
|
348
|
+
completedAt: null,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function appendPtySessionBuffer(session, data) {
|
|
352
|
+
if (!session)
|
|
353
|
+
return;
|
|
354
|
+
if (session.buffer.length < 5000) {
|
|
355
|
+
session.buffer.push(data);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
session.buffer.shift();
|
|
359
|
+
session.buffer.push(data);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
322
362
|
function normalizeShellPermissionMode(value) {
|
|
323
363
|
return typeof value === 'string' ? value.trim() : '';
|
|
324
364
|
}
|
|
@@ -524,7 +564,7 @@ app.get('/api/shell/sessions/provider-output', authenticateToken, (req, res) =>
|
|
|
524
564
|
}
|
|
525
565
|
const rawOutput = matchedSession.buffer.join('').slice(-maxChars);
|
|
526
566
|
const output = stripAnsiSequences(rawOutput);
|
|
527
|
-
const terminalState =
|
|
567
|
+
const terminalState = resolveProviderTerminalState(matchedSession, provider, output);
|
|
528
568
|
res.json({
|
|
529
569
|
active: true,
|
|
530
570
|
provider,
|
|
@@ -2202,24 +2242,29 @@ function handleShellConnection(ws, request) {
|
|
|
2202
2242
|
}
|
|
2203
2243
|
const existingSession = (isLoginCommand || forceNewSession) ? null : ptySessionsMap.get(ptySessionKey);
|
|
2204
2244
|
if (existingSession) {
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2245
|
+
if (!existingSession.pty || existingSession.lifecycleState === 'completed' || existingSession.lifecycleState === 'failed') {
|
|
2246
|
+
ptySessionsMap.delete(ptySessionKey);
|
|
2247
|
+
}
|
|
2248
|
+
else {
|
|
2249
|
+
console.log('♻️ Reconnecting to existing PTY session:', ptySessionKey);
|
|
2250
|
+
shellProcess = existingSession.pty;
|
|
2251
|
+
clearTimeout(existingSession.timeoutId);
|
|
2252
|
+
ws.send(JSON.stringify({
|
|
2253
|
+
type: 'output',
|
|
2254
|
+
data: `\x1b[36m[Reconnected to existing session]\x1b[0m\r\n`
|
|
2255
|
+
}));
|
|
2256
|
+
if (existingSession.buffer && existingSession.buffer.length > 0) {
|
|
2257
|
+
console.log(`📜 Sending ${existingSession.buffer.length} buffered messages`);
|
|
2258
|
+
existingSession.buffer.forEach(bufferedData => {
|
|
2259
|
+
ws.send(JSON.stringify({
|
|
2260
|
+
type: 'output',
|
|
2261
|
+
data: bufferedData
|
|
2262
|
+
}));
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
existingSession.ws = ws;
|
|
2266
|
+
return;
|
|
2220
2267
|
}
|
|
2221
|
-
existingSession.ws = ws;
|
|
2222
|
-
return;
|
|
2223
2268
|
}
|
|
2224
2269
|
console.log('[INFO] Starting shell in:', projectPath);
|
|
2225
2270
|
console.log('📋 Session info:', hasSession ? `Resume session ${sessionId}` : (isPlainShell ? 'Plain shell mode' : 'New session'));
|
|
@@ -2425,6 +2470,10 @@ function handleShellConnection(ws, request) {
|
|
|
2425
2470
|
hermesLaunchId,
|
|
2426
2471
|
provider,
|
|
2427
2472
|
isPlainShell,
|
|
2473
|
+
lifecycleState: 'running',
|
|
2474
|
+
exitCode: null,
|
|
2475
|
+
exitSignal: null,
|
|
2476
|
+
completedAt: null,
|
|
2428
2477
|
keepAliveUntilExit: false,
|
|
2429
2478
|
updatedAt: Date.now(),
|
|
2430
2479
|
});
|
|
@@ -2434,13 +2483,7 @@ function handleShellConnection(ws, request) {
|
|
|
2434
2483
|
if (!session)
|
|
2435
2484
|
return;
|
|
2436
2485
|
session.updatedAt = Date.now();
|
|
2437
|
-
|
|
2438
|
-
session.buffer.push(data);
|
|
2439
|
-
}
|
|
2440
|
-
else {
|
|
2441
|
-
session.buffer.shift();
|
|
2442
|
-
session.buffer.push(data);
|
|
2443
|
-
}
|
|
2486
|
+
appendPtySessionBuffer(session, data);
|
|
2444
2487
|
if (session.ws && session.ws.readyState === WebSocket.OPEN) {
|
|
2445
2488
|
let outputData = data;
|
|
2446
2489
|
const cleanChunk = stripAnsiSequences(data);
|
|
@@ -2481,16 +2524,41 @@ function handleShellConnection(ws, request) {
|
|
|
2481
2524
|
shellProcess.onExit((exitCode) => {
|
|
2482
2525
|
console.log('🔚 Shell process exited with code:', exitCode.exitCode, 'signal:', exitCode.signal);
|
|
2483
2526
|
const session = ptySessionsMap.get(ptySessionKey);
|
|
2527
|
+
if (session?.pty && session.pty !== shellProcess) {
|
|
2528
|
+
console.log('↩️ Ignoring stale PTY exit for replacement session:', ptySessionKey);
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
const exitMessage = `\r\n\x1b[33mProcess exited with code ${exitCode.exitCode}${exitCode.signal ? ` (${exitCode.signal})` : ''}\x1b[0m\r\n`;
|
|
2532
|
+
if (session) {
|
|
2533
|
+
session.lifecycleState = exitCode.exitCode === 0 && !exitCode.signal ? 'completed' : 'failed';
|
|
2534
|
+
session.exitCode = typeof exitCode.exitCode === 'number' ? exitCode.exitCode : null;
|
|
2535
|
+
session.exitSignal = exitCode.signal || null;
|
|
2536
|
+
session.completedAt = new Date().toISOString();
|
|
2537
|
+
session.updatedAt = Date.now();
|
|
2538
|
+
session.pty = null;
|
|
2539
|
+
appendPtySessionBuffer(session, exitMessage);
|
|
2540
|
+
}
|
|
2484
2541
|
if (session && session.ws && session.ws.readyState === WebSocket.OPEN) {
|
|
2485
2542
|
session.ws.send(JSON.stringify({
|
|
2486
2543
|
type: 'output',
|
|
2487
|
-
data:
|
|
2544
|
+
data: exitMessage
|
|
2488
2545
|
}));
|
|
2489
2546
|
}
|
|
2490
2547
|
if (session && session.timeoutId) {
|
|
2491
2548
|
clearTimeout(session.timeoutId);
|
|
2492
2549
|
}
|
|
2493
|
-
|
|
2550
|
+
if (session) {
|
|
2551
|
+
session.ws = null;
|
|
2552
|
+
session.timeoutId = setTimeout(() => {
|
|
2553
|
+
const current = ptySessionsMap.get(ptySessionKey);
|
|
2554
|
+
if (current && current.lifecycleState !== 'running') {
|
|
2555
|
+
ptySessionsMap.delete(ptySessionKey);
|
|
2556
|
+
}
|
|
2557
|
+
}, COMPLETED_PTY_SESSION_TTL);
|
|
2558
|
+
}
|
|
2559
|
+
else {
|
|
2560
|
+
ptySessionsMap.delete(ptySessionKey);
|
|
2561
|
+
}
|
|
2494
2562
|
shellProcess = null;
|
|
2495
2563
|
});
|
|
2496
2564
|
}
|
|
@@ -3195,6 +3263,9 @@ async function startServer() {
|
|
|
3195
3263
|
catch (err) {
|
|
3196
3264
|
console.log(`${c.dim('[INFO]')} Port-access helper failed: ${err?.message || err}`);
|
|
3197
3265
|
}
|
|
3266
|
+
restoreRequestedTunnel({ port: Number(SERVER_PORT) }).catch((err) => {
|
|
3267
|
+
console.warn('[external-access] tunnel restore failed:', err?.message || err);
|
|
3268
|
+
});
|
|
3198
3269
|
console.log(`${c.tip('[TIP]')} Run "pixcode status" for full configuration details`);
|
|
3199
3270
|
console.log('');
|
|
3200
3271
|
// Start watching the projects folder for changes
|