@pixelbyte-software/pixcode 1.50.3 → 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-DpdiWohD.js → index-BSxc8Vid.js} +91 -91
- package/dist/index.html +1 -1
- package/dist-server/server/index.js +160 -28
- 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 +148 -14
- package/scripts/smoke/hermes-mcp-pixcode-roundtrip.mjs +69 -3
- package/scripts/smoke/hermes-rest-codex-launch.mjs +14 -0
- package/scripts/smoke/hermes-settings-commands.mjs +60 -0
- package/scripts/smoke/tunnel-persistence.mjs +56 -0
- package/server/index.js +179 -30
- package/server/routes/network.js +2 -2
- package/server/services/external-access.js +199 -11
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
2
5
|
/**
|
|
3
6
|
* External-access service.
|
|
4
7
|
*
|
|
@@ -27,15 +30,58 @@ export const getUpnpState = () => UPNP_UNAVAILABLE;
|
|
|
27
30
|
// stdout. We keep a single live tunnel per process — starting a new one
|
|
28
31
|
// stops the previous one to avoid dangling child processes.
|
|
29
32
|
// ============================================================================
|
|
33
|
+
export const TUNNEL_PERSISTENCE_PATH = process.env.PIXCODE_TUNNEL_STATE_PATH
|
|
34
|
+
|| path.join(os.homedir(), '.pixcode', 'external-access.json');
|
|
30
35
|
let tunnelProc = null;
|
|
36
|
+
let suppressNextTunnelRestore = false;
|
|
37
|
+
let restoreTimer = null;
|
|
38
|
+
let restoreInFlight = null;
|
|
31
39
|
let tunnelState = {
|
|
32
40
|
running: false,
|
|
33
41
|
binary: null, // 'cloudflared' | 'ngrok'
|
|
34
42
|
url: null,
|
|
35
43
|
error: null,
|
|
36
44
|
installHint: null,
|
|
45
|
+
desired: false,
|
|
46
|
+
restoring: false,
|
|
37
47
|
log: [],
|
|
38
48
|
};
|
|
49
|
+
const DEFAULT_TUNNEL_PREFERENCE = Object.freeze({
|
|
50
|
+
desired: false,
|
|
51
|
+
port: null,
|
|
52
|
+
provider: null,
|
|
53
|
+
lastUrl: null,
|
|
54
|
+
lastStartedAt: null,
|
|
55
|
+
lastStoppedAt: null,
|
|
56
|
+
updatedAt: null,
|
|
57
|
+
});
|
|
58
|
+
const readTunnelPreference = async () => {
|
|
59
|
+
try {
|
|
60
|
+
const raw = await fs.readFile(TUNNEL_PERSISTENCE_PATH, 'utf8');
|
|
61
|
+
const parsed = JSON.parse(raw);
|
|
62
|
+
return {
|
|
63
|
+
...DEFAULT_TUNNEL_PREFERENCE,
|
|
64
|
+
...(parsed && typeof parsed === 'object' ? parsed : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error?.code !== 'ENOENT') {
|
|
69
|
+
console.warn('[external-access] Failed to read tunnel preference:', error?.message || error);
|
|
70
|
+
}
|
|
71
|
+
return { ...DEFAULT_TUNNEL_PREFERENCE };
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
export const persistTunnelPreference = async (patch) => {
|
|
75
|
+
const current = await readTunnelPreference();
|
|
76
|
+
const next = {
|
|
77
|
+
...current,
|
|
78
|
+
...patch,
|
|
79
|
+
updatedAt: new Date().toISOString(),
|
|
80
|
+
};
|
|
81
|
+
await fs.mkdir(path.dirname(TUNNEL_PERSISTENCE_PATH), { recursive: true });
|
|
82
|
+
await fs.writeFile(TUNNEL_PERSISTENCE_PATH, `${JSON.stringify(next, null, 2)}\n`, 'utf8');
|
|
83
|
+
return next;
|
|
84
|
+
};
|
|
39
85
|
const appendLog = (line) => {
|
|
40
86
|
// Tunnels can be noisy. Cap the tail we retain so a long-running tunnel
|
|
41
87
|
// doesn't grow the log into an OOM risk.
|
|
@@ -89,16 +135,33 @@ const extractUrl = (binary, text) => {
|
|
|
89
135
|
return text.match(ngrokUrlRegex)?.[0] ?? null;
|
|
90
136
|
return null;
|
|
91
137
|
};
|
|
92
|
-
export const startTunnel = async ({ port }) => {
|
|
138
|
+
export const startTunnel = async ({ port, persistPreference = false, restoring = false } = {}) => {
|
|
93
139
|
if (tunnelProc) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
140
|
+
if (persistPreference) {
|
|
141
|
+
await persistTunnelPreference({
|
|
142
|
+
desired: true,
|
|
143
|
+
port,
|
|
144
|
+
provider: tunnelState.binary,
|
|
145
|
+
lastUrl: tunnelState.url,
|
|
146
|
+
});
|
|
147
|
+
tunnelState = { ...tunnelState, desired: true, restoring: false };
|
|
148
|
+
}
|
|
149
|
+
return tunnelState;
|
|
97
150
|
}
|
|
151
|
+
suppressNextTunnelRestore = false;
|
|
98
152
|
const binary = await detectBinary();
|
|
99
153
|
if (!binary) {
|
|
100
154
|
const installHint = createTunnelInstallHint();
|
|
101
|
-
tunnelState = {
|
|
155
|
+
tunnelState = {
|
|
156
|
+
running: false,
|
|
157
|
+
binary: null,
|
|
158
|
+
url: null,
|
|
159
|
+
error: 'No tunnel binary found',
|
|
160
|
+
installHint,
|
|
161
|
+
desired: Boolean(persistPreference || restoring),
|
|
162
|
+
restoring,
|
|
163
|
+
log: [],
|
|
164
|
+
};
|
|
102
165
|
const err = new Error('No tunnel binary found (tried cloudflared, ngrok)');
|
|
103
166
|
err.code = 'ENOENT_TUNNEL';
|
|
104
167
|
err.installHint = installHint;
|
|
@@ -107,7 +170,16 @@ export const startTunnel = async ({ port }) => {
|
|
|
107
170
|
const args = buildTunnelArgs(binary, port);
|
|
108
171
|
const child = spawn(binary, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
109
172
|
tunnelProc = child;
|
|
110
|
-
tunnelState = {
|
|
173
|
+
tunnelState = {
|
|
174
|
+
running: true,
|
|
175
|
+
binary,
|
|
176
|
+
url: null,
|
|
177
|
+
error: null,
|
|
178
|
+
installHint: null,
|
|
179
|
+
desired: Boolean(persistPreference || restoring),
|
|
180
|
+
restoring,
|
|
181
|
+
log: [],
|
|
182
|
+
};
|
|
111
183
|
const handleChunk = (chunk) => {
|
|
112
184
|
const text = chunk.toString();
|
|
113
185
|
text.split(/\r?\n/).filter(Boolean).forEach(appendLog);
|
|
@@ -127,35 +199,86 @@ export const startTunnel = async ({ port }) => {
|
|
|
127
199
|
url: null,
|
|
128
200
|
error: code === 0 ? null : `Tunnel exited with code ${code}`,
|
|
129
201
|
installHint: null,
|
|
202
|
+
desired: tunnelState.desired,
|
|
203
|
+
restoring: false,
|
|
130
204
|
log: tunnelState.log,
|
|
131
205
|
};
|
|
206
|
+
if (suppressNextTunnelRestore) {
|
|
207
|
+
suppressNextTunnelRestore = false;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
void readTunnelPreference().then((preference) => {
|
|
211
|
+
if (!preference.desired)
|
|
212
|
+
return;
|
|
213
|
+
if (restoreTimer)
|
|
214
|
+
clearTimeout(restoreTimer);
|
|
215
|
+
restoreTimer = setTimeout(() => {
|
|
216
|
+
restoreTimer = null;
|
|
217
|
+
restoreRequestedTunnel({ port: Number(preference.port || port) || port }).catch((error) => {
|
|
218
|
+
console.warn('[external-access] Tunnel restore failed after exit:', error?.message || error);
|
|
219
|
+
});
|
|
220
|
+
}, 3000);
|
|
221
|
+
});
|
|
132
222
|
});
|
|
133
223
|
// Wait up to 15s for the public URL to appear in the log. We don't block
|
|
134
224
|
// indefinitely — if the binary is hanging on login/auth, the UI should see
|
|
135
225
|
// a clear failure instead of a spinner that never resolves.
|
|
136
226
|
const start = Date.now();
|
|
137
227
|
while (Date.now() - start < 15000) {
|
|
138
|
-
if (tunnelState.url)
|
|
228
|
+
if (tunnelState.url) {
|
|
229
|
+
if (persistPreference || restoring) {
|
|
230
|
+
await persistTunnelPreference({
|
|
231
|
+
desired: true,
|
|
232
|
+
port,
|
|
233
|
+
provider: binary,
|
|
234
|
+
lastUrl: tunnelState.url,
|
|
235
|
+
lastStartedAt: new Date().toISOString(),
|
|
236
|
+
});
|
|
237
|
+
tunnelState = { ...tunnelState, desired: true, restoring: false };
|
|
238
|
+
}
|
|
139
239
|
return tunnelState;
|
|
240
|
+
}
|
|
140
241
|
if (!tunnelProc)
|
|
141
242
|
break; // process died early
|
|
142
243
|
await new Promise((r) => setTimeout(r, 250));
|
|
143
244
|
}
|
|
144
245
|
if (!tunnelState.url) {
|
|
145
246
|
// If we never captured a URL, kill the child so we don't leak it.
|
|
247
|
+
suppressNextTunnelRestore = true;
|
|
146
248
|
try {
|
|
147
249
|
child.kill();
|
|
148
250
|
}
|
|
149
251
|
catch { /* ignore */ }
|
|
150
252
|
tunnelProc = null;
|
|
151
|
-
tunnelState = { ...tunnelState, running: false, error: 'Tunnel did not report a public URL', installHint: null };
|
|
253
|
+
tunnelState = { ...tunnelState, running: false, error: 'Tunnel did not report a public URL', installHint: null, restoring: false };
|
|
152
254
|
throw new Error(tunnelState.error);
|
|
153
255
|
}
|
|
154
256
|
return tunnelState;
|
|
155
257
|
};
|
|
156
|
-
export const stopTunnel = async () => {
|
|
258
|
+
export const stopTunnel = async ({ persistPreference = true } = {}) => {
|
|
259
|
+
suppressNextTunnelRestore = Boolean(tunnelProc);
|
|
260
|
+
if (restoreTimer) {
|
|
261
|
+
clearTimeout(restoreTimer);
|
|
262
|
+
restoreTimer = null;
|
|
263
|
+
}
|
|
264
|
+
if (persistPreference) {
|
|
265
|
+
await persistTunnelPreference({
|
|
266
|
+
desired: false,
|
|
267
|
+
lastStoppedAt: new Date().toISOString(),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
157
270
|
if (!tunnelProc) {
|
|
158
|
-
|
|
271
|
+
suppressNextTunnelRestore = false;
|
|
272
|
+
tunnelState = {
|
|
273
|
+
running: false,
|
|
274
|
+
binary: null,
|
|
275
|
+
url: null,
|
|
276
|
+
error: null,
|
|
277
|
+
installHint: null,
|
|
278
|
+
desired: false,
|
|
279
|
+
restoring: false,
|
|
280
|
+
log: [],
|
|
281
|
+
};
|
|
159
282
|
return tunnelState;
|
|
160
283
|
}
|
|
161
284
|
try {
|
|
@@ -165,9 +288,68 @@ export const stopTunnel = async () => {
|
|
|
165
288
|
// already dead
|
|
166
289
|
}
|
|
167
290
|
tunnelProc = null;
|
|
168
|
-
tunnelState = {
|
|
291
|
+
tunnelState = {
|
|
292
|
+
running: false,
|
|
293
|
+
binary: null,
|
|
294
|
+
url: null,
|
|
295
|
+
error: null,
|
|
296
|
+
installHint: null,
|
|
297
|
+
desired: false,
|
|
298
|
+
restoring: false,
|
|
299
|
+
log: [],
|
|
300
|
+
};
|
|
169
301
|
return tunnelState;
|
|
170
302
|
};
|
|
303
|
+
export const restoreRequestedTunnel = async ({ port } = {}) => {
|
|
304
|
+
if (restoreInFlight)
|
|
305
|
+
return restoreInFlight;
|
|
306
|
+
restoreInFlight = (async () => {
|
|
307
|
+
const preference = await readTunnelPreference();
|
|
308
|
+
if (!preference.desired) {
|
|
309
|
+
tunnelState = { ...tunnelState, desired: false, restoring: false };
|
|
310
|
+
return tunnelState;
|
|
311
|
+
}
|
|
312
|
+
if (tunnelProc) {
|
|
313
|
+
tunnelState = { ...tunnelState, desired: true, restoring: false };
|
|
314
|
+
return tunnelState;
|
|
315
|
+
}
|
|
316
|
+
const restorePort = Number(port || preference.port);
|
|
317
|
+
if (!Number.isFinite(restorePort) || restorePort <= 0) {
|
|
318
|
+
tunnelState = {
|
|
319
|
+
...tunnelState,
|
|
320
|
+
running: false,
|
|
321
|
+
desired: true,
|
|
322
|
+
restoring: false,
|
|
323
|
+
error: 'Tunnel restore skipped: no valid server port',
|
|
324
|
+
};
|
|
325
|
+
return tunnelState;
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
return await startTunnel({
|
|
329
|
+
port: restorePort,
|
|
330
|
+
persistPreference: true,
|
|
331
|
+
restoring: true,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
tunnelState = {
|
|
336
|
+
...tunnelState,
|
|
337
|
+
running: false,
|
|
338
|
+
desired: true,
|
|
339
|
+
restoring: false,
|
|
340
|
+
error: `Tunnel restore failed: ${error?.message || error}`,
|
|
341
|
+
installHint: error?.installHint || tunnelState.installHint || null,
|
|
342
|
+
};
|
|
343
|
+
return tunnelState;
|
|
344
|
+
}
|
|
345
|
+
})();
|
|
346
|
+
try {
|
|
347
|
+
return await restoreInFlight;
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
restoreInFlight = null;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
171
353
|
export const getTunnelState = () => tunnelState;
|
|
172
354
|
// Explicit cleanup so the server process can shut down without leaking the
|
|
173
355
|
// child tunnel process.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-access.js","sourceRoot":"","sources":["../../../server/services/external-access.js"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"external-access.js","sourceRoot":"","sources":["../../../server/services/external-access.js"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixelbyte-software/pixcode",
|
|
3
|
-
"version": "1.50.
|
|
3
|
+
"version": "1.50.5",
|
|
4
4
|
"description": "Self-hosted AI coding agent control room for Claude Code, Cursor CLI, OpenAI Codex, Gemini CLI, Qwen Code, and OpenCode with chat, files, shell, Git, orchestration, API keys, Telegram, MCP, plugins, themes, and desktop/server deployment.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist-server/server/index.js",
|
|
@@ -7,6 +7,7 @@ const baseUrl = (process.env.PIXCODE_BASE_URL || '').replace(/\/$/, '');
|
|
|
7
7
|
const apiKey = process.env.PIXCODE_API_KEY || '';
|
|
8
8
|
const appRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
9
9
|
const mcpServerPath = path.join(appRoot, 'scripts', 'hermes', 'pixcode-mcp-server.mjs');
|
|
10
|
+
const READBACK_IDLE_STABLE_MS = 2500;
|
|
10
11
|
|
|
11
12
|
const tools = [
|
|
12
13
|
{
|
|
@@ -35,7 +36,7 @@ const tools = [
|
|
|
35
36
|
},
|
|
36
37
|
{
|
|
37
38
|
name: 'pixcode_open_cli_terminal',
|
|
38
|
-
description: 'Use this instead of Hermes shell/proc/skill execution whenever the user asks to open Codex, Claude, Cursor, Gemini, Qwen, or OpenCode inside Pixcode. It asks the open Pixcode workbench to open a visible provider CLI terminal in the project and submit startup input there. Do not run a parallel Hermes codex/claude/proc command for the same request. For multi-step, piece-by-piece, or long-running work, put the full user instruction in startupInput so the provider CLI does the work visibly inside Pixcode.
|
|
39
|
+
description: 'Use this instead of Hermes shell/proc/skill execution whenever the user asks to open Codex, Claude, Cursor, Gemini, Qwen, or OpenCode inside Pixcode. It asks the open Pixcode workbench to open a visible provider CLI terminal in the project and submit startup input there. Do not run a parallel Hermes codex/claude/proc command for the same request. For multi-step, piece-by-piece, or long-running work, put the full user instruction in startupInput so the provider CLI does the work visibly inside Pixcode. When startupInput is present, Pixcode waits for the terminal to become idle before returning readback by default; never treat the first working frame as final output.',
|
|
39
40
|
inputSchema: {
|
|
40
41
|
type: 'object',
|
|
41
42
|
properties: {
|
|
@@ -66,7 +67,15 @@ const tools = [
|
|
|
66
67
|
},
|
|
67
68
|
waitForOutputMs: {
|
|
68
69
|
type: 'number',
|
|
69
|
-
description: 'Optional milliseconds to wait
|
|
70
|
+
description: 'Optional milliseconds to wait for recent terminal output. Pixcode keeps polling while terminalState is busy, so use a large value when the user asks for the final provider answer.',
|
|
71
|
+
},
|
|
72
|
+
waitForCompletionMs: {
|
|
73
|
+
type: 'number',
|
|
74
|
+
description: 'Optional explicit milliseconds to wait for the visible provider CLI to return to an idle prompt before reporting output. Overrides waitForOutputMs.',
|
|
75
|
+
},
|
|
76
|
+
launchId: {
|
|
77
|
+
type: 'number',
|
|
78
|
+
description: 'Optional Pixcode terminal launch id. Use the id returned by pixcode_open_cli_terminal when reading one specific visible terminal.',
|
|
70
79
|
},
|
|
71
80
|
},
|
|
72
81
|
required: ['provider'],
|
|
@@ -186,15 +195,125 @@ async function readProviderStatus(provider) {
|
|
|
186
195
|
return body?.data ?? body;
|
|
187
196
|
}
|
|
188
197
|
|
|
189
|
-
async function readProviderTerminalOutput(provider, projectPath, maxChars) {
|
|
198
|
+
async function readProviderTerminalOutput(provider, projectPath, maxChars, launchId = null) {
|
|
190
199
|
const params = new URLSearchParams({
|
|
191
200
|
provider,
|
|
192
201
|
maxChars: String(maxChars || 12000),
|
|
193
202
|
});
|
|
194
203
|
if (projectPath) params.set('projectPath', projectPath);
|
|
204
|
+
if (launchId) params.set('launchId', String(launchId));
|
|
195
205
|
return pixcodeFetch(`/api/shell/sessions/provider-output?${params.toString()}`);
|
|
196
206
|
}
|
|
197
207
|
|
|
208
|
+
function getLastMatchIndex(text, pattern) {
|
|
209
|
+
let lastIndex = -1;
|
|
210
|
+
for (const match of text.matchAll(pattern)) {
|
|
211
|
+
lastIndex = match.index ?? lastIndex;
|
|
212
|
+
}
|
|
213
|
+
return lastIndex;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function inferTerminalState(provider, terminalOutput) {
|
|
217
|
+
if (!terminalOutput) return 'unknown';
|
|
218
|
+
if (typeof terminalOutput.terminalState === 'string') return terminalOutput.terminalState;
|
|
219
|
+
if (typeof terminalOutput.isBusy === 'boolean') return terminalOutput.isBusy ? 'busy' : 'idle';
|
|
220
|
+
if (terminalOutput.active === false) return terminalOutput.output ? 'idle' : 'unknown';
|
|
221
|
+
|
|
222
|
+
const output = String(terminalOutput.output || '');
|
|
223
|
+
if (!output.trim()) return 'unknown';
|
|
224
|
+
if (/Process exited with code/iu.test(output)) return 'idle';
|
|
225
|
+
|
|
226
|
+
const lastBusy = Math.max(
|
|
227
|
+
getLastMatchIndex(output, /(?:^|\n)\s*[•*]\s*(?:Working|Running|Thinking)\b/giu),
|
|
228
|
+
getLastMatchIndex(output, /\bWorking\s*\([^)]*esc to interrupt[^)]*\)/giu),
|
|
229
|
+
getLastMatchIndex(output, /\bmsg=interrupt\b/giu),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
if (provider === 'codex') {
|
|
233
|
+
const lastPrompt = Math.max(
|
|
234
|
+
getLastMatchIndex(output, /(?:^|\n)\s*›(?:\s|$)/gu),
|
|
235
|
+
getLastMatchIndex(output, /(?:^|\n)\s*❯(?:\s|$)/gu),
|
|
236
|
+
);
|
|
237
|
+
if (lastBusy >= 0) return lastPrompt > lastBusy ? 'idle' : 'busy';
|
|
238
|
+
return lastPrompt >= 0 && /(?:Initialized|Baseline check passed|I did not modify files|Use \/skills)/iu.test(output)
|
|
239
|
+
? 'idle'
|
|
240
|
+
: 'unknown';
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (lastBusy >= 0) return 'busy';
|
|
244
|
+
return 'unknown';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function isTerminalReadbackFinal(provider, terminalOutput) {
|
|
248
|
+
const terminalState = inferTerminalState(provider, terminalOutput);
|
|
249
|
+
return terminalState === 'idle' || terminalState === 'completed' || terminalState === 'exited' || terminalState === 'failed';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function isTerminalReadbackHardFinal(provider, terminalOutput) {
|
|
253
|
+
const terminalState = inferTerminalState(provider, terminalOutput);
|
|
254
|
+
return terminalState === 'completed' || terminalState === 'exited' || terminalState === 'failed' || Boolean(terminalOutput?.terminalFailed);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function getReadbackFingerprint(terminalOutput) {
|
|
258
|
+
return [
|
|
259
|
+
terminalOutput?.terminalState || '',
|
|
260
|
+
terminalOutput?.lifecycleState || '',
|
|
261
|
+
terminalOutput?.exitCode ?? '',
|
|
262
|
+
terminalOutput?.exitSignal || '',
|
|
263
|
+
String(terminalOutput?.output || '').slice(-12000),
|
|
264
|
+
].join('\n---pixcode-readback---\n');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function waitForProviderTerminalOutput(provider, projectPath, waitMs, launchId = null) {
|
|
268
|
+
const startedAt = Date.now();
|
|
269
|
+
let latestOutput = null;
|
|
270
|
+
let stableFingerprint = null;
|
|
271
|
+
let stableSince = 0;
|
|
272
|
+
let stableFinal = false;
|
|
273
|
+
do {
|
|
274
|
+
const elapsed = Date.now() - startedAt;
|
|
275
|
+
const remaining = Math.max(0, waitMs - elapsed);
|
|
276
|
+
await sleep(Math.min(1000, Math.max(250, remaining)));
|
|
277
|
+
latestOutput = await readProviderTerminalOutput(provider, projectPath, 12000, launchId).catch((error) => ({
|
|
278
|
+
active: false,
|
|
279
|
+
terminalState: 'unknown',
|
|
280
|
+
error: error instanceof Error ? error.message : String(error),
|
|
281
|
+
}));
|
|
282
|
+
|
|
283
|
+
if (latestOutput?.output && isTerminalReadbackFinal(provider, latestOutput)) {
|
|
284
|
+
if (isTerminalReadbackHardFinal(provider, latestOutput)) {
|
|
285
|
+
stableFinal = true;
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const fingerprint = getReadbackFingerprint(latestOutput);
|
|
290
|
+
if (fingerprint !== stableFingerprint) {
|
|
291
|
+
stableFingerprint = fingerprint;
|
|
292
|
+
stableSince = Date.now();
|
|
293
|
+
}
|
|
294
|
+
if (Date.now() - stableSince >= READBACK_IDLE_STABLE_MS) {
|
|
295
|
+
stableFinal = true;
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
stableFingerprint = null;
|
|
300
|
+
stableSince = 0;
|
|
301
|
+
}
|
|
302
|
+
} while (Date.now() - startedAt < waitMs);
|
|
303
|
+
|
|
304
|
+
if (latestOutput && !latestOutput.terminalState) {
|
|
305
|
+
latestOutput.terminalState = inferTerminalState(provider, latestOutput);
|
|
306
|
+
}
|
|
307
|
+
if (latestOutput && typeof latestOutput.isBusy !== 'boolean') {
|
|
308
|
+
latestOutput.isBusy = latestOutput.terminalState === 'busy';
|
|
309
|
+
}
|
|
310
|
+
if (latestOutput) {
|
|
311
|
+
latestOutput.readbackStable = stableFinal;
|
|
312
|
+
latestOutput.terminalOutputFinal = stableFinal;
|
|
313
|
+
}
|
|
314
|
+
return latestOutput;
|
|
315
|
+
}
|
|
316
|
+
|
|
198
317
|
function isLegacyPromptLikelyStartupInput(prompt) {
|
|
199
318
|
if (!prompt || prompt.length > 160 || prompt.includes('\n')) return false;
|
|
200
319
|
if (/^[/:!@]/u.test(prompt)) return true;
|
|
@@ -287,26 +406,32 @@ async function callTool(name, args = {}) {
|
|
|
287
406
|
permissionMode,
|
|
288
407
|
}),
|
|
289
408
|
});
|
|
409
|
+
const launchId = Number(body?.event?.id || body?.id || 0) || null;
|
|
290
410
|
let terminalOutput = null;
|
|
291
|
-
const
|
|
411
|
+
const defaultWaitMs = startupInput ? 180000 : 0;
|
|
412
|
+
const requestedWaitMs = Number(args.waitForCompletionMs ?? args.waitForOutputMs ?? defaultWaitMs);
|
|
413
|
+
const waitForOutputMs = Math.min(600000, Math.max(0, requestedWaitMs));
|
|
292
414
|
if (waitForOutputMs > 0) {
|
|
293
|
-
|
|
294
|
-
do {
|
|
295
|
-
await sleep(Math.min(1000, Math.max(250, waitForOutputMs)));
|
|
296
|
-
terminalOutput = await readProviderTerminalOutput(provider, projectPath, 12000).catch((error) => ({
|
|
297
|
-
active: false,
|
|
298
|
-
error: error instanceof Error ? error.message : String(error),
|
|
299
|
-
}));
|
|
300
|
-
if (terminalOutput?.active && terminalOutput?.output) break;
|
|
301
|
-
} while (Date.now() - startedAt < waitForOutputMs);
|
|
415
|
+
terminalOutput = await waitForProviderTerminalOutput(provider, projectPath, waitForOutputMs, launchId);
|
|
302
416
|
}
|
|
417
|
+
const terminalOutputFinal = terminalOutput
|
|
418
|
+
? Boolean(terminalOutput.terminalOutputFinal ?? isTerminalReadbackFinal(provider, terminalOutput))
|
|
419
|
+
: false;
|
|
303
420
|
return textResult(JSON.stringify({
|
|
304
421
|
launched: true,
|
|
422
|
+
launchId,
|
|
305
423
|
pixcodeMcpConfigured: mcpConfigured,
|
|
306
424
|
pixcodeMcpError: mcpError,
|
|
307
425
|
event: body?.event ?? body,
|
|
308
426
|
permissionBypass: bypassPermissions,
|
|
309
427
|
status,
|
|
428
|
+
terminalOutputFinal,
|
|
429
|
+
terminalFailed: Boolean(terminalOutput?.terminalFailed),
|
|
430
|
+
message: terminalOutput && !terminalOutputFinal
|
|
431
|
+
? 'Provider terminal is still running or not at an idle prompt yet. Do not summarize this as final output; call pixcode_read_cli_terminal with launchId later.'
|
|
432
|
+
: terminalOutput?.terminalFailed
|
|
433
|
+
? 'Provider terminal exited with a failure. Do not report this as successful; tell the user the visible CLI failed and include the exit code/output.'
|
|
434
|
+
: undefined,
|
|
310
435
|
terminalOutput,
|
|
311
436
|
}, null, 2));
|
|
312
437
|
}
|
|
@@ -317,7 +442,16 @@ async function callTool(name, args = {}) {
|
|
|
317
442
|
? args.projectPath.trim()
|
|
318
443
|
: null;
|
|
319
444
|
const maxChars = Math.min(20000, Math.max(1000, Number(args.maxChars || 12000)));
|
|
320
|
-
const
|
|
445
|
+
const launchId = Number(args.launchId || 0) || null;
|
|
446
|
+
const body = await readProviderTerminalOutput(provider, projectPath, maxChars, launchId);
|
|
447
|
+
if (body && !body.terminalState) {
|
|
448
|
+
body.terminalState = inferTerminalState(provider, body);
|
|
449
|
+
}
|
|
450
|
+
if (body && typeof body.isBusy !== 'boolean') {
|
|
451
|
+
body.isBusy = body.terminalState === 'busy';
|
|
452
|
+
}
|
|
453
|
+
body.terminalOutputFinal = isTerminalReadbackFinal(provider, body);
|
|
454
|
+
body.terminalFailed = Boolean(body.terminalFailed);
|
|
321
455
|
return textResult(JSON.stringify(body, null, 2));
|
|
322
456
|
}
|
|
323
457
|
|
|
@@ -9,6 +9,7 @@ const apiKey = 'px_smoke_key';
|
|
|
9
9
|
const seen = [];
|
|
10
10
|
const providerMcpUpserts = [];
|
|
11
11
|
const terminalLaunches = [];
|
|
12
|
+
const providerOutputReads = [];
|
|
12
13
|
|
|
13
14
|
function readJson(req) {
|
|
14
15
|
return new Promise((resolve, reject) => {
|
|
@@ -90,7 +91,7 @@ const server = createServer(async (req, res) => {
|
|
|
90
91
|
res.statusCode = 201;
|
|
91
92
|
res.end(JSON.stringify({
|
|
92
93
|
event: {
|
|
93
|
-
id:
|
|
94
|
+
id: terminalLaunches.length,
|
|
94
95
|
provider: body.provider,
|
|
95
96
|
projectPath: body.projectPath,
|
|
96
97
|
prompt: body.prompt,
|
|
@@ -100,6 +101,46 @@ const server = createServer(async (req, res) => {
|
|
|
100
101
|
return;
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
if (req.method === 'GET' && url.pathname === '/api/shell/sessions/provider-output') {
|
|
105
|
+
providerOutputReads.push({
|
|
106
|
+
provider: url.searchParams.get('provider'),
|
|
107
|
+
projectPath: url.searchParams.get('projectPath'),
|
|
108
|
+
launchId: url.searchParams.get('launchId'),
|
|
109
|
+
});
|
|
110
|
+
const outputs = [
|
|
111
|
+
{
|
|
112
|
+
active: true,
|
|
113
|
+
provider: 'codex',
|
|
114
|
+
projectPath: '/root/pixcode',
|
|
115
|
+
terminalState: 'busy',
|
|
116
|
+
output: 'OpenAI Codex\n› /init\n\n• Working (10s • esc to interrupt)\n',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
active: true,
|
|
120
|
+
provider: 'codex',
|
|
121
|
+
projectPath: '/root/pixcode',
|
|
122
|
+
terminalState: 'busy',
|
|
123
|
+
output: 'OpenAI Codex\n› /init\n\n• Ran npm test\n• Working (30s • esc to interrupt)\n',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
active: true,
|
|
127
|
+
provider: 'codex',
|
|
128
|
+
projectPath: '/root/pixcode',
|
|
129
|
+
terminalState: 'idle',
|
|
130
|
+
output: 'OpenAI Codex\n› /init\n\n• Ran npm test\n\n› Implement {feature}\n',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
active: true,
|
|
134
|
+
provider: 'codex',
|
|
135
|
+
projectPath: '/root/pixcode',
|
|
136
|
+
terminalState: 'idle',
|
|
137
|
+
output: 'Baseline check passed: npm test reports 195 passing, 0 failing.\n\n› Use /skills to list available skills\n',
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
res.end(JSON.stringify(outputs[Math.min(providerOutputReads.length - 1, outputs.length - 1)]));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
103
144
|
res.statusCode = 404;
|
|
104
145
|
res.end(JSON.stringify({ error: url.pathname }));
|
|
105
146
|
});
|
|
@@ -121,7 +162,7 @@ function callMcp(child, method, params = undefined) {
|
|
|
121
162
|
return new Promise((resolve, reject) => {
|
|
122
163
|
const timeout = setTimeout(() => {
|
|
123
164
|
reject(new Error(`MCP call timed out: ${method}`));
|
|
124
|
-
},
|
|
165
|
+
}, 10000);
|
|
125
166
|
const onLine = (line) => {
|
|
126
167
|
if (!line.trim()) return;
|
|
127
168
|
const message = JSON.parse(line);
|
|
@@ -190,13 +231,38 @@ try {
|
|
|
190
231
|
assert.equal(providerMcpUpserts.length, 1, 'Codex launch should upsert a project-scoped Pixcode MCP server');
|
|
191
232
|
assert.equal(providerMcpUpserts[0].name, 'pixcode', 'Provider MCP server should be named pixcode');
|
|
192
233
|
|
|
234
|
+
providerOutputReads.length = 0;
|
|
235
|
+
const launchWithReadback = await callMcp(child, 'tools/call', {
|
|
236
|
+
name: 'pixcode_open_cli_terminal',
|
|
237
|
+
arguments: {
|
|
238
|
+
provider: 'codex',
|
|
239
|
+
projectPath: '/root/pixcode',
|
|
240
|
+
prompt: 'read final output',
|
|
241
|
+
startupInput: '/init',
|
|
242
|
+
waitForOutputMs: 7000,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
assert(
|
|
246
|
+
providerOutputReads.length >= 5,
|
|
247
|
+
`readback should keep polling until the provider terminal is idle, reads=${providerOutputReads.length}`,
|
|
248
|
+
);
|
|
249
|
+
assert.match(
|
|
250
|
+
launchWithReadback.content[0].text,
|
|
251
|
+
/195 passing, 0 failing/,
|
|
252
|
+
'readback should return the final Codex output instead of the first working frame',
|
|
253
|
+
);
|
|
254
|
+
assert(
|
|
255
|
+
providerOutputReads.every((read) => read.launchId === '2'),
|
|
256
|
+
`readback should be tied to the Hermes terminal launch id, reads=${JSON.stringify(providerOutputReads)}`,
|
|
257
|
+
);
|
|
258
|
+
|
|
193
259
|
const blockedLaunch = await callMcp(child, 'tools/call', {
|
|
194
260
|
name: 'pixcode_open_cli_terminal',
|
|
195
261
|
arguments: { provider: 'qwen', projectPath: '/root/pixcode', prompt: 'smoke' },
|
|
196
262
|
});
|
|
197
263
|
assert.match(blockedLaunch.content[0].text, /"launched": false/, 'uninstalled providers should not create terminal launches');
|
|
198
264
|
assert.match(blockedLaunch.content[0].text, /"reason": "not_installed"/, 'uninstalled provider response should explain the block');
|
|
199
|
-
assert.equal(terminalLaunches.length,
|
|
265
|
+
assert.equal(terminalLaunches.length, 2, 'Only installed Codex provider launches should be created');
|
|
200
266
|
|
|
201
267
|
assert(seen.every((entry) => entry.auth === `Bearer ${apiKey}`), 'all MCP calls should use the Pixcode bearer key');
|
|
202
268
|
console.log('hermes MCP Pixcode roundtrip smoke passed');
|