@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.
@@ -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
- // Already running — tell the caller to stop it first rather than silently
95
- // replacing, which would orphan the old child and lie about state.
96
- throw new Error('Tunnel already running; stop it first');
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 = { running: false, binary: null, url: null, error: 'No tunnel binary found', installHint, log: [] };
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 = { running: true, binary, url: null, error: null, installHint: null, log: [] };
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
- tunnelState = { running: false, binary: null, url: null, error: null, installHint: null, log: [] };
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 = { running: false, binary: null, url: null, error: null, installHint: null, log: [] };
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.4",
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
  {
@@ -245,12 +246,30 @@ function inferTerminalState(provider, terminalOutput) {
245
246
 
246
247
  function isTerminalReadbackFinal(provider, terminalOutput) {
247
248
  const terminalState = inferTerminalState(provider, terminalOutput);
248
- return terminalState === 'idle' || terminalState === 'completed' || terminalState === 'exited';
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');
249
265
  }
250
266
 
251
267
  async function waitForProviderTerminalOutput(provider, projectPath, waitMs, launchId = null) {
252
268
  const startedAt = Date.now();
253
269
  let latestOutput = null;
270
+ let stableFingerprint = null;
271
+ let stableSince = 0;
272
+ let stableFinal = false;
254
273
  do {
255
274
  const elapsed = Date.now() - startedAt;
256
275
  const remaining = Math.max(0, waitMs - elapsed);
@@ -262,7 +281,23 @@ async function waitForProviderTerminalOutput(provider, projectPath, waitMs, laun
262
281
  }));
263
282
 
264
283
  if (latestOutput?.output && isTerminalReadbackFinal(provider, latestOutput)) {
265
- break;
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;
266
301
  }
267
302
  } while (Date.now() - startedAt < waitMs);
268
303
 
@@ -272,6 +307,10 @@ async function waitForProviderTerminalOutput(provider, projectPath, waitMs, laun
272
307
  if (latestOutput && typeof latestOutput.isBusy !== 'boolean') {
273
308
  latestOutput.isBusy = latestOutput.terminalState === 'busy';
274
309
  }
310
+ if (latestOutput) {
311
+ latestOutput.readbackStable = stableFinal;
312
+ latestOutput.terminalOutputFinal = stableFinal;
313
+ }
275
314
  return latestOutput;
276
315
  }
277
316
 
@@ -375,7 +414,9 @@ async function callTool(name, args = {}) {
375
414
  if (waitForOutputMs > 0) {
376
415
  terminalOutput = await waitForProviderTerminalOutput(provider, projectPath, waitForOutputMs, launchId);
377
416
  }
378
- const terminalOutputFinal = terminalOutput ? isTerminalReadbackFinal(provider, terminalOutput) : false;
417
+ const terminalOutputFinal = terminalOutput
418
+ ? Boolean(terminalOutput.terminalOutputFinal ?? isTerminalReadbackFinal(provider, terminalOutput))
419
+ : false;
379
420
  return textResult(JSON.stringify({
380
421
  launched: true,
381
422
  launchId,
@@ -385,8 +426,11 @@ async function callTool(name, args = {}) {
385
426
  permissionBypass: bypassPermissions,
386
427
  status,
387
428
  terminalOutputFinal,
429
+ terminalFailed: Boolean(terminalOutput?.terminalFailed),
388
430
  message: terminalOutput && !terminalOutputFinal
389
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.'
390
434
  : undefined,
391
435
  terminalOutput,
392
436
  }, null, 2));
@@ -407,6 +451,7 @@ async function callTool(name, args = {}) {
407
451
  body.isBusy = body.terminalState === 'busy';
408
452
  }
409
453
  body.terminalOutputFinal = isTerminalReadbackFinal(provider, body);
454
+ body.terminalFailed = Boolean(body.terminalFailed);
410
455
  return textResult(JSON.stringify(body, null, 2));
411
456
  }
412
457
 
@@ -122,6 +122,13 @@ const server = createServer(async (req, res) => {
122
122
  terminalState: 'busy',
123
123
  output: 'OpenAI Codex\n› /init\n\n• Ran npm test\n• Working (30s • esc to interrupt)\n',
124
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
+ },
125
132
  {
126
133
  active: true,
127
134
  provider: 'codex',
@@ -155,7 +162,7 @@ function callMcp(child, method, params = undefined) {
155
162
  return new Promise((resolve, reject) => {
156
163
  const timeout = setTimeout(() => {
157
164
  reject(new Error(`MCP call timed out: ${method}`));
158
- }, 5000);
165
+ }, 10000);
159
166
  const onLine = (line) => {
160
167
  if (!line.trim()) return;
161
168
  const message = JSON.parse(line);
@@ -232,11 +239,11 @@ try {
232
239
  projectPath: '/root/pixcode',
233
240
  prompt: 'read final output',
234
241
  startupInput: '/init',
235
- waitForOutputMs: 3000,
242
+ waitForOutputMs: 7000,
236
243
  },
237
244
  });
238
245
  assert(
239
- providerOutputReads.length >= 3,
246
+ providerOutputReads.length >= 5,
240
247
  `readback should keep polling until the provider terminal is idle, reads=${providerOutputReads.length}`,
241
248
  );
242
249
  assert.match(
@@ -98,6 +98,26 @@ assert.match(
98
98
  /requestedLaunchId[\s\S]+session\.hermesLaunchId === requestedLaunchId/,
99
99
  'Provider output API should filter by Hermes terminal launch id when supplied.',
100
100
  );
101
+ assert.match(
102
+ serverIndex,
103
+ /lifecycleState/,
104
+ 'Provider output API should expose provider-agnostic PTY lifecycle state instead of relying only on terminal text regex.',
105
+ );
106
+ assert.match(
107
+ serverIndex,
108
+ /terminalFailed/,
109
+ 'Provider output API should expose non-zero visible terminal exits as failures for Hermes readback.',
110
+ );
111
+ assert.match(
112
+ serverIndex,
113
+ /existingSession[\s\S]+existingSession\.pty/,
114
+ 'Completed visible terminal records should not be reattached as live PTYs.',
115
+ );
116
+ assert.match(
117
+ pixcodeMcpServer,
118
+ /terminalFailed/,
119
+ 'Pixcode MCP should tell Hermes when the visible provider terminal failed.',
120
+ );
101
121
  assert.match(
102
122
  serverIndex,
103
123
  /const hermesLaunchId = Number\.isFinite\(Number\(data\.hermesLaunchId\)\)/,
@@ -113,6 +133,16 @@ assert.match(
113
133
  /terminalState is busy|terminalState.+busy|terminal to become idle/i,
114
134
  'Pixcode MCP should not summarize the first busy terminal frame as final output.',
115
135
  );
136
+ assert.match(
137
+ pixcodeMcpServer,
138
+ /READBACK_IDLE_STABLE_MS/,
139
+ 'Pixcode MCP should require a stable idle readback before reporting provider output as final.',
140
+ );
141
+ assert.match(
142
+ pixcodeMcpServer,
143
+ /readbackStable/,
144
+ 'Pixcode MCP should mark whether a visible provider readback was stable before Hermes summarizes it.',
145
+ );
116
146
  assert.match(
117
147
  pixcodeMcpServer,
118
148
  /startup input typed into the provider CLI/,
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+
3
+ import assert from 'node:assert/strict';
4
+ import { readFileSync } from 'node:fs';
5
+
6
+ const externalAccess = readFileSync('server/services/external-access.js', 'utf8');
7
+ const networkRoutes = readFileSync('server/routes/network.js', 'utf8');
8
+ const serverIndex = readFileSync('server/index.js', 'utf8');
9
+
10
+ assert.match(
11
+ externalAccess,
12
+ /TUNNEL_PERSISTENCE_PATH/,
13
+ 'Tunnel service should persist the user-requested tunnel state outside process memory.',
14
+ );
15
+ assert.match(
16
+ externalAccess,
17
+ /persistTunnelPreference/,
18
+ 'Tunnel service should write tunnel start/stop intent to disk.',
19
+ );
20
+ assert.match(
21
+ externalAccess,
22
+ /desired:\s*true/,
23
+ 'Starting a tunnel should mark tunnel intent as desired until the user stops it.',
24
+ );
25
+ assert.match(
26
+ externalAccess,
27
+ /desired:\s*false/,
28
+ 'Stopping a tunnel should clear persisted tunnel intent.',
29
+ );
30
+ assert.match(
31
+ externalAccess,
32
+ /restoreRequestedTunnel/,
33
+ 'Tunnel service should expose a startup restore hook.',
34
+ );
35
+ assert.match(
36
+ externalAccess,
37
+ /restoring/,
38
+ 'Tunnel restore should distinguish automatic restart attempts from direct user starts.',
39
+ );
40
+ assert.match(
41
+ networkRoutes,
42
+ /persistPreference:\s*true/,
43
+ 'Manual tunnel starts should persist the user preference through the network route.',
44
+ );
45
+ assert.match(
46
+ serverIndex,
47
+ /restoreRequestedTunnel/,
48
+ 'Server startup should restore a requested tunnel after updates/restarts.',
49
+ );
50
+ assert.match(
51
+ serverIndex,
52
+ /restoreRequestedTunnel\(\{ port: Number\(SERVER_PORT\) \}\)/,
53
+ 'Server startup should restore the tunnel against the current backend port.',
54
+ );
55
+
56
+ console.log('tunnel persistence smoke passed');