@fdcn/sourcefed 0.3.5 → 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.
@@ -1,34 +1,80 @@
1
+ import { type QueuedMonitorEvent } from "@fdcn/sourcefed/core";
1
2
  import { type Plugin } from "@opencode/plugin";
3
+ import { OpenCodeSubscriptions, type SessionLocation } from "./subscriptions.js";
2
4
  type SessionDomain = Plugin.Context["session"];
3
- type RpcDomain = Plugin.Context["rpc"];
5
+ type RpcDomain = (definition: typeof ChauffeurGate) => {
6
+ gate(input: unknown, options: {
7
+ signal: AbortSignal;
8
+ }): Promise<unknown>;
9
+ };
10
+ /**
11
+ * Chauffeur's gate, declared here so sourcefed does not depend on Chauffeur.
12
+ * When the Chauffeur plugin runs in this OpenCode process, it decides which
13
+ * events reach the session; otherwise every event is delivered.
14
+ */
15
+ declare const ChauffeurGate: {
16
+ readonly id: "chauffeur";
17
+ readonly methods: {
18
+ readonly gate: {
19
+ readonly input: {
20
+ readonly type: "object";
21
+ readonly properties: {
22
+ readonly sessionID: {
23
+ readonly type: "string";
24
+ };
25
+ readonly monitorID: {
26
+ readonly type: "string";
27
+ };
28
+ readonly source: {
29
+ readonly type: "string";
30
+ };
31
+ readonly kind: {
32
+ readonly type: "string";
33
+ };
34
+ readonly summary: {
35
+ readonly type: "string";
36
+ };
37
+ readonly body: {
38
+ readonly type: "string";
39
+ };
40
+ readonly actionable: {
41
+ readonly type: "boolean";
42
+ };
43
+ };
44
+ readonly required: readonly ["sessionID", "source", "kind", "summary", "actionable"];
45
+ };
46
+ readonly output: {
47
+ readonly type: "object";
48
+ readonly properties: {
49
+ readonly deliver: {
50
+ readonly type: "boolean";
51
+ };
52
+ };
53
+ readonly required: readonly ["deliver"];
54
+ };
55
+ };
56
+ };
57
+ readonly events: {};
58
+ };
4
59
  export declare class OpenCodeBridge {
5
60
  private readonly session;
6
- private readonly rpc?;
7
- private daemon?;
8
- private readonly listeners;
9
- private lastAttemptAt;
10
- private lastError;
11
- private timer;
12
- constructor(session: SessionDomain, rpc?: RpcDomain | undefined);
61
+ private readonly rpc;
62
+ readonly location: SessionLocation;
63
+ private readonly hub;
64
+ private started;
65
+ private closed;
66
+ private closing;
67
+ constructor(session: Pick<SessionDomain, "get" | "synthetic">, rpc: RpcDomain | undefined, location: SessionLocation, hub?: OpenCodeSubscriptions);
13
68
  /**
14
69
  * Chauffeur or the CLI can create a monitor for a session here at any time,
15
70
  * without this bridge's tools, so check for new targets periodically too.
16
71
  */
17
72
  start(): Promise<void>;
18
- ensureTarget(sessionID: string): Promise<void>;
19
73
  callTool(name: string, arguments_: Record<string, unknown>, sessionID: string): Promise<unknown>;
20
74
  close(): Promise<void>;
21
- private refresh;
22
- private ensureDaemon;
23
- /**
24
- * Monitors outlive this process, so after a restart nothing listens for their
25
- * sessions until something subscribes. Subscribe every session that has an
26
- * enabled monitor and still exists here; a deleted session is skipped, since
27
- * delivering to it would fail and retry forever.
28
- */
29
- private resubscribe;
30
- private resubscribeTarget;
31
- private routeEvents;
75
+ refresh(): Promise<void>;
76
+ getSession(sessionID: string, signal: AbortSignal): Promise<import("@opencode/client").SessionInfo>;
77
+ routeEvents(events: QueuedMonitorEvent[]): Promise<void>;
32
78
  /**
33
79
  * Ask Chauffeur's gate, when it runs in this process. Only an explicit
34
80
  * `deliver: false` withholds the event; no gate, an error, or a slow answer
@@ -37,6 +83,4 @@ export declare class OpenCodeBridge {
37
83
  private admitted;
38
84
  private target;
39
85
  }
40
- export declare function setOpenCodeBridge(bridge: OpenCodeBridge): void;
41
- export declare function getOpenCodeBridge(): OpenCodeBridge;
42
86
  export {};
@@ -0,0 +1,46 @@
1
+ import type { QueuedMonitorEvent } from "@fdcn/sourcefed/core";
2
+ import type { DaemonClient } from "@fdcn/sourcefed/daemon";
3
+ export type SessionLocation = {
4
+ directory: string;
5
+ project: {
6
+ id: string;
7
+ };
8
+ };
9
+ type SessionInfo = {
10
+ projectID: string;
11
+ location: {
12
+ directory: string;
13
+ };
14
+ };
15
+ export interface SubscriptionOwner {
16
+ readonly location: SessionLocation;
17
+ getSession(sessionID: string, signal: AbortSignal): Promise<SessionInfo>;
18
+ routeEvents(events: QueuedMonitorEvent[]): Promise<void>;
19
+ }
20
+ /** One client, timer and stream per enabled session, shared by all plugin locations. */
21
+ export declare class OpenCodeSubscriptions {
22
+ private readonly connect;
23
+ private readonly owners;
24
+ private readonly subscriptions;
25
+ private readonly lifetime;
26
+ private daemon;
27
+ private connecting;
28
+ private refreshing;
29
+ private dirty;
30
+ private lastAttemptAt;
31
+ private timer;
32
+ constructor(connect: () => Promise<DaemonClient>);
33
+ get closed(): boolean;
34
+ add(owner: SubscriptionOwner): Promise<void>;
35
+ remove(owner: SubscriptionOwner): Promise<void>;
36
+ request(method: string, params: Record<string, unknown>): Promise<unknown>;
37
+ refresh(): Promise<void>;
38
+ private refreshLoop;
39
+ private ensureDaemon;
40
+ private reconcile;
41
+ private ownerFor;
42
+ private subscribe;
43
+ private route;
44
+ private closeSubscription;
45
+ }
46
+ export {};
@@ -1 +1,2 @@
1
- export declare function callMonitorTool(name: string, arguments_: Record<string, unknown>, sessionID: string): Promise<string>;
1
+ import type { OpenCodeBridge } from "./bridge.js";
2
+ export declare function callMonitorTool(bridge: OpenCodeBridge, name: string, arguments_: Record<string, unknown>, sessionID: string): Promise<string>;
@@ -1,5 +1,6 @@
1
1
  import type { ToolContext } from "@opencode/plugin/promise/tool";
2
- declare const definition: {
2
+ import type { OpenCodeBridge } from "../bridge.js";
3
+ declare const _default: (bridge: OpenCodeBridge) => {
3
4
  name: string;
4
5
  description: string;
5
6
  input: {
@@ -50,4 +51,4 @@ declare const definition: {
50
51
  content: string;
51
52
  }>;
52
53
  };
53
- export default definition;
54
+ export default _default;
@@ -1,5 +1,6 @@
1
1
  import type { ToolContext } from "@opencode/plugin/promise/tool";
2
- declare const definition: {
2
+ import type { OpenCodeBridge } from "../bridge.js";
3
+ declare const _default: (bridge: OpenCodeBridge) => {
3
4
  name: string;
4
5
  description: string;
5
6
  input: {
@@ -11,4 +12,4 @@ declare const definition: {
11
12
  content: string;
12
13
  }>;
13
14
  };
14
- export default definition;
15
+ export default _default;
@@ -1,5 +1,6 @@
1
1
  import type { ToolContext } from "@opencode/plugin/promise/tool";
2
- declare const definition: {
2
+ import type { OpenCodeBridge } from "../bridge.js";
3
+ declare const _default: (bridge: OpenCodeBridge) => {
3
4
  name: string;
4
5
  description: string;
5
6
  input: {
@@ -17,4 +18,4 @@ declare const definition: {
17
18
  content: string;
18
19
  }>;
19
20
  };
20
- export default definition;
21
+ export default _default;
@@ -1,5 +1,6 @@
1
1
  import type { ToolContext } from "@opencode/plugin/promise/tool";
2
- declare const definition: {
2
+ import type { OpenCodeBridge } from "../bridge.js";
3
+ declare const _default: (bridge: OpenCodeBridge) => {
3
4
  name: string;
4
5
  description: string;
5
6
  input: {
@@ -17,4 +18,4 @@ declare const definition: {
17
18
  content: string;
18
19
  }>;
19
20
  };
20
- export default definition;
21
+ export default _default;
@@ -1,5 +1,6 @@
1
1
  import type { ToolContext } from "@opencode/plugin/promise/tool";
2
- declare const definition: {
2
+ import type { OpenCodeBridge } from "../bridge.js";
3
+ declare const _default: (bridge: OpenCodeBridge) => {
3
4
  name: string;
4
5
  description: string;
5
6
  input: {
@@ -17,4 +18,4 @@ declare const definition: {
17
18
  content: string;
18
19
  }>;
19
20
  };
20
- export default definition;
21
+ export default _default;
@@ -1,3 +1,2 @@
1
1
  export declare function setupSpec(): string;
2
2
  export declare function setupHost(name: string, args: string[], spec: string): Promise<boolean>;
3
- export declare function configureOpenCodeCliPlugin(spec: string): void;
@@ -0,0 +1,26 @@
1
+ import type { QueuedMonitorEvent } from "@fdcn/sourcefed/core";
2
+ /** Owns one SSE connection, its setup, serialized delivery and reconnect loop. */
3
+ export declare class DaemonEventStream {
4
+ private readonly url;
5
+ private readonly headers;
6
+ private readonly onEvents;
7
+ private readonly acknowledge;
8
+ private readonly onClose;
9
+ private readonly controller;
10
+ private readonly subscribed;
11
+ private stream;
12
+ private delivering;
13
+ private closing;
14
+ private ready;
15
+ private failedDelivery;
16
+ constructor(url: string, headers: Record<string, string>, onEvents: (events: QueuedMonitorEvent[]) => Promise<void>, acknowledge: (events: QueuedMonitorEvent[], signal: AbortSignal) => Promise<void>, onClose: () => void);
17
+ start(signal?: AbortSignal): Promise<void>;
18
+ close(): Promise<void>;
19
+ private drain;
20
+ private run;
21
+ private read;
22
+ private frame;
23
+ private deliver;
24
+ private waitForDelivery;
25
+ }
26
+ export declare function pause(ms: number, signal: AbortSignal): Promise<void>;
@@ -10,7 +10,9 @@ export type DaemonClientOptions = {
10
10
  };
11
11
  export interface DaemonClient {
12
12
  request(method: string, params?: Record<string, unknown>): Promise<unknown>;
13
- subscribe(target: MonitorTarget, onEvents: (events: QueuedMonitorEvent[]) => Promise<void>): Promise<{
13
+ subscribe(target: MonitorTarget, onEvents: (events: QueuedMonitorEvent[]) => Promise<void>, options?: {
14
+ signal?: AbortSignal;
15
+ }): Promise<{
14
16
  close(): Promise<void>;
15
17
  }>;
16
18
  close(): Promise<void>;
@@ -1,6 +1,5 @@
1
1
  export { daemonEnvironment } from "./env.js";
2
2
  export { serveHttp, type HttpServer, type HttpServerOptions } from "./http-server.js";
3
- export { RequestRouter } from "./request-router.js";
4
3
  export { DEFAULT_DAEMON_URL, daemonCommand, defaultDaemonUrl, sleep, spawnLocalDaemon, withTimeout } from "./spawn.js";
5
4
  export { NotifyingEventSink } from "./sink.js";
6
5
  export { decodeTarget, encodeTarget, targetKey } from "./target.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fdcn/sourcefed",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "Live Jira, GitHub, and Slack monitoring for agent hosts: CLI, daemon, MCP server, and host plugins.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,32 +0,0 @@
1
- import type { MonitorEvent, MonitorEventSink, MonitorRecord } from "@fdcn/sourcefed/core";
2
- export type ForwardOptions = {
3
- url: string;
4
- token?: string;
5
- fetch?: typeof fetch;
6
- };
7
- /** The JSON body POSTed for each delivered event. */
8
- export type ForwardedEvent = {
9
- target: MonitorRecord["target"];
10
- monitorID: string;
11
- source: MonitorRecord["source"];
12
- event: MonitorEvent;
13
- };
14
- /** `SOURCEFED_FORWARD_URL` (and optional `SOURCEFED_FORWARD_TOKEN`), when set. */
15
- export declare function forwardFromEnv(): ForwardOptions | undefined;
16
- /**
17
- * Delivers to `inner`, then POSTs a copy of each delivered event to another
18
- * local consumer. Forwarding never blocks or fails delivery.
19
- */
20
- export declare class ForwardingEventSink implements MonitorEventSink {
21
- private readonly inner;
22
- private readonly options;
23
- constructor(inner: MonitorEventSink, options: ForwardOptions);
24
- deliver(input: {
25
- monitor: MonitorRecord;
26
- event: MonitorEvent;
27
- }): Promise<{
28
- ok: boolean;
29
- error?: string;
30
- }>;
31
- private forward;
32
- }
@@ -1,33 +0,0 @@
1
- import type { MonitorEvent, MonitorEventSink, MonitorRecord } from "@fdcn/sourcefed/core";
2
- export type GateOptions = {
3
- url: string;
4
- token?: string;
5
- fetch?: typeof fetch;
6
- };
7
- /** The JSON body POSTed for each event; the gate replies `{ "deliver": boolean }`. */
8
- export type GatedEvent = {
9
- target: MonitorRecord["target"];
10
- monitorID: string;
11
- source: MonitorRecord["source"];
12
- event: MonitorEvent;
13
- };
14
- /** `SOURCEFED_GATE_URL` (and optional `SOURCEFED_GATE_TOKEN`), when set. */
15
- export declare function gateFromEnv(): GateOptions | undefined;
16
- /**
17
- * Asks another local consumer whether each event should reach the session.
18
- * Only an explicit `{ "deliver": false }` withholds it; a failed, slow, or
19
- * unclear answer delivers, so the gate can never lose an event.
20
- */
21
- export declare class GatedEventSink implements MonitorEventSink {
22
- private readonly inner;
23
- private readonly options;
24
- constructor(inner: MonitorEventSink, options: GateOptions);
25
- deliver(input: {
26
- monitor: MonitorRecord;
27
- event: MonitorEvent;
28
- }): Promise<{
29
- ok: boolean;
30
- error?: string;
31
- }>;
32
- private withheld;
33
- }
@@ -1,11 +0,0 @@
1
- export declare class RequestRouter {
2
- private readonly pending;
3
- private cursor;
4
- nextID(): number;
5
- register(id: number): Promise<unknown>;
6
- settle(id: number, frame: {
7
- result?: unknown;
8
- error?: string;
9
- }): void;
10
- rejectAll(error: Error): void;
11
- }