@fdcn/sourcefed 0.2.4 → 0.2.5

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/README.md CHANGED
@@ -33,12 +33,12 @@ OpenCode loads this package as both a server and a TUI plugin:
33
33
 
34
34
  ```json
35
35
  // opencode.json
36
- { "plugin": ["@fdcn/sourcefed@0.2.4"] }
36
+ { "plugin": ["@fdcn/sourcefed@0.2.5"] }
37
37
  ```
38
38
 
39
39
  ```json
40
40
  // tui.json
41
- { "plugin": ["@fdcn/sourcefed@0.2.4"] }
41
+ { "plugin": ["@fdcn/sourcefed@0.2.5"] }
42
42
  ```
43
43
 
44
44
  The package exposes `./server`, `./tui`, `./opencode`, `./opencode/tui`, and `./pi` entrypoints for hosts that load them directly.
@@ -11638,11 +11638,16 @@ var SourcefedDaemon = class {
11638
11638
  this.listeners.set(key, listeners);
11639
11639
  }
11640
11640
  listeners.add(onEvents);
11641
+ this.emit(target);
11641
11642
  return () => {
11642
11643
  listeners.delete(onEvents);
11644
+ const receivedByListener = this.received.get(key);
11645
+ const released = receivedByListener?.delete(onEvents) ?? false;
11643
11646
  if (listeners.size === 0) {
11644
11647
  this.listeners.delete(key);
11645
11648
  this.received.delete(key);
11649
+ } else if (released) {
11650
+ this.emit(target);
11646
11651
  }
11647
11652
  };
11648
11653
  }
@@ -11678,23 +11683,28 @@ var SourcefedDaemon = class {
11678
11683
  receivedByListener = /* @__PURE__ */ new Map();
11679
11684
  this.received.set(key, receivedByListener);
11680
11685
  }
11681
- const listenerResults = [...listeners].map(async (listener) => {
11686
+ const claimed = /* @__PURE__ */ new Set();
11687
+ for (const received of receivedByListener.values()) {
11688
+ for (const id of received) claimed.add(id);
11689
+ }
11690
+ const fresh = queued.filter((event) => !claimed.has(event.id));
11691
+ if (fresh.length === 0) return;
11692
+ for (const listener of [...listeners]) {
11682
11693
  let received = receivedByListener.get(listener);
11683
11694
  if (!received) {
11684
11695
  received = /* @__PURE__ */ new Set();
11685
11696
  receivedByListener.set(listener, received);
11686
11697
  }
11687
- const fresh = queued.filter((event) => !received.has(event.id));
11688
- if (fresh.length === 0) return;
11689
11698
  for (const event of fresh) received.add(event.id);
11690
11699
  try {
11691
11700
  await listener(fresh);
11701
+ return;
11692
11702
  } catch (error) {
11693
11703
  console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
11694
11704
  for (const event of fresh) received.delete(event.id);
11705
+ if (received.size === 0) receivedByListener.delete(listener);
11695
11706
  }
11696
- });
11697
- await Promise.all(listenerResults);
11707
+ }
11698
11708
  }
11699
11709
  };
11700
11710
  function monitorView(monitor) {
@@ -11803,72 +11813,48 @@ var HttpDaemonClient = class {
11803
11813
  const subscribed = new Promise((resolve) => {
11804
11814
  markSubscribed = () => resolve();
11805
11815
  });
11806
- let drainComplete = false;
11807
- let draining = false;
11808
- let redrainRequested = false;
11809
- let retryTimer;
11810
- let markDrained;
11811
- const drained = new Promise((resolve) => {
11812
- markDrained = resolve;
11813
- });
11814
- let buffered = [];
11815
11816
  let delivering = Promise.resolve();
11817
+ let activeStream;
11818
+ let retryStream = false;
11816
11819
  const deliver = (events) => {
11817
11820
  if (!callbacks?.has(onEvents) || events.length === 0) return Promise.resolve();
11818
- delivering = delivering.then(() => deliverWithRetry(events, 0)).catch((error) => {
11821
+ delivering = delivering.then(async () => {
11822
+ try {
11823
+ await onEvents(events);
11824
+ } catch (error) {
11825
+ console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
11826
+ retryStream = true;
11827
+ activeStream?.abort();
11828
+ return;
11829
+ }
11830
+ await acknowledge(events);
11831
+ }).catch((error) => {
11819
11832
  console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
11820
11833
  });
11821
11834
  return delivering;
11822
11835
  };
11823
- const deliverWithRetry = async (events, attempt) => {
11824
- try {
11825
- await onEvents(events);
11826
- } catch (error) {
11827
- if (attempt >= 2) {
11828
- console.error(`[sourcefed] event delivery failed after retries; re-draining queue: ${error instanceof Error ? error.message : String(error)}`);
11829
- redrainRequested = true;
11830
- void performDrain();
11836
+ const acknowledge = async (events) => {
11837
+ let attempt = 0;
11838
+ while (!controller.signal.aborted && callbacks?.has(onEvents)) {
11839
+ try {
11840
+ await this.request("monitor.ack", { target, eventIDs: events.map((event) => event.id) });
11831
11841
  return;
11842
+ } catch (error) {
11843
+ attempt += 1;
11844
+ if (attempt === 3) {
11845
+ console.error(`[sourcefed] event acknowledgement still failing; retrying without repeating delivery: ${error instanceof Error ? error.message : String(error)}`);
11846
+ }
11847
+ await sleep(Math.min(250 * 2 ** (attempt - 1), 4e3));
11832
11848
  }
11833
- await sleep(250 * (attempt + 1));
11834
- await deliverWithRetry(events, attempt + 1);
11835
- }
11836
- };
11837
- const performDrain = async () => {
11838
- if (draining) {
11839
- redrainRequested = true;
11840
- return;
11841
- }
11842
- draining = true;
11843
- try {
11844
- let passes = 0;
11845
- do {
11846
- redrainRequested = false;
11847
- passes += 1;
11848
- try {
11849
- const drainResult = await this.request("monitor.events", { target });
11850
- drainComplete = true;
11851
- const drainedIds = new Set((drainResult.events ?? []).map((event) => event.id));
11852
- if (drainResult.events && drainResult.events.length > 0) await deliver(drainResult.events);
11853
- for (const batch of buffered) {
11854
- const fresh = batch.filter((event) => !drainedIds.has(event.id));
11855
- if (fresh.length > 0) await deliver(fresh);
11856
- }
11857
- buffered = [];
11858
- markDrained();
11859
- } catch (error) {
11860
- console.error(`[sourcefed] event drain failed: ${error instanceof Error ? error.message : String(error)}`);
11861
- redrainRequested = true;
11862
- }
11863
- } while (redrainRequested && passes < 3);
11864
- if (redrainRequested) markDrained();
11865
- } finally {
11866
- draining = false;
11867
11849
  }
11868
11850
  };
11869
11851
  const runStream = async () => {
11852
+ const streamController = new AbortController();
11853
+ const closeStream = () => streamController.abort();
11854
+ activeStream = streamController;
11855
+ controller.signal.addEventListener("abort", closeStream, { once: true });
11870
11856
  try {
11871
- const response = await fetch(url, { signal: controller.signal, headers: this.headers() });
11857
+ const response = await fetch(url, { signal: streamController.signal, headers: this.headers() });
11872
11858
  if (!response.ok || !response.body) throw new Error(`event stream failed: ${response.status}`);
11873
11859
  const reader = response.body.getReader();
11874
11860
  const decoder = new TextDecoder();
@@ -11884,26 +11870,23 @@ var HttpDaemonClient = class {
11884
11870
  if (!line.startsWith("data: ")) continue;
11885
11871
  const frame = parseDaemonFrame(line.slice(6));
11886
11872
  if (!frame || !("type" in frame)) continue;
11887
- if (frame.type === "subscribed") {
11888
- markSubscribed();
11889
- drainComplete = false;
11890
- void performDrain();
11891
- }
11892
- if (frame.type === "event") {
11893
- if (drainComplete) void deliver(frame.events);
11894
- else buffered.push(frame.events);
11895
- }
11873
+ if (frame.type === "subscribed") markSubscribed();
11874
+ if (frame.type === "event") void deliver(frame.events);
11896
11875
  }
11897
11876
  }
11898
11877
  }
11899
11878
  consecutiveFailures = 0;
11900
11879
  } catch (error) {
11901
- if (!controller.signal.aborted) {
11880
+ if (!controller.signal.aborted && !retryStream) {
11902
11881
  consecutiveFailures += 1;
11903
11882
  if (consecutiveFailures === 1) {
11904
11883
  console.error(`[sourcefed] event stream ended: ${error instanceof Error ? error.message : String(error)}`);
11905
11884
  }
11906
11885
  }
11886
+ } finally {
11887
+ controller.signal.removeEventListener("abort", closeStream);
11888
+ if (activeStream === streamController) activeStream = void 0;
11889
+ retryStream = false;
11907
11890
  }
11908
11891
  };
11909
11892
  let consecutiveFailures = 0;
@@ -11916,9 +11899,7 @@ var HttpDaemonClient = class {
11916
11899
  })();
11917
11900
  try {
11918
11901
  await withTimeout(subscribed, 15e3, `sourcefed event stream did not subscribe at ${url}`);
11919
- await withTimeout(drained, 15e3, "sourcefed initial event drain timed out");
11920
11902
  } catch (error) {
11921
- if (retryTimer) clearTimeout(retryTimer);
11922
11903
  const callbacks2 = this.listeners.get(key);
11923
11904
  callbacks2?.delete(onEvents);
11924
11905
  if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
@@ -11928,7 +11909,6 @@ var HttpDaemonClient = class {
11928
11909
  }
11929
11910
  return {
11930
11911
  close: async () => {
11931
- if (retryTimer) clearTimeout(retryTimer);
11932
11912
  const callbacks2 = this.listeners.get(key);
11933
11913
  callbacks2?.delete(onEvents);
11934
11914
  if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
package/dist/cli.js CHANGED
@@ -11561,11 +11561,16 @@ var SourcefedDaemon = class {
11561
11561
  this.listeners.set(key, listeners);
11562
11562
  }
11563
11563
  listeners.add(onEvents);
11564
+ this.emit(target);
11564
11565
  return () => {
11565
11566
  listeners.delete(onEvents);
11567
+ const receivedByListener = this.received.get(key);
11568
+ const released = receivedByListener?.delete(onEvents) ?? false;
11566
11569
  if (listeners.size === 0) {
11567
11570
  this.listeners.delete(key);
11568
11571
  this.received.delete(key);
11572
+ } else if (released) {
11573
+ this.emit(target);
11569
11574
  }
11570
11575
  };
11571
11576
  }
@@ -11601,23 +11606,28 @@ var SourcefedDaemon = class {
11601
11606
  receivedByListener = /* @__PURE__ */ new Map();
11602
11607
  this.received.set(key, receivedByListener);
11603
11608
  }
11604
- const listenerResults = [...listeners].map(async (listener) => {
11609
+ const claimed = /* @__PURE__ */ new Set();
11610
+ for (const received of receivedByListener.values()) {
11611
+ for (const id of received) claimed.add(id);
11612
+ }
11613
+ const fresh = queued.filter((event) => !claimed.has(event.id));
11614
+ if (fresh.length === 0) return;
11615
+ for (const listener of [...listeners]) {
11605
11616
  let received = receivedByListener.get(listener);
11606
11617
  if (!received) {
11607
11618
  received = /* @__PURE__ */ new Set();
11608
11619
  receivedByListener.set(listener, received);
11609
11620
  }
11610
- const fresh = queued.filter((event) => !received.has(event.id));
11611
- if (fresh.length === 0) return;
11612
11621
  for (const event of fresh) received.add(event.id);
11613
11622
  try {
11614
11623
  await listener(fresh);
11624
+ return;
11615
11625
  } catch (error2) {
11616
11626
  console.error(`[sourcefed] event delivery failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
11617
11627
  for (const event of fresh) received.delete(event.id);
11628
+ if (received.size === 0) receivedByListener.delete(listener);
11618
11629
  }
11619
- });
11620
- await Promise.all(listenerResults);
11630
+ }
11621
11631
  }
11622
11632
  };
11623
11633
  function monitorView(monitor) {
@@ -11726,72 +11736,48 @@ var HttpDaemonClient = class {
11726
11736
  const subscribed = new Promise((resolve) => {
11727
11737
  markSubscribed = () => resolve();
11728
11738
  });
11729
- let drainComplete = false;
11730
- let draining = false;
11731
- let redrainRequested = false;
11732
- let retryTimer;
11733
- let markDrained;
11734
- const drained = new Promise((resolve) => {
11735
- markDrained = resolve;
11736
- });
11737
- let buffered = [];
11738
11739
  let delivering = Promise.resolve();
11740
+ let activeStream;
11741
+ let retryStream = false;
11739
11742
  const deliver = (events2) => {
11740
11743
  if (!callbacks?.has(onEvents) || events2.length === 0) return Promise.resolve();
11741
- delivering = delivering.then(() => deliverWithRetry(events2, 0)).catch((error2) => {
11744
+ delivering = delivering.then(async () => {
11745
+ try {
11746
+ await onEvents(events2);
11747
+ } catch (error2) {
11748
+ console.error(`[sourcefed] event delivery failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
11749
+ retryStream = true;
11750
+ activeStream?.abort();
11751
+ return;
11752
+ }
11753
+ await acknowledge(events2);
11754
+ }).catch((error2) => {
11742
11755
  console.error(`[sourcefed] event delivery failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
11743
11756
  });
11744
11757
  return delivering;
11745
11758
  };
11746
- const deliverWithRetry = async (events2, attempt) => {
11747
- try {
11748
- await onEvents(events2);
11749
- } catch (error2) {
11750
- if (attempt >= 2) {
11751
- console.error(`[sourcefed] event delivery failed after retries; re-draining queue: ${error2 instanceof Error ? error2.message : String(error2)}`);
11752
- redrainRequested = true;
11753
- void performDrain();
11759
+ const acknowledge = async (events2) => {
11760
+ let attempt = 0;
11761
+ while (!controller.signal.aborted && callbacks?.has(onEvents)) {
11762
+ try {
11763
+ await this.request("monitor.ack", { target, eventIDs: events2.map((event) => event.id) });
11754
11764
  return;
11755
- }
11756
- await sleep(250 * (attempt + 1));
11757
- await deliverWithRetry(events2, attempt + 1);
11758
- }
11759
- };
11760
- const performDrain = async () => {
11761
- if (draining) {
11762
- redrainRequested = true;
11763
- return;
11764
- }
11765
- draining = true;
11766
- try {
11767
- let passes = 0;
11768
- do {
11769
- redrainRequested = false;
11770
- passes += 1;
11771
- try {
11772
- const drainResult = await this.request("monitor.events", { target });
11773
- drainComplete = true;
11774
- const drainedIds = new Set((drainResult.events ?? []).map((event) => event.id));
11775
- if (drainResult.events && drainResult.events.length > 0) await deliver(drainResult.events);
11776
- for (const batch of buffered) {
11777
- const fresh = batch.filter((event) => !drainedIds.has(event.id));
11778
- if (fresh.length > 0) await deliver(fresh);
11779
- }
11780
- buffered = [];
11781
- markDrained();
11782
- } catch (error2) {
11783
- console.error(`[sourcefed] event drain failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
11784
- redrainRequested = true;
11765
+ } catch (error2) {
11766
+ attempt += 1;
11767
+ if (attempt === 3) {
11768
+ console.error(`[sourcefed] event acknowledgement still failing; retrying without repeating delivery: ${error2 instanceof Error ? error2.message : String(error2)}`);
11785
11769
  }
11786
- } while (redrainRequested && passes < 3);
11787
- if (redrainRequested) markDrained();
11788
- } finally {
11789
- draining = false;
11770
+ await sleep(Math.min(250 * 2 ** (attempt - 1), 4e3));
11771
+ }
11790
11772
  }
11791
11773
  };
11792
11774
  const runStream = async () => {
11775
+ const streamController = new AbortController();
11776
+ const closeStream = () => streamController.abort();
11777
+ activeStream = streamController;
11778
+ controller.signal.addEventListener("abort", closeStream, { once: true });
11793
11779
  try {
11794
- const response = await fetch(url2, { signal: controller.signal, headers: this.headers() });
11780
+ const response = await fetch(url2, { signal: streamController.signal, headers: this.headers() });
11795
11781
  if (!response.ok || !response.body) throw new Error(`event stream failed: ${response.status}`);
11796
11782
  const reader = response.body.getReader();
11797
11783
  const decoder = new TextDecoder();
@@ -11807,26 +11793,23 @@ var HttpDaemonClient = class {
11807
11793
  if (!line.startsWith("data: ")) continue;
11808
11794
  const frame = parseDaemonFrame(line.slice(6));
11809
11795
  if (!frame || !("type" in frame)) continue;
11810
- if (frame.type === "subscribed") {
11811
- markSubscribed();
11812
- drainComplete = false;
11813
- void performDrain();
11814
- }
11815
- if (frame.type === "event") {
11816
- if (drainComplete) void deliver(frame.events);
11817
- else buffered.push(frame.events);
11818
- }
11796
+ if (frame.type === "subscribed") markSubscribed();
11797
+ if (frame.type === "event") void deliver(frame.events);
11819
11798
  }
11820
11799
  }
11821
11800
  }
11822
11801
  consecutiveFailures = 0;
11823
11802
  } catch (error2) {
11824
- if (!controller.signal.aborted) {
11803
+ if (!controller.signal.aborted && !retryStream) {
11825
11804
  consecutiveFailures += 1;
11826
11805
  if (consecutiveFailures === 1) {
11827
11806
  console.error(`[sourcefed] event stream ended: ${error2 instanceof Error ? error2.message : String(error2)}`);
11828
11807
  }
11829
11808
  }
11809
+ } finally {
11810
+ controller.signal.removeEventListener("abort", closeStream);
11811
+ if (activeStream === streamController) activeStream = void 0;
11812
+ retryStream = false;
11830
11813
  }
11831
11814
  };
11832
11815
  let consecutiveFailures = 0;
@@ -11839,9 +11822,7 @@ var HttpDaemonClient = class {
11839
11822
  })();
11840
11823
  try {
11841
11824
  await withTimeout(subscribed, 15e3, `sourcefed event stream did not subscribe at ${url2}`);
11842
- await withTimeout(drained, 15e3, "sourcefed initial event drain timed out");
11843
11825
  } catch (error2) {
11844
- if (retryTimer) clearTimeout(retryTimer);
11845
11826
  const callbacks2 = this.listeners.get(key);
11846
11827
  callbacks2?.delete(onEvents);
11847
11828
  if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
@@ -11851,7 +11832,6 @@ var HttpDaemonClient = class {
11851
11832
  }
11852
11833
  return {
11853
11834
  close: async () => {
11854
- if (retryTimer) clearTimeout(retryTimer);
11855
11835
  const callbacks2 = this.listeners.get(key);
11856
11836
  callbacks2?.delete(onEvents);
11857
11837
  if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
package/dist/daemon.js CHANGED
@@ -11663,11 +11663,16 @@ var SourcefedDaemon = class {
11663
11663
  this.listeners.set(key, listeners);
11664
11664
  }
11665
11665
  listeners.add(onEvents);
11666
+ this.emit(target);
11666
11667
  return () => {
11667
11668
  listeners.delete(onEvents);
11669
+ const receivedByListener = this.received.get(key);
11670
+ const released = receivedByListener?.delete(onEvents) ?? false;
11668
11671
  if (listeners.size === 0) {
11669
11672
  this.listeners.delete(key);
11670
11673
  this.received.delete(key);
11674
+ } else if (released) {
11675
+ this.emit(target);
11671
11676
  }
11672
11677
  };
11673
11678
  }
@@ -11703,23 +11708,28 @@ var SourcefedDaemon = class {
11703
11708
  receivedByListener = /* @__PURE__ */ new Map();
11704
11709
  this.received.set(key, receivedByListener);
11705
11710
  }
11706
- const listenerResults = [...listeners].map(async (listener) => {
11711
+ const claimed = /* @__PURE__ */ new Set();
11712
+ for (const received of receivedByListener.values()) {
11713
+ for (const id of received) claimed.add(id);
11714
+ }
11715
+ const fresh = queued.filter((event) => !claimed.has(event.id));
11716
+ if (fresh.length === 0) return;
11717
+ for (const listener of [...listeners]) {
11707
11718
  let received = receivedByListener.get(listener);
11708
11719
  if (!received) {
11709
11720
  received = /* @__PURE__ */ new Set();
11710
11721
  receivedByListener.set(listener, received);
11711
11722
  }
11712
- const fresh = queued.filter((event) => !received.has(event.id));
11713
- if (fresh.length === 0) return;
11714
11723
  for (const event of fresh) received.add(event.id);
11715
11724
  try {
11716
11725
  await listener(fresh);
11726
+ return;
11717
11727
  } catch (error) {
11718
11728
  console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
11719
11729
  for (const event of fresh) received.delete(event.id);
11730
+ if (received.size === 0) receivedByListener.delete(listener);
11720
11731
  }
11721
- });
11722
- await Promise.all(listenerResults);
11732
+ }
11723
11733
  }
11724
11734
  };
11725
11735
  function monitorView(monitor) {
@@ -11832,72 +11842,48 @@ var HttpDaemonClient = class {
11832
11842
  const subscribed = new Promise((resolve) => {
11833
11843
  markSubscribed = () => resolve();
11834
11844
  });
11835
- let drainComplete = false;
11836
- let draining = false;
11837
- let redrainRequested = false;
11838
- let retryTimer;
11839
- let markDrained;
11840
- const drained = new Promise((resolve) => {
11841
- markDrained = resolve;
11842
- });
11843
- let buffered = [];
11844
11845
  let delivering = Promise.resolve();
11846
+ let activeStream;
11847
+ let retryStream = false;
11845
11848
  const deliver = (events) => {
11846
11849
  if (!callbacks?.has(onEvents) || events.length === 0) return Promise.resolve();
11847
- delivering = delivering.then(() => deliverWithRetry(events, 0)).catch((error) => {
11850
+ delivering = delivering.then(async () => {
11851
+ try {
11852
+ await onEvents(events);
11853
+ } catch (error) {
11854
+ console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
11855
+ retryStream = true;
11856
+ activeStream?.abort();
11857
+ return;
11858
+ }
11859
+ await acknowledge(events);
11860
+ }).catch((error) => {
11848
11861
  console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
11849
11862
  });
11850
11863
  return delivering;
11851
11864
  };
11852
- const deliverWithRetry = async (events, attempt) => {
11853
- try {
11854
- await onEvents(events);
11855
- } catch (error) {
11856
- if (attempt >= 2) {
11857
- console.error(`[sourcefed] event delivery failed after retries; re-draining queue: ${error instanceof Error ? error.message : String(error)}`);
11858
- redrainRequested = true;
11859
- void performDrain();
11865
+ const acknowledge = async (events) => {
11866
+ let attempt = 0;
11867
+ while (!controller.signal.aborted && callbacks?.has(onEvents)) {
11868
+ try {
11869
+ await this.request("monitor.ack", { target, eventIDs: events.map((event) => event.id) });
11860
11870
  return;
11871
+ } catch (error) {
11872
+ attempt += 1;
11873
+ if (attempt === 3) {
11874
+ console.error(`[sourcefed] event acknowledgement still failing; retrying without repeating delivery: ${error instanceof Error ? error.message : String(error)}`);
11875
+ }
11876
+ await sleep(Math.min(250 * 2 ** (attempt - 1), 4e3));
11861
11877
  }
11862
- await sleep(250 * (attempt + 1));
11863
- await deliverWithRetry(events, attempt + 1);
11864
- }
11865
- };
11866
- const performDrain = async () => {
11867
- if (draining) {
11868
- redrainRequested = true;
11869
- return;
11870
- }
11871
- draining = true;
11872
- try {
11873
- let passes = 0;
11874
- do {
11875
- redrainRequested = false;
11876
- passes += 1;
11877
- try {
11878
- const drainResult = await this.request("monitor.events", { target });
11879
- drainComplete = true;
11880
- const drainedIds = new Set((drainResult.events ?? []).map((event) => event.id));
11881
- if (drainResult.events && drainResult.events.length > 0) await deliver(drainResult.events);
11882
- for (const batch of buffered) {
11883
- const fresh = batch.filter((event) => !drainedIds.has(event.id));
11884
- if (fresh.length > 0) await deliver(fresh);
11885
- }
11886
- buffered = [];
11887
- markDrained();
11888
- } catch (error) {
11889
- console.error(`[sourcefed] event drain failed: ${error instanceof Error ? error.message : String(error)}`);
11890
- redrainRequested = true;
11891
- }
11892
- } while (redrainRequested && passes < 3);
11893
- if (redrainRequested) markDrained();
11894
- } finally {
11895
- draining = false;
11896
11878
  }
11897
11879
  };
11898
11880
  const runStream = async () => {
11881
+ const streamController = new AbortController();
11882
+ const closeStream = () => streamController.abort();
11883
+ activeStream = streamController;
11884
+ controller.signal.addEventListener("abort", closeStream, { once: true });
11899
11885
  try {
11900
- const response = await fetch(url, { signal: controller.signal, headers: this.headers() });
11886
+ const response = await fetch(url, { signal: streamController.signal, headers: this.headers() });
11901
11887
  if (!response.ok || !response.body) throw new Error(`event stream failed: ${response.status}`);
11902
11888
  const reader = response.body.getReader();
11903
11889
  const decoder = new TextDecoder();
@@ -11913,26 +11899,23 @@ var HttpDaemonClient = class {
11913
11899
  if (!line.startsWith("data: ")) continue;
11914
11900
  const frame = parseDaemonFrame(line.slice(6));
11915
11901
  if (!frame || !("type" in frame)) continue;
11916
- if (frame.type === "subscribed") {
11917
- markSubscribed();
11918
- drainComplete = false;
11919
- void performDrain();
11920
- }
11921
- if (frame.type === "event") {
11922
- if (drainComplete) void deliver(frame.events);
11923
- else buffered.push(frame.events);
11924
- }
11902
+ if (frame.type === "subscribed") markSubscribed();
11903
+ if (frame.type === "event") void deliver(frame.events);
11925
11904
  }
11926
11905
  }
11927
11906
  }
11928
11907
  consecutiveFailures = 0;
11929
11908
  } catch (error) {
11930
- if (!controller.signal.aborted) {
11909
+ if (!controller.signal.aborted && !retryStream) {
11931
11910
  consecutiveFailures += 1;
11932
11911
  if (consecutiveFailures === 1) {
11933
11912
  console.error(`[sourcefed] event stream ended: ${error instanceof Error ? error.message : String(error)}`);
11934
11913
  }
11935
11914
  }
11915
+ } finally {
11916
+ controller.signal.removeEventListener("abort", closeStream);
11917
+ if (activeStream === streamController) activeStream = void 0;
11918
+ retryStream = false;
11936
11919
  }
11937
11920
  };
11938
11921
  let consecutiveFailures = 0;
@@ -11945,9 +11928,7 @@ var HttpDaemonClient = class {
11945
11928
  })();
11946
11929
  try {
11947
11930
  await withTimeout(subscribed, 15e3, `sourcefed event stream did not subscribe at ${url}`);
11948
- await withTimeout(drained, 15e3, "sourcefed initial event drain timed out");
11949
11931
  } catch (error) {
11950
- if (retryTimer) clearTimeout(retryTimer);
11951
11932
  const callbacks2 = this.listeners.get(key);
11952
11933
  callbacks2?.delete(onEvents);
11953
11934
  if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
@@ -11957,7 +11938,6 @@ var HttpDaemonClient = class {
11957
11938
  }
11958
11939
  return {
11959
11940
  close: async () => {
11960
- if (retryTimer) clearTimeout(retryTimer);
11961
11941
  const callbacks2 = this.listeners.get(key);
11962
11942
  callbacks2?.delete(onEvents);
11963
11943
  if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
package/dist/mcp.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  decodeTarget,
3
3
  encodeTarget
4
- } from "./chunk-SKEBKW2L.js";
4
+ } from "./chunk-5NFCKXDQ.js";
5
5
  import {
6
6
  __export
7
7
  } from "./chunk-MLKGABMK.js";
package/dist/opencode.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  daemonCommand,
5
5
  daemonEnvironment,
6
6
  spawnLocalDaemon
7
- } from "./chunk-SKEBKW2L.js";
7
+ } from "./chunk-5NFCKXDQ.js";
8
8
  import {
9
9
  __export
10
10
  } from "./chunk-MLKGABMK.js";
@@ -74,20 +74,12 @@ var OpenCodeBridge = class {
74
74
  }
75
75
  }
76
76
  async routeEvents(events) {
77
- const delivered = [];
78
77
  for (const queued of events) {
79
78
  const body = {
80
79
  ...queued.event.actionable ? {} : { noReply: true },
81
80
  parts: [{ type: "text", text: eventText(queued.event) }]
82
81
  };
83
82
  await this.opencode.session.prompt({ path: { id: queued.target.id }, body });
84
- delivered.push(queued);
85
- }
86
- if (delivered.length > 0 && this.daemon) {
87
- await this.daemon.request("monitor.ack", {
88
- target: { kind: "opencode-session", id: delivered[0].target.id },
89
- eventIDs: delivered.map((queued) => queued.id)
90
- });
91
83
  }
92
84
  }
93
85
  target(sessionID) {