@interactive-inc/claude-funnel 0.64.2 → 0.66.0
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/bin.js +5 -5
- package/dist/broadcaster-DVNcw00K.d.ts +138 -0
- package/dist/channel.d.ts +161 -0
- package/dist/channel.js +203 -0
- package/dist/{channels-CRGb6B5_.d.ts → channels-DR90RmUP.d.ts} +5 -4
- package/dist/claude.d.ts +4 -4
- package/dist/claude.js +1 -1
- package/dist/{connector-descriptor-BFIhyTfa.d.ts → connector-descriptor-CmyHlEiT.d.ts} +3 -26
- package/dist/connectors/discord.d.ts +27 -6
- package/dist/connectors/discord.js +1 -1
- package/dist/connectors/gh.d.ts +4 -3
- package/dist/connectors/schedule.d.ts +4 -3
- package/dist/connectors/slack.d.ts +4 -3
- package/dist/diagnostics.d.ts +1 -1
- package/dist/{discord-connector-DIFkYBbi.js → discord-connector-DXARcO6w.js} +18 -2
- package/dist/docs.d.ts +1 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/{file-process-guard-tVcgckH6.d.ts → file-process-guard-CiBwXjzm.d.ts} +5 -4
- package/dist/{flume-source-listener-BNyAII7N.d.ts → flume-source-listener-FzR5lGpL.d.ts} +2 -1
- package/dist/{funnel-diagnostics-b9ar0Ing.d.ts → funnel-diagnostics-kbd27d7I.d.ts} +1 -1
- package/dist/{funnel-doctor-CnRQi4kM.d.ts → funnel-doctor-CVTNUtDn.d.ts} +2 -2
- package/dist/{funnel-recovery-CMhY8Jfk.d.ts → funnel-recovery-D8MkiTpk.d.ts} +1 -1
- package/dist/gateway/daemon.js +19 -19
- package/dist/gateway.d.ts +3 -2
- package/dist/{index-Ds6sHhA-.d.ts → index-BU1TtuFy.d.ts} +6 -139
- package/dist/index.d.ts +19 -17
- package/dist/local-config.d.ts +2 -2
- package/dist/logger-Bc_Ooypb.d.ts +25 -0
- package/dist/{memory-token-prompter-BoV8Hf-n.d.ts → memory-token-prompter-D0kIdhar.d.ts} +2 -2
- package/dist/{profiles-cVZQkM69.d.ts → profiles-M24JrcOh.d.ts} +3 -3
- package/dist/profiles.d.ts +1 -1
- package/dist/recovery.d.ts +1 -1
- package/dist/{settings-reader-BNxjsxCB.d.ts → settings-reader-BnKIbmsD.d.ts} +1 -1
- package/package.json +7 -2
- /package/dist/{discord-connector-schema-D-bOVAKt.d.ts → discord-connector-schema-D1dyxhXk.d.ts} +0 -0
- /package/dist/{file-system-VhwwXZbm.d.ts → file-system-DHgbuTFb.d.ts} +0 -0
- /package/dist/{funnel-docs-CNklHvbt.d.ts → funnel-docs-Cd66sKyV.d.ts} +0 -0
- /package/dist/{gh-connector-schema-DWQaB6gX.d.ts → gh-connector-schema-BCHEqpUg.d.ts} +0 -0
- /package/dist/{schedule-connector-schema-Z0RXLgPI.d.ts → schedule-connector-schema-D7ZEKvit.d.ts} +0 -0
- /package/dist/{settings-schema-BL_c2Udm.d.ts → settings-schema-IShSMm5G.d.ts} +0 -0
- /package/dist/{slack-event-processor-BhCf5Wiy.d.ts → slack-event-processor-BDTr62cN.d.ts} +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { t as FunnelLogger } from "./logger-Bc_Ooypb.js";
|
|
2
|
+
import { ServerWebSocket } from "bun";
|
|
3
|
+
|
|
4
|
+
//#region lib/engine/error/on-funnel-error.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Host integration hook called when Funnel catches an exception that would
|
|
7
|
+
* otherwise be silently swallowed (subscriber throw, listener start failure,
|
|
8
|
+
* MCP forward failure, etc.). Pass `Sentry.captureException` from the host to
|
|
9
|
+
* pipe these into your error reporter. Defaults to a no-op when omitted.
|
|
10
|
+
*
|
|
11
|
+
* `context` carries the component name and any extra metadata the caller had
|
|
12
|
+
* at the catch site (channel / connector / subscriber id when available).
|
|
13
|
+
*/
|
|
14
|
+
type OnFunnelError = (error: Error, context?: Record<string, unknown>) => void;
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region lib/gateway/broadcaster.d.ts
|
|
17
|
+
type ClientData = {
|
|
18
|
+
/** Stable channel id (uuid) that the WS client subscribed to. */channel: string; /** Human-facing channel name resolved at upgrade time, kept for log readability. */
|
|
19
|
+
channelName?: string | null; /** Connector names belonging to that channel. */
|
|
20
|
+
connectors: string[]; /** Routing mode resolved from channel config at upgrade time. Defaults to fanout. */
|
|
21
|
+
delivery?: "fanout" | "exclusive";
|
|
22
|
+
/**
|
|
23
|
+
* Opaque per-client id declared at upgrade time (`?id=<subscriberId>`). When an
|
|
24
|
+
* event carries `meta.target`, only the client whose `subscriberId` equals it
|
|
25
|
+
* receives the event among that channel's regular subscribers. Targeted delivery
|
|
26
|
+
* is how a publisher addresses one specific instance (e.g. a single agent
|
|
27
|
+
* session) without every subscriber having to receive and discard it.
|
|
28
|
+
*/
|
|
29
|
+
subscriberId?: string;
|
|
30
|
+
};
|
|
31
|
+
type BroadcastEvent = {
|
|
32
|
+
content: string;
|
|
33
|
+
meta?: Record<string, string>;
|
|
34
|
+
};
|
|
35
|
+
type ReplayableEvent = BroadcastEvent & {
|
|
36
|
+
offset: number;
|
|
37
|
+
};
|
|
38
|
+
type BroadcastSubscriber = (event: ReplayableEvent) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Optional persistent replay source. Wired in by the gateway-server with a
|
|
41
|
+
* `FunnelEventLog` (SQLite-backed by default) so reconnects across daemon
|
|
42
|
+
* restarts can recover events older than the in-memory buffer via an indexed
|
|
43
|
+
* `seq > since` range scan.
|
|
44
|
+
*/
|
|
45
|
+
type ReplaySource = {
|
|
46
|
+
loadSince(since: number): ReplayableEvent[];
|
|
47
|
+
};
|
|
48
|
+
type Deps = {
|
|
49
|
+
logger?: FunnelLogger; /** Host hook for surfacing subscriber-throw exceptions. Defaults to no-op. */
|
|
50
|
+
onError?: OnFunnelError;
|
|
51
|
+
maxBufferedBytes?: number;
|
|
52
|
+
now?: () => number; /** Number of recent events kept in the in-memory replay buffer. */
|
|
53
|
+
replayBufferSize?: number; /** Hard byte cap on replay buffer payloads. Older events are evicted FIFO until under this cap. */
|
|
54
|
+
replayBufferMaxBytes?: number; /** Persistent replay source consulted when the in-memory buffer cannot satisfy `since`. */
|
|
55
|
+
persistentReplay?: ReplaySource;
|
|
56
|
+
};
|
|
57
|
+
type BroadcasterMetrics = {
|
|
58
|
+
clients: number;
|
|
59
|
+
subscribers: number;
|
|
60
|
+
eventsBroadcast: number;
|
|
61
|
+
droppedSlowClients: number;
|
|
62
|
+
lastBroadcastAt: string | null; /** Latest emitted offset. Clients can `?since=<offset>` to ask for events strictly after this point. */
|
|
63
|
+
latestOffset: number; /** Oldest offset still held in the replay buffer. Older values cannot be replayed and trigger a full resync. */
|
|
64
|
+
oldestReplayableOffset: number | null;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* In-process pub/sub for connector events.
|
|
68
|
+
*
|
|
69
|
+
* Two outbound paths:
|
|
70
|
+
* - WS clients connected via the gateway's `/ws` endpoint, scoped per channel
|
|
71
|
+
* - In-process subscribers registered via `subscribe()` (programmable API)
|
|
72
|
+
*
|
|
73
|
+
* Backpressure: if a WS client's `bufferedAmount` exceeds `maxBufferedBytes`
|
|
74
|
+
* (default 1 MiB), the client is closed with code 1009 and dropped from the
|
|
75
|
+
* registry to keep one slow consumer from blocking the daemon.
|
|
76
|
+
*
|
|
77
|
+
* Replay: every emitted event gets a strictly increasing `offset`. The latest
|
|
78
|
+
* `replayBufferSize` events are kept in memory; reconnecting WS clients can
|
|
79
|
+
* pass `?since=<offset>` and the broadcaster resends matching events before
|
|
80
|
+
* resuming the live stream. The in-memory ring covers short reconnects;
|
|
81
|
+
* older history is served from the event log wired in as `persistentReplay`.
|
|
82
|
+
*/
|
|
83
|
+
declare class FunnelBroadcaster {
|
|
84
|
+
private readonly clients;
|
|
85
|
+
private readonly subscribers;
|
|
86
|
+
private readonly logger;
|
|
87
|
+
private readonly onError;
|
|
88
|
+
private readonly maxBufferedBytes;
|
|
89
|
+
private readonly now;
|
|
90
|
+
private readonly replayBufferSize;
|
|
91
|
+
private readonly replayBufferMaxBytes;
|
|
92
|
+
private readonly replayBuffer;
|
|
93
|
+
private readonly persistentReplay;
|
|
94
|
+
private readonly exclusiveCursor;
|
|
95
|
+
private replayBufferBytes;
|
|
96
|
+
private eventsBroadcast;
|
|
97
|
+
private droppedSlowClients;
|
|
98
|
+
private lastBroadcastAt;
|
|
99
|
+
private latestOffset;
|
|
100
|
+
constructor(deps?: Deps);
|
|
101
|
+
getMetrics(): BroadcasterMetrics;
|
|
102
|
+
/**
|
|
103
|
+
* Returns events with offset > since, filtered by the connector subscription
|
|
104
|
+
* rules of `data`. Used at WS upgrade time when the client passes `?since=<offset>`.
|
|
105
|
+
*
|
|
106
|
+
* Two-tier lookup:
|
|
107
|
+
* 1. The in-memory ring buffer (covers short reconnects, last `replayBufferSize` events).
|
|
108
|
+
* 2. If `since` predates the oldest in-memory entry and a persistent replay source
|
|
109
|
+
* is wired in (SQLite by default), the gap is filled from it. This covers reconnects
|
|
110
|
+
* across daemon restarts where the in-memory buffer was lost.
|
|
111
|
+
*
|
|
112
|
+
* Result is sorted ascending by offset and de-duplicated against the in-memory buffer.
|
|
113
|
+
*/
|
|
114
|
+
replaySince(since: number, data: ClientData): ReplayableEvent[];
|
|
115
|
+
private matchesClient;
|
|
116
|
+
/**
|
|
117
|
+
* Returns the list of WS clients that should receive `event`. For each per-channel group:
|
|
118
|
+
* - fanout → every matching client receives
|
|
119
|
+
* - exclusive → exactly one client receives, picked round-robin per channel
|
|
120
|
+
*
|
|
121
|
+
* `meta.target` narrows the recipient set via `matchesClient`: only the subscriber
|
|
122
|
+
* whose `subscriberId` equals `target` receives a targeted event.
|
|
123
|
+
*/
|
|
124
|
+
private pickRecipients;
|
|
125
|
+
addClient(ws: ServerWebSocket<unknown>, data: ClientData): void;
|
|
126
|
+
removeClient(ws: ServerWebSocket<unknown>): void;
|
|
127
|
+
getClientCount(): number;
|
|
128
|
+
listChannels(): {
|
|
129
|
+
channel: string;
|
|
130
|
+
connectors: string[];
|
|
131
|
+
}[];
|
|
132
|
+
subscribe(handler: BroadcastSubscriber): () => void;
|
|
133
|
+
broadcast(content: string, meta?: Record<string, string>): ReplayableEvent;
|
|
134
|
+
/** Forward-seed the offset counter (used at startup from the persisted event store). */
|
|
135
|
+
seedLatestOffset(offset: number): void;
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
export { OnFunnelError as a, ReplayableEvent as i, BroadcastSubscriber as n, FunnelBroadcaster as r, BroadcastEvent as t };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { n as FunnelClock, t as FunnelLogger } from "./logger-Bc_Ooypb.js";
|
|
2
|
+
import { n as FunnelFileSystem } from "./file-system-DHgbuTFb.js";
|
|
3
|
+
import { r as FunnelBroadcaster } from "./broadcaster-DVNcw00K.js";
|
|
4
|
+
import { FlumeCatchupPolicy, FlumeErrorHandler, FlumeEvent, FlumeRuntimeDeps, FlumeSource, FlumeStatePersister } from "@interactive-inc/flume";
|
|
5
|
+
|
|
6
|
+
//#region lib/engine/channel/channel.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* 1 tick / 1 event を `broadcaster.broadcast(content, meta)` に流すための整形済みペイロード。
|
|
9
|
+
* `null` を返すと「drop」(broadcast しない) を意味する
|
|
10
|
+
*/
|
|
11
|
+
type ChannelBroadcastPayload = {
|
|
12
|
+
readonly content: string;
|
|
13
|
+
readonly meta?: Record<string, string>;
|
|
14
|
+
};
|
|
15
|
+
type ChannelTransform = (event: FlumeEvent) => ChannelBroadcastPayload | null;
|
|
16
|
+
/**
|
|
17
|
+
* Channel が `build(ctx)` で返す実体。flume sources の集合と任意の transform を 1 まとまりとして
|
|
18
|
+
* supervisor に渡す
|
|
19
|
+
*/
|
|
20
|
+
type ChannelRuntime = {
|
|
21
|
+
readonly sources: ReadonlyArray<FlumeSource>;
|
|
22
|
+
/**
|
|
23
|
+
* 各 source の FlumeEvent を broadcast payload に変換する関数。
|
|
24
|
+
* 省略時は `{ content: JSON.stringify(event.data), meta: event.meta }` でフォールバック
|
|
25
|
+
*/
|
|
26
|
+
readonly transform?: ChannelTransform;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Channel.build に渡される環境。FunnelClock / FunnelLogger / FunnelFileSystem を経由して
|
|
30
|
+
* IO 境界を抽象化する。statePersister は funnel-fs に紐づいた `FlumeStatePersister<S>` を
|
|
31
|
+
* channel id-scoped なファイル名で生成するヘルパー
|
|
32
|
+
*/
|
|
33
|
+
type ChannelBuildContext = {
|
|
34
|
+
readonly channelId: string;
|
|
35
|
+
readonly channelName: string;
|
|
36
|
+
readonly signal: AbortSignal;
|
|
37
|
+
readonly logger: FunnelLogger;
|
|
38
|
+
readonly clock: FunnelClock;
|
|
39
|
+
readonly fs: FunnelFileSystem;
|
|
40
|
+
/**
|
|
41
|
+
* `<funnelDir>/channels/<channelId>/<filename>.json` に Read/Write する純粋 DI 用 helper。
|
|
42
|
+
* flume が要求する `FlumeStatePersister<S>` 形に合わせる
|
|
43
|
+
*/
|
|
44
|
+
readonly statePersister: <S>(filename: string) => FlumeStatePersister<S>;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* 1 channel = 1 inbound 受信単位。`build` が confluence に挿す sources を返す。
|
|
48
|
+
* `build` は host abort / token rotation 等で何度でも再呼び出しされうる「純粋なファクトリ」
|
|
49
|
+
*/
|
|
50
|
+
type Channel = {
|
|
51
|
+
readonly id: string;
|
|
52
|
+
readonly name?: string;
|
|
53
|
+
readonly build: (ctx: ChannelBuildContext) => ChannelRuntime | Promise<ChannelRuntime>;
|
|
54
|
+
};
|
|
55
|
+
/** `Channel` を frozen で返す identity helper。consumer の型推論を整える */
|
|
56
|
+
declare function defineChannel(channel: Channel): Channel;
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region lib/engine/channel/channel-supervisor.d.ts
|
|
59
|
+
type Props$1 = {
|
|
60
|
+
readonly broadcaster: FunnelBroadcaster;
|
|
61
|
+
readonly logger: FunnelLogger;
|
|
62
|
+
readonly clock: FunnelClock;
|
|
63
|
+
readonly fs: FunnelFileSystem;
|
|
64
|
+
/**
|
|
65
|
+
* Channel ごとの state / 永続化ファイルの起点。
|
|
66
|
+
* `<root>/channels/<channelId>/` 配下に書き込む
|
|
67
|
+
*/
|
|
68
|
+
readonly dir: string;
|
|
69
|
+
readonly deps?: FlumeRuntimeDeps;
|
|
70
|
+
readonly onError?: FlumeErrorHandler; /** Host abort. fired すると全 channel を close する */
|
|
71
|
+
readonly signal?: AbortSignal;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Channel-manifest を `FlumeConfluence` 1 つにマッピングする supervisor。
|
|
75
|
+
*
|
|
76
|
+
* - `register(channel)` で受け入れた channel は `start()` 時に `build(ctx)` され
|
|
77
|
+
* sources が confluence に `add(channelId, sources)` で挿される
|
|
78
|
+
* - confluence の onItem (groupId 付き) を 1 本受けて、対応する channel の `transform`
|
|
79
|
+
* を通してから `broadcaster.broadcast(content, meta)` に流す
|
|
80
|
+
* - `unregister(id)` で個別停止、`stop()` で全停止
|
|
81
|
+
*
|
|
82
|
+
* 既存の `FunnelListenerRegistry` (ConnectorDescriptor 系) とは独立。
|
|
83
|
+
* 並走させて段階的に移行する設計
|
|
84
|
+
*/
|
|
85
|
+
declare class FunnelChannelSupervisor {
|
|
86
|
+
private readonly props;
|
|
87
|
+
private readonly confluence;
|
|
88
|
+
private readonly registered;
|
|
89
|
+
private readonly pending;
|
|
90
|
+
private started;
|
|
91
|
+
constructor(props: Props$1);
|
|
92
|
+
/** start 前 / 後どちらでも受け付ける。start 後は即座に build + add する */
|
|
93
|
+
register(channel: Channel): void;
|
|
94
|
+
unregister(id: string): Promise<void>;
|
|
95
|
+
start(): Promise<void>;
|
|
96
|
+
stop(): Promise<void>;
|
|
97
|
+
ids(): ReadonlyArray<string>;
|
|
98
|
+
has(id: string): boolean;
|
|
99
|
+
private openChannel;
|
|
100
|
+
private handleItem;
|
|
101
|
+
private transformToPayload;
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region lib/engine/channel/time-channel.d.ts
|
|
105
|
+
type Options = {
|
|
106
|
+
readonly id: string;
|
|
107
|
+
readonly name?: string;
|
|
108
|
+
readonly cron: string; /** 既定: `{ mode: "lastOnly" }` (起動時に直近 1 件だけ取り戻す) */
|
|
109
|
+
readonly catchupPolicy?: FlumeCatchupPolicy;
|
|
110
|
+
/**
|
|
111
|
+
* 各 tick を broadcast payload に変換する関数。省略時は
|
|
112
|
+
* `{ content: "tick", meta: { cron, firedAt } }` 相当
|
|
113
|
+
*/
|
|
114
|
+
readonly transform?: ChannelTransform;
|
|
115
|
+
/**
|
|
116
|
+
* `statePersister` を有効にするかどうか (既定 true)。`false` にすると catchup も無効
|
|
117
|
+
*/
|
|
118
|
+
readonly persist?: boolean;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* 1 cron entry の time channel を作る factory。
|
|
122
|
+
*
|
|
123
|
+
* 使用例 (inta jiho):
|
|
124
|
+
* ```ts
|
|
125
|
+
* const jiho = timeChannel({
|
|
126
|
+
* id: "inta-jiho",
|
|
127
|
+
* cron: "0 * * * *",
|
|
128
|
+
* transform: (event) => ({
|
|
129
|
+
* content: `⏰ ${new Date(event.data.firedAt as number).getHours()}時をお知らせします。`,
|
|
130
|
+
* meta: { event_type: "schedule", source: "inta-jiho", target_role: "primary" },
|
|
131
|
+
* }),
|
|
132
|
+
* })
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* `persist: true` (既定) の場合、`<channelDir>/time.json` に `lastFiredAt` を残し、
|
|
136
|
+
* 起動時に `catchupPolicy` (既定 lastOnly) に従って過去 tick を取り戻す
|
|
137
|
+
*/
|
|
138
|
+
declare function timeChannel(options: Options): Channel;
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region lib/engine/channel/file-state-persister.d.ts
|
|
141
|
+
type Props = {
|
|
142
|
+
readonly fs: FunnelFileSystem;
|
|
143
|
+
readonly path: string;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* `FunnelFileSystem` 上に JSON で 1 行 / 1 状態を保存する `FlumeStatePersister`。
|
|
147
|
+
* 保存先ディレクトリは load/save の前に再帰作成する。
|
|
148
|
+
* load は不在 / parse 失敗時に `null` を返す (flume 側で素直に「初回扱い」になる)
|
|
149
|
+
*/
|
|
150
|
+
declare function createFileStatePersister<S>(props: Props): FlumeStatePersister<S>;
|
|
151
|
+
type DirProps = {
|
|
152
|
+
readonly fs: FunnelFileSystem; /** `<funnelDir>/channels/<channelId>` の絶対パス */
|
|
153
|
+
readonly channelDir: string;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* channel 単位の state persister ファクトリ。`statePersister<S>("time")` を呼ぶと
|
|
157
|
+
* `<channelDir>/time.json` 用の persister を返す
|
|
158
|
+
*/
|
|
159
|
+
declare function createChannelStatePersisterFactory(props: DirProps): <S>(filename: string) => FlumeStatePersister<S>;
|
|
160
|
+
//#endregion
|
|
161
|
+
export { type Channel, type ChannelBroadcastPayload, type ChannelBuildContext, type ChannelRuntime, type ChannelTransform, FunnelChannelSupervisor, createChannelStatePersisterFactory, createFileStatePersister, defineChannel, timeChannel };
|
package/dist/channel.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { FlumeConfluence, FlumeTimeSource } from "@interactive-inc/flume";
|
|
3
|
+
//#region lib/engine/channel/channel.ts
|
|
4
|
+
/** `Channel` を frozen で返す identity helper。consumer の型推論を整える */
|
|
5
|
+
function defineChannel(channel) {
|
|
6
|
+
return Object.freeze({ ...channel });
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region lib/engine/channel/file-state-persister.ts
|
|
10
|
+
/**
|
|
11
|
+
* `FunnelFileSystem` 上に JSON で 1 行 / 1 状態を保存する `FlumeStatePersister`。
|
|
12
|
+
* 保存先ディレクトリは load/save の前に再帰作成する。
|
|
13
|
+
* load は不在 / parse 失敗時に `null` を返す (flume 側で素直に「初回扱い」になる)
|
|
14
|
+
*/
|
|
15
|
+
function createFileStatePersister(props) {
|
|
16
|
+
return {
|
|
17
|
+
async load() {
|
|
18
|
+
if (!props.fs.existsSync(props.path)) return null;
|
|
19
|
+
const raw = props.fs.readFileSync(props.path);
|
|
20
|
+
if (raw === "") return null;
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
async save(state) {
|
|
28
|
+
const dir = dirname(props.path);
|
|
29
|
+
if (!props.fs.existsSync(dir)) props.fs.mkdirSync(dir, { recursive: true });
|
|
30
|
+
props.fs.writeFileSync(props.path, JSON.stringify(state));
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* channel 単位の state persister ファクトリ。`statePersister<S>("time")` を呼ぶと
|
|
36
|
+
* `<channelDir>/time.json` 用の persister を返す
|
|
37
|
+
*/
|
|
38
|
+
function createChannelStatePersisterFactory(props) {
|
|
39
|
+
return (filename) => createFileStatePersister({
|
|
40
|
+
fs: props.fs,
|
|
41
|
+
path: join(props.channelDir, `${filename}.json`)
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region lib/engine/channel/channel-supervisor.ts
|
|
46
|
+
/**
|
|
47
|
+
* Channel-manifest を `FlumeConfluence` 1 つにマッピングする supervisor。
|
|
48
|
+
*
|
|
49
|
+
* - `register(channel)` で受け入れた channel は `start()` 時に `build(ctx)` され
|
|
50
|
+
* sources が confluence に `add(channelId, sources)` で挿される
|
|
51
|
+
* - confluence の onItem (groupId 付き) を 1 本受けて、対応する channel の `transform`
|
|
52
|
+
* を通してから `broadcaster.broadcast(content, meta)` に流す
|
|
53
|
+
* - `unregister(id)` で個別停止、`stop()` で全停止
|
|
54
|
+
*
|
|
55
|
+
* 既存の `FunnelListenerRegistry` (ConnectorDescriptor 系) とは独立。
|
|
56
|
+
* 並走させて段階的に移行する設計
|
|
57
|
+
*/
|
|
58
|
+
var FunnelChannelSupervisor = class {
|
|
59
|
+
confluence;
|
|
60
|
+
registered = /* @__PURE__ */ new Map();
|
|
61
|
+
pending = /* @__PURE__ */ new Map();
|
|
62
|
+
started = false;
|
|
63
|
+
constructor(props) {
|
|
64
|
+
this.props = props;
|
|
65
|
+
this.confluence = new FlumeConfluence({
|
|
66
|
+
onEvent: (item) => this.handleItem(item),
|
|
67
|
+
onError: props.onError,
|
|
68
|
+
deps: props.deps
|
|
69
|
+
});
|
|
70
|
+
if (props.signal) {
|
|
71
|
+
const onAbort = () => {
|
|
72
|
+
this.stop().catch(() => {});
|
|
73
|
+
};
|
|
74
|
+
if (props.signal.aborted) this.started = true;
|
|
75
|
+
else props.signal.addEventListener("abort", onAbort, { once: true });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** start 前 / 後どちらでも受け付ける。start 後は即座に build + add する */
|
|
79
|
+
register(channel) {
|
|
80
|
+
if (this.registered.has(channel.id) || this.pending.has(channel.id)) throw new Error(`FunnelChannelSupervisor: channel id already registered: ${channel.id}`);
|
|
81
|
+
if (!this.started) {
|
|
82
|
+
this.pending.set(channel.id, channel);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this.openChannel(channel);
|
|
86
|
+
}
|
|
87
|
+
async unregister(id) {
|
|
88
|
+
this.pending.delete(id);
|
|
89
|
+
const entry = this.registered.get(id);
|
|
90
|
+
if (!entry) return;
|
|
91
|
+
this.registered.delete(id);
|
|
92
|
+
entry.abortController.abort();
|
|
93
|
+
await this.confluence.remove(id);
|
|
94
|
+
}
|
|
95
|
+
async start() {
|
|
96
|
+
if (this.started) return;
|
|
97
|
+
this.started = true;
|
|
98
|
+
const channels = [...this.pending.values()];
|
|
99
|
+
this.pending.clear();
|
|
100
|
+
for (const channel of channels) await this.openChannel(channel);
|
|
101
|
+
}
|
|
102
|
+
async stop() {
|
|
103
|
+
this.started = false;
|
|
104
|
+
for (const entry of this.registered.values()) entry.abortController.abort();
|
|
105
|
+
this.registered.clear();
|
|
106
|
+
this.pending.clear();
|
|
107
|
+
await this.confluence.closeAll();
|
|
108
|
+
}
|
|
109
|
+
ids() {
|
|
110
|
+
return [...this.registered.keys()];
|
|
111
|
+
}
|
|
112
|
+
has(id) {
|
|
113
|
+
return this.registered.has(id) || this.pending.has(id);
|
|
114
|
+
}
|
|
115
|
+
async openChannel(channel) {
|
|
116
|
+
const channelDir = join(this.props.dir, "channels", channel.id);
|
|
117
|
+
const abortController = new AbortController();
|
|
118
|
+
const runtime = await channel.build({
|
|
119
|
+
channelId: channel.id,
|
|
120
|
+
channelName: channel.name ?? channel.id,
|
|
121
|
+
signal: abortController.signal,
|
|
122
|
+
logger: this.props.logger,
|
|
123
|
+
clock: this.props.clock,
|
|
124
|
+
fs: this.props.fs,
|
|
125
|
+
statePersister: createChannelStatePersisterFactory({
|
|
126
|
+
fs: this.props.fs,
|
|
127
|
+
channelDir
|
|
128
|
+
})
|
|
129
|
+
});
|
|
130
|
+
const result = await this.confluence.add(channel.id, runtime.sources);
|
|
131
|
+
if (result instanceof Error) {
|
|
132
|
+
this.props.logger.error(`channel "${channel.id}" failed to start`, { error: result.message });
|
|
133
|
+
abortController.abort();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
this.registered.set(channel.id, {
|
|
137
|
+
channel,
|
|
138
|
+
runtime,
|
|
139
|
+
abortController
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
handleItem(item) {
|
|
143
|
+
if (item.kind !== "event") return;
|
|
144
|
+
const entry = this.registered.get(item.groupId);
|
|
145
|
+
if (!entry) return;
|
|
146
|
+
const payload = this.transformToPayload(entry, item.event);
|
|
147
|
+
if (payload === null) return;
|
|
148
|
+
this.props.broadcaster.broadcast(payload.content, payload.meta);
|
|
149
|
+
}
|
|
150
|
+
transformToPayload(entry, event) {
|
|
151
|
+
const transform = entry.runtime.transform;
|
|
152
|
+
if (transform === void 0) return {
|
|
153
|
+
content: JSON.stringify(event.data),
|
|
154
|
+
meta: event.meta
|
|
155
|
+
};
|
|
156
|
+
const out = transform(event);
|
|
157
|
+
if (out === null) return null;
|
|
158
|
+
return {
|
|
159
|
+
content: out.content,
|
|
160
|
+
meta: out.meta
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region lib/engine/channel/time-channel.ts
|
|
166
|
+
/**
|
|
167
|
+
* 1 cron entry の time channel を作る factory。
|
|
168
|
+
*
|
|
169
|
+
* 使用例 (inta jiho):
|
|
170
|
+
* ```ts
|
|
171
|
+
* const jiho = timeChannel({
|
|
172
|
+
* id: "inta-jiho",
|
|
173
|
+
* cron: "0 * * * *",
|
|
174
|
+
* transform: (event) => ({
|
|
175
|
+
* content: `⏰ ${new Date(event.data.firedAt as number).getHours()}時をお知らせします。`,
|
|
176
|
+
* meta: { event_type: "schedule", source: "inta-jiho", target_role: "primary" },
|
|
177
|
+
* }),
|
|
178
|
+
* })
|
|
179
|
+
* ```
|
|
180
|
+
*
|
|
181
|
+
* `persist: true` (既定) の場合、`<channelDir>/time.json` に `lastFiredAt` を残し、
|
|
182
|
+
* 起動時に `catchupPolicy` (既定 lastOnly) に従って過去 tick を取り戻す
|
|
183
|
+
*/
|
|
184
|
+
function timeChannel(options) {
|
|
185
|
+
const persist = options.persist ?? true;
|
|
186
|
+
const catchupPolicy = options.catchupPolicy ?? { mode: "lastOnly" };
|
|
187
|
+
return defineChannel({
|
|
188
|
+
id: options.id,
|
|
189
|
+
name: options.name ?? options.id,
|
|
190
|
+
build: (ctx) => {
|
|
191
|
+
return {
|
|
192
|
+
sources: [new FlumeTimeSource({
|
|
193
|
+
cron: options.cron,
|
|
194
|
+
statePersister: persist ? ctx.statePersister("time") : void 0,
|
|
195
|
+
catchupPolicy: persist ? catchupPolicy : { mode: "off" }
|
|
196
|
+
})],
|
|
197
|
+
transform: options.transform
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
export { FunnelChannelSupervisor, createChannelStatePersisterFactory, createFileStatePersister, defineChannel, timeChannel };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import { n as
|
|
3
|
-
import {
|
|
4
|
-
import { n as
|
|
1
|
+
import { n as FunnelClock, t as FunnelLogger } from "./logger-Bc_Ooypb.js";
|
|
2
|
+
import { n as FunnelFileSystem } from "./file-system-DHgbuTFb.js";
|
|
3
|
+
import { n as ChannelDeliveryMode, t as ChannelConfig } from "./settings-schema-IShSMm5G.js";
|
|
4
|
+
import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-BnKIbmsD.js";
|
|
5
|
+
import { A as FunnelConnectorListener, F as BaseConnectorConfig, M as CallInput, N as FunnelConnectorAdapter, c as FunnelHttpClient, g as ConnectorDiagnosticLog, n as ConnectorBuildContext, o as ConnectorOperationContext, r as ConnectorDescriptor, s as ConnectorUpdateContext, z as FunnelProcessRunner } from "./connector-descriptor-CmyHlEiT.js";
|
|
5
6
|
|
|
6
7
|
//#region lib/engine/connectors/connector-registry.d.ts
|
|
7
8
|
type Deps$1 = {
|
package/dist/claude.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as
|
|
3
|
-
import {
|
|
4
|
-
import { t as FunnelProfiles } from "./profiles-
|
|
1
|
+
import { n as FunnelFileSystem } from "./file-system-DHgbuTFb.js";
|
|
2
|
+
import { a as ProcessGuard, c as ChannelResolver, i as SessionStore, n as FunnelClaude, o as McpInstaller, r as LaunchOptions, s as GatewayController, t as FunnelFileProcessGuard } from "./file-process-guard-CiBwXjzm.js";
|
|
3
|
+
import { r as ConnectorDescriptor } from "./connector-descriptor-CmyHlEiT.js";
|
|
4
|
+
import { t as FunnelProfiles } from "./profiles-M24JrcOh.js";
|
|
5
5
|
|
|
6
6
|
//#region lib/engine/mcp/mcp.d.ts
|
|
7
7
|
declare const FUNNEL_MCP_COMMAND = "bun";
|
package/dist/claude.js
CHANGED
|
@@ -4,7 +4,7 @@ import { a as FunnelMcp, i as FUNNEL_MCP_NAME, n as FUNNEL_MCP_ARGS, o as Funnel
|
|
|
4
4
|
import { t as errorMessageOf } from "./error-message-of-ColuYmAk.js";
|
|
5
5
|
import { t as FunnelDocs } from "./funnel-docs-C-ge0MuB.js";
|
|
6
6
|
import { t as FunnelProfiles } from "./profiles-ZHLONml4.js";
|
|
7
|
-
import { t as discordConnector } from "./discord-connector-
|
|
7
|
+
import { t as discordConnector } from "./discord-connector-DXARcO6w.js";
|
|
8
8
|
import { t as ghConnector } from "./gh-connector-BUGCOEWS.js";
|
|
9
9
|
import { t as scheduleConnector } from "./schedule-connector-9k3gOIgl.js";
|
|
10
10
|
import { t as slackConnector } from "./slack-connector-CxpWagbT.js";
|
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
import { n as
|
|
1
|
+
import { n as FunnelClock, t as FunnelLogger } from "./logger-Bc_Ooypb.js";
|
|
2
|
+
import { n as FunnelFileSystem } from "./file-system-DHgbuTFb.js";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
|
|
4
|
-
//#region lib/engine/logger/logger.d.ts
|
|
5
|
-
/**
|
|
6
|
-
* Structured logger with three levels and an optional log-file path.
|
|
7
|
-
* Defaults to NodeFunnelLogger (appends to `<os.tmpdir()>/funnel/funnel.log`);
|
|
8
|
-
* MemoryFunnelLogger captures entries in memory and NoopFunnelLogger silences output.
|
|
9
|
-
*/
|
|
10
|
-
declare abstract class FunnelLogger {
|
|
11
|
-
abstract info(message: string, meta?: Record<string, unknown>): void;
|
|
12
|
-
abstract warn(message: string, meta?: Record<string, unknown>): void;
|
|
13
|
-
abstract error(message: string, meta?: Record<string, unknown>): void;
|
|
14
|
-
abstract readonly file: string | null;
|
|
15
|
-
}
|
|
16
|
-
//#endregion
|
|
17
5
|
//#region lib/engine/process/process-runner.d.ts
|
|
18
6
|
type RunOptions = {
|
|
19
7
|
cwd?: string;
|
|
@@ -315,17 +303,6 @@ declare abstract class FunnelHttpClient {
|
|
|
315
303
|
abstract fetch(request: HttpRequest): Promise<HttpResponse>;
|
|
316
304
|
}
|
|
317
305
|
//#endregion
|
|
318
|
-
//#region lib/engine/time/clock.d.ts
|
|
319
|
-
/**
|
|
320
|
-
* Time boundary. Default NodeFunnelClock returns `new Date()`; MemoryFunnelClock
|
|
321
|
-
* is settable and `advance(ms)`-able for deterministic schedule / timeout tests.
|
|
322
|
-
*/
|
|
323
|
-
declare abstract class FunnelClock {
|
|
324
|
-
abstract now(): Date;
|
|
325
|
-
millis(): number;
|
|
326
|
-
iso(): string;
|
|
327
|
-
}
|
|
328
|
-
//#endregion
|
|
329
306
|
//#region lib/engine/connectors/connector-descriptor.d.ts
|
|
330
307
|
/** Boundaries a listener needs, supplied by the registry at build time. */
|
|
331
308
|
type ConnectorListenerDeps = {
|
|
@@ -400,4 +377,4 @@ type ConnectorDescriptor = {
|
|
|
400
377
|
operations: Record<string, ConnectorOperation>;
|
|
401
378
|
};
|
|
402
379
|
//#endregion
|
|
403
|
-
export {
|
|
380
|
+
export { FunnelConnectorListener as A, ProcessSnapshot as B, ConnectorRawRecord as C, connectorConnectionEventSchema as D, StoredRawEvent as E, BaseConnectorConfig as F, RunResult as H, baseConnectorConfigSchema as I, AttachOptions as L, CallInput as M, FunnelConnectorAdapter as N, connectorProcessedEventSchema as O, JsonValue as P, DetachOptions as R, ConnectorRawQuery as S, StoredProcessedEvent as T, RunOptions as V, ConnectorProcessedEvent as _, ConnectorOperation as a, ConnectorQuery as b, FunnelHttpClient as c, CONNECTOR_CONNECTION_STATUSES as d, ConnectorConnectionEvent as f, ConnectorDiagnosticLog as g, ConnectorConnectionStatus as h, ConnectorListenerDeps as i, NotifyFn as j, connectorRawEventSchema as k, HttpRequest as l, ConnectorConnectionRecord as m, ConnectorBuildContext as n, ConnectorOperationContext as o, ConnectorConnectionQuery as p, ConnectorDescriptor as r, ConnectorUpdateContext as s, ConnectorAdapterDeps as t, HttpResponse as u, ConnectorProcessedQuery as v, StoredConnectionEvent as w, ConnectorRawEvent as x, ConnectorProcessedRecord as y, FunnelProcessRunner as z };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { t as
|
|
1
|
+
import { t as FunnelLogger } from "../logger-Bc_Ooypb.js";
|
|
2
|
+
import { M as CallInput, N as FunnelConnectorAdapter, c as FunnelHttpClient, g as ConnectorDiagnosticLog, j as NotifyFn, r as ConnectorDescriptor } from "../connector-descriptor-CmyHlEiT.js";
|
|
3
|
+
import { n as discordConnectorSchema, t as DiscordConnectorConfig } from "../discord-connector-schema-D1dyxhXk.js";
|
|
4
|
+
import { t as FunnelFlumeSourceListener } from "../flume-source-listener-FzR5lGpL.js";
|
|
4
5
|
import { FlumeRuntimeDeps } from "@interactive-inc/flume";
|
|
5
6
|
|
|
6
7
|
//#region lib/engine/connectors/discord-adapter.d.ts
|
|
@@ -17,6 +18,24 @@ declare class FunnelDiscordAdapter extends FunnelConnectorAdapter {
|
|
|
17
18
|
}
|
|
18
19
|
//#endregion
|
|
19
20
|
//#region lib/engine/connectors/discord-connector.d.ts
|
|
21
|
+
type DiscordConnectorOptions = {
|
|
22
|
+
/**
|
|
23
|
+
* Discord gateway dispatch types funnel forwards to the broadcaster.
|
|
24
|
+
* Pass an explicit allowlist (`["MESSAGE_CREATE", "MESSAGE_UPDATE"]`) for
|
|
25
|
+
* fine-grained control, or `"all"` to skip the filter entirely and forward
|
|
26
|
+
* every dispatch type the gateway emits. Defaults to MESSAGE_CREATE and
|
|
27
|
+
* MESSAGE_UPDATE so the typical chat-style consumer is not flooded by
|
|
28
|
+
* GUILD_CREATE / PRESENCE_UPDATE / VOICE_STATE_UPDATE snapshots on
|
|
29
|
+
* connect. Reactions, interactivity, and guild-state dispatches require
|
|
30
|
+
* opt-in via this list.
|
|
31
|
+
*
|
|
32
|
+
* Common values:
|
|
33
|
+
* ["MESSAGE_CREATE", "MESSAGE_UPDATE", "MESSAGE_REACTION_ADD"]
|
|
34
|
+
* ["MESSAGE_CREATE", "INTERACTION_CREATE"]
|
|
35
|
+
* "all" — debug / firehose
|
|
36
|
+
*/
|
|
37
|
+
eventTypes?: ReadonlyArray<string> | "all";
|
|
38
|
+
};
|
|
20
39
|
/**
|
|
21
40
|
* Discord connector descriptor. Pass `discordConnector()` to
|
|
22
41
|
* `new Funnel({ connectors: [...] })` to enable the type.
|
|
@@ -24,7 +43,7 @@ declare class FunnelDiscordAdapter extends FunnelConnectorAdapter {
|
|
|
24
43
|
* The listener is backed by `@interactive-inc/flume`'s `FlumeDiscordSource`
|
|
25
44
|
* (raw Gateway WebSocket).
|
|
26
45
|
*/
|
|
27
|
-
declare const discordConnector: () => ConnectorDescriptor;
|
|
46
|
+
declare const discordConnector: (options?: DiscordConnectorOptions) => ConnectorDescriptor;
|
|
28
47
|
//#endregion
|
|
29
48
|
//#region lib/engine/connectors/discord-event-processor.d.ts
|
|
30
49
|
type DiscordInboundMessage = {
|
|
@@ -61,7 +80,8 @@ type Deps = {
|
|
|
61
80
|
logger?: FunnelLogger;
|
|
62
81
|
diagnosticLog?: ConnectorDiagnosticLog;
|
|
63
82
|
flumeDeps?: Partial<FlumeRuntimeDeps>; /** Shutdown signal forwarded to the underlying Flume. */
|
|
64
|
-
signal?: AbortSignal;
|
|
83
|
+
signal?: AbortSignal; /** See `DiscordConnectorOptions.eventTypes`. */
|
|
84
|
+
eventTypes?: ReadonlyArray<string> | "all";
|
|
65
85
|
};
|
|
66
86
|
/**
|
|
67
87
|
* Discord listener backed by `@interactive-inc/flume`'s `FlumeDiscordSource`
|
|
@@ -77,6 +97,7 @@ declare class FunnelFlumeDiscordListener extends FunnelFlumeSourceListener {
|
|
|
77
97
|
private readonly env;
|
|
78
98
|
private readonly flumeDeps;
|
|
79
99
|
private readonly signal;
|
|
100
|
+
private readonly eventTypes;
|
|
80
101
|
private processor;
|
|
81
102
|
constructor(deps: Deps);
|
|
82
103
|
start(notify: NotifyFn): Promise<void>;
|
|
@@ -86,4 +107,4 @@ declare class FunnelFlumeDiscordListener extends FunnelFlumeSourceListener {
|
|
|
86
107
|
private deliver;
|
|
87
108
|
}
|
|
88
109
|
//#endregion
|
|
89
|
-
export { DiscordConnectorConfig, DiscordInboundMessage, DiscordProcessed, DiscordProcessedEmit, DiscordProcessedSkip, FunnelDiscordAdapter, FunnelDiscordEventProcessor, FunnelFlumeDiscordListener, discordConnector, discordConnectorSchema };
|
|
110
|
+
export { DiscordConnectorConfig, DiscordConnectorOptions, DiscordInboundMessage, DiscordProcessed, DiscordProcessedEmit, DiscordProcessedSkip, FunnelDiscordAdapter, FunnelDiscordEventProcessor, FunnelFlumeDiscordListener, discordConnector, discordConnectorSchema };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { t as discordConnectorSchema } from "../discord-connector-schema-B4YpWpR3.js";
|
|
2
|
-
import { i as FunnelDiscordAdapter, n as FunnelFlumeDiscordListener, r as FunnelDiscordEventProcessor, t as discordConnector } from "../discord-connector-
|
|
2
|
+
import { i as FunnelDiscordAdapter, n as FunnelFlumeDiscordListener, r as FunnelDiscordEventProcessor, t as discordConnector } from "../discord-connector-DXARcO6w.js";
|
|
3
3
|
export { FunnelDiscordAdapter, FunnelDiscordEventProcessor, FunnelFlumeDiscordListener, discordConnector, discordConnectorSchema };
|