@interactive-inc/claude-funnel 0.52.0 → 0.55.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/README.md +25 -3
- package/dist/bin.js +1276 -520
- package/dist/claude.d.ts +22 -5
- package/dist/claude.js +456 -169
- package/dist/connector-adapter-1PxjN-Uk.d.ts +25 -0
- package/dist/{connector-adapter-D5Utumgz.js → connector-adapter-qwXLjQId.js} +1 -1
- package/dist/{connector-listener-DU54DN-f.js → connector-listener-CpHBecCj.js} +1 -1
- package/dist/connectors/discord.d.ts +6 -6
- package/dist/connectors/discord.js +2 -2
- package/dist/connectors/gh.d.ts +6 -6
- package/dist/connectors/gh.js +2 -2
- package/dist/connectors/schedule.d.ts +12 -2
- package/dist/connectors/schedule.js +2 -2
- package/dist/connectors/slack.d.ts +3 -3
- package/dist/connectors/slack.js +2 -2
- package/dist/{connector-diagnostic-log-yTOojKUR.d.ts → diagnostic-log-Bxe7Bbvw.d.ts} +2 -2
- package/dist/diagnostic-sql-reader-CzYgZpq2.js +83 -0
- package/dist/diagnostics.d.ts +2 -0
- package/dist/diagnostics.js +2 -0
- package/dist/{discord-connector-schema-CBDyGdOI.js → discord-connector-schema-B_N6IXLz.js} +1 -1
- package/dist/{discord-connector-schema-R0Uu-3ns.d.ts → discord-connector-schema-CPgcZkXh.d.ts} +1 -1
- package/dist/{discord-listener-_jSE3HsQ.js → discord-listener-C0MoKdQO.js} +6 -6
- package/dist/docs.d.ts +2 -0
- package/dist/docs.js +2 -0
- package/dist/doctor.d.ts +2 -0
- package/dist/doctor.js +2 -0
- package/dist/{file-process-guard-BgrVHe9I.d.ts → file-process-guard-DI1742H5.d.ts} +31 -15
- package/dist/funnel-diagnostics-BpKYrMSu.js +300 -0
- package/dist/funnel-diagnostics-qWy5tPSq.d.ts +176 -0
- package/dist/funnel-docs-dXPokzr5.d.ts +18 -0
- package/dist/funnel-docs-ng5K8w4j.js +653 -0
- package/dist/funnel-doctor-BF3Rdgk0.d.ts +34 -0
- package/dist/funnel-doctor-CApCezTq.js +82 -0
- package/dist/funnel-recovery-BUBsu7WX.d.ts +101 -0
- package/dist/funnel-recovery-D9CxD5Zs.js +134 -0
- package/dist/gateway/daemon.js +810 -211
- package/dist/{settings-store-D2XSXTyt.js → gateway-base-url-6foMXfFf.js} +19 -6
- package/dist/gateway.d.ts +3 -3
- package/dist/gateway.js +3 -2
- package/dist/{gh-connector-schema-eoTtHbY6.d.ts → gh-connector-schema-CU1ojfIF.d.ts} +1 -1
- package/dist/{gh-connector-schema-o3Q1-ojL.js → gh-connector-schema-DUcZgN2Q.js} +1 -1
- package/dist/{gh-listener-DH-fClQm.js → gh-listener-Dsx6AmhH.js} +5 -5
- package/dist/{index-NFs2jzCa.d.ts → index-CrngHrne.d.ts} +187 -619
- package/dist/index.d.ts +16 -11
- package/dist/index.js +512 -976
- package/dist/{local-config-json-schema-8IHjS4Q7.js → local-config-json-schema-DE1zkMcb.js} +35 -9
- package/dist/{local-config-sync-BdsrDZOu.d.ts → local-config-sync-B8b04LrZ.d.ts} +45 -25
- package/dist/local-config.d.ts +2 -2
- package/dist/local-config.js +2 -2
- package/dist/{memory-connector-diagnostic-log-CrW1ltLM.js → memory-diagnostic-log-BZ1VD80X.js} +61 -99
- package/dist/{memory-token-prompter-B5FFCsGP.d.ts → memory-token-prompter-Lo3YRDzq.d.ts} +4 -4
- package/dist/{memory-token-prompter-CLerGsgM.js → memory-token-prompter-vBXxY20-.js} +2 -2
- package/dist/{profiles-f0mNmEyP.d.ts → profiles-EHTeCOqB.d.ts} +3 -2
- package/dist/profiles.d.ts +1 -1
- package/dist/profiles.js +1 -1
- package/dist/recovery.d.ts +2 -0
- package/dist/recovery.js +2 -0
- package/dist/{resolve-connector-token-BHmZLRrV.js → resolve-connector-token-CczqG_Ig.js} +1 -1
- package/dist/{schedule-connector-schema-iCI61gzU.js → schedule-connector-schema-B_xO5z5B.js} +1 -1
- package/dist/{schedule-listener-CUyUFFR1.d.ts → schedule-listener-DKh0hnkK.d.ts} +5 -5
- package/dist/{schedule-listener-ePAjians.js → schedule-listener-DP9Jhc6U.js} +14 -4
- package/dist/settings-reader-CBrgz01o.d.ts +18 -0
- package/dist/{settings-reader-BSU6JyvM.d.ts → settings-schema-zhnMIa8I.d.ts} +1 -16
- package/dist/{slack-connector-schema-BCNWluHM.js → slack-connector-schema-C1zEf4TG.js} +1 -1
- package/dist/{slack-listener-Bv5xI9gC.d.ts → slack-listener-COQA8wAZ.d.ts} +4 -4
- package/dist/{slack-listener-ClQuHhEF.js → slack-listener-DUKPcpJH.js} +7 -7
- package/dist/{mcp-Dr-nIBwN.js → yaml-render-OhUN-qkS.js} +52 -34
- package/package.json +21 -1
- package/dist/connector-adapter-DKgsVuMH.d.ts +0 -11
- /package/dist/{file-system-BeOKXjlV.d.ts → file-system-Wub9Nto4.d.ts} +0 -0
- /package/dist/{process-runner-DfniuWVU.d.ts → process-runner-D5I_jhYQ.d.ts} +0 -0
- /package/dist/{profiles-wMRnjSid.js → profiles-MnXvYfZF.js} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { stderr, stdin } from "node:process";
|
|
4
|
-
//#region lib/
|
|
4
|
+
//#region lib/services/local-config/local-config-schema.ts
|
|
5
5
|
/**
|
|
6
6
|
* Per-repo launch config (`funnel.json`).
|
|
7
7
|
*
|
|
@@ -79,7 +79,7 @@ const localConfigSchema = z.object({
|
|
|
79
79
|
});
|
|
80
80
|
const LOCAL_CONFIG_FILENAME = "funnel.json";
|
|
81
81
|
//#endregion
|
|
82
|
-
//#region lib/
|
|
82
|
+
//#region lib/services/local-config/local-config.ts
|
|
83
83
|
/**
|
|
84
84
|
* Reads `funnel.json` from a directory. Returns `null` when the file is
|
|
85
85
|
* absent so callers can fall through to other resolution paths (default
|
|
@@ -122,6 +122,18 @@ var FunnelLocalConfig = class {
|
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
//#endregion
|
|
125
|
+
//#region lib/engine/connectors/either-token.ts
|
|
126
|
+
function botTokenSlot(slot) {
|
|
127
|
+
if (slot.env !== void 0) return { botTokenEnv: slot.env };
|
|
128
|
+
if (slot.literal !== void 0) return { botToken: slot.literal };
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
function appTokenSlot(slot) {
|
|
132
|
+
if (slot.env !== void 0) return { appTokenEnv: slot.env };
|
|
133
|
+
if (slot.literal !== void 0) return { appToken: slot.literal };
|
|
134
|
+
return {};
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
125
137
|
//#region lib/engine/token-prompter/token-prompter.ts
|
|
126
138
|
/**
|
|
127
139
|
* Asks the user for a secret value on stdin. Used as a last resort when a
|
|
@@ -131,18 +143,22 @@ var FunnelLocalConfig = class {
|
|
|
131
143
|
*/
|
|
132
144
|
var FunnelTokenPrompter = class {};
|
|
133
145
|
//#endregion
|
|
134
|
-
//#region lib/
|
|
146
|
+
//#region lib/services/local-config/local-config-sync.ts
|
|
135
147
|
/**
|
|
136
148
|
* Reconciles a single funnel.json channel spec with `~/.funnel/settings.json`.
|
|
137
149
|
* The spec is the source of truth for the channel it declares:
|
|
138
150
|
*
|
|
139
151
|
* - missing channel → created
|
|
140
152
|
* - declared connector matched by name → tokens reconciled
|
|
141
|
-
* - declared connector
|
|
142
|
-
* different name → renamed in place (then tokens reconciled)
|
|
143
|
-
* - declared connector with no match → added
|
|
153
|
+
* - declared connector with no name match → added (prompting for its tokens)
|
|
144
154
|
* - any connector left in the channel that the spec did not touch → removed
|
|
145
155
|
*
|
|
156
|
+
* Connectors are matched by NAME only — there is no rename-by-token path. A spec
|
|
157
|
+
* that renames a connector (same token, new name) is reconciled as "add the new
|
|
158
|
+
* name, remove the old one". Because the collision check runs at add time while
|
|
159
|
+
* the old connector is still present, re-using its token at the new name throws
|
|
160
|
+
* a token-collision error; remove the old connector via the CLI first.
|
|
161
|
+
*
|
|
146
162
|
* Removal only fires when the channel spec has a `connectors` field. An
|
|
147
163
|
* absent field means "do not manage connectors from here" and leaves
|
|
148
164
|
* everything in `~/.funnel` alone. Other channels in funnel.json (not
|
|
@@ -223,7 +239,14 @@ var FunnelLocalConfigSync = class {
|
|
|
223
239
|
id: this.channels.addConnector(channelName, {
|
|
224
240
|
type: "slack",
|
|
225
241
|
name: spec.name,
|
|
226
|
-
...
|
|
242
|
+
...botTokenSlot({
|
|
243
|
+
literal: bot.token,
|
|
244
|
+
env: bot.tokenEnv
|
|
245
|
+
}),
|
|
246
|
+
...appTokenSlot({
|
|
247
|
+
literal: app.token,
|
|
248
|
+
env: app.tokenEnv
|
|
249
|
+
}),
|
|
227
250
|
...spec.minify !== void 0 ? { minify: spec.minify } : {}
|
|
228
251
|
}).id,
|
|
229
252
|
name: spec.name,
|
|
@@ -260,7 +283,10 @@ var FunnelLocalConfigSync = class {
|
|
|
260
283
|
id: this.channels.addConnector(channelName, {
|
|
261
284
|
type: "discord",
|
|
262
285
|
name: spec.name,
|
|
263
|
-
...
|
|
286
|
+
...botTokenSlot({
|
|
287
|
+
literal: bot.token,
|
|
288
|
+
env: bot.tokenEnv
|
|
289
|
+
})
|
|
264
290
|
}).id,
|
|
265
291
|
name: spec.name,
|
|
266
292
|
changed: true
|
|
@@ -421,7 +447,7 @@ var NodeFunnelTokenPrompter = class extends FunnelTokenPrompter {
|
|
|
421
447
|
}
|
|
422
448
|
};
|
|
423
449
|
//#endregion
|
|
424
|
-
//#region lib/
|
|
450
|
+
//#region lib/services/local-config/local-config-json-schema.ts
|
|
425
451
|
/**
|
|
426
452
|
* Generates the JSON Schema (draft 2020-12) for `funnel.json`. Useful for
|
|
427
453
|
* `$schema` references in committed `funnel.json` files so editors can give
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { n as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { n as ChannelDeliveryMode, t as ChannelConfig } from "./settings-schema-zhnMIa8I.js";
|
|
2
|
+
import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-CBrgz01o.js";
|
|
3
|
+
import { S as FunnelLogger, b as FunnelConnectorListener, o as ConnectorDiagnosticLog } from "./diagnostic-log-Bxe7Bbvw.js";
|
|
4
|
+
import { r as FunnelProcessRunner } from "./process-runner-D5I_jhYQ.js";
|
|
5
|
+
import { n as FunnelFileSystem } from "./file-system-Wub9Nto4.js";
|
|
6
|
+
import { n as FunnelConnectorAdapter, t as CallInput } from "./connector-adapter-1PxjN-Uk.js";
|
|
7
|
+
import { a as ScheduleEntry, n as ScheduleOnFired } from "./schedule-listener-DKh0hnkK.js";
|
|
8
|
+
import { n as SlackOnAppCreated, r as SlackPreprocessEvent } from "./slack-listener-COQA8wAZ.js";
|
|
8
9
|
import { z } from "zod";
|
|
9
10
|
|
|
10
|
-
//#region lib/
|
|
11
|
+
//#region lib/services/local-config/local-config-schema.d.ts
|
|
11
12
|
declare const connectorSpecSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
12
13
|
type: z.ZodLiteral<"slack">;
|
|
13
14
|
name: z.ZodString;
|
|
@@ -83,7 +84,7 @@ declare const localConfigSchema: z.ZodObject<{
|
|
|
83
84
|
type LocalConfig = z.infer<typeof localConfigSchema>;
|
|
84
85
|
declare const LOCAL_CONFIG_FILENAME = "funnel.json";
|
|
85
86
|
//#endregion
|
|
86
|
-
//#region lib/
|
|
87
|
+
//#region lib/services/local-config/local-config.d.ts
|
|
87
88
|
type Deps$3 = {
|
|
88
89
|
fs: FunnelFileSystem;
|
|
89
90
|
};
|
|
@@ -100,7 +101,7 @@ declare class FunnelLocalConfig {
|
|
|
100
101
|
private assertProfilesValid;
|
|
101
102
|
}
|
|
102
103
|
//#endregion
|
|
103
|
-
//#region lib/connectors/connector-config-schema.d.ts
|
|
104
|
+
//#region lib/engine/connectors/connector-config-schema.d.ts
|
|
104
105
|
declare const connectorConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
105
106
|
id: z.ZodString;
|
|
106
107
|
name: z.ZodString;
|
|
@@ -148,7 +149,28 @@ declare const connectorConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
148
149
|
type ConnectorConfig = z.infer<typeof connectorConfigSchema>;
|
|
149
150
|
type ConnectorType = ConnectorConfig["type"];
|
|
150
151
|
//#endregion
|
|
151
|
-
//#region lib/connectors/
|
|
152
|
+
//#region lib/engine/connectors/either-token.d.ts
|
|
153
|
+
/**
|
|
154
|
+
* A single connector token slot is supplied one of two non-empty ways, which
|
|
155
|
+
* are mutually exclusive, or left empty:
|
|
156
|
+
*
|
|
157
|
+
* - the literal secret (`botToken: "xoxb-…"`)
|
|
158
|
+
* - the *name* of an env var holding it (`botTokenEnv: "SLACK_BOT_TOKEN"`)
|
|
159
|
+
* - neither — left for the CLI / TTY prompt to fill in at launch
|
|
160
|
+
*
|
|
161
|
+
* `EitherToken<"botToken", "botTokenEnv">` makes "both set at once" a compile
|
|
162
|
+
* error while still allowing "neither". Compose multiple slots with `&` (a slack
|
|
163
|
+
* connector intersects a bot slot and an app slot); the intersection keeps each
|
|
164
|
+
* slot independently exclusive without enumerating the cross-product.
|
|
165
|
+
*
|
|
166
|
+
* To build a value, use the slot helpers below (`botTokenSlot` etc.). They take
|
|
167
|
+
* a resolved `{ literal, env }` and return the exclusive shape. A generic
|
|
168
|
+
* builder can't: TS can't prove a `Record<Env, …>` omits the `Literal` key when
|
|
169
|
+
* both are free type params, so each helper fixes concrete key names instead.
|
|
170
|
+
*/
|
|
171
|
+
type EitherToken<Literal extends string, Env extends string> = (Partial<Record<Literal, string>> & Partial<Record<Env, never>>) | (Partial<Record<Literal, never>> & Partial<Record<Env, string>>);
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region lib/engine/connectors/connector-factory.d.ts
|
|
152
174
|
type SlackListenerOptions = {
|
|
153
175
|
onAppCreated?: SlackOnAppCreated;
|
|
154
176
|
preprocessEvent?: SlackPreprocessEvent;
|
|
@@ -224,24 +246,18 @@ type ChannelConnectorView = ConnectorConfig & {
|
|
|
224
246
|
channelId: string;
|
|
225
247
|
channelName: string;
|
|
226
248
|
};
|
|
227
|
-
type AddConnectorInput = {
|
|
249
|
+
type AddConnectorInput = ({
|
|
228
250
|
type: "slack";
|
|
229
251
|
name: string;
|
|
230
|
-
botToken?: string;
|
|
231
|
-
appToken?: string;
|
|
232
|
-
botTokenEnv?: string;
|
|
233
|
-
appTokenEnv?: string;
|
|
234
252
|
minify?: boolean;
|
|
235
|
-
} | {
|
|
253
|
+
} & EitherToken<"botToken", "botTokenEnv"> & EitherToken<"appToken", "appTokenEnv">) | {
|
|
236
254
|
type: "gh";
|
|
237
255
|
name: string;
|
|
238
256
|
pollInterval?: number;
|
|
239
|
-
} | {
|
|
257
|
+
} | ({
|
|
240
258
|
type: "discord";
|
|
241
259
|
name: string;
|
|
242
|
-
|
|
243
|
-
botTokenEnv?: string;
|
|
244
|
-
} | {
|
|
260
|
+
} & EitherToken<"botToken", "botTokenEnv">) | {
|
|
245
261
|
type: "schedule";
|
|
246
262
|
name: string;
|
|
247
263
|
entries?: ScheduleEntry[];
|
|
@@ -322,7 +338,7 @@ declare abstract class FunnelTokenPrompter {
|
|
|
322
338
|
abstract promptSecret(label: string): Promise<string>;
|
|
323
339
|
}
|
|
324
340
|
//#endregion
|
|
325
|
-
//#region lib/
|
|
341
|
+
//#region lib/services/local-config/local-config-sync.d.ts
|
|
326
342
|
type Deps = {
|
|
327
343
|
channels: FunnelChannels;
|
|
328
344
|
prompter: FunnelTokenPrompter;
|
|
@@ -341,11 +357,15 @@ type LocalConfigSyncResult = {
|
|
|
341
357
|
*
|
|
342
358
|
* - missing channel → created
|
|
343
359
|
* - declared connector matched by name → tokens reconciled
|
|
344
|
-
* - declared connector
|
|
345
|
-
* different name → renamed in place (then tokens reconciled)
|
|
346
|
-
* - declared connector with no match → added
|
|
360
|
+
* - declared connector with no name match → added (prompting for its tokens)
|
|
347
361
|
* - any connector left in the channel that the spec did not touch → removed
|
|
348
362
|
*
|
|
363
|
+
* Connectors are matched by NAME only — there is no rename-by-token path. A spec
|
|
364
|
+
* that renames a connector (same token, new name) is reconciled as "add the new
|
|
365
|
+
* name, remove the old one". Because the collision check runs at add time while
|
|
366
|
+
* the old connector is still present, re-using its token at the new name throws
|
|
367
|
+
* a token-collision error; remove the old connector via the CLI first.
|
|
368
|
+
*
|
|
349
369
|
* Removal only fires when the channel spec has a `connectors` field. An
|
|
350
370
|
* absent field means "do not manage connectors from here" and leaves
|
|
351
371
|
* everything in `~/.funnel` alone. Other channels in funnel.json (not
|
package/dist/local-config.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as profileSpecSchema, S as localConfigSchema, _ as LOCAL_CONFIG_FILENAME, b as channelSpecSchema, g as ConnectorSpec, h as ChannelSpec, i as FunnelTokenPrompter, m as FunnelLocalConfig, n as FunnelLocalConfigSync, r as LocalConfigSyncResult, t as ConnectorSyncOutcome, v as LocalConfig, x as connectorSpecSchema, y as ProfileSpec } from "./local-config-sync-
|
|
2
|
-
import { i as funnelJsonSchema, n as NodeFunnelTokenPrompter, r as FunnelLocalConfigWriter, t as MemoryFunnelTokenPrompter } from "./memory-token-prompter-
|
|
1
|
+
import { C as profileSpecSchema, S as localConfigSchema, _ as LOCAL_CONFIG_FILENAME, b as channelSpecSchema, g as ConnectorSpec, h as ChannelSpec, i as FunnelTokenPrompter, m as FunnelLocalConfig, n as FunnelLocalConfigSync, r as LocalConfigSyncResult, t as ConnectorSyncOutcome, v as LocalConfig, x as connectorSpecSchema, y as ProfileSpec } from "./local-config-sync-B8b04LrZ.js";
|
|
2
|
+
import { i as funnelJsonSchema, n as NodeFunnelTokenPrompter, r as FunnelLocalConfigWriter, t as MemoryFunnelTokenPrompter } from "./memory-token-prompter-Lo3YRDzq.js";
|
|
3
3
|
export { ChannelSpec, ConnectorSpec, ConnectorSyncOutcome, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLocalConfigWriter, FunnelTokenPrompter, LOCAL_CONFIG_FILENAME, LocalConfig, LocalConfigSyncResult, MemoryFunnelTokenPrompter, NodeFunnelTokenPrompter, ProfileSpec, channelSpecSchema, connectorSpecSchema, funnelJsonSchema, localConfigSchema, profileSpecSchema };
|
package/dist/local-config.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as FunnelLocalConfig, c as connectorSpecSchema, i as FunnelTokenPrompter, l as localConfigSchema, n as NodeFunnelTokenPrompter, o as LOCAL_CONFIG_FILENAME, r as FunnelLocalConfigSync, s as channelSpecSchema, t as funnelJsonSchema, u as profileSpecSchema } from "./local-config-json-schema-
|
|
2
|
-
import { n as FunnelLocalConfigWriter, t as MemoryFunnelTokenPrompter } from "./memory-token-prompter-
|
|
1
|
+
import { a as FunnelLocalConfig, c as connectorSpecSchema, i as FunnelTokenPrompter, l as localConfigSchema, n as NodeFunnelTokenPrompter, o as LOCAL_CONFIG_FILENAME, r as FunnelLocalConfigSync, s as channelSpecSchema, t as funnelJsonSchema, u as profileSpecSchema } from "./local-config-json-schema-DE1zkMcb.js";
|
|
2
|
+
import { n as FunnelLocalConfigWriter, t as MemoryFunnelTokenPrompter } from "./memory-token-prompter-vBXxY20-.js";
|
|
3
3
|
export { FunnelLocalConfig, FunnelLocalConfigSync, FunnelLocalConfigWriter, FunnelTokenPrompter, LOCAL_CONFIG_FILENAME, MemoryFunnelTokenPrompter, NodeFunnelTokenPrompter, channelSpecSchema, connectorSpecSchema, funnelJsonSchema, localConfigSchema, profileSpecSchema };
|
package/dist/{memory-connector-diagnostic-log-CrW1ltLM.js → memory-diagnostic-log-BZ1VD80X.js}
RENAMED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { n as NodeFunnelProcessRunner } from "./gh-connector-schema-
|
|
1
|
+
import { n as NodeFunnelProcessRunner } from "./gh-connector-schema-DUcZgN2Q.js";
|
|
2
2
|
import { t as NodeFunnelFileSystem } from "./node-file-system-BcrmWN9I.js";
|
|
3
|
-
import {
|
|
3
|
+
import { r as FUNNEL_DIR, s as resolveFunnelPort, t as gatewayLoopbackUrl } from "./gateway-base-url-6foMXfFf.js";
|
|
4
|
+
import { t as ConnectorDiagnosticSqlReader } from "./diagnostic-sql-reader-CzYgZpq2.js";
|
|
4
5
|
import { dirname, join } from "node:path";
|
|
5
6
|
import { chmodSync, existsSync, mkdirSync } from "node:fs";
|
|
6
7
|
import { z } from "zod";
|
|
7
8
|
import { homedir, tmpdir } from "node:os";
|
|
9
|
+
import { Database } from "bun:sqlite";
|
|
8
10
|
import { timingSafeEqual } from "node:crypto";
|
|
9
11
|
import { createFactory } from "hono/factory";
|
|
10
|
-
import { Database } from "bun:sqlite";
|
|
11
12
|
import { HTTPException } from "hono/http-exception";
|
|
12
13
|
import { zValidator } from "@hono/zod-validator";
|
|
13
14
|
//#region lib/engine/settings/tmp-dir.ts
|
|
@@ -65,7 +66,7 @@ var FunnelChannelPublisher = class {
|
|
|
65
66
|
async publish(channelName, request) {
|
|
66
67
|
if (!this.isDaemonRunning()) return OFFLINE;
|
|
67
68
|
try {
|
|
68
|
-
const url =
|
|
69
|
+
const url = `${gatewayLoopbackUrl(this.port)}/channels/${encodeURIComponent(channelName)}/publish`;
|
|
69
70
|
const res = await fetch(url, {
|
|
70
71
|
method: "POST",
|
|
71
72
|
headers: {
|
|
@@ -328,7 +329,7 @@ var FunnelBroadcaster = class {
|
|
|
328
329
|
}
|
|
329
330
|
};
|
|
330
331
|
//#endregion
|
|
331
|
-
//#region lib/gateway/
|
|
332
|
+
//#region lib/gateway/event-log/event-log.ts
|
|
332
333
|
/**
|
|
333
334
|
* Replayable event payload persisted by the gateway. Domain events the
|
|
334
335
|
* broadcaster emits to WS clients land here so reconnects across daemon
|
|
@@ -647,7 +648,7 @@ function toRecord(row) {
|
|
|
647
648
|
};
|
|
648
649
|
}
|
|
649
650
|
//#endregion
|
|
650
|
-
//#region lib/gateway/sqlite-
|
|
651
|
+
//#region lib/gateway/event-log/sqlite-event-log.ts
|
|
651
652
|
const MAX_CONTENT_CHARS = 2e3;
|
|
652
653
|
/**
|
|
653
654
|
* SQLite-backed `FunnelEventLog`. One indexed table holds every broadcaster
|
|
@@ -669,9 +670,11 @@ const MAX_CONTENT_CHARS = 2e3;
|
|
|
669
670
|
var SqliteFunnelEventLog = class extends FunnelEventLog {
|
|
670
671
|
sink;
|
|
671
672
|
now;
|
|
673
|
+
logger;
|
|
672
674
|
constructor(props) {
|
|
673
675
|
super();
|
|
674
676
|
this.now = props.now ?? (() => Date.now());
|
|
677
|
+
this.logger = props.logger;
|
|
675
678
|
this.sink = new LeucoLoggerSqliteSink({
|
|
676
679
|
path: props.path,
|
|
677
680
|
indexes: ["channel_id", "connector_id"],
|
|
@@ -699,11 +702,15 @@ var SqliteFunnelEventLog = class extends FunnelEventLog {
|
|
|
699
702
|
connector_id: record.connectorId,
|
|
700
703
|
meta: record.meta
|
|
701
704
|
};
|
|
702
|
-
this.sink.write({
|
|
705
|
+
const result = this.sink.write({
|
|
703
706
|
seq: record.offset,
|
|
704
707
|
ts: this.now(),
|
|
705
708
|
event
|
|
706
709
|
});
|
|
710
|
+
if (result instanceof Error) this.logger?.error("event log write failed", {
|
|
711
|
+
offset: record.offset,
|
|
712
|
+
error: result.message
|
|
713
|
+
});
|
|
707
714
|
}
|
|
708
715
|
/**
|
|
709
716
|
* Returns events with offset > since. Filtering by channel/connector is
|
|
@@ -1040,10 +1047,18 @@ const zParam = (schema) => zValidator("param", schema, (result, c) => {
|
|
|
1040
1047
|
});
|
|
1041
1048
|
//#endregion
|
|
1042
1049
|
//#region lib/gateway/routes/channels.connectors.call.ts
|
|
1050
|
+
const jsonValueSchema = z.lazy(() => z.union([
|
|
1051
|
+
z.string(),
|
|
1052
|
+
z.number(),
|
|
1053
|
+
z.boolean(),
|
|
1054
|
+
z.null(),
|
|
1055
|
+
z.array(jsonValueSchema),
|
|
1056
|
+
z.record(z.string(), jsonValueSchema)
|
|
1057
|
+
]));
|
|
1043
1058
|
const bodySchema = z.object({
|
|
1044
1059
|
method: z.string().min(1),
|
|
1045
1060
|
path: z.string().min(1),
|
|
1046
|
-
body:
|
|
1061
|
+
body: jsonValueSchema.optional()
|
|
1047
1062
|
});
|
|
1048
1063
|
/**
|
|
1049
1064
|
* POST /channels/:channel/connectors/:connector/call
|
|
@@ -1064,7 +1079,7 @@ const channelsConnectorsCallHandler = factory.createHandlers(zParam(z.object({
|
|
|
1064
1079
|
const result = await c.var.deps.channels.call(param.channel, param.connector, {
|
|
1065
1080
|
method: parsed.data.method,
|
|
1066
1081
|
path: parsed.data.path,
|
|
1067
|
-
body: parsed.data.body
|
|
1082
|
+
body: parsed.data.body
|
|
1068
1083
|
});
|
|
1069
1084
|
return c.json({
|
|
1070
1085
|
ok: true,
|
|
@@ -1111,87 +1126,6 @@ const channelsPublishHandler = factory.createHandlers(zParam(z.object({ channel:
|
|
|
1111
1126
|
return c.json(response);
|
|
1112
1127
|
});
|
|
1113
1128
|
//#endregion
|
|
1114
|
-
//#region lib/gateway/connector-diagnostic-sql-reader.ts
|
|
1115
|
-
/**
|
|
1116
|
-
* Read-only SQL surface over the three diagnostic tables, for Claude to query
|
|
1117
|
-
* the log with arbitrary `SELECT`s. It opens all files read-only and exposes
|
|
1118
|
-
* three views — `raw`, `processed`, `connection` — that hide the storage
|
|
1119
|
-
* details (the physical table is `leuco_log` and each row's columns live
|
|
1120
|
-
* inside a JSON `event` blob): the views surface the columns as plain fields,
|
|
1121
|
-
* with `payload` already pulled out of the nested JSON.
|
|
1122
|
-
*
|
|
1123
|
-
* The tables are separate files. `raw` and `processed` share an `event_id`,
|
|
1124
|
-
* so a `JOIN` answers "the event arrived, but what verdict did it get?";
|
|
1125
|
-
* `connection` answers the other half — "did the listener ever connect at
|
|
1126
|
-
* all?". Writes are impossible: the connection is read-only and `query`
|
|
1127
|
-
* rejects anything but a single `SELECT`.
|
|
1128
|
-
*/
|
|
1129
|
-
var ConnectorDiagnosticSqlReader = class {
|
|
1130
|
-
db;
|
|
1131
|
-
constructor(props) {
|
|
1132
|
-
const db = new Database(props.rawPath, { readonly: true });
|
|
1133
|
-
try {
|
|
1134
|
-
db.run("PRAGMA busy_timeout = 500");
|
|
1135
|
-
db.prepare("ATTACH DATABASE ? AS processeddb").run(props.processedPath);
|
|
1136
|
-
db.prepare("ATTACH DATABASE ? AS connectiondb").run(props.connectionPath);
|
|
1137
|
-
db.run(rawViewSql);
|
|
1138
|
-
db.run(processedViewSql);
|
|
1139
|
-
db.run(connectionViewSql);
|
|
1140
|
-
} catch (error) {
|
|
1141
|
-
db.close();
|
|
1142
|
-
throw error;
|
|
1143
|
-
}
|
|
1144
|
-
this.db = db;
|
|
1145
|
-
Object.freeze(this);
|
|
1146
|
-
}
|
|
1147
|
-
/**
|
|
1148
|
-
* Run one read-only `SELECT` and return the rows. Returns an `Error` (rather
|
|
1149
|
-
* than throwing) for a non-SELECT statement or a SQL error, so the caller
|
|
1150
|
-
* can surface the message without a stack trace.
|
|
1151
|
-
*/
|
|
1152
|
-
query(sql, params = []) {
|
|
1153
|
-
const trimmed = sql.trim().replace(/;$/, "").trim();
|
|
1154
|
-
if (!/^select\b/i.test(trimmed)) return /* @__PURE__ */ new Error("only a single SELECT statement is allowed");
|
|
1155
|
-
if (trimmed.includes(";")) return /* @__PURE__ */ new Error("only a single statement is allowed (remove the ';')");
|
|
1156
|
-
try {
|
|
1157
|
-
return this.db.prepare(trimmed).all(...params);
|
|
1158
|
-
} catch (error) {
|
|
1159
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
close() {
|
|
1163
|
-
this.db.close();
|
|
1164
|
-
}
|
|
1165
|
-
};
|
|
1166
|
-
const rawViewSql = `CREATE TEMP VIEW raw AS SELECT
|
|
1167
|
-
seq,
|
|
1168
|
-
ts,
|
|
1169
|
-
json_extract(event, '$.event_id') AS event_id,
|
|
1170
|
-
json_extract(event, '$.type') AS type,
|
|
1171
|
-
json_extract(event, '$.connector_id') AS connector_id,
|
|
1172
|
-
json_extract(event, '$.channel_id') AS channel_id,
|
|
1173
|
-
json_extract(event, '$.payload') AS payload
|
|
1174
|
-
FROM main.leuco_log`;
|
|
1175
|
-
const processedViewSql = `CREATE TEMP VIEW processed AS SELECT
|
|
1176
|
-
seq,
|
|
1177
|
-
ts,
|
|
1178
|
-
json_extract(event, '$.event_id') AS event_id,
|
|
1179
|
-
json_extract(event, '$.type') AS type,
|
|
1180
|
-
json_extract(event, '$.connector_id') AS connector_id,
|
|
1181
|
-
json_extract(event, '$.channel_id') AS channel_id,
|
|
1182
|
-
json_extract(event, '$.outcome') AS outcome,
|
|
1183
|
-
json_extract(event, '$.payload') AS payload
|
|
1184
|
-
FROM processeddb.leuco_log`;
|
|
1185
|
-
const connectionViewSql = `CREATE TEMP VIEW connection AS SELECT
|
|
1186
|
-
seq,
|
|
1187
|
-
ts,
|
|
1188
|
-
json_extract(event, '$.type') AS type,
|
|
1189
|
-
json_extract(event, '$.connector_id') AS connector_id,
|
|
1190
|
-
json_extract(event, '$.channel_id') AS channel_id,
|
|
1191
|
-
json_extract(event, '$.status') AS status,
|
|
1192
|
-
json_extract(event, '$.detail') AS detail
|
|
1193
|
-
FROM connectiondb.leuco_log`;
|
|
1194
|
-
//#endregion
|
|
1195
1129
|
//#region lib/gateway/routes/debug.ts
|
|
1196
1130
|
const extractPreview = (payload) => {
|
|
1197
1131
|
if (typeof payload !== "string" || payload.length === 0) return null;
|
|
@@ -1412,7 +1346,11 @@ const statusHandler = factory.createHandlers((c) => {
|
|
|
1412
1346
|
//#endregion
|
|
1413
1347
|
//#region lib/gateway/routes/index.ts
|
|
1414
1348
|
function buildGatewayRoutes() {
|
|
1415
|
-
return factory.createApp().
|
|
1349
|
+
return factory.createApp().onError((error, c) => {
|
|
1350
|
+
if (error instanceof HTTPException) return error.getResponse();
|
|
1351
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1352
|
+
return c.json({ error: message }, 500);
|
|
1353
|
+
}).get("/health", ...healthHandler).get("/status", ...statusHandler).get("/debug", ...debugHandler).get("/listeners", ...listenersListHandler).post("/listeners/:channel/:connector/start", ...listenersStartHandler).delete("/listeners/:channel/:connector", ...listenersStopHandler).post("/listeners/:channel/:connector/restart", ...listenersRestartHandler).post("/channels/:channel/connectors/:connector/call", ...channelsConnectorsCallHandler).post("/channels/:channel/publish", ...channelsPublishHandler);
|
|
1416
1354
|
}
|
|
1417
1355
|
const gatewayRoutes = buildGatewayRoutes();
|
|
1418
1356
|
//#endregion
|
|
@@ -1448,6 +1386,7 @@ var FunnelGatewayServer = class {
|
|
|
1448
1386
|
dir;
|
|
1449
1387
|
killCompetingSlack;
|
|
1450
1388
|
token;
|
|
1389
|
+
allowInsecureHost;
|
|
1451
1390
|
broadcaster;
|
|
1452
1391
|
eventLog;
|
|
1453
1392
|
supervisor;
|
|
@@ -1467,6 +1406,7 @@ var FunnelGatewayServer = class {
|
|
|
1467
1406
|
this.dir = deps.dir ?? FUNNEL_DIR;
|
|
1468
1407
|
this.killCompetingSlack = deps.killCompetingSlack ?? true;
|
|
1469
1408
|
this.token = deps.token ?? "";
|
|
1409
|
+
this.allowInsecureHost = deps.allowInsecureHost ?? false;
|
|
1470
1410
|
this.extraRoutes = deps.extraRoutes ?? null;
|
|
1471
1411
|
const clock = deps.clock;
|
|
1472
1412
|
this.nowMs = clock ? () => clock.millis() : () => Date.now();
|
|
@@ -1476,7 +1416,8 @@ var FunnelGatewayServer = class {
|
|
|
1476
1416
|
if (!existsSync(dbDir)) mkdirSync(dbDir, { recursive: true });
|
|
1477
1417
|
this.eventLog = new SqliteFunnelEventLog({
|
|
1478
1418
|
path: this.dbPath,
|
|
1479
|
-
now: this.nowMs
|
|
1419
|
+
now: this.nowMs,
|
|
1420
|
+
logger: this.logger
|
|
1480
1421
|
});
|
|
1481
1422
|
}
|
|
1482
1423
|
this.broadcaster = new FunnelBroadcaster({
|
|
@@ -1503,7 +1444,7 @@ var FunnelGatewayServer = class {
|
|
|
1503
1444
|
}
|
|
1504
1445
|
async start() {
|
|
1505
1446
|
if (this.server) return this.server;
|
|
1506
|
-
if (!this.token && !LOOPBACK_HOSTS.has(this.hostname)
|
|
1447
|
+
if (!this.token && !LOOPBACK_HOSTS.has(this.hostname) && !this.allowInsecureHost) throw new Error(`refusing to start gateway: hostname "${this.hostname}" is reachable off-box but no token is set. Set a token, bind to loopback (127.0.0.1), or pass allowInsecureHost: true.`);
|
|
1507
1448
|
const app = this.buildApp();
|
|
1508
1449
|
this.startedAt = this.nowMs();
|
|
1509
1450
|
this.server = Bun.serve({
|
|
@@ -1714,7 +1655,7 @@ var FunnelGatewayServer = class {
|
|
|
1714
1655
|
return { offset: event.offset };
|
|
1715
1656
|
}
|
|
1716
1657
|
lookupChannelId(channelName) {
|
|
1717
|
-
return this.channels.get(channelName)?.id ?? null;
|
|
1658
|
+
return this.channels.get(channelName)?.id ?? this.channels.getById(channelName)?.id ?? null;
|
|
1718
1659
|
}
|
|
1719
1660
|
lookupConnectorId(channelId, connectorName) {
|
|
1720
1661
|
return this.channels.getById(channelId)?.connectors.find((c) => c.name === connectorName)?.id ?? null;
|
|
@@ -1775,7 +1716,28 @@ var FunnelGatewayToken = class {
|
|
|
1775
1716
|
};
|
|
1776
1717
|
const DEFAULT_GATEWAY_TOKEN_PATH = join(homedir(), ".funnel", TOKEN_FILE_NAME);
|
|
1777
1718
|
//#endregion
|
|
1778
|
-
//#region lib/gateway/
|
|
1719
|
+
//#region lib/gateway/channel-ws-url.ts
|
|
1720
|
+
function channelWsUrl(input) {
|
|
1721
|
+
const url = new URL(input.base);
|
|
1722
|
+
url.searchParams.set("channel", input.channel);
|
|
1723
|
+
if (input.subscriberId !== void 0) url.searchParams.set("id", input.subscriberId);
|
|
1724
|
+
if (input.since !== void 0) url.searchParams.set("since", String(input.since));
|
|
1725
|
+
return url.toString();
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* Builds the `Sec-WebSocket-Protocol` values that authenticate a gateway WS
|
|
1729
|
+
* upgrade. Browser `WebSocket` cannot set an `Authorization` header, so the
|
|
1730
|
+
* gateway also accepts the token as a `funnel.token.<token>` subprotocol.
|
|
1731
|
+
* Returns an empty array when no token is given (auth disabled / loopback).
|
|
1732
|
+
*
|
|
1733
|
+
* Usage: `new WebSocket(channelWsUrl({ base, channel }), channelWsProtocols(token))`
|
|
1734
|
+
*/
|
|
1735
|
+
function channelWsProtocols(token) {
|
|
1736
|
+
if (!token) return [];
|
|
1737
|
+
return [`funnel.token.${token}`];
|
|
1738
|
+
}
|
|
1739
|
+
//#endregion
|
|
1740
|
+
//#region lib/gateway/event-log/memory-event-log.ts
|
|
1779
1741
|
/**
|
|
1780
1742
|
* In-process `FunnelEventLog` backed by a plain array. Used by tests and by
|
|
1781
1743
|
* embedders that do not need durability — replay works within the process
|
|
@@ -1818,7 +1780,7 @@ var MemoryFunnelEventLog = class extends FunnelEventLog {
|
|
|
1818
1780
|
close() {}
|
|
1819
1781
|
};
|
|
1820
1782
|
//#endregion
|
|
1821
|
-
//#region lib/gateway/
|
|
1783
|
+
//#region lib/gateway/diagnostic-log/diagnostic-log.ts
|
|
1822
1784
|
/**
|
|
1823
1785
|
* Points in the listener's connection lifecycle. The single source of truth
|
|
1824
1786
|
* for the value set: the `status` column schema, the `ConnectorConnectionStatus`
|
|
@@ -1909,7 +1871,7 @@ const connectorConnectionEventSchema = z.object({
|
|
|
1909
1871
|
*/
|
|
1910
1872
|
var ConnectorDiagnosticLog = class {};
|
|
1911
1873
|
//#endregion
|
|
1912
|
-
//#region lib/gateway/sqlite-
|
|
1874
|
+
//#region lib/gateway/diagnostic-log/sqlite-diagnostic-log.ts
|
|
1913
1875
|
/**
|
|
1914
1876
|
* Cap on a raw payload kept verbatim. The point of the raw table is to see
|
|
1915
1877
|
* what Slack/Discord actually sent, and a typical event is a few KB — so 256
|
|
@@ -2165,7 +2127,7 @@ const headFields = (payload) => {
|
|
|
2165
2127
|
}
|
|
2166
2128
|
};
|
|
2167
2129
|
//#endregion
|
|
2168
|
-
//#region lib/gateway/memory-
|
|
2130
|
+
//#region lib/gateway/diagnostic-log/memory-diagnostic-log.ts
|
|
2169
2131
|
/**
|
|
2170
2132
|
* In-process `ConnectorDiagnosticLog` backed by one array per table. Used by tests
|
|
2171
2133
|
* and embedders that do not need durability. Like the SQLite log it keeps
|
|
@@ -2242,4 +2204,4 @@ const takeRecent = (events, limit) => {
|
|
|
2242
2204
|
return events.slice(-limit);
|
|
2243
2205
|
};
|
|
2244
2206
|
//#endregion
|
|
2245
|
-
export {
|
|
2207
|
+
export { funnelTmpDir as C, publishResponseSchema as S, funnelEventSchema as _, connectorConnectionEventSchema as a, FunnelChannelPublisher as b, MemoryFunnelEventLog as c, DEFAULT_GATEWAY_TOKEN_PATH as d, FunnelGatewayToken as f, FunnelEventLog as g, SqliteFunnelEventLog as h, ConnectorDiagnosticLog as i, channelWsProtocols as l, FunnelListenerSupervisor as m, SqliteConnectorDiagnosticLog as n, connectorProcessedEventSchema as o, FunnelGatewayServer as p, CONNECTOR_CONNECTION_STATUSES as r, connectorRawEventSchema as s, MemoryConnectorDiagnosticLog as t, channelWsUrl as u, FunnelBroadcaster as v, publishRequestSchema as x, requireBearerToken as y };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as FunnelFileSystem } from "./file-system-
|
|
2
|
-
import { i as FunnelTokenPrompter } from "./local-config-sync-
|
|
1
|
+
import { n as FunnelFileSystem } from "./file-system-Wub9Nto4.js";
|
|
2
|
+
import { i as FunnelTokenPrompter } from "./local-config-sync-B8b04LrZ.js";
|
|
3
3
|
|
|
4
|
-
//#region lib/
|
|
4
|
+
//#region lib/services/local-config/local-config-json-schema.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* Generates the JSON Schema (draft 2020-12) for `funnel.json`. Useful for
|
|
7
7
|
* `$schema` references in committed `funnel.json` files so editors can give
|
|
@@ -10,7 +10,7 @@ import { i as FunnelTokenPrompter } from "./local-config-sync-BdsrDZOu.js";
|
|
|
10
10
|
*/
|
|
11
11
|
declare const funnelJsonSchema: () => Record<string, unknown>;
|
|
12
12
|
//#endregion
|
|
13
|
-
//#region lib/
|
|
13
|
+
//#region lib/services/local-config/local-config-writer.d.ts
|
|
14
14
|
type Deps = {
|
|
15
15
|
fs: FunnelFileSystem;
|
|
16
16
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as FunnelTokenPrompter, o as LOCAL_CONFIG_FILENAME } from "./local-config-json-schema-
|
|
1
|
+
import { i as FunnelTokenPrompter, o as LOCAL_CONFIG_FILENAME } from "./local-config-json-schema-DE1zkMcb.js";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
//#region lib/
|
|
3
|
+
//#region lib/services/local-config/local-config-writer.ts
|
|
4
4
|
const isRecord = (value) => {
|
|
5
5
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
6
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
1
|
+
import { r as ProfileConfig } from "./settings-schema-zhnMIa8I.js";
|
|
2
|
+
import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-CBrgz01o.js";
|
|
3
|
+
import { n as FunnelFileSystem } from "./file-system-Wub9Nto4.js";
|
|
3
4
|
|
|
4
5
|
//#region lib/engine/profiles/profiles.d.ts
|
|
5
6
|
type Deps = {
|
package/dist/profiles.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as FunnelProfiles } from "./profiles-
|
|
1
|
+
import { t as FunnelProfiles } from "./profiles-EHTeCOqB.js";
|
|
2
2
|
export { FunnelProfiles };
|
package/dist/profiles.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as FunnelProfiles } from "./profiles-
|
|
1
|
+
import { t as FunnelProfiles } from "./profiles-MnXvYfZF.js";
|
|
2
2
|
export { FunnelProfiles };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as RecoveryListenerControl, i as RecoveryGatewayControl, n as RecoveryAction, o as RecoveryResult, r as RecoveryChannelSource, t as FunnelRecovery } from "./funnel-recovery-BUBsu7WX.js";
|
|
2
|
+
export { FunnelRecovery, RecoveryAction, RecoveryChannelSource, RecoveryGatewayControl, RecoveryListenerControl, RecoveryResult };
|
package/dist/recovery.js
ADDED