sapper-iq 1.1.37 → 1.1.39

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/sapper-ui.mjs CHANGED
@@ -116,13 +116,17 @@ function getSapperIgnorePatterns() {
116
116
  }
117
117
 
118
118
  function ignorePatternToRegex(pattern) {
119
- let p = pattern.replace(/\/+$/, '');
120
- p = p.replace(/([.+^${}()|[\]\\])/g, '\\$1');
121
- p = p.replace(/\*\*/g, '<<<GLOBSTAR>>>');
122
- p = p.replace(/\*/g, '[^/]*');
123
- p = p.replace(/<<<GLOBSTAR>>>/g, '.*');
124
- p = p.replace(/\?/g, '[^/]');
125
- return new RegExp(`(^|/)${p}($|/)`, 'i');
119
+ try {
120
+ let p = pattern.replace(/\/+$/, '');
121
+ p = p.replace(/([.+^${}()|[\]\\])/g, '\\$1');
122
+ p = p.replace(/\*\*/g, '<<<GLOBSTAR>>>');
123
+ p = p.replace(/\*/g, '[^/]*');
124
+ p = p.replace(/<<<GLOBSTAR>>>/g, '.*');
125
+ p = p.replace(/\?/g, '[^/]');
126
+ return new RegExp(`(^|/)${p}($|/)`, 'i');
127
+ } catch (e) {
128
+ return /^$/; // Return a regex that never matches on error
129
+ }
126
130
  }
127
131
 
128
132
  function shouldIgnore(nameOrPath) {
@@ -262,12 +266,30 @@ const tools = {
262
266
  for (const { pattern: p, negate } of getSapperIgnorePatterns()) {
263
267
  if (!negate && p.endsWith('/')) allIgnoreDirs.add(p.replace(/\/+$/, ''));
264
268
  }
265
- const excludes = [...allIgnoreDirs].join(',');
266
- const cmd = `grep -rEin "${pattern.replace(/"/g, '\\"')}" . --exclude-dir={${excludes}} --include="*.{js,ts,jsx,tsx,py,java,go,rs,rb,php,c,cpp,h,css,scss,html,json,md,txt,yml,yaml,toml,sh}" 2>/dev/null | head -50`;
267
- const proc = spawn('sh', ['-c', cmd], { cwd: workingDir });
269
+ // Use args array to avoid command injection
270
+ const args = ['-rEin', pattern, '.'];
271
+ for (const dir of allIgnoreDirs) {
272
+ args.push(`--exclude-dir=${dir}`);
273
+ }
274
+ args.push('--include=*.js', '--include=*.ts', '--include=*.jsx', '--include=*.tsx',
275
+ '--include=*.py', '--include=*.java', '--include=*.go', '--include=*.rs',
276
+ '--include=*.rb', '--include=*.php', '--include=*.c', '--include=*.cpp',
277
+ '--include=*.h', '--include=*.css', '--include=*.scss', '--include=*.html',
278
+ '--include=*.json', '--include=*.md', '--include=*.txt', '--include=*.yml',
279
+ '--include=*.yaml', '--include=*.toml', '--include=*.sh');
280
+ const proc = spawn('grep', args, { cwd: workingDir });
268
281
  let out = '';
269
- proc.stdout.on('data', d => out += d);
270
- proc.stderr.on('data', d => out += d);
282
+ let lineCount = 0;
283
+ proc.stdout.on('data', d => {
284
+ const text = d.toString();
285
+ const lines = text.split('\n');
286
+ for (const line of lines) {
287
+ if (lineCount >= 50) { proc.kill(); return; }
288
+ if (line) { out += line + '\n'; lineCount++; }
289
+ }
290
+ });
291
+ proc.stderr.on('data', () => {});
292
+ proc.on('error', (err) => res(`Error searching: ${err.message}`));
271
293
  proc.on('close', () => res(out.trim() || `No matches for: ${pattern}`));
272
294
  });
273
295
  },
@@ -277,6 +299,9 @@ const tools = {
277
299
  let out = '';
278
300
  proc.stdout.on('data', d => out += d);
279
301
  proc.stderr.on('data', d => out += d);
302
+ proc.on('error', (err) => {
303
+ res(`Shell error: ${err.message}`);
304
+ });
280
305
  proc.on('close', code => {
281
306
  let result = out.trim();
282
307
  if (result.length > 10000) result = result.substring(0, 10000) + '\n...(truncated)';
@@ -297,7 +322,12 @@ async function executeTool(type, path, content, agentTools) {
297
322
  else if (t === 'mkdir') result = tools.mkdir(path);
298
323
  else if (t === 'write') result = tools.write(path, content || '');
299
324
  else if (t === 'patch') {
300
- const parts = content?.split('|||');
325
+ // Use indexOf to split into exactly 2 parts, preserving ||| in content
326
+ const sepIdx = content?.indexOf('|||');
327
+ let parts = null;
328
+ if (sepIdx > -1) {
329
+ parts = [content.substring(0, sepIdx), content.substring(sepIdx + 3)];
330
+ }
301
331
  if (parts && parts.length === 2) result = tools.patch(path, parts[0], parts[1]);
302
332
  else result = 'Error: PATCH needs OLD_TEXT|||NEW_TEXT';
303
333
  }
@@ -501,9 +531,20 @@ function json(res, data, status = 200) {
501
531
  }
502
532
 
503
533
  function readBody(req) {
504
- return new Promise((resolve) => {
534
+ const MAX_BODY_SIZE = 5 * 1024 * 1024; // 5MB limit
535
+ return new Promise((resolve, reject) => {
505
536
  let body = '';
506
- req.on('data', c => body += c);
537
+ let size = 0;
538
+ req.on('data', c => {
539
+ size += c.length;
540
+ if (size > MAX_BODY_SIZE) {
541
+ req.destroy();
542
+ resolve({ _error: 'Request body too large' });
543
+ return;
544
+ }
545
+ body += c;
546
+ });
547
+ req.on('error', () => resolve({}));
507
548
  req.on('end', () => { try { resolve(JSON.parse(body)); } catch { resolve({}); } });
508
549
  });
509
550
  }
@@ -1566,7 +1607,7 @@ function replacePairs(text, marker, open, close) {
1566
1607
 
1567
1608
  function esc(s) {
1568
1609
  if (!s) return '';
1569
- return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
1610
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
1570
1611
  }
1571
1612
 
1572
1613
  // ─── File Panel ────────────────────────────────────────────
@@ -1943,12 +1984,18 @@ const server = http.createServer(async (req, res) => {
1943
1984
 
1944
1985
  try {
1945
1986
  const stream = chatStream(serverMessages, serverModel, serverAgentTools);
1987
+ let clientClosed = false;
1988
+ res.on('close', () => { clientClosed = true; });
1946
1989
  for await (const evt of stream) {
1990
+ if (clientClosed) break;
1947
1991
  const ok = res.write(`data: ${JSON.stringify(evt)}\n\n`);
1948
- if (!ok) await new Promise(r => res.once('drain', r));
1992
+ if (!ok && !clientClosed) await new Promise(r => {
1993
+ res.once('drain', r);
1994
+ res.once('close', r);
1995
+ });
1949
1996
  }
1950
1997
  } catch (e) {
1951
- res.write(`data: ${JSON.stringify({ type: 'system', data: 'Error: ' + e.message })}\n\n`);
1998
+ try { res.write(`data: ${JSON.stringify({ type: 'system', data: 'Error: ' + e.message })}\n\n`); } catch {}
1952
1999
  }
1953
2000
  res.write('data: [DONE]\n\n');
1954
2001
  res.end();
@@ -1956,7 +2003,7 @@ const server = http.createServer(async (req, res) => {
1956
2003
  }
1957
2004
 
1958
2005
  // ── 404 ──
1959
- res.writeHead(404);
2006
+ res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
1960
2007
  res.end('Not found');
1961
2008
  });
1962
2009