@fdcn/sourcefed 0.3.6 → 0.3.7
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 +1 -1
- package/dist/{chunk-X3BZSHKZ.js → chunk-E6J3BAXP.js} +198 -160
- package/dist/cli.js +200 -165
- package/dist/daemon.js +200 -162
- package/dist/mcp.js +1 -1
- package/dist/opencode.js +258 -121
- package/dist/pi.js +1 -1
- package/dist.dts/adapters/opencode/bridge.d.ts +66 -22
- package/dist.dts/adapters/opencode/subscriptions.d.ts +46 -0
- package/dist.dts/adapters/opencode/tool-result.d.ts +2 -1
- package/dist.dts/adapters/opencode/tools/monitor_create.d.ts +3 -2
- package/dist.dts/adapters/opencode/tools/monitor_list.d.ts +3 -2
- package/dist.dts/adapters/opencode/tools/monitor_start.d.ts +3 -2
- package/dist.dts/adapters/opencode/tools/monitor_status.d.ts +3 -2
- package/dist.dts/adapters/opencode/tools/monitor_stop.d.ts +3 -2
- package/dist.dts/packages/daemon/src/event-stream.d.ts +26 -0
- package/dist.dts/packages/daemon/src/types/client.d.ts +3 -1
- package/dist.dts/packages/daemon/src/utils/index.d.ts +0 -1
- package/package.json +1 -1
- package/dist.dts/packages/daemon/src/utils/forward.d.ts +0 -32
- package/dist.dts/packages/daemon/src/utils/gate.d.ts +0 -33
- package/dist.dts/packages/daemon/src/utils/request-router.d.ts +0 -11
- /package/dist.dts/{packages/daemon/src/forward.test.d.ts → adapters/opencode/lifecycle.test.d.ts} +0 -0
- /package/dist.dts/packages/daemon/src/{gate.test.d.ts → stream-lifecycle.test.d.ts} +0 -0
package/dist/daemon.js
CHANGED
|
@@ -10460,31 +10460,6 @@ async function writeResponse(outgoing, response) {
|
|
|
10460
10460
|
});
|
|
10461
10461
|
}
|
|
10462
10462
|
|
|
10463
|
-
// packages/daemon/src/utils/request-router.ts
|
|
10464
|
-
var RequestRouter = class {
|
|
10465
|
-
pending = /* @__PURE__ */ new Map();
|
|
10466
|
-
cursor = 1;
|
|
10467
|
-
nextID() {
|
|
10468
|
-
return this.cursor++;
|
|
10469
|
-
}
|
|
10470
|
-
register(id) {
|
|
10471
|
-
return new Promise((resolve, reject) => {
|
|
10472
|
-
this.pending.set(id, { resolve, reject });
|
|
10473
|
-
});
|
|
10474
|
-
}
|
|
10475
|
-
settle(id, frame) {
|
|
10476
|
-
const pending = this.pending.get(id);
|
|
10477
|
-
if (!pending) return;
|
|
10478
|
-
this.pending.delete(id);
|
|
10479
|
-
if (frame.error) pending.reject(new Error(frame.error));
|
|
10480
|
-
else pending.resolve(frame.result);
|
|
10481
|
-
}
|
|
10482
|
-
rejectAll(error) {
|
|
10483
|
-
for (const { reject } of this.pending.values()) reject(error);
|
|
10484
|
-
this.pending.clear();
|
|
10485
|
-
}
|
|
10486
|
-
};
|
|
10487
|
-
|
|
10488
10463
|
// packages/daemon/src/utils/spawn.ts
|
|
10489
10464
|
import { spawn } from "node:child_process";
|
|
10490
10465
|
import path5 from "node:path";
|
|
@@ -10890,165 +10865,228 @@ function parseDaemonFrame(line) {
|
|
|
10890
10865
|
}
|
|
10891
10866
|
}
|
|
10892
10867
|
|
|
10868
|
+
// packages/daemon/src/event-stream.ts
|
|
10869
|
+
var DaemonEventStream = class {
|
|
10870
|
+
constructor(url, headers, onEvents, acknowledge, onClose) {
|
|
10871
|
+
this.url = url;
|
|
10872
|
+
this.headers = headers;
|
|
10873
|
+
this.onEvents = onEvents;
|
|
10874
|
+
this.acknowledge = acknowledge;
|
|
10875
|
+
this.onClose = onClose;
|
|
10876
|
+
}
|
|
10877
|
+
controller = new AbortController();
|
|
10878
|
+
subscribed = Promise.withResolvers();
|
|
10879
|
+
stream;
|
|
10880
|
+
delivering = Promise.resolve();
|
|
10881
|
+
closing;
|
|
10882
|
+
ready = false;
|
|
10883
|
+
failedDelivery = false;
|
|
10884
|
+
async start(signal) {
|
|
10885
|
+
const abort = () => {
|
|
10886
|
+
void this.close();
|
|
10887
|
+
};
|
|
10888
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
10889
|
+
const setup = withTimeout(this.subscribed.promise, 15e3, `sourcefed event stream did not subscribe at ${this.url}`);
|
|
10890
|
+
if (signal?.aborted) abort();
|
|
10891
|
+
const running = this.run();
|
|
10892
|
+
void running.finally(() => signal?.removeEventListener("abort", abort));
|
|
10893
|
+
try {
|
|
10894
|
+
await setup;
|
|
10895
|
+
} catch (error) {
|
|
10896
|
+
await this.close();
|
|
10897
|
+
throw error;
|
|
10898
|
+
}
|
|
10899
|
+
}
|
|
10900
|
+
close() {
|
|
10901
|
+
if (!this.closing) {
|
|
10902
|
+
this.subscribed.reject(new Error("sourcefed event stream closed"));
|
|
10903
|
+
if (!this.ready) this.controller.abort();
|
|
10904
|
+
this.closing = this.drain();
|
|
10905
|
+
}
|
|
10906
|
+
return this.closing;
|
|
10907
|
+
}
|
|
10908
|
+
async drain() {
|
|
10909
|
+
try {
|
|
10910
|
+
await withTimeout(this.delivering, 15e3, "sourcefed event delivery did not drain");
|
|
10911
|
+
} catch (error) {
|
|
10912
|
+
console.error(`[sourcefed] closing event stream: ${String(error)}`);
|
|
10913
|
+
} finally {
|
|
10914
|
+
this.controller.abort();
|
|
10915
|
+
this.stream?.abort();
|
|
10916
|
+
this.onClose();
|
|
10917
|
+
}
|
|
10918
|
+
}
|
|
10919
|
+
async run() {
|
|
10920
|
+
let consecutiveFailures = 0;
|
|
10921
|
+
while (!this.controller.signal.aborted) {
|
|
10922
|
+
this.failedDelivery = false;
|
|
10923
|
+
try {
|
|
10924
|
+
await this.read();
|
|
10925
|
+
consecutiveFailures = 0;
|
|
10926
|
+
} catch (error) {
|
|
10927
|
+
if (!this.controller.signal.aborted && !this.failedDelivery && ++consecutiveFailures === 1) {
|
|
10928
|
+
console.error(`[sourcefed] event stream ended: ${String(error)}`);
|
|
10929
|
+
}
|
|
10930
|
+
}
|
|
10931
|
+
await this.waitForDelivery();
|
|
10932
|
+
if (!this.controller.signal.aborted) await pause(500, this.controller.signal);
|
|
10933
|
+
}
|
|
10934
|
+
}
|
|
10935
|
+
async read() {
|
|
10936
|
+
const controller = new AbortController();
|
|
10937
|
+
const abort = () => controller.abort();
|
|
10938
|
+
this.stream = controller;
|
|
10939
|
+
this.controller.signal.addEventListener("abort", abort, { once: true });
|
|
10940
|
+
let reader;
|
|
10941
|
+
try {
|
|
10942
|
+
this.controller.signal.throwIfAborted();
|
|
10943
|
+
const response = await fetch(this.url, { signal: controller.signal, headers: this.headers });
|
|
10944
|
+
if (!response.ok || !response.body) {
|
|
10945
|
+
await response.body?.cancel();
|
|
10946
|
+
throw new Error(`event stream failed: ${response.status}`);
|
|
10947
|
+
}
|
|
10948
|
+
reader = response.body.getReader();
|
|
10949
|
+
const decoder = new TextDecoder();
|
|
10950
|
+
let buffer = "";
|
|
10951
|
+
while (!controller.signal.aborted) {
|
|
10952
|
+
const { done, value } = await reader.read();
|
|
10953
|
+
if (done) return;
|
|
10954
|
+
buffer += decoder.decode(value, { stream: true });
|
|
10955
|
+
const frames = buffer.split("\n\n");
|
|
10956
|
+
buffer = frames.pop() ?? "";
|
|
10957
|
+
for (const chunk of frames) await this.frame(chunk);
|
|
10958
|
+
}
|
|
10959
|
+
} finally {
|
|
10960
|
+
await reader?.cancel().catch(() => void 0);
|
|
10961
|
+
reader?.releaseLock();
|
|
10962
|
+
this.controller.signal.removeEventListener("abort", abort);
|
|
10963
|
+
if (this.stream === controller) this.stream = void 0;
|
|
10964
|
+
}
|
|
10965
|
+
}
|
|
10966
|
+
async frame(chunk) {
|
|
10967
|
+
if (this.closing || this.controller.signal.aborted) return;
|
|
10968
|
+
for (const line of chunk.split("\n")) {
|
|
10969
|
+
if (!line.startsWith("data: ")) continue;
|
|
10970
|
+
const frame = parseDaemonFrame(line.slice(6));
|
|
10971
|
+
if (!frame || !("type" in frame)) continue;
|
|
10972
|
+
if (frame.type === "subscribed") {
|
|
10973
|
+
this.ready = true;
|
|
10974
|
+
this.subscribed.resolve();
|
|
10975
|
+
}
|
|
10976
|
+
if (frame.type === "event" && frame.events.length > 0) {
|
|
10977
|
+
this.deliver(frame.events);
|
|
10978
|
+
await this.waitForDelivery();
|
|
10979
|
+
}
|
|
10980
|
+
}
|
|
10981
|
+
}
|
|
10982
|
+
deliver(events) {
|
|
10983
|
+
this.delivering = this.delivering.then(async () => {
|
|
10984
|
+
if (this.failedDelivery || this.controller.signal.aborted) return;
|
|
10985
|
+
try {
|
|
10986
|
+
await this.onEvents(events);
|
|
10987
|
+
await this.acknowledge(events, this.controller.signal);
|
|
10988
|
+
} catch (error) {
|
|
10989
|
+
if (!this.controller.signal.aborted) console.error(`[sourcefed] event delivery failed: ${String(error)}`);
|
|
10990
|
+
this.failedDelivery = true;
|
|
10991
|
+
this.stream?.abort();
|
|
10992
|
+
}
|
|
10993
|
+
});
|
|
10994
|
+
}
|
|
10995
|
+
async waitForDelivery() {
|
|
10996
|
+
if (this.controller.signal.aborted) return;
|
|
10997
|
+
const stopped = Promise.withResolvers();
|
|
10998
|
+
const abort = () => stopped.resolve();
|
|
10999
|
+
this.controller.signal.addEventListener("abort", abort, { once: true });
|
|
11000
|
+
try {
|
|
11001
|
+
await Promise.race([this.delivering, stopped.promise]);
|
|
11002
|
+
} finally {
|
|
11003
|
+
this.controller.signal.removeEventListener("abort", abort);
|
|
11004
|
+
}
|
|
11005
|
+
}
|
|
11006
|
+
};
|
|
11007
|
+
function pause(ms, signal) {
|
|
11008
|
+
return new Promise((resolve) => {
|
|
11009
|
+
const finish = () => {
|
|
11010
|
+
clearTimeout(timer);
|
|
11011
|
+
signal.removeEventListener("abort", finish);
|
|
11012
|
+
resolve();
|
|
11013
|
+
};
|
|
11014
|
+
const timer = setTimeout(finish, ms);
|
|
11015
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
11016
|
+
if (signal.aborted) finish();
|
|
11017
|
+
});
|
|
11018
|
+
}
|
|
11019
|
+
|
|
10893
11020
|
// packages/daemon/src/client.ts
|
|
10894
11021
|
async function connectDaemonClient(options) {
|
|
10895
11022
|
const url = options.url ?? process.env.SOURCEFED_DAEMON_URL ?? defaultDaemonUrl();
|
|
10896
11023
|
return new HttpDaemonClient(url, options.token ?? process.env.SOURCEFED_DAEMON_TOKEN);
|
|
10897
11024
|
}
|
|
10898
11025
|
var HttpDaemonClient = class {
|
|
10899
|
-
baseURL;
|
|
10900
|
-
token;
|
|
10901
|
-
router = new RequestRouter();
|
|
10902
|
-
listeners = /* @__PURE__ */ new Map();
|
|
10903
|
-
streams = /* @__PURE__ */ new Set();
|
|
10904
11026
|
constructor(baseURL, token) {
|
|
10905
|
-
this.baseURL = baseURL.replace(/\/+$/, "");
|
|
10906
11027
|
this.token = token;
|
|
11028
|
+
this.baseURL = baseURL.replace(/\/+$/, "");
|
|
10907
11029
|
}
|
|
11030
|
+
baseURL;
|
|
11031
|
+
streams = /* @__PURE__ */ new Set();
|
|
11032
|
+
lifetime = new AbortController();
|
|
11033
|
+
cursor = 0;
|
|
11034
|
+
closing;
|
|
10908
11035
|
headers() {
|
|
10909
11036
|
return this.token ? { authorization: `Bearer ${this.token}` } : {};
|
|
10910
11037
|
}
|
|
10911
|
-
async request(method, params) {
|
|
10912
|
-
|
|
10913
|
-
|
|
11038
|
+
async request(method, params, signal = this.lifetime.signal) {
|
|
11039
|
+
if (this.closing && signal === this.lifetime.signal) throw new Error("daemon closed");
|
|
11040
|
+
signal.throwIfAborted();
|
|
11041
|
+
const response = await fetch(`${this.baseURL}/rpc`, {
|
|
11042
|
+
method: "POST",
|
|
11043
|
+
headers: { "content-type": "application/json", ...this.headers() },
|
|
11044
|
+
body: JSON.stringify({ id: ++this.cursor, method, params }),
|
|
11045
|
+
signal: AbortSignal.any([signal, this.lifetime.signal, AbortSignal.timeout(15e3)])
|
|
11046
|
+
});
|
|
11047
|
+
const frame = await response.json();
|
|
11048
|
+
if (!response.ok || frame.error) throw new Error(frame.error ?? `daemon request failed: ${response.status}`);
|
|
11049
|
+
return frame.result;
|
|
11050
|
+
}
|
|
11051
|
+
async subscribe(target, onEvents, options) {
|
|
11052
|
+
if (this.closing) throw new Error("daemon closed");
|
|
11053
|
+
this.lifetime.signal.throwIfAborted();
|
|
11054
|
+
const stream = new DaemonEventStream(
|
|
11055
|
+
`${this.baseURL}/events?target=${encodeTarget(target)}`,
|
|
11056
|
+
this.headers(),
|
|
11057
|
+
onEvents,
|
|
11058
|
+
(events, signal) => this.acknowledge(target, events, signal),
|
|
11059
|
+
() => this.streams.delete(stream)
|
|
11060
|
+
);
|
|
11061
|
+
this.streams.add(stream);
|
|
10914
11062
|
try {
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
headers: { "content-type": "application/json", ...this.headers() },
|
|
10918
|
-
body: JSON.stringify({ id, method, params })
|
|
10919
|
-
});
|
|
10920
|
-
const frame = await response.json();
|
|
10921
|
-
if (!response.ok || frame.error) throw new Error(frame.error ?? `daemon request failed: ${response.status}`);
|
|
10922
|
-
this.router.settle(id, { result: frame.result });
|
|
11063
|
+
await stream.start(options?.signal);
|
|
11064
|
+
return { close: () => stream.close() };
|
|
10923
11065
|
} catch (error) {
|
|
10924
|
-
|
|
11066
|
+
await stream.close();
|
|
11067
|
+
throw error;
|
|
10925
11068
|
}
|
|
10926
|
-
return promise;
|
|
10927
11069
|
}
|
|
10928
|
-
async
|
|
10929
|
-
|
|
10930
|
-
|
|
10931
|
-
if (!callbacks) {
|
|
10932
|
-
callbacks = /* @__PURE__ */ new Set();
|
|
10933
|
-
this.listeners.set(key, callbacks);
|
|
10934
|
-
}
|
|
10935
|
-
callbacks.add(onEvents);
|
|
10936
|
-
const controller = new AbortController();
|
|
10937
|
-
this.streams.add(controller);
|
|
10938
|
-
const url = `${this.baseURL}/events?target=${encodeTarget(target)}`;
|
|
10939
|
-
let markSubscribed;
|
|
10940
|
-
const subscribed = new Promise((resolve) => {
|
|
10941
|
-
markSubscribed = () => resolve();
|
|
10942
|
-
});
|
|
10943
|
-
let delivering = Promise.resolve();
|
|
10944
|
-
let activeStream;
|
|
10945
|
-
let retryStream = false;
|
|
10946
|
-
const deliver = (events) => {
|
|
10947
|
-
if (!callbacks?.has(onEvents) || events.length === 0) return Promise.resolve();
|
|
10948
|
-
delivering = delivering.then(async () => {
|
|
10949
|
-
try {
|
|
10950
|
-
await onEvents(events);
|
|
10951
|
-
} catch (error) {
|
|
10952
|
-
console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
10953
|
-
retryStream = true;
|
|
10954
|
-
activeStream?.abort();
|
|
10955
|
-
return;
|
|
10956
|
-
}
|
|
10957
|
-
await acknowledge(events);
|
|
10958
|
-
}).catch((error) => {
|
|
10959
|
-
console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
10960
|
-
});
|
|
10961
|
-
return delivering;
|
|
10962
|
-
};
|
|
10963
|
-
const acknowledge = async (events) => {
|
|
10964
|
-
let attempt = 0;
|
|
10965
|
-
while (!controller.signal.aborted && callbacks?.has(onEvents)) {
|
|
10966
|
-
try {
|
|
10967
|
-
await this.request("monitor.ack", { target, eventIDs: events.map((event) => event.id) });
|
|
10968
|
-
return;
|
|
10969
|
-
} catch (error) {
|
|
10970
|
-
attempt += 1;
|
|
10971
|
-
if (attempt === 3) {
|
|
10972
|
-
console.error(`[sourcefed] event acknowledgement still failing; retrying without repeating delivery: ${error instanceof Error ? error.message : String(error)}`);
|
|
10973
|
-
}
|
|
10974
|
-
await sleep(Math.min(250 * 2 ** (attempt - 1), 4e3));
|
|
10975
|
-
}
|
|
10976
|
-
}
|
|
10977
|
-
};
|
|
10978
|
-
const runStream = async () => {
|
|
10979
|
-
const streamController = new AbortController();
|
|
10980
|
-
const closeStream = () => streamController.abort();
|
|
10981
|
-
activeStream = streamController;
|
|
10982
|
-
controller.signal.addEventListener("abort", closeStream, { once: true });
|
|
11070
|
+
async acknowledge(target, events, signal) {
|
|
11071
|
+
let attempt = 0;
|
|
11072
|
+
while (!signal.aborted) {
|
|
10983
11073
|
try {
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
const reader = response.body.getReader();
|
|
10987
|
-
const decoder = new TextDecoder();
|
|
10988
|
-
let buffer = "";
|
|
10989
|
-
while (true) {
|
|
10990
|
-
const { done, value } = await reader.read();
|
|
10991
|
-
if (done) break;
|
|
10992
|
-
buffer += decoder.decode(value, { stream: true });
|
|
10993
|
-
const frames = buffer.split("\n\n");
|
|
10994
|
-
buffer = frames.pop() ?? "";
|
|
10995
|
-
for (const chunk of frames) {
|
|
10996
|
-
for (const line of chunk.split("\n")) {
|
|
10997
|
-
if (!line.startsWith("data: ")) continue;
|
|
10998
|
-
const frame = parseDaemonFrame(line.slice(6));
|
|
10999
|
-
if (!frame || !("type" in frame)) continue;
|
|
11000
|
-
if (frame.type === "subscribed") markSubscribed();
|
|
11001
|
-
if (frame.type === "event") void deliver(frame.events);
|
|
11002
|
-
}
|
|
11003
|
-
}
|
|
11004
|
-
}
|
|
11005
|
-
consecutiveFailures = 0;
|
|
11074
|
+
await this.request("monitor.ack", { target, eventIDs: events.map((event) => event.id) }, signal);
|
|
11075
|
+
return;
|
|
11006
11076
|
} catch (error) {
|
|
11007
|
-
if (
|
|
11008
|
-
|
|
11009
|
-
if (consecutiveFailures === 1) {
|
|
11010
|
-
console.error(`[sourcefed] event stream ended: ${error instanceof Error ? error.message : String(error)}`);
|
|
11011
|
-
}
|
|
11077
|
+
if (++attempt === 3) {
|
|
11078
|
+
console.error(`[sourcefed] event acknowledgement still failing; retrying without repeating delivery: ${String(error)}`);
|
|
11012
11079
|
}
|
|
11013
|
-
|
|
11014
|
-
controller.signal.removeEventListener("abort", closeStream);
|
|
11015
|
-
if (activeStream === streamController) activeStream = void 0;
|
|
11016
|
-
retryStream = false;
|
|
11017
|
-
}
|
|
11018
|
-
};
|
|
11019
|
-
let consecutiveFailures = 0;
|
|
11020
|
-
void (async () => {
|
|
11021
|
-
while (!controller.signal.aborted && callbacks?.has(onEvents)) {
|
|
11022
|
-
await runStream();
|
|
11023
|
-
if (controller.signal.aborted) break;
|
|
11024
|
-
await sleep(500);
|
|
11080
|
+
await pause(Math.min(250 * 2 ** Math.min(attempt - 1, 4), 4e3), signal);
|
|
11025
11081
|
}
|
|
11026
|
-
})();
|
|
11027
|
-
try {
|
|
11028
|
-
await withTimeout(subscribed, 15e3, `sourcefed event stream did not subscribe at ${url}`);
|
|
11029
|
-
} catch (error) {
|
|
11030
|
-
const callbacks2 = this.listeners.get(key);
|
|
11031
|
-
callbacks2?.delete(onEvents);
|
|
11032
|
-
if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
|
|
11033
|
-
controller.abort();
|
|
11034
|
-
this.streams.delete(controller);
|
|
11035
|
-
throw error;
|
|
11036
11082
|
}
|
|
11037
|
-
return {
|
|
11038
|
-
close: async () => {
|
|
11039
|
-
const callbacks2 = this.listeners.get(key);
|
|
11040
|
-
callbacks2?.delete(onEvents);
|
|
11041
|
-
if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
|
|
11042
|
-
controller.abort();
|
|
11043
|
-
this.streams.delete(controller);
|
|
11044
|
-
}
|
|
11045
|
-
};
|
|
11046
11083
|
}
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
|
|
11084
|
+
close() {
|
|
11085
|
+
this.closing ??= Promise.all([...this.streams].map((stream) => stream.close())).then(() => {
|
|
11086
|
+
this.lifetime.abort();
|
|
11087
|
+
this.streams.clear();
|
|
11088
|
+
});
|
|
11089
|
+
return this.closing;
|
|
11052
11090
|
}
|
|
11053
11091
|
};
|
|
11054
11092
|
|