@taewooopark/agent-blackbox 0.47.2 → 0.47.4

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/cli.js CHANGED
@@ -2048,7 +2048,7 @@ async function readTraceEvents(filePath) {
2048
2048
  }
2049
2049
 
2050
2050
  // apps/daemon/dist/server.js
2051
- import { readFile as readFile4 } from "node:fs/promises";
2051
+ import { open as open2, stat as stat3 } from "node:fs/promises";
2052
2052
  import { createServer as createServer2 } from "node:http";
2053
2053
  import { join as join4 } from "node:path";
2054
2054
  import { WebSocket, WebSocketServer } from "ws";
@@ -2642,24 +2642,67 @@ async function loadTraceEvents(eventsFile) {
2642
2642
  }
2643
2643
  }
2644
2644
  var SNAPSHOT_EVENT_CAP = 3e4;
2645
+ var eventCaches = /* @__PURE__ */ new Map();
2646
+ var cacheLocks = /* @__PURE__ */ new Map();
2647
+ function withCacheLock(key, run) {
2648
+ const prev = cacheLocks.get(key) ?? Promise.resolve();
2649
+ const result = prev.then(run, run);
2650
+ cacheLocks.set(key, result.then(() => void 0, () => void 0));
2651
+ return result;
2652
+ }
2645
2653
  async function loadRecentTraceEvents(eventsFile, cap = SNAPSHOT_EVENT_CAP) {
2646
- let text;
2647
- try {
2648
- text = await readFile4(eventsFile, "utf8");
2649
- } catch (error) {
2650
- if (isNodeError(error) && error.code === "ENOENT")
2651
- return [];
2652
- throw error;
2653
- }
2654
- const lines = text.split("\n");
2655
- let kept = 0;
2656
- let start = lines.length;
2657
- while (start > 0 && kept < cap) {
2658
- start -= 1;
2659
- if (lines[start].trim().length > 0)
2660
- kept += 1;
2661
- }
2662
- return parseTraceEvents(lines.slice(start).join("\n"));
2654
+ return withCacheLock(eventsFile, async () => {
2655
+ let cache = eventCaches.get(eventsFile);
2656
+ if (!cache) {
2657
+ cache = { offset: 0, buffer: "", events: [] };
2658
+ eventCaches.set(eventsFile, cache);
2659
+ }
2660
+ let size;
2661
+ try {
2662
+ size = (await stat3(eventsFile)).size;
2663
+ } catch (error) {
2664
+ if (isNodeError(error) && error.code === "ENOENT")
2665
+ return [];
2666
+ throw error;
2667
+ }
2668
+ if (size < cache.offset) {
2669
+ cache.offset = 0;
2670
+ cache.buffer = "";
2671
+ cache.events = [];
2672
+ }
2673
+ if (size > cache.offset) {
2674
+ const handle = await open2(eventsFile, "r");
2675
+ try {
2676
+ const length = size - cache.offset;
2677
+ const buf = Buffer.alloc(length);
2678
+ await handle.read(buf, 0, length, cache.offset);
2679
+ cache.offset = size;
2680
+ cache.buffer += buf.toString("utf8");
2681
+ } finally {
2682
+ await handle.close();
2683
+ }
2684
+ const lines = cache.buffer.split("\n");
2685
+ cache.buffer = lines.pop() ?? "";
2686
+ let from = 0;
2687
+ if (cache.events.length === 0 && lines.length > SNAPSHOT_EVENT_CAP) {
2688
+ let kept = 0;
2689
+ from = lines.length;
2690
+ while (from > 0 && kept < SNAPSHOT_EVENT_CAP) {
2691
+ from -= 1;
2692
+ if (lines[from].trim().length > 0)
2693
+ kept += 1;
2694
+ }
2695
+ }
2696
+ const fresh = parseTraceEvents(lines.slice(from).join("\n"));
2697
+ if (fresh.length > 0) {
2698
+ cache.events.push(...fresh);
2699
+ if (cache.events.length > SNAPSHOT_EVENT_CAP) {
2700
+ cache.events.splice(0, cache.events.length - SNAPSHOT_EVENT_CAP);
2701
+ }
2702
+ }
2703
+ }
2704
+ return cap >= cache.events.length ? cache.events.slice() : cache.events.slice(cache.events.length - cap);
2705
+ });
2663
2706
  }
2664
2707
  async function buildReplaySummary(eventsFile) {
2665
2708
  const events = await loadTraceEvents(eventsFile);
@@ -2785,20 +2828,49 @@ async function broadcastSnapshot(clients, eventsFile) {
2785
2828
  if (clients.size === 0) {
2786
2829
  return;
2787
2830
  }
2788
- await Promise.allSettled([...clients].map((client) => sendSnapshot(client, eventsFile)));
2831
+ let frame;
2832
+ try {
2833
+ frame = JSON.stringify({ type: "snapshot", data: await buildTraceSnapshot(eventsFile) });
2834
+ } catch (error) {
2835
+ const errFrame = JSON.stringify({
2836
+ type: "error",
2837
+ error: { message: error instanceof Error ? error.message : String(error) }
2838
+ });
2839
+ for (const client of clients)
2840
+ if (client.readyState === WebSocket.OPEN)
2841
+ client.send(errFrame);
2842
+ return;
2843
+ }
2844
+ for (const client of clients)
2845
+ if (client.readyState === WebSocket.OPEN)
2846
+ client.send(frame);
2789
2847
  }
2790
2848
  function makeBroadcastScheduler(clients, eventsFile, delayMs = 150) {
2791
2849
  let timer = null;
2792
- return () => {
2850
+ let building = false;
2851
+ let pending = false;
2852
+ const schedule = () => {
2853
+ if (building) {
2854
+ pending = true;
2855
+ return;
2856
+ }
2793
2857
  if (timer)
2794
2858
  return;
2795
2859
  timer = setTimeout(() => {
2796
2860
  timer = null;
2797
- void broadcastSnapshot(clients, eventsFile);
2861
+ building = true;
2862
+ void broadcastSnapshot(clients, eventsFile).finally(() => {
2863
+ building = false;
2864
+ if (pending) {
2865
+ pending = false;
2866
+ schedule();
2867
+ }
2868
+ });
2798
2869
  }, delayMs);
2799
2870
  if (typeof timer.unref === "function")
2800
2871
  timer.unref();
2801
2872
  };
2873
+ return schedule;
2802
2874
  }
2803
2875
  async function sendSnapshot(client, eventsFile) {
2804
2876
  if (client.readyState !== WebSocket.OPEN) {
@@ -2892,7 +2964,7 @@ function isNodeError(error) {
2892
2964
  }
2893
2965
 
2894
2966
  // apps/daemon/dist/initOpenCode.js
2895
- import { mkdir as mkdir3, readFile as readFile5, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
2967
+ import { mkdir as mkdir3, readFile as readFile4, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
2896
2968
  import { homedir as homedir2 } from "node:os";
2897
2969
  import { dirname as dirname3, join as join5 } from "node:path";
2898
2970
  var defaultAdapterPackage = "@agent-blackbox/opencode-adapter";
@@ -2909,7 +2981,7 @@ async function installGlobalRecorder(options) {
2909
2981
  throw new Error("Self-contained recorder bundle not found. Use the published npx package, or build it from source with `npm run build:cli`.");
2910
2982
  }
2911
2983
  const pluginPath = globalRecorderPath();
2912
- const bundle = (await readFile5(options.pluginBundlePath, "utf8")).replaceAll("__ABB_DAEMON_URL__", options.daemonUrl);
2984
+ const bundle = (await readFile4(options.pluginBundlePath, "utf8")).replaceAll("__ABB_DAEMON_URL__", options.daemonUrl);
2913
2985
  await mkdir3(dirname3(pluginPath), { recursive: true });
2914
2986
  await writeFile2(pluginPath, bundle, "utf8");
2915
2987
  return { pluginPath };
@@ -2938,7 +3010,7 @@ async function initOpenCodeProject(options) {
2938
3010
  throw new Error(`${pluginPath} already exists. Re-run with --force to overwrite it.`);
2939
3011
  }
2940
3012
  if (options.pluginBundlePath && await pathExists(options.pluginBundlePath)) {
2941
- const bundle = await readFile5(options.pluginBundlePath, "utf8");
3013
+ const bundle = await readFile4(options.pluginBundlePath, "utf8");
2942
3014
  const inlined = bundle.replaceAll("__ABB_DAEMON_URL__", daemonUrl);
2943
3015
  await writeFile2(pluginPath, inlined, "utf8");
2944
3016
  return { pluginPath, packageJsonPath, adapterPackage, adapterImport };
@@ -2977,7 +3049,7 @@ function inferAdapterImport(adapterPackage) {
2977
3049
  }
2978
3050
  async function readPackageJson(packageJsonPath) {
2979
3051
  try {
2980
- return JSON.parse(await readFile5(packageJsonPath, "utf8"));
3052
+ return JSON.parse(await readFile4(packageJsonPath, "utf8"));
2981
3053
  } catch (error) {
2982
3054
  if (isNodeError2(error) && error.code === "ENOENT") {
2983
3055
  return {};
@@ -2987,7 +3059,7 @@ async function readPackageJson(packageJsonPath) {
2987
3059
  }
2988
3060
  async function pathExists(path) {
2989
3061
  try {
2990
- await readFile5(path, "utf8");
3062
+ await readFile4(path, "utf8");
2991
3063
  return true;
2992
3064
  } catch (error) {
2993
3065
  if (isNodeError2(error) && error.code === "ENOENT") {
@@ -3023,7 +3095,7 @@ function describeDaemon() {
3023
3095
  }
3024
3096
 
3025
3097
  // apps/daemon/dist/initClaudeHooks.js
3026
- import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile3 } from "node:fs/promises";
3098
+ import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
3027
3099
  import { homedir as homedir3 } from "node:os";
3028
3100
  import { dirname as dirname5, join as join7 } from "node:path";
3029
3101
  function globalClaudeDir() {
@@ -3046,7 +3118,7 @@ async function uninstallClaudeCodeHooks() {
3046
3118
  const settingsPath = claudeSettingsPath();
3047
3119
  let settings;
3048
3120
  try {
3049
- settings = JSON.parse(await readFile6(settingsPath, "utf8"));
3121
+ settings = JSON.parse(await readFile5(settingsPath, "utf8"));
3050
3122
  } catch (error) {
3051
3123
  if (isNotFound(error))
3052
3124
  return { settingsPath, removed: false };
@@ -3060,7 +3132,7 @@ async function uninstallClaudeCodeHooks() {
3060
3132
  }
3061
3133
  async function readSettings(path) {
3062
3134
  try {
3063
- return JSON.parse(await readFile6(path, "utf8"));
3135
+ return JSON.parse(await readFile5(path, "utf8"));
3064
3136
  } catch (error) {
3065
3137
  if (isNotFound(error))
3066
3138
  return {};