teapot-coding-agent 0.16.1 → 0.16.2
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/server/api.js +60 -3
- package/package.json +1 -1
- package/public/assets/index-DXTAUylp.js +28 -0
- package/public/assets/index-DXTAUylp.js.map +1 -0
- package/public/assets/{index-CL7BIo-z.css → index-NiA8e42l.css} +1 -1
- package/public/assets/{shiki-KLdGO1C6.js → shiki-uS2Hy0mb.js} +2 -2
- package/public/assets/{shiki-KLdGO1C6.js.map → shiki-uS2Hy0mb.js.map} +1 -1
- package/public/index.html +2 -2
- package/public/assets/index-wnwUI0ke.js +0 -28
- package/public/assets/index-wnwUI0ke.js.map +0 -1
package/dist/server/api.js
CHANGED
|
@@ -300,6 +300,54 @@ export function buildApp(master) {
|
|
|
300
300
|
return c.json({ error: "cannot read" }, 400);
|
|
301
301
|
}
|
|
302
302
|
});
|
|
303
|
+
/**
|
|
304
|
+
* Batched ignore lookup: runs `git check-ignore --stdin` once per directory
|
|
305
|
+
* and returns the subset of names that are gitignored. Never throws — a
|
|
306
|
+
* non-git workspace simply yields an empty set.
|
|
307
|
+
*/
|
|
308
|
+
async function checkIgnoredBatch(workspace, dirAbs, dirents) {
|
|
309
|
+
const out = new Set();
|
|
310
|
+
try {
|
|
311
|
+
const relPaths = dirents.filter((d) => !d.name.startsWith(".")).map((d) => path.relative(workspace, path.join(dirAbs, d.name)));
|
|
312
|
+
if (relPaths.length === 0)
|
|
313
|
+
return out;
|
|
314
|
+
const { execFile } = await import("node:child_process");
|
|
315
|
+
const res = await new Promise((resolve) => {
|
|
316
|
+
let timer;
|
|
317
|
+
const p = execFile("git",
|
|
318
|
+
// NOTE: no --quiet — it suppresses the stdout we parse
|
|
319
|
+
["-C", workspace, "check-ignore", "--stdin", "-z", "--verbose"], { encoding: "utf8", maxBuffer: 1 << 20 }, (err, stdout) => {
|
|
320
|
+
if (timer)
|
|
321
|
+
clearTimeout(timer);
|
|
322
|
+
resolve(err || !stdout ? "" : stdout);
|
|
323
|
+
});
|
|
324
|
+
// end stdin only AFTER the write flushes (ending immediately raced
|
|
325
|
+
// the write and git saw an empty list), and hard-cap in case git
|
|
326
|
+
// wedges — a tree listing must never hang
|
|
327
|
+
p.stdin?.write(relPaths.join("\0") + "\0", () => p.stdin?.end());
|
|
328
|
+
timer = setTimeout(() => {
|
|
329
|
+
try {
|
|
330
|
+
p.kill("SIGKILL");
|
|
331
|
+
}
|
|
332
|
+
catch { /* */ }
|
|
333
|
+
resolve("");
|
|
334
|
+
}, 5_000);
|
|
335
|
+
});
|
|
336
|
+
// -z --verbose emits records as
|
|
337
|
+
// "<source> NUL <line> NUL <pattern> NUL <path> NUL"
|
|
338
|
+
// so the ignored path is every 4th field starting at index 3
|
|
339
|
+
const fields = res.split("\0");
|
|
340
|
+
for (let i = 3; i < fields.length; i += 4) {
|
|
341
|
+
const p = fields[i];
|
|
342
|
+
if (p)
|
|
343
|
+
out.add(path.basename(p));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
/* no git, not a repo, or spawn failed → treat all as unignored */
|
|
348
|
+
}
|
|
349
|
+
return out;
|
|
350
|
+
}
|
|
303
351
|
// ---- workspace file tree (read-only, powers the 🗂 files panel) ----
|
|
304
352
|
app.get("/api/agents/:id/tree", async (c) => {
|
|
305
353
|
const a = master.agents.get(c.req.param("id"));
|
|
@@ -320,10 +368,14 @@ export function buildApp(master) {
|
|
|
320
368
|
catch {
|
|
321
369
|
return c.json({ error: "cannot read directory" }, 400);
|
|
322
370
|
}
|
|
371
|
+
const showHidden = c.req.query("hidden") === "1";
|
|
323
372
|
const entries = [];
|
|
373
|
+
// one batched `git check-ignore` for the whole directory (respects
|
|
374
|
+
// nested .gitignore files); absent repo / no git → nothing is "ignored"
|
|
375
|
+
const ignoredNames = await checkIgnoredBatch(a.workspace, abs, dirents);
|
|
324
376
|
for (const d of dirents) {
|
|
325
|
-
if (d.name.startsWith("."))
|
|
326
|
-
continue; // hidden files stay out
|
|
377
|
+
if (d.name.startsWith(".") && !(showHidden && d.name !== "." && d.name !== ".."))
|
|
378
|
+
continue; // hidden files stay out unless the caller opted in
|
|
327
379
|
const isDir = d.isDirectory();
|
|
328
380
|
let size;
|
|
329
381
|
if (!isDir) {
|
|
@@ -334,7 +386,12 @@ export function buildApp(master) {
|
|
|
334
386
|
/* vanished mid-scan — fine */
|
|
335
387
|
}
|
|
336
388
|
}
|
|
337
|
-
entries.push({
|
|
389
|
+
entries.push({
|
|
390
|
+
name: d.name,
|
|
391
|
+
dir: isDir,
|
|
392
|
+
...(size !== undefined ? { size } : {}),
|
|
393
|
+
...(ignoredNames.has(d.name) ? { ignored: true } : {}),
|
|
394
|
+
});
|
|
338
395
|
}
|
|
339
396
|
entries.sort((x, y) => (x.dir !== y.dir ? (x.dir ? -1 : 1) : x.name.localeCompare(y.name)));
|
|
340
397
|
return c.json({
|