@pixelbyte-software/pixcode 1.53.7 → 1.53.9

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-DM-KfWAm.js"></script>
38
+ <script type="module" crossorigin src="/assets/index-nl5YZ8Wr.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">
@@ -37,12 +37,18 @@ const DAEMON_COMMAND_CONTEXT = {
37
37
  nodeExecPath: process.execPath,
38
38
  };
39
39
  function resolveMonacoAssetsPath() {
40
+ const appParent = path.dirname(APP_ROOT);
41
+ const appGrandparent = path.dirname(appParent);
40
42
  const candidates = [
41
43
  path.join(APP_ROOT, 'node_modules', 'monaco-editor', 'min', 'vs'),
44
+ path.join(appParent, 'node_modules', 'monaco-editor', 'min', 'vs'),
45
+ path.join(appParent, 'monaco-editor', 'min', 'vs'),
46
+ path.join(appGrandparent, 'node_modules', 'monaco-editor', 'min', 'vs'),
47
+ path.join(appGrandparent, 'monaco-editor', 'min', 'vs'),
42
48
  ];
43
49
  try {
44
50
  const monacoPackagePath = require.resolve('monaco-editor/package.json', {
45
- paths: [APP_ROOT, __dirname],
51
+ paths: [APP_ROOT, appParent, appGrandparent, __dirname, process.cwd()],
46
52
  });
47
53
  candidates.push(path.join(path.dirname(monacoPackagePath), 'min', 'vs'));
48
54
  }
@@ -909,6 +915,11 @@ const ptySessionsMap = new Map();
909
915
  const PTY_SESSION_TIMEOUT = 30 * 60 * 1000;
910
916
  const COMPLETED_PTY_SESSION_TTL = 5 * 60 * 1000;
911
917
  const SHELL_URL_PARSE_BUFFER_LIMIT = 32768;
918
+ const SHELL_OUTPUT_FLUSH_MAX_CHARS = 128 * 1024;
919
+ const SHELL_WS_BACKPRESSURE_LIMIT = 8 * 1024 * 1024;
920
+ const PTY_SESSION_BUFFER_MAX_BYTES = Number.parseInt(process.env.PIXCODE_PTY_BUFFER_MAX_BYTES || '', 10) || (2 * 1024 * 1024);
921
+ const SHELL_INPUT_CHUNK_CHARS = 4096;
922
+ const SHELL_PENDING_INPUT_MAX_BYTES = 2 * 1024 * 1024;
912
923
  const SHELL_CLI_PROVIDERS = new Set(['claude', 'codex', 'cursor', 'gemini', 'qwen', 'opencode']);
913
924
  import { stripAnsiSequences, normalizeDetectedUrl, extractUrlsFromText, shouldAutoOpenUrlFromOutput } from './utils/url-detection.js';
914
925
  function terminatePtySession(sessionKey, session, reason) {
@@ -1030,13 +1041,22 @@ function resolveProviderTerminalState(session, provider, output) {
1030
1041
  function appendPtySessionBuffer(session, data) {
1031
1042
  if (!session)
1032
1043
  return;
1033
- if (session.buffer.length < 5000) {
1034
- session.buffer.push(data);
1035
- }
1036
- else {
1037
- session.buffer.shift();
1038
- session.buffer.push(data);
1039
- }
1044
+ let value = String(data || '');
1045
+ if (!value)
1046
+ return;
1047
+ let valueBytes = Buffer.byteLength(value, 'utf8');
1048
+ if (valueBytes > PTY_SESSION_BUFFER_MAX_BYTES) {
1049
+ value = value.slice(-PTY_SESSION_BUFFER_MAX_BYTES);
1050
+ valueBytes = Buffer.byteLength(value, 'utf8');
1051
+ }
1052
+ session.buffer.push(value);
1053
+ session.bufferBytes = (session.bufferBytes || 0) + valueBytes;
1054
+ while (session.buffer.length > 0 && session.bufferBytes > PTY_SESSION_BUFFER_MAX_BYTES) {
1055
+ const removed = session.buffer.shift();
1056
+ session.bufferBytes -= Buffer.byteLength(String(removed || ''), 'utf8');
1057
+ }
1058
+ if (session.bufferBytes < 0)
1059
+ session.bufferBytes = 0;
1040
1060
  }
1041
1061
  function normalizeTerminalStartupInput(input) {
1042
1062
  return `\x15${String(input || '').replace(/(?:\r\n|\r|\n)+$/u, '')}\r`;
@@ -1120,6 +1140,27 @@ function queueTerminalStartupInput(session, startupInput, reason, delayMs = 500)
1120
1140
  function writeTerminalStartupInput(session, startupInput, reason, delayMs = 500) {
1121
1141
  queueTerminalStartupInput(session, startupInput, reason, delayMs);
1122
1142
  }
1143
+ function splitTerminalInput(data) {
1144
+ const value = String(data || '');
1145
+ if (!value)
1146
+ return [];
1147
+ const chunks = [];
1148
+ for (let index = 0; index < value.length; index += SHELL_INPUT_CHUNK_CHARS) {
1149
+ chunks.push(value.slice(index, index + SHELL_INPUT_CHUNK_CHARS));
1150
+ }
1151
+ return chunks;
1152
+ }
1153
+ function writeTerminalInputChunks(ptyProcess, data) {
1154
+ if (!ptyProcess?.write)
1155
+ return false;
1156
+ const chunks = splitTerminalInput(data);
1157
+ if (chunks.length === 0)
1158
+ return false;
1159
+ for (const chunk of chunks) {
1160
+ ptyProcess.write(chunk);
1161
+ }
1162
+ return true;
1163
+ }
1123
1164
  function normalizeShellPermissionMode(value) {
1124
1165
  return typeof value === 'string' ? value.trim() : '';
1125
1166
  }
@@ -1356,7 +1397,7 @@ app.post('/api/shell/sessions/provider-input', authenticateToken, requireProject
1356
1397
  }
1357
1398
  const data = submit ? normalizeTerminalStartupInput(input) : input;
1358
1399
  try {
1359
- matchedSession.pty.write(data);
1400
+ writeTerminalInputChunks(matchedSession.pty, data);
1360
1401
  matchedSession.updatedAt = Date.now();
1361
1402
  res.json({
1362
1403
  ok: true,
@@ -3135,7 +3176,58 @@ function handleShellConnection(ws, request) {
3135
3176
  let urlDetectionBuffer = '';
3136
3177
  let outputBuffer = '';
3137
3178
  let outputFlushTimer = null;
3179
+ let inputFlushTimer = null;
3180
+ let pendingInputBytes = 0;
3181
+ const pendingInputQueue = [];
3138
3182
  const announcedAuthUrls = new Set();
3183
+ function getActiveShellPty() {
3184
+ const session = ptySessionKey ? ptySessionsMap.get(ptySessionKey) : null;
3185
+ return session?.pty || shellProcess;
3186
+ }
3187
+ function scheduleInputFlush(delayMs = 0) {
3188
+ if (inputFlushTimer)
3189
+ return;
3190
+ inputFlushTimer = setTimeout(flushPendingInput, delayMs);
3191
+ }
3192
+ function flushPendingInput() {
3193
+ inputFlushTimer = null;
3194
+ if (pendingInputQueue.length === 0)
3195
+ return;
3196
+ const activePty = getActiveShellPty();
3197
+ if (!activePty?.write) {
3198
+ scheduleInputFlush(100);
3199
+ return;
3200
+ }
3201
+ const chunk = pendingInputQueue.shift();
3202
+ pendingInputBytes = Math.max(0, pendingInputBytes - Buffer.byteLength(chunk, 'utf8'));
3203
+ try {
3204
+ activePty.write(chunk);
3205
+ }
3206
+ catch (error) {
3207
+ console.error('Error writing to shell:', error);
3208
+ pendingInputQueue.length = 0;
3209
+ pendingInputBytes = 0;
3210
+ return;
3211
+ }
3212
+ if (pendingInputQueue.length > 0)
3213
+ scheduleInputFlush(1);
3214
+ }
3215
+ function enqueueShellInput(data) {
3216
+ const chunks = splitTerminalInput(data);
3217
+ if (chunks.length === 0)
3218
+ return;
3219
+ for (const chunk of chunks) {
3220
+ pendingInputQueue.push(chunk);
3221
+ pendingInputBytes += Buffer.byteLength(chunk, 'utf8');
3222
+ }
3223
+ while (pendingInputQueue.length > 0 && pendingInputBytes > SHELL_PENDING_INPUT_MAX_BYTES) {
3224
+ const dropped = pendingInputQueue.shift();
3225
+ pendingInputBytes -= Buffer.byteLength(String(dropped || ''), 'utf8');
3226
+ }
3227
+ if (pendingInputBytes < 0)
3228
+ pendingInputBytes = 0;
3229
+ scheduleInputFlush();
3230
+ }
3139
3231
  function flushOutputBuffer() {
3140
3232
  if (!outputBuffer || !ptySessionKey) {
3141
3233
  outputFlushTimer = null;
@@ -3150,7 +3242,16 @@ function handleShellConnection(ws, request) {
3150
3242
  session.updatedAt = Date.now();
3151
3243
  appendPtySessionBuffer(session, rawData);
3152
3244
  if (session.ws && session.ws.readyState === WebSocket.OPEN) {
3245
+ if (session.ws.bufferedAmount > SHELL_WS_BACKPRESSURE_LIMIT) {
3246
+ session.droppedOutputBytes = (session.droppedOutputBytes || 0) + Buffer.byteLength(rawData, 'utf8');
3247
+ return;
3248
+ }
3249
+ const droppedOutputBytes = session.droppedOutputBytes || 0;
3250
+ session.droppedOutputBytes = 0;
3153
3251
  let outputData = rawData;
3252
+ if (droppedOutputBytes > 0) {
3253
+ outputData = `\r\n\x1b[33m[Pixcode] ${droppedOutputBytes} bytes of terminal output were skipped because the browser connection was backpressured.\x1b[0m\r\n${outputData}`;
3254
+ }
3154
3255
  const cleanChunk = stripAnsiSequences(rawData);
3155
3256
  urlDetectionBuffer = `${urlDetectionBuffer}${cleanChunk}`.slice(-SHELL_URL_PARSE_BUFFER_LIMIT);
3156
3257
  outputData = outputData.replace(/OPEN_URL:\s*(https?:\/\/[^\s\x1b\x07]+)/g, '[INFO] Opening in browser: $1');
@@ -3491,6 +3592,8 @@ function handleShellConnection(ws, request) {
3491
3592
  pty: shellProcess,
3492
3593
  ws: ws,
3493
3594
  buffer: [],
3595
+ bufferBytes: 0,
3596
+ droppedOutputBytes: 0,
3494
3597
  timeoutId: null,
3495
3598
  userId: ownerUserId,
3496
3599
  projectPath,
@@ -3511,11 +3614,20 @@ function handleShellConnection(ws, request) {
3511
3614
  if (terminalStartupInput && !isPlainShell) {
3512
3615
  writeTerminalStartupInput(createdSession, terminalStartupInput, 'new provider session', 4500);
3513
3616
  }
3617
+ scheduleInputFlush();
3514
3618
  // Handle data output — batch rapid chunks so high-volume CLI
3515
3619
  // output does not flood the WebSocket with one JSON frame per
3516
3620
  // keystroke-sized chunk.
3517
3621
  shellProcess.onData((data) => {
3518
3622
  outputBuffer += data;
3623
+ if (outputBuffer.length >= SHELL_OUTPUT_FLUSH_MAX_CHARS) {
3624
+ if (outputFlushTimer) {
3625
+ clearTimeout(outputFlushTimer);
3626
+ outputFlushTimer = null;
3627
+ }
3628
+ flushOutputBuffer();
3629
+ return;
3630
+ }
3519
3631
  if (!outputFlushTimer) {
3520
3632
  outputFlushTimer = setTimeout(flushOutputBuffer, 8);
3521
3633
  }
@@ -3581,18 +3693,7 @@ function handleShellConnection(ws, request) {
3581
3693
  }
3582
3694
  }
3583
3695
  else if (data.type === 'input') {
3584
- // Send input to shell process
3585
- if (shellProcess && shellProcess.write) {
3586
- try {
3587
- shellProcess.write(data.data);
3588
- }
3589
- catch (error) {
3590
- console.error('Error writing to shell:', error);
3591
- }
3592
- }
3593
- else {
3594
- console.warn('No active shell process to send input to');
3595
- }
3696
+ enqueueShellInput(data.data);
3596
3697
  }
3597
3698
  else if (data.type === 'resize') {
3598
3699
  // Handle terminal resize
@@ -3627,6 +3728,12 @@ function handleShellConnection(ws, request) {
3627
3728
  clearTimeout(outputFlushTimer);
3628
3729
  outputFlushTimer = null;
3629
3730
  }
3731
+ if (inputFlushTimer) {
3732
+ clearTimeout(inputFlushTimer);
3733
+ inputFlushTimer = null;
3734
+ }
3735
+ pendingInputQueue.length = 0;
3736
+ pendingInputBytes = 0;
3630
3737
  if (ptySessionKey) {
3631
3738
  const session = ptySessionsMap.get(ptySessionKey);
3632
3739
  if (session) {