@interactive-inc/claude-funnel 0.8.0 → 0.8.1
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 +583 -656
- package/dist/cli/factory.d.ts +7 -0
- package/dist/cli/router/query-to-cli-args.d.ts +1 -0
- package/dist/cli/router/to-request.d.ts +5 -0
- package/dist/cli/router/validator.d.ts +5 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.d.ts +42 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.rename.$newName.d.ts +46 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.request.d.ts +54 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.d.ts +66 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.d.ts +42 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.d.ts +46 -0
- package/dist/cli/routes/channels.$channel.connectors.add.$connector.d.ts +90 -0
- package/dist/cli/routes/channels.$channel.connectors.d.ts +38 -0
- package/dist/cli/routes/channels.$channel.connectors.remove.$connector.d.ts +42 -0
- package/dist/cli/routes/channels.$channel.connectors.set.$connector.d.ts +62 -0
- package/dist/cli/routes/channels.$channel.d.ts +38 -0
- package/dist/cli/routes/channels.$channel.rename.$newName.d.ts +42 -0
- package/dist/cli/routes/channels.$channel.set.delivery.$mode.d.ts +28 -0
- package/dist/cli/routes/channels.add.$channel.d.ts +46 -0
- package/dist/cli/routes/channels.d.ts +16 -0
- package/dist/cli/routes/channels.remove.$channel.d.ts +38 -0
- package/dist/cli/routes/claude.d.ts +32 -0
- package/dist/cli/routes/gateway.d.ts +20 -0
- package/dist/cli/routes/gateway.listeners.d.ts +17 -0
- package/dist/cli/routes/gateway.logs.d.ts +24 -0
- package/dist/cli/routes/gateway.restart.d.ts +24 -0
- package/dist/cli/routes/gateway.run.d.ts +24 -0
- package/dist/cli/routes/gateway.start.d.ts +24 -0
- package/dist/cli/routes/gateway.status.d.ts +13 -0
- package/dist/cli/routes/gateway.stop.d.ts +16 -0
- package/dist/cli/routes/index.d.ts +1222 -0
- package/dist/cli/routes/profiles.$profile.as-default.d.ts +38 -0
- package/dist/cli/routes/profiles.$profile.rename.$newName.d.ts +42 -0
- package/dist/cli/routes/profiles.$profile.run.d.ts +46 -0
- package/dist/cli/routes/profiles.add.$profile.d.ts +54 -0
- package/dist/cli/routes/profiles.d.ts +16 -0
- package/dist/cli/routes/profiles.remove.$profile.d.ts +38 -0
- package/dist/cli/routes/profiles.set.$profile.d.ts +54 -0
- package/dist/cli/routes/status.d.ts +16 -0
- package/dist/cli/routes/update.d.ts +16 -0
- package/dist/connectors/connector-adapter.d.ts +8 -0
- package/dist/connectors/connector-config-schema.d.ts +43 -0
- package/dist/connectors/connector-factory.d.ts +32 -0
- package/dist/connectors/connector-listener.d.ts +17 -0
- package/dist/connectors/discord-adapter.d.ts +14 -0
- package/dist/connectors/discord-connector-schema.d.ts +10 -0
- package/dist/connectors/discord-event-processor.d.ts +26 -0
- package/dist/connectors/discord-listener.d.ts +17 -0
- package/dist/connectors/gh-adapter.d.ts +11 -0
- package/dist/connectors/gh-connector-schema.d.ts +10 -0
- package/dist/connectors/gh-listener.d.ts +26 -0
- package/dist/connectors/match-cron.d.ts +1 -0
- package/dist/connectors/schedule-connector-schema.d.ts +45 -0
- package/dist/connectors/schedule-listener.d.ts +30 -0
- package/dist/connectors/schedule-state-store.d.ts +19 -0
- package/dist/connectors/slack-adapter.d.ts +15 -0
- package/dist/connectors/slack-connector-schema.d.ts +11 -0
- package/dist/connectors/slack-event-processor.d.ts +27 -0
- package/dist/connectors/slack-listener.d.ts +17 -0
- package/dist/engine/channels/channels.d.ts +106 -0
- package/dist/engine/claude/claude.d.ts +49 -0
- package/dist/engine/claude/gateway-controller.d.ts +6 -0
- package/dist/engine/fs/file-system.d.ts +24 -0
- package/dist/engine/fs/memory-file-system.d.ts +31 -0
- package/dist/engine/fs/node-file-system.d.ts +15 -0
- package/dist/engine/http/http-client.d.ts +15 -0
- package/dist/engine/http/memory-http-client.d.ts +12 -0
- package/dist/engine/http/node-http-client.d.ts +5 -0
- package/dist/engine/id/id-generator.d.ts +7 -0
- package/dist/engine/id/memory-id-generator.d.ts +11 -0
- package/dist/engine/id/node-id-generator.d.ts +4 -0
- package/dist/engine/logger/logger.d.ts +11 -0
- package/dist/engine/logger/memory-logger.d.ts +14 -0
- package/dist/engine/logger/node-logger.d.ts +15 -0
- package/dist/engine/logger/noop-logger.d.ts +7 -0
- package/dist/engine/mcp/channel-server.d.ts +1 -0
- package/dist/engine/mcp/mcp.d.ts +22 -0
- package/dist/engine/process/memory-process-runner.d.ts +43 -0
- package/dist/engine/process/node-process-runner.d.ts +9 -0
- package/dist/engine/process/process-runner.d.ts +29 -0
- package/dist/engine/profiles/profile-channel-checker.d.ts +7 -0
- package/dist/engine/profiles/profiles.d.ts +31 -0
- package/dist/engine/settings/mock-settings-reader.d.ts +9 -0
- package/dist/engine/settings/settings-reader.d.ts +5 -0
- package/dist/engine/settings/settings-schema.d.ts +132 -0
- package/dist/engine/settings/settings-store.d.ts +18 -0
- package/dist/engine/time/clock.d.ts +9 -0
- package/dist/engine/time/memory-clock.d.ts +12 -0
- package/dist/engine/time/node-clock.d.ts +4 -0
- package/dist/funnel.d.ts +95 -0
- package/dist/gateway/auth-middleware.d.ts +14 -0
- package/dist/gateway/broadcaster.d.ts +122 -0
- package/dist/gateway/daemon.d.ts +2 -0
- package/dist/gateway/daemon.js +192 -220
- package/dist/gateway/factory.d.ts +7 -0
- package/dist/gateway/funnel-event-store.d.ts +81 -0
- package/dist/gateway/gateway-server.d.ts +94 -0
- package/dist/gateway/gateway-token.d.ts +33 -0
- package/dist/gateway/gateway.d.ts +58 -0
- package/dist/gateway/kill-competing-slack-gateways.d.ts +9 -0
- package/dist/gateway/listener-supervisor.d.ts +85 -0
- package/dist/gateway/listeners-client.d.ts +53 -0
- package/dist/gateway/resolve-daemon-script.d.ts +11 -0
- package/dist/gateway/routes/channels.connectors.call.d.ts +41 -0
- package/dist/gateway/routes/health.d.ts +17 -0
- package/dist/gateway/routes/index.d.ts +209 -0
- package/dist/gateway/routes/listeners.list.d.ts +14 -0
- package/dist/gateway/routes/listeners.restart.d.ts +34 -0
- package/dist/gateway/routes/listeners.start.d.ts +34 -0
- package/dist/gateway/routes/listeners.stop.d.ts +34 -0
- package/dist/gateway/routes/route-deps.d.ts +10 -0
- package/dist/gateway/routes/status.d.ts +30 -0
- package/dist/gateway/routes/validator.d.ts +19 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +3575 -0
- package/dist/logger/leuco-human-file-writer.d.ts +33 -0
- package/dist/logger/leuco-human-logger.d.ts +46 -0
- package/dist/logger/leuco-human-record.d.ts +15 -0
- package/dist/logger/leuco-human-stdout-writer.d.ts +20 -0
- package/dist/logger/leuco-human-writer.d.ts +13 -0
- package/dist/logger/leuco-logger-memory-sink.d.ts +33 -0
- package/dist/logger/leuco-logger-record.d.ts +13 -0
- package/dist/logger/leuco-logger-sink.d.ts +34 -0
- package/dist/logger/leuco-logger-sqlite-sink.d.ts +102 -0
- package/dist/logger/leuco-logger.d.ts +56 -0
- package/lib/index.ts +2 -0
- package/package.json +14 -9
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FunnelConnectorAdapter, type CallInput } from "./connector-adapter";
|
|
2
|
+
import type { SlackConnectorConfig } from "./slack-connector-schema";
|
|
3
|
+
export type SlackWebClientLike = {
|
|
4
|
+
apiCall: (method: string, options?: Record<string, unknown>) => Promise<unknown>;
|
|
5
|
+
};
|
|
6
|
+
type Deps = {
|
|
7
|
+
config: SlackConnectorConfig;
|
|
8
|
+
client?: SlackWebClientLike;
|
|
9
|
+
};
|
|
10
|
+
export declare class FunnelSlackAdapter extends FunnelConnectorAdapter {
|
|
11
|
+
private readonly client;
|
|
12
|
+
constructor(deps: Deps);
|
|
13
|
+
call(input: CallInput): Promise<unknown>;
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const slackConnectorSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodString;
|
|
4
|
+
name: z.ZodString;
|
|
5
|
+
type: z.ZodLiteral<"slack">;
|
|
6
|
+
botToken: z.ZodString;
|
|
7
|
+
appToken: z.ZodString;
|
|
8
|
+
createdAt: z.ZodOptional<z.ZodString>;
|
|
9
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type SlackConnectorConfig = z.infer<typeof slackConnectorSchema>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type SlackRawEvent = Record<string, unknown>;
|
|
2
|
+
export type SlackProcessedSkip = {
|
|
3
|
+
skip: true;
|
|
4
|
+
};
|
|
5
|
+
export type SlackProcessedEmit = {
|
|
6
|
+
skip: false;
|
|
7
|
+
content: string;
|
|
8
|
+
meta: Record<string, string>;
|
|
9
|
+
shouldReact: boolean;
|
|
10
|
+
channel: string;
|
|
11
|
+
timestamp: string;
|
|
12
|
+
};
|
|
13
|
+
export type SlackProcessed = SlackProcessedSkip | SlackProcessedEmit;
|
|
14
|
+
type Props = {
|
|
15
|
+
ownBotUserId: string;
|
|
16
|
+
ownBotId: string;
|
|
17
|
+
now?: () => number;
|
|
18
|
+
};
|
|
19
|
+
export declare class FunnelSlackEventProcessor {
|
|
20
|
+
private readonly ownBotUserId;
|
|
21
|
+
private readonly ownBotId;
|
|
22
|
+
private readonly now;
|
|
23
|
+
private readonly dedup;
|
|
24
|
+
constructor(props: Props);
|
|
25
|
+
process(event: SlackRawEvent): SlackProcessed;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FunnelConnectorListener, type NotifyFn } from "./connector-listener";
|
|
2
|
+
import { FunnelLogger } from "../engine/logger/logger";
|
|
3
|
+
import type { SlackConnectorConfig } from "./slack-connector-schema";
|
|
4
|
+
type Deps = {
|
|
5
|
+
config: SlackConnectorConfig;
|
|
6
|
+
logger?: FunnelLogger;
|
|
7
|
+
};
|
|
8
|
+
export declare class FunnelSlackListener extends FunnelConnectorListener {
|
|
9
|
+
private readonly config;
|
|
10
|
+
private readonly logger;
|
|
11
|
+
private app;
|
|
12
|
+
constructor(deps: Deps);
|
|
13
|
+
start(notify: NotifyFn): Promise<void>;
|
|
14
|
+
stop(): Promise<void>;
|
|
15
|
+
isAlive(): boolean;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { CallInput } from "../../connectors/connector-adapter";
|
|
2
|
+
import type { ConnectorConfig } from "../../connectors/connector-config-schema";
|
|
3
|
+
import type { FunnelConnectorFactory } from "../../connectors/connector-factory";
|
|
4
|
+
import type { FunnelConnectorListener } from "../../connectors/connector-listener";
|
|
5
|
+
import type { ScheduleConnectorConfig, ScheduleEntry } from "../../connectors/schedule-connector-schema";
|
|
6
|
+
import type { ProfileChannelChecker } from "../profiles/profile-channel-checker";
|
|
7
|
+
import { FunnelClock } from "../time/clock";
|
|
8
|
+
import { FunnelIdGenerator } from "../id/id-generator";
|
|
9
|
+
import { FunnelSettingsReader } from "../settings/settings-reader";
|
|
10
|
+
import type { ChannelConfig, ChannelDeliveryMode } from "../settings/settings-schema";
|
|
11
|
+
type Deps = {
|
|
12
|
+
store: FunnelSettingsReader;
|
|
13
|
+
factory: FunnelConnectorFactory;
|
|
14
|
+
profileChecker: ProfileChannelChecker;
|
|
15
|
+
clock?: FunnelClock;
|
|
16
|
+
idGenerator?: FunnelIdGenerator;
|
|
17
|
+
};
|
|
18
|
+
export type ChannelConnectorView = ConnectorConfig & {
|
|
19
|
+
channelId: string;
|
|
20
|
+
channelName: string;
|
|
21
|
+
};
|
|
22
|
+
type AddConnectorInput = {
|
|
23
|
+
type: "slack";
|
|
24
|
+
name: string;
|
|
25
|
+
botToken: string;
|
|
26
|
+
appToken: string;
|
|
27
|
+
} | {
|
|
28
|
+
type: "gh";
|
|
29
|
+
name: string;
|
|
30
|
+
pollInterval?: number;
|
|
31
|
+
} | {
|
|
32
|
+
type: "discord";
|
|
33
|
+
name: string;
|
|
34
|
+
botToken: string;
|
|
35
|
+
} | {
|
|
36
|
+
type: "schedule";
|
|
37
|
+
name: string;
|
|
38
|
+
entries?: ScheduleEntry[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Channels own their connectors. Each channel has a stable id (UUID); the
|
|
42
|
+
* `name` is the human-facing label used by the CLI. Connectors live nested
|
|
43
|
+
* inside `channel.connectors[]`, so add/remove/rename are channel-scoped — no
|
|
44
|
+
* global connector namespace exists. Token uniqueness is enforced across all
|
|
45
|
+
* channels at add/update time so the same Slack/Discord credentials cannot
|
|
46
|
+
* be registered twice.
|
|
47
|
+
*/
|
|
48
|
+
export declare class FunnelChannels {
|
|
49
|
+
private readonly store;
|
|
50
|
+
private readonly factory;
|
|
51
|
+
private readonly profileChecker;
|
|
52
|
+
private readonly clock;
|
|
53
|
+
private readonly idGenerator;
|
|
54
|
+
constructor(deps: Deps);
|
|
55
|
+
list(): ChannelConfig[];
|
|
56
|
+
get(name: string): ChannelConfig | null;
|
|
57
|
+
getById(id: string): ChannelConfig | null;
|
|
58
|
+
add(input: {
|
|
59
|
+
name: string;
|
|
60
|
+
delivery?: ChannelDeliveryMode;
|
|
61
|
+
}): ChannelConfig;
|
|
62
|
+
setDelivery(name: string, delivery: ChannelDeliveryMode): void;
|
|
63
|
+
remove(name: string): void;
|
|
64
|
+
rename(oldName: string, newName: string): void;
|
|
65
|
+
listConnectors(channelName: string): ConnectorConfig[];
|
|
66
|
+
getConnector(channelName: string, connectorName: string): ConnectorConfig | null;
|
|
67
|
+
listAllConnectors(): ChannelConnectorView[];
|
|
68
|
+
addConnector(channelName: string, input: AddConnectorInput): ConnectorConfig;
|
|
69
|
+
private fromInput;
|
|
70
|
+
removeConnector(channelName: string, connectorName: string): void;
|
|
71
|
+
renameConnector(channelName: string, oldName: string, newName: string): void;
|
|
72
|
+
updateSlackConnector(channelName: string, connectorName: string, fields: {
|
|
73
|
+
botToken?: string;
|
|
74
|
+
appToken?: string;
|
|
75
|
+
}): void;
|
|
76
|
+
updateGhConnector(channelName: string, connectorName: string, fields: {
|
|
77
|
+
pollInterval?: number;
|
|
78
|
+
}): void;
|
|
79
|
+
updateDiscordConnector(channelName: string, connectorName: string, fields: {
|
|
80
|
+
botToken?: string;
|
|
81
|
+
}): void;
|
|
82
|
+
listScheduleEntries(channelName: string, connectorName: string): ScheduleEntry[];
|
|
83
|
+
addScheduleEntry(channelName: string, connectorName: string, entry: Pick<ScheduleEntry, "cron" | "prompt"> & Partial<Pick<ScheduleEntry, "id" | "enabled" | "catchupPolicy">>): ScheduleEntry;
|
|
84
|
+
removeScheduleEntry(channelName: string, connectorName: string, id: string): void;
|
|
85
|
+
call(channelName: string, connectorName: string, input: CallInput): Promise<unknown>;
|
|
86
|
+
createListener(channelName: string, connectorName: string): {
|
|
87
|
+
config: ConnectorConfig;
|
|
88
|
+
channelId: string;
|
|
89
|
+
listener: FunnelConnectorListener;
|
|
90
|
+
} | null;
|
|
91
|
+
createAllListeners(): {
|
|
92
|
+
config: ConnectorConfig;
|
|
93
|
+
channelId: string;
|
|
94
|
+
channelName: string;
|
|
95
|
+
listener: FunnelConnectorListener;
|
|
96
|
+
}[];
|
|
97
|
+
private requireChannel;
|
|
98
|
+
private requireConnector;
|
|
99
|
+
private requireSlackConnector;
|
|
100
|
+
private requireGhConnector;
|
|
101
|
+
private requireDiscordConnector;
|
|
102
|
+
private requireScheduleConnector;
|
|
103
|
+
private assertNoTokenCollision;
|
|
104
|
+
private tokensOf;
|
|
105
|
+
}
|
|
106
|
+
export type { ScheduleConnectorConfig };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { FunnelChannels } from "../channels/channels";
|
|
2
|
+
import type { GatewayController } from "./gateway-controller";
|
|
3
|
+
import { FunnelFileSystem } from "../fs/file-system";
|
|
4
|
+
import { FunnelLogger } from "../logger/logger";
|
|
5
|
+
import type { FunnelMcp } from "../mcp/mcp";
|
|
6
|
+
import { FunnelProcessRunner } from "../process/process-runner";
|
|
7
|
+
export type LaunchOptions = {
|
|
8
|
+
channel: string;
|
|
9
|
+
cwd?: string;
|
|
10
|
+
subAgent?: string;
|
|
11
|
+
userArgs?: string[];
|
|
12
|
+
profileName?: string;
|
|
13
|
+
};
|
|
14
|
+
type Deps = {
|
|
15
|
+
channels: FunnelChannels;
|
|
16
|
+
mcp: FunnelMcp;
|
|
17
|
+
gateway: GatewayController;
|
|
18
|
+
process?: FunnelProcessRunner;
|
|
19
|
+
fs?: FunnelFileSystem;
|
|
20
|
+
logger?: FunnelLogger;
|
|
21
|
+
dir?: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Launches Claude Code with funnel pre-wired: ensures the gateway is running,
|
|
25
|
+
* installs the funnel MCP into the target repo's `.mcp.json` if missing,
|
|
26
|
+
* injects `FUNNEL_CHANNEL_ID` into the child env, and writes a per-profile
|
|
27
|
+
* PID file to enforce singleton launches.
|
|
28
|
+
*/
|
|
29
|
+
export declare class FunnelClaude {
|
|
30
|
+
private readonly channels;
|
|
31
|
+
private readonly mcp;
|
|
32
|
+
private readonly gateway;
|
|
33
|
+
private readonly process;
|
|
34
|
+
private readonly fs;
|
|
35
|
+
private readonly logger;
|
|
36
|
+
private readonly pidDir;
|
|
37
|
+
constructor(deps: Deps);
|
|
38
|
+
launch(options: LaunchOptions): Promise<number>;
|
|
39
|
+
isRunning(profileName: string): boolean;
|
|
40
|
+
private pidPath;
|
|
41
|
+
private readPid;
|
|
42
|
+
private writePidFile;
|
|
43
|
+
private removePidFile;
|
|
44
|
+
private installCleanup;
|
|
45
|
+
private isProcessAlive;
|
|
46
|
+
private buildArgs;
|
|
47
|
+
private buildEnv;
|
|
48
|
+
}
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type FileStat = {
|
|
2
|
+
mtimeMs: number;
|
|
3
|
+
/** POSIX mode bits (e.g. 0o600). `null` when the underlying FS does not expose mode. */
|
|
4
|
+
mode: number | null;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Filesystem boundary used everywhere funnel reads or writes.
|
|
8
|
+
* Default is NodeFunnelFileSystem (real `node:fs`); MemoryFunnelFileSystem
|
|
9
|
+
* provides a sandbox for tests and embedded use.
|
|
10
|
+
*/
|
|
11
|
+
export declare abstract class FunnelFileSystem {
|
|
12
|
+
abstract existsSync(path: string): boolean;
|
|
13
|
+
abstract readFileSync(path: string): string;
|
|
14
|
+
abstract writeFileSync(path: string, data: string): void;
|
|
15
|
+
/** Write `data` and ensure the resulting file is owner-only (0600). Use for tokens and any file that may contain secrets. */
|
|
16
|
+
abstract writeSecretFileSync(path: string, data: string): void;
|
|
17
|
+
abstract appendFileSync(path: string, data: string): void;
|
|
18
|
+
abstract unlink(path: string): void;
|
|
19
|
+
abstract mkdirSync(path: string, options?: {
|
|
20
|
+
recursive?: boolean;
|
|
21
|
+
}): void;
|
|
22
|
+
abstract readdirSync(path: string): string[];
|
|
23
|
+
abstract statSync(path: string): FileStat;
|
|
24
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type FileStat, FunnelFileSystem } from "./file-system";
|
|
2
|
+
type Props = {
|
|
3
|
+
dirs?: string[];
|
|
4
|
+
files?: Record<string, string>;
|
|
5
|
+
mtimes?: Record<string, number>;
|
|
6
|
+
modes?: Record<string, number>;
|
|
7
|
+
now?: () => number;
|
|
8
|
+
};
|
|
9
|
+
export declare class MemoryFunnelFileSystem extends FunnelFileSystem {
|
|
10
|
+
private readonly dirs;
|
|
11
|
+
private readonly files;
|
|
12
|
+
private readonly mtimes;
|
|
13
|
+
private readonly modes;
|
|
14
|
+
private readonly now;
|
|
15
|
+
constructor(props?: Props);
|
|
16
|
+
existsSync(path: string): boolean;
|
|
17
|
+
readFileSync(path: string): string;
|
|
18
|
+
writeFileSync(path: string, data: string): void;
|
|
19
|
+
writeSecretFileSync(path: string, data: string): void;
|
|
20
|
+
appendFileSync(path: string, data: string): void;
|
|
21
|
+
unlink(path: string): void;
|
|
22
|
+
mkdirSync(path: string, options?: {
|
|
23
|
+
recursive?: boolean;
|
|
24
|
+
}): void;
|
|
25
|
+
readdirSync(path: string): string[];
|
|
26
|
+
statSync(path: string): FileStat;
|
|
27
|
+
setMtime(path: string, mtimeMs: number): void;
|
|
28
|
+
setMode(path: string, mode: number): void;
|
|
29
|
+
private touch;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type FileStat, FunnelFileSystem } from "./file-system";
|
|
2
|
+
export declare class NodeFunnelFileSystem extends FunnelFileSystem {
|
|
3
|
+
constructor();
|
|
4
|
+
existsSync(path: string): boolean;
|
|
5
|
+
readFileSync(path: string): string;
|
|
6
|
+
writeFileSync(path: string, data: string): void;
|
|
7
|
+
writeSecretFileSync(path: string, data: string): void;
|
|
8
|
+
appendFileSync(path: string, data: string): void;
|
|
9
|
+
unlink(path: string): void;
|
|
10
|
+
mkdirSync(path: string, options?: {
|
|
11
|
+
recursive?: boolean;
|
|
12
|
+
}): void;
|
|
13
|
+
readdirSync(path: string): string[];
|
|
14
|
+
statSync(path: string): FileStat;
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type HttpRequest = {
|
|
2
|
+
method: string;
|
|
3
|
+
url: string;
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
body?: string;
|
|
6
|
+
};
|
|
7
|
+
export type HttpResponse = {
|
|
8
|
+
status: number;
|
|
9
|
+
ok: boolean;
|
|
10
|
+
text(): Promise<string>;
|
|
11
|
+
json(): Promise<unknown>;
|
|
12
|
+
};
|
|
13
|
+
export declare abstract class FunnelHttpClient {
|
|
14
|
+
abstract fetch(request: HttpRequest): Promise<HttpResponse>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FunnelHttpClient, type HttpRequest, type HttpResponse } from "./http-client";
|
|
2
|
+
export type MemoryHttpResponse = {
|
|
3
|
+
status?: number;
|
|
4
|
+
body?: string;
|
|
5
|
+
};
|
|
6
|
+
export type MemoryHttpHandler = (request: HttpRequest) => MemoryHttpResponse | Promise<MemoryHttpResponse>;
|
|
7
|
+
export declare class MemoryFunnelHttpClient extends FunnelHttpClient {
|
|
8
|
+
readonly calls: HttpRequest[];
|
|
9
|
+
private handler;
|
|
10
|
+
on(handler: MemoryHttpHandler): this;
|
|
11
|
+
fetch(request: HttpRequest): Promise<HttpResponse>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID generator boundary. Default NodeFunnelIdGenerator wraps `crypto.randomUUID()`;
|
|
3
|
+
* MemoryFunnelIdGenerator emits `<prefix>-1, <prefix>-2, ...` for deterministic tests.
|
|
4
|
+
*/
|
|
5
|
+
export declare abstract class FunnelIdGenerator {
|
|
6
|
+
abstract generate(): string;
|
|
7
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FunnelIdGenerator } from "./id-generator";
|
|
2
|
+
type Props = {
|
|
3
|
+
prefix?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare class MemoryFunnelIdGenerator extends FunnelIdGenerator {
|
|
6
|
+
private counter;
|
|
7
|
+
private readonly prefix;
|
|
8
|
+
constructor(props?: Props);
|
|
9
|
+
generate(): string;
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured logger with three levels and an optional log-file path.
|
|
3
|
+
* Defaults to NodeFunnelLogger (appends to /tmp/funnel/funnel.log);
|
|
4
|
+
* MemoryFunnelLogger captures entries in memory and NoopFunnelLogger silences output.
|
|
5
|
+
*/
|
|
6
|
+
export declare abstract class FunnelLogger {
|
|
7
|
+
abstract info(message: string, meta?: Record<string, unknown>): void;
|
|
8
|
+
abstract warn(message: string, meta?: Record<string, unknown>): void;
|
|
9
|
+
abstract error(message: string, meta?: Record<string, unknown>): void;
|
|
10
|
+
abstract readonly file: string | null;
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FunnelLogger } from "./logger";
|
|
2
|
+
export type LogEntry = {
|
|
3
|
+
level: "info" | "warn" | "error";
|
|
4
|
+
message: string;
|
|
5
|
+
meta?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
export declare class MemoryFunnelLogger extends FunnelLogger {
|
|
8
|
+
readonly file: null;
|
|
9
|
+
readonly entries: LogEntry[];
|
|
10
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
11
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
12
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
13
|
+
clear(): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FunnelLogger } from "./logger";
|
|
2
|
+
type Props = {
|
|
3
|
+
file?: string;
|
|
4
|
+
now?: () => Date;
|
|
5
|
+
};
|
|
6
|
+
export declare class NodeFunnelLogger extends FunnelLogger {
|
|
7
|
+
readonly file: string;
|
|
8
|
+
private readonly now;
|
|
9
|
+
constructor(props?: Props);
|
|
10
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
11
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
12
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
13
|
+
private write;
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const startChannelServer: () => Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FunnelFileSystem } from "../fs/file-system";
|
|
2
|
+
export declare const FUNNEL_MCP_COMMAND = "funnel";
|
|
3
|
+
export declare const FUNNEL_MCP_NAME = "funnel";
|
|
4
|
+
type Deps = {
|
|
5
|
+
fs?: FunnelFileSystem;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Installs/uninstalls the funnel MCP entry into a target repository's
|
|
9
|
+
* `.mcp.json`. Detects an existing entry by command match so renaming is
|
|
10
|
+
* preserved across re-installs.
|
|
11
|
+
*/
|
|
12
|
+
export declare class FunnelMcp {
|
|
13
|
+
private readonly fs;
|
|
14
|
+
constructor(deps?: Deps);
|
|
15
|
+
install(repoPath: string): void;
|
|
16
|
+
uninstall(repoPath: string): void;
|
|
17
|
+
findInstalledName(cwd: string): string | null;
|
|
18
|
+
private findServerName;
|
|
19
|
+
private readConfig;
|
|
20
|
+
private writeConfig;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type AttachOptions, type DetachOptions, FunnelProcessRunner, type RunOptions, type RunResult } from "./process-runner";
|
|
2
|
+
export type MemoryProcessResponse = {
|
|
3
|
+
exitCode?: number;
|
|
4
|
+
stdout?: string;
|
|
5
|
+
stderr?: string;
|
|
6
|
+
};
|
|
7
|
+
export type MemoryProcessHandler = (command: string[]) => MemoryProcessResponse | Promise<MemoryProcessResponse>;
|
|
8
|
+
export type MemoryProcessSyncHandler = (command: string[]) => MemoryProcessResponse;
|
|
9
|
+
export type MemoryProcessCall = {
|
|
10
|
+
kind: "run";
|
|
11
|
+
command: string[];
|
|
12
|
+
options: RunOptions;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "runSync";
|
|
15
|
+
command: string[];
|
|
16
|
+
} | {
|
|
17
|
+
kind: "attach";
|
|
18
|
+
command: string[];
|
|
19
|
+
options: AttachOptions;
|
|
20
|
+
} | {
|
|
21
|
+
kind: "detach";
|
|
22
|
+
command: string[];
|
|
23
|
+
options: DetachOptions;
|
|
24
|
+
} | {
|
|
25
|
+
kind: "kill";
|
|
26
|
+
command: string[];
|
|
27
|
+
};
|
|
28
|
+
export declare class MemoryFunnelProcessRunner extends FunnelProcessRunner {
|
|
29
|
+
readonly calls: MemoryProcessCall[];
|
|
30
|
+
readonly killed: {
|
|
31
|
+
pid: number;
|
|
32
|
+
signal: string;
|
|
33
|
+
}[];
|
|
34
|
+
private handler;
|
|
35
|
+
private syncHandler;
|
|
36
|
+
on(handler: MemoryProcessHandler): this;
|
|
37
|
+
onSync(handler: MemoryProcessSyncHandler): this;
|
|
38
|
+
run(command: string[], options?: RunOptions): Promise<RunResult>;
|
|
39
|
+
runSync(command: string[]): RunResult;
|
|
40
|
+
attach(command: string[], options?: AttachOptions): Promise<number>;
|
|
41
|
+
detach(command: string[], options?: DetachOptions): void;
|
|
42
|
+
kill(pid: number, signal?: string): void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type AttachOptions, type DetachOptions, FunnelProcessRunner, type RunOptions, type RunResult } from "./process-runner";
|
|
2
|
+
export declare class NodeFunnelProcessRunner extends FunnelProcessRunner {
|
|
3
|
+
constructor();
|
|
4
|
+
runSync(command: string[]): RunResult;
|
|
5
|
+
run(command: string[], options?: RunOptions): Promise<RunResult>;
|
|
6
|
+
attach(command: string[], options?: AttachOptions): Promise<number>;
|
|
7
|
+
detach(command: string[], options?: DetachOptions): void;
|
|
8
|
+
kill(pid: number, signal?: string): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type RunOptions = {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
env?: Record<string, string>;
|
|
4
|
+
input?: string;
|
|
5
|
+
};
|
|
6
|
+
export type RunResult = {
|
|
7
|
+
exitCode: number;
|
|
8
|
+
stdout: string;
|
|
9
|
+
stderr: string;
|
|
10
|
+
};
|
|
11
|
+
export type AttachOptions = {
|
|
12
|
+
cwd?: string;
|
|
13
|
+
env?: Record<string, string>;
|
|
14
|
+
};
|
|
15
|
+
export type DetachOptions = {
|
|
16
|
+
env?: Record<string, string>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Process boundary covering one-shot runs, sync runs, foreground attach, and
|
|
20
|
+
* detached background spawns. Default is NodeFunnelProcessRunner (Bun.spawn);
|
|
21
|
+
* MemoryFunnelProcessRunner records calls and lets tests stub responses.
|
|
22
|
+
*/
|
|
23
|
+
export declare abstract class FunnelProcessRunner {
|
|
24
|
+
abstract run(command: string[], options?: RunOptions): Promise<RunResult>;
|
|
25
|
+
abstract runSync(command: string[]): RunResult;
|
|
26
|
+
abstract attach(command: string[], options?: AttachOptions): Promise<number>;
|
|
27
|
+
abstract detach(command: string[], options?: DetachOptions): void;
|
|
28
|
+
abstract kill(pid: number, signal?: string): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FunnelSettingsReader } from "../settings/settings-reader";
|
|
2
|
+
import type { ProfileConfig } from "../settings/settings-schema";
|
|
3
|
+
type Deps = {
|
|
4
|
+
store: FunnelSettingsReader;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Named launch presets for `fnl claude`. Each profile bundles a working
|
|
8
|
+
* directory, a sub-agent name, and the channel id its Claude instance will
|
|
9
|
+
* subscribe to. Implements ProfileChannelChecker so FunnelChannels can refuse
|
|
10
|
+
* to remove a channel that is still referenced.
|
|
11
|
+
*
|
|
12
|
+
* The first entry in the persisted array is treated as the default profile;
|
|
13
|
+
* `asDefault` reorders the array to put a named profile first.
|
|
14
|
+
*
|
|
15
|
+
* `channelId` always stores the channel's stable id (uuid). CLI surfaces
|
|
16
|
+
* resolve channel name → id before calling `add`/`update` here.
|
|
17
|
+
*/
|
|
18
|
+
export declare class FunnelProfiles {
|
|
19
|
+
private readonly store;
|
|
20
|
+
constructor(deps: Deps);
|
|
21
|
+
list(): ProfileConfig[];
|
|
22
|
+
get(name: string): ProfileConfig | null;
|
|
23
|
+
getDefault(): ProfileConfig | null;
|
|
24
|
+
add(config: ProfileConfig): void;
|
|
25
|
+
remove(name: string): void;
|
|
26
|
+
rename(oldName: string, newName: string): void;
|
|
27
|
+
asDefault(name: string): void;
|
|
28
|
+
hasChannelRef(channelId: string): boolean;
|
|
29
|
+
update(name: string, fields: Partial<Omit<ProfileConfig, "name">>): void;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FunnelSettingsReader } from "./settings-reader";
|
|
2
|
+
import type { Settings } from "./settings-schema";
|
|
3
|
+
export declare const createSettings: (partial?: Partial<Settings>) => Settings;
|
|
4
|
+
export declare class MockFunnelSettingsReader extends FunnelSettingsReader {
|
|
5
|
+
private state;
|
|
6
|
+
constructor(initial?: Partial<Settings>);
|
|
7
|
+
read(): Settings;
|
|
8
|
+
write(settings: Settings): void;
|
|
9
|
+
}
|