@pdpp/local-collector 0.7.8 → 0.7.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.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createReadStream, statSync } from "node:fs";
3
- import { readdir, readFile, stat } from "node:fs/promises";
3
+ import { readdir, stat } from "node:fs/promises";
4
4
  import { homedir } from "node:os";
5
5
  import { basename, join } from "node:path";
6
6
  import { createInterface as createFileReader } from "node:readline";
@@ -379,6 +379,10 @@ function markFileMtimeAndShouldSkip(fileMtimes, newMtimes, path, mtime) {
379
379
  newMtimes[path] = mtime;
380
380
  return fileMtimes[path] === mtime;
381
381
  }
382
+ async function readBoundedUtf8(path) {
383
+ const preview = await readBoundedFilePreview(path);
384
+ return preview?.buffer.toString("utf8") ?? null;
385
+ }
382
386
  async function emitSkills({ claudeHome, requested, emitRecord, fileMtimes, newMtimes }) {
383
387
  if (!requested.has("skills")) {
384
388
  return;
@@ -411,11 +415,14 @@ async function emitSkills({ claudeHome, requested, emitRecord, fileMtimes, newMt
411
415
  continue;
412
416
  }
413
417
  try {
414
- raw = await readFile(skillPath, "utf8");
418
+ raw = await readBoundedUtf8(skillPath);
415
419
  }
416
420
  catch {
417
421
  continue;
418
422
  }
423
+ if (raw === null) {
424
+ continue;
425
+ }
419
426
  const { frontmatter, body } = parseFrontmatter(raw);
420
427
  await emitRecord("skills", buildSkillRecord({ name: ent.name, frontmatter, body, path: skillPath, mtimeMs: st.mtimeMs }));
421
428
  }
@@ -436,11 +443,14 @@ async function processSlashCommandFile(args) {
436
443
  return;
437
444
  }
438
445
  try {
439
- raw = await readFile(args.full, "utf8");
446
+ raw = await readBoundedUtf8(args.full);
440
447
  }
441
448
  catch {
442
449
  return;
443
450
  }
451
+ if (raw === null) {
452
+ return;
453
+ }
444
454
  const { frontmatter, body } = parseFrontmatter(raw);
445
455
  const base = basename(args.name, ".md");
446
456
  const idPath = args.prefix ? `${args.prefix}/${base}` : base;
@@ -495,11 +505,14 @@ async function emitProjectMemoryNotes({ emitRecord, fileMtimes, newMtimes, proje
495
505
  continue;
496
506
  }
497
507
  try {
498
- raw = await readFile(fullPath, "utf8");
508
+ raw = await readBoundedUtf8(fullPath);
499
509
  }
500
510
  catch {
501
511
  continue;
502
512
  }
513
+ if (raw === null) {
514
+ continue;
515
+ }
503
516
  const { frontmatter, body } = parseFrontmatter(raw);
504
517
  await emitRecord("memory_notes", buildMemoryNoteRecord({ projectDir, relPath, frontmatter, body, path: fullPath, mtimeMs: st.mtimeMs }));
505
518
  }
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { createHash } from "node:crypto";
3
- import { createReadStream, existsSync, statSync } from "node:fs";
4
- import { readdir, readFile, stat } from "node:fs/promises";
3
+ import { createReadStream, statSync } from "node:fs";
4
+ import { readdir, stat } from "node:fs/promises";
5
5
  import { homedir } from "node:os";
6
6
  import { join } from "node:path";
7
7
  import { createInterface } from "node:readline";
8
8
  import { DatabaseSync } from "node:sqlite";
9
+ import { readBoundedFilePreview } from "../../src/bounded-file-preview.js";
9
10
  import { flushAndExitAfterRuntimeAck } from "../../src/connector-exit.js";
10
11
  import { openCarryForwardCursor } from "../../src/fingerprint-cursor.js";
11
12
  import { isMainModule } from "../../src/is-main-module.js";
@@ -17,6 +18,7 @@ import { validateRecord } from "./schemas.js";
17
18
  const DEFAULT_ACTIVE_ROLLOUT_QUIET_MS = 120_000;
18
19
  const ACTIVE_ROLLOUT_QUIET_MS_ENV = "PDPP_CODEX_ACTIVE_ROLLOUT_QUIET_MS";
19
20
  const GUARD_PREFIX_BYTES = 64 * 1024;
21
+ const MAX_PENDING_FUNCTION_CALLS = 1024;
20
22
  let stdoutDrainPromise = null;
21
23
  const emit = (m) => {
22
24
  const ok = process.stdout.write(stringifyForJsonl(m));
@@ -270,46 +272,32 @@ function openThreadsDb(dbPath) {
270
272
  return null;
271
273
  }
272
274
  }
273
- function queryThreadsRows(db) {
275
+ function isThreadRow(row) {
276
+ return typeof row === "object" && row !== null && typeof row.id === "string";
277
+ }
278
+ function* queryThreadsRows(db) {
274
279
  try {
275
- const rawRows = db.prepare(THREADS_QUERY).all();
276
- return rawRows;
280
+ for (const row of db.prepare(THREADS_QUERY).iterate()) {
281
+ if (isThreadRow(row)) {
282
+ yield row;
283
+ }
284
+ }
277
285
  }
278
286
  catch {
279
287
  emit({
280
288
  type: "PROGRESS",
281
289
  message: "Codex phase=index pass=index state_db_query_failed=true fallback=rollouts_only",
282
290
  });
283
- return [];
284
291
  }
285
292
  }
286
- function loadThreadsMap(dbPath) {
287
- if (!existsSync(dbPath)) {
288
- return { map: new Map(), present: false };
289
- }
290
- const db = openThreadsDb(dbPath);
291
- if (!db) {
292
- return { map: new Map(), present: false };
293
- }
294
- const map = new Map();
295
- try {
296
- for (const r of queryThreadsRows(db)) {
297
- map.set(r.id, r);
298
- }
299
- }
300
- finally {
301
- try {
302
- db.close();
303
- }
304
- catch {
305
- }
306
- }
307
- return { map, present: true };
308
- }
309
293
  async function statAndRead(path) {
310
294
  try {
311
295
  const st = await stat(path);
312
- const text = await readFile(path, "utf8");
296
+ const preview = await readBoundedFilePreview(path);
297
+ if (preview === null) {
298
+ return null;
299
+ }
300
+ const text = preview.buffer.toString("utf8");
313
301
  return { mtimeMs: Number(st.mtimeMs), size: Number(st.size), text };
314
302
  }
315
303
  catch {
@@ -428,12 +416,21 @@ function emitMessageRecord(state, payload, ts, emitRecord) {
428
416
  timestamp: ts,
429
417
  });
430
418
  }
431
- function registerFunctionCall(state, payload, ts) {
419
+ function registerFunctionCall(state, payload, ts, emitRecord) {
432
420
  const sessionId = state.sessionId;
433
421
  if (!sessionId) {
434
422
  return;
435
423
  }
436
424
  const callId = payload.call_id || `${sessionId}:${state.lineCount}`;
425
+ while (state.pendingCalls.size >= MAX_PENDING_FUNCTION_CALLS) {
426
+ const oldestEntry = state.pendingCalls.entries().next();
427
+ if (oldestEntry.done) {
428
+ break;
429
+ }
430
+ const [oldestCallId, oldest] = oldestEntry.value;
431
+ emitRecord("function_calls", oldest);
432
+ state.pendingCalls.delete(oldestCallId);
433
+ }
437
434
  state.pendingCalls.set(callId, {
438
435
  id: callId,
439
436
  session_id: sessionId,
@@ -485,7 +482,7 @@ export function processResponseItem({ deps, payload, state, ts }) {
485
482
  if (payload.type === "function_call") {
486
483
  state.functionCallCount++;
487
484
  if (deps.requested.has("function_calls")) {
488
- registerFunctionCall(state, payload, ts);
485
+ registerFunctionCall(state, payload, ts, deps.emitRecord);
489
486
  }
490
487
  return;
491
488
  }
@@ -575,6 +572,20 @@ export function emitSessionsFromMaps({ threadsMap, rolloutAggregates, emitRecord
575
572
  emitRecord("sessions", buildRolloutOnlySessionRecord(id, agg));
576
573
  }
577
574
  }
575
+ function emitSessionsFromRows({ threadsRows, rolloutAggregates, emitRecord, cursor }) {
576
+ for (const t of threadsRows) {
577
+ const agg = rolloutAggregates.get(t.id);
578
+ rolloutAggregates.delete(t.id);
579
+ const prior = cursor?.prior(t.id);
580
+ if (shouldReemitThreadSession(t, agg, prior)) {
581
+ emitRecord("sessions", buildThreadSessionRecord(t.id, t, agg, prior));
582
+ }
583
+ cursor?.note(t.id, makeThreadFingerprint(t, agg, prior));
584
+ }
585
+ for (const [id, agg] of rolloutAggregates) {
586
+ emitRecord("sessions", buildRolloutOnlySessionRecord(id, agg));
587
+ }
588
+ }
578
589
  async function parseRolloutFile(args) {
579
590
  const state = makeRolloutParseState(args.seed);
580
591
  const deps = {
@@ -754,13 +765,24 @@ async function scanRollouts(args) {
754
765
  return { parsedFiles: parsedRollouts };
755
766
  }
756
767
  function emitSessions({ stateDbPath, rolloutAggregates, emitRecord, cursor }) {
757
- const { map: threadsById } = loadThreadsMap(stateDbPath);
758
- emitSessionsFromMaps({
759
- threadsMap: threadsById,
760
- rolloutAggregates,
761
- emitRecord,
762
- cursor,
763
- });
768
+ const db = openThreadsDb(stateDbPath);
769
+ if (!db) {
770
+ for (const [id, agg] of rolloutAggregates) {
771
+ emitRecord("sessions", buildRolloutOnlySessionRecord(id, agg));
772
+ }
773
+ return;
774
+ }
775
+ try {
776
+ emitSessionsFromRows({
777
+ threadsRows: queryThreadsRows(db),
778
+ rolloutAggregates,
779
+ emitRecord,
780
+ cursor,
781
+ });
782
+ }
783
+ finally {
784
+ db.close();
785
+ }
764
786
  }
765
787
  async function readStartMessage() {
766
788
  const rl = createInterface({ input: process.stdin, terminal: false });
@@ -1,8 +1,8 @@
1
1
  const COLLECTOR_BUILD_SOURCE_SENTINEL = "source";
2
2
  const COLLECTOR_BUILD_INFO = {
3
- builtAt: "2026-06-17T17:19:47.443Z",
4
- revision: "adc321f24092",
5
- version: "0.7.8",
3
+ builtAt: "2026-06-17T18:00:24.313Z",
4
+ revision: "e5531179a760",
5
+ version: "0.7.9",
6
6
  };
7
7
  function buildAgentVersion(info = COLLECTOR_BUILD_INFO) {
8
8
  return `${info.version}+${info.revision}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdpp/local-collector",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "description": "Publishable local collector runtime for PDPP: filesystem-class connectors (Claude Code, Codex) plus the device-exporter ingest client.",
5
5
  "type": "module",
6
6
  "private": false,