@interactive-inc/claude-funnel 0.41.0 → 0.50.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.
Files changed (55) hide show
  1. package/README.md +34 -9
  2. package/dist/bin.js +255 -256
  3. package/dist/claude-CB1WkV77.d.ts +115 -0
  4. package/dist/claude.d.ts +59 -0
  5. package/dist/claude.js +322 -0
  6. package/dist/{connector-diagnostic-log-OPpPi9V9.d.ts → connector-diagnostic-log-yTOojKUR.d.ts} +14 -14
  7. package/dist/{logger-Czli2OKh.js → connector-listener-DU54DN-f.js} +1 -9
  8. package/dist/connectors/discord.d.ts +3 -3
  9. package/dist/connectors/discord.js +2 -1
  10. package/dist/connectors/gh.d.ts +4 -3
  11. package/dist/connectors/gh.js +2 -1
  12. package/dist/connectors/schedule.d.ts +1 -1
  13. package/dist/connectors/schedule.js +2 -1
  14. package/dist/connectors/slack.d.ts +2 -2
  15. package/dist/connectors/slack.js +2 -1
  16. package/dist/discord-connector-schema-CBDyGdOI.js +21 -0
  17. package/dist/{discord-connector-schema-BeThExJp.js → discord-listener-_jSE3HsQ.js} +2 -22
  18. package/dist/file-system-BeOKXjlV.d.ts +26 -0
  19. package/dist/file-system-PWKKU7lA.js +9 -0
  20. package/dist/gateway/daemon.js +151 -152
  21. package/dist/gateway.d.ts +3 -0
  22. package/dist/gateway.js +2 -0
  23. package/dist/gh-connector-schema-eoTtHbY6.d.ts +14 -0
  24. package/dist/{gh-connector-schema-eYE4g77K.js → gh-connector-schema-o3Q1-ojL.js} +1 -176
  25. package/dist/gh-listener-DH-fClQm.js +178 -0
  26. package/dist/index-ChomoTZ5.d.ts +3404 -0
  27. package/dist/index.d.ts +11 -4214
  28. package/dist/index.js +195 -3869
  29. package/dist/local-config-json-schema-8IHjS4Q7.js +439 -0
  30. package/dist/local-config-sync-BdsrDZOu.d.ts +381 -0
  31. package/dist/local-config.d.ts +3 -0
  32. package/dist/local-config.js +3 -0
  33. package/dist/logger-BP6SisKt.js +9 -0
  34. package/dist/mcp-Dr-nIBwN.js +253 -0
  35. package/dist/memory-connector-diagnostic-log-CrW1ltLM.js +2245 -0
  36. package/dist/memory-token-prompter-B5FFCsGP.d.ts +57 -0
  37. package/dist/memory-token-prompter-CLerGsgM.js +61 -0
  38. package/dist/node-file-system-BcrmWN9I.js +48 -0
  39. package/dist/{gh-connector-schema-CQmEWzdV.d.ts → process-runner-DfniuWVU.d.ts} +1 -14
  40. package/dist/profiles-f0mNmEyP.d.ts +64 -0
  41. package/dist/profiles-wMRnjSid.js +129 -0
  42. package/dist/profiles.d.ts +2 -0
  43. package/dist/profiles.js +2 -0
  44. package/dist/schedule-connector-schema-iCI61gzU.js +31 -0
  45. package/dist/{schedule-listener-3M6WkH1Y.d.ts → schedule-listener-CUyUFFR1.d.ts} +22 -46
  46. package/dist/{schedule-connector-schema-CM-sRkac.js → schedule-listener-ePAjians.js} +3 -86
  47. package/dist/settings-reader-BSU6JyvM.d.ts +167 -0
  48. package/dist/settings-reader-DPqrpV7s.js +11 -0
  49. package/dist/settings-store-D2XSXTyt.js +186 -0
  50. package/dist/slack-connector-schema-BCNWluHM.js +32 -0
  51. package/dist/{slack-listener-9UdAn_ui.d.ts → slack-listener-Bv5xI9gC.d.ts} +31 -31
  52. package/dist/{slack-connector-schema-DDbSGPZn.js → slack-listener-ClQuHhEF.js} +2 -32
  53. package/package.json +16 -1
  54. /package/dist/{connector-adapter-VA6undzc.d.ts → connector-adapter-DKgsVuMH.d.ts} +0 -0
  55. /package/dist/{discord-connector-schema-DF4pL3Sc.d.ts → discord-connector-schema-R0Uu-3ns.d.ts} +0 -0
@@ -0,0 +1,57 @@
1
+ import { n as FunnelFileSystem } from "./file-system-BeOKXjlV.js";
2
+ import { i as FunnelTokenPrompter } from "./local-config-sync-BdsrDZOu.js";
3
+
4
+ //#region lib/engine/local-config/local-config-json-schema.d.ts
5
+ /**
6
+ * Generates the JSON Schema (draft 2020-12) for `funnel.json`. Useful for
7
+ * `$schema` references in committed `funnel.json` files so editors can give
8
+ * autocomplete and validation for channels[] (transport) and profiles[]
9
+ * (launch recipe) without anyone hand-maintaining a separate schema.
10
+ */
11
+ declare const funnelJsonSchema: () => Record<string, unknown>;
12
+ //#endregion
13
+ //#region lib/engine/local-config/local-config-writer.d.ts
14
+ type Deps = {
15
+ fs: FunnelFileSystem;
16
+ };
17
+ /**
18
+ * The one path that mutates the repo-committed funnel.json, and it only ever
19
+ * inserts `id`. On first launch a repo has no `id`; funnel generates one and
20
+ * writes it back here so future launches resolve the same `~/.funnel/projects/<id>/`.
21
+ * Idempotent — a no-op once `id` is present. Kept separate from the read-only
22
+ * FunnelLocalConfig so reads stay side-effect free.
23
+ */
24
+ declare class FunnelLocalConfigWriter {
25
+ private readonly fs;
26
+ constructor(deps: Deps);
27
+ ensureId(cwd: string, id: string): void;
28
+ }
29
+ //#endregion
30
+ //#region lib/engine/token-prompter/node-token-prompter.d.ts
31
+ /**
32
+ * Reads a secret from stdin in raw mode. Echoes a `*` per byte so the user
33
+ * can see progress without exposing the token. Refuses to prompt when stdin
34
+ * is not a TTY — callers should surface the resulting error with a hint
35
+ * pointing at the corresponding env var or CLI command.
36
+ */
37
+ declare class NodeFunnelTokenPrompter extends FunnelTokenPrompter {
38
+ promptSecret(label: string): Promise<string>;
39
+ private readSecret;
40
+ }
41
+ //#endregion
42
+ //#region lib/engine/token-prompter/memory-token-prompter.d.ts
43
+ type Props = {
44
+ answers?: Record<string, string>;
45
+ };
46
+ /**
47
+ * Pre-seeded answers keyed by prompt label. Tests configure the map up front;
48
+ * unmapped labels throw so the test surfaces unexpected prompts loudly.
49
+ */
50
+ declare class MemoryFunnelTokenPrompter extends FunnelTokenPrompter {
51
+ private readonly answers;
52
+ readonly asked: string[];
53
+ constructor(props?: Props);
54
+ promptSecret(label: string): Promise<string>;
55
+ }
56
+ //#endregion
57
+ export { funnelJsonSchema as i, NodeFunnelTokenPrompter as n, FunnelLocalConfigWriter as r, MemoryFunnelTokenPrompter as t };
@@ -0,0 +1,61 @@
1
+ import { i as FunnelTokenPrompter, o as LOCAL_CONFIG_FILENAME } from "./local-config-json-schema-8IHjS4Q7.js";
2
+ import { join } from "node:path";
3
+ //#region lib/engine/local-config/local-config-writer.ts
4
+ const isRecord = (value) => {
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
+ };
7
+ const withIdFirst = (config, id) => {
8
+ const ordered = {};
9
+ if (config.$schema !== void 0) ordered.$schema = config.$schema;
10
+ ordered.id = id;
11
+ for (const key of Object.keys(config)) {
12
+ if (key === "$schema" || key === "id") continue;
13
+ ordered[key] = config[key];
14
+ }
15
+ return ordered;
16
+ };
17
+ /**
18
+ * The one path that mutates the repo-committed funnel.json, and it only ever
19
+ * inserts `id`. On first launch a repo has no `id`; funnel generates one and
20
+ * writes it back here so future launches resolve the same `~/.funnel/projects/<id>/`.
21
+ * Idempotent — a no-op once `id` is present. Kept separate from the read-only
22
+ * FunnelLocalConfig so reads stay side-effect free.
23
+ */
24
+ var FunnelLocalConfigWriter = class {
25
+ fs;
26
+ constructor(deps) {
27
+ this.fs = deps.fs;
28
+ Object.freeze(this);
29
+ }
30
+ ensureId(cwd, id) {
31
+ const path = join(cwd, LOCAL_CONFIG_FILENAME);
32
+ if (!this.fs.existsSync(path)) return;
33
+ const parsed = JSON.parse(this.fs.readFileSync(path));
34
+ if (!isRecord(parsed)) return;
35
+ if (typeof parsed.id === "string" && parsed.id !== "") return;
36
+ const ordered = withIdFirst(parsed, id);
37
+ this.fs.writeFileSync(path, `${JSON.stringify(ordered, null, 2)}\n`);
38
+ }
39
+ };
40
+ //#endregion
41
+ //#region lib/engine/token-prompter/memory-token-prompter.ts
42
+ /**
43
+ * Pre-seeded answers keyed by prompt label. Tests configure the map up front;
44
+ * unmapped labels throw so the test surfaces unexpected prompts loudly.
45
+ */
46
+ var MemoryFunnelTokenPrompter = class extends FunnelTokenPrompter {
47
+ answers;
48
+ asked = [];
49
+ constructor(props = {}) {
50
+ super();
51
+ this.answers = new Map(Object.entries(props.answers ?? {}));
52
+ }
53
+ async promptSecret(label) {
54
+ this.asked.push(label);
55
+ const answer = this.answers.get(label);
56
+ if (answer === void 0) throw new Error(`no answer seeded for prompt "${label}"`);
57
+ return answer;
58
+ }
59
+ };
60
+ //#endregion
61
+ export { FunnelLocalConfigWriter as n, MemoryFunnelTokenPrompter as t };
@@ -0,0 +1,48 @@
1
+ import { t as FunnelFileSystem } from "./file-system-PWKKU7lA.js";
2
+ import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
+ //#region lib/engine/fs/node-file-system.ts
4
+ const SECRET_MODE = 384;
5
+ var NodeFunnelFileSystem = class extends FunnelFileSystem {
6
+ constructor() {
7
+ super();
8
+ Object.freeze(this);
9
+ }
10
+ existsSync(path) {
11
+ return existsSync(path);
12
+ }
13
+ readFileSync(path) {
14
+ return readFileSync(path, "utf-8");
15
+ }
16
+ writeFileSync(path, data) {
17
+ writeFileSync(path, data);
18
+ }
19
+ writeSecretFileSync(path, data) {
20
+ writeFileSync(path, data, { mode: SECRET_MODE });
21
+ try {
22
+ chmodSync(path, SECRET_MODE);
23
+ } catch {}
24
+ }
25
+ appendFileSync(path, data) {
26
+ appendFileSync(path, data);
27
+ }
28
+ unlink(path) {
29
+ try {
30
+ unlinkSync(path);
31
+ } catch {}
32
+ }
33
+ mkdirSync(path, options) {
34
+ mkdirSync(path, { recursive: options?.recursive ?? false });
35
+ }
36
+ readdirSync(path) {
37
+ return readdirSync(path);
38
+ }
39
+ statSync(path) {
40
+ const stat = statSync(path);
41
+ return {
42
+ mtimeMs: stat.mtimeMs,
43
+ mode: stat.mode & 511
44
+ };
45
+ }
46
+ };
47
+ //#endregion
48
+ export { NodeFunnelFileSystem as t };
@@ -1,5 +1,3 @@
1
- import { z } from "zod";
2
-
3
1
  //#region lib/engine/process/process-runner.d.ts
4
2
  type RunOptions = {
5
3
  cwd?: string;
@@ -48,15 +46,4 @@ declare abstract class FunnelProcessRunner {
48
46
  abstract listProcessesContaining(marker: string): ProcessSnapshot[];
49
47
  }
50
48
  //#endregion
51
- //#region lib/connectors/gh-connector-schema.d.ts
52
- declare const ghConnectorSchema: z.ZodObject<{
53
- id: z.ZodString;
54
- name: z.ZodString;
55
- type: z.ZodLiteral<"gh">;
56
- pollInterval: z.ZodOptional<z.ZodNumber>;
57
- createdAt: z.ZodOptional<z.ZodString>;
58
- updatedAt: z.ZodOptional<z.ZodString>;
59
- }, z.core.$strip>;
60
- type GhConnectorConfig = z.infer<typeof ghConnectorSchema>;
61
- //#endregion
62
- export { FunnelProcessRunner as a, RunResult as c, DetachOptions as i, ghConnectorSchema as n, ProcessSnapshot as o, AttachOptions as r, RunOptions as s, GhConnectorConfig as t };
49
+ export { RunOptions as a, ProcessSnapshot as i, DetachOptions as n, RunResult as o, FunnelProcessRunner as r, AttachOptions as t };
@@ -0,0 +1,64 @@
1
+ import { a as ProfileConfig, n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-BSU6JyvM.js";
2
+ import { n as FunnelFileSystem } from "./file-system-BeOKXjlV.js";
3
+
4
+ //#region lib/engine/profiles/profiles.d.ts
5
+ type Deps = {
6
+ store: FunnelSettingsReader;
7
+ idGenerator: FunnelIdGenerator;
8
+ fs?: FunnelFileSystem;
9
+ };
10
+ /**
11
+ * Named launch presets for `fnl claude`. Each profile bundles a working
12
+ * directory, the channel id its Claude instance subscribes to, and the launch
13
+ * recipe (`options` prepended to the claude argv, `env` layered under the
14
+ * process, `resume` toggling session reuse). Implements ProfileChannelChecker
15
+ * so FunnelChannels can refuse to remove a channel that is still referenced.
16
+ *
17
+ * Each profile has a stable `id` (uuid) minted at `add`. That id is the unit
18
+ * everything internal keys on — the PID file, the resumable session id — so a
19
+ * rename never strands either. `name` is purely the CLI/TUI handle; the CRUD
20
+ * methods here take it because that is what the user types, but resolve to the
21
+ * id before touching id-keyed state. The first array entry is the default
22
+ * profile; `asDefault` reorders to put one first.
23
+ *
24
+ * `channelId` always stores the channel's stable id (uuid). CLI surfaces
25
+ * resolve channel name → id before calling `add`/`update` here.
26
+ */
27
+ declare class FunnelProfiles {
28
+ private readonly store;
29
+ private readonly idGenerator;
30
+ private readonly fs;
31
+ constructor(deps: Deps);
32
+ list(): ProfileConfig[];
33
+ get(name: string): ProfileConfig | null;
34
+ getById(id: string): ProfileConfig | null;
35
+ getDefault(): ProfileConfig | null;
36
+ add(input: {
37
+ name: string;
38
+ path: string;
39
+ channelId: string;
40
+ options?: string[];
41
+ env?: Record<string, string>;
42
+ resume?: boolean;
43
+ }): void;
44
+ remove(name: string): void;
45
+ rename(oldName: string, newName: string): void;
46
+ asDefault(name: string): void;
47
+ hasChannelRef(channelId: string): boolean;
48
+ /** Resumable claude session id last launched by this profile (by id), or null. */
49
+ getSessionId(id: string): string | null;
50
+ /** Records the claude session id this profile launched, overwriting any prior one. */
51
+ setSessionId(id: string, sessionId: string): void;
52
+ /**
53
+ * Mirrors claude's session storage path
54
+ * (`<config-dir>/projects/<cwd-with-slashes-as-dashes>/<id>.jsonl`) to check
55
+ * whether a recorded session still exists AND is non-empty. Reads the same
56
+ * `CLAUDE_CONFIG_DIR` the child will run under so the check matches reality; a
57
+ * wrong guess can only ever produce a false negative (start fresh), never a
58
+ * bad resume.
59
+ */
60
+ sessionFileExists(cwd: string, sessionId: string, env: Record<string, string>): boolean;
61
+ update(name: string, fields: Partial<Omit<ProfileConfig, "name">>): void;
62
+ }
63
+ //#endregion
64
+ export { FunnelProfiles as t };
@@ -0,0 +1,129 @@
1
+ import { t as NodeFunnelFileSystem } from "./node-file-system-BcrmWN9I.js";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ //#region lib/engine/profiles/profiles.ts
5
+ const defaultFs = new NodeFunnelFileSystem();
6
+ /**
7
+ * Named launch presets for `fnl claude`. Each profile bundles a working
8
+ * directory, the channel id its Claude instance subscribes to, and the launch
9
+ * recipe (`options` prepended to the claude argv, `env` layered under the
10
+ * process, `resume` toggling session reuse). Implements ProfileChannelChecker
11
+ * so FunnelChannels can refuse to remove a channel that is still referenced.
12
+ *
13
+ * Each profile has a stable `id` (uuid) minted at `add`. That id is the unit
14
+ * everything internal keys on — the PID file, the resumable session id — so a
15
+ * rename never strands either. `name` is purely the CLI/TUI handle; the CRUD
16
+ * methods here take it because that is what the user types, but resolve to the
17
+ * id before touching id-keyed state. The first array entry is the default
18
+ * profile; `asDefault` reorders to put one first.
19
+ *
20
+ * `channelId` always stores the channel's stable id (uuid). CLI surfaces
21
+ * resolve channel name → id before calling `add`/`update` here.
22
+ */
23
+ var FunnelProfiles = class {
24
+ store;
25
+ idGenerator;
26
+ fs;
27
+ constructor(deps) {
28
+ this.store = deps.store;
29
+ this.idGenerator = deps.idGenerator;
30
+ this.fs = deps.fs ?? defaultFs;
31
+ Object.freeze(this);
32
+ }
33
+ list() {
34
+ return this.store.read().profiles;
35
+ }
36
+ get(name) {
37
+ return this.list().find((p) => p.name === name) ?? null;
38
+ }
39
+ getById(id) {
40
+ return this.list().find((p) => p.id === id) ?? null;
41
+ }
42
+ getDefault() {
43
+ return this.list()[0] ?? null;
44
+ }
45
+ add(input) {
46
+ const settings = this.store.read();
47
+ if (settings.profiles.some((p) => p.name === input.name)) throw new Error(`profile "${input.name}" already exists`);
48
+ if (!settings.channels.some((c) => c.id === input.channelId)) throw new Error(`channel id "${input.channelId}" not found`);
49
+ settings.profiles.push({
50
+ id: this.idGenerator.generate(),
51
+ name: input.name,
52
+ path: input.path,
53
+ channelId: input.channelId,
54
+ options: input.options ?? [],
55
+ env: input.env ?? {},
56
+ resume: input.resume ?? true
57
+ });
58
+ this.store.write(settings);
59
+ }
60
+ remove(name) {
61
+ const settings = this.store.read();
62
+ const index = settings.profiles.findIndex((p) => p.name === name);
63
+ if (index < 0) throw new Error(`profile "${name}" not found`);
64
+ settings.profiles.splice(index, 1);
65
+ this.store.write(settings);
66
+ }
67
+ rename(oldName, newName) {
68
+ const settings = this.store.read();
69
+ const profile = settings.profiles.find((p) => p.name === oldName);
70
+ if (!profile) throw new Error(`profile "${oldName}" not found`);
71
+ if (settings.profiles.some((p) => p.name === newName)) throw new Error(`profile "${newName}" already exists`);
72
+ profile.name = newName;
73
+ this.store.write(settings);
74
+ }
75
+ asDefault(name) {
76
+ const settings = this.store.read();
77
+ const index = settings.profiles.findIndex((p) => p.name === name);
78
+ if (index < 0) throw new Error(`profile "${name}" not found`);
79
+ if (index === 0) return;
80
+ const [profile] = settings.profiles.splice(index, 1);
81
+ if (!profile) return;
82
+ settings.profiles.unshift(profile);
83
+ this.store.write(settings);
84
+ }
85
+ hasChannelRef(channelId) {
86
+ return this.store.read().profiles.some((p) => p.channelId === channelId);
87
+ }
88
+ /** Resumable claude session id last launched by this profile (by id), or null. */
89
+ getSessionId(id) {
90
+ return this.getById(id)?.sessionId ?? null;
91
+ }
92
+ /** Records the claude session id this profile launched, overwriting any prior one. */
93
+ setSessionId(id, sessionId) {
94
+ const settings = this.store.read();
95
+ const profile = settings.profiles.find((p) => p.id === id);
96
+ if (!profile) throw new Error(`profile id "${id}" not found`);
97
+ profile.sessionId = sessionId;
98
+ this.store.write(settings);
99
+ }
100
+ /**
101
+ * Mirrors claude's session storage path
102
+ * (`<config-dir>/projects/<cwd-with-slashes-as-dashes>/<id>.jsonl`) to check
103
+ * whether a recorded session still exists AND is non-empty. Reads the same
104
+ * `CLAUDE_CONFIG_DIR` the child will run under so the check matches reality; a
105
+ * wrong guess can only ever produce a false negative (start fresh), never a
106
+ * bad resume.
107
+ */
108
+ sessionFileExists(cwd, sessionId, env) {
109
+ const path = join(env.CLAUDE_CONFIG_DIR ?? globalThis.process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude"), "projects", cwd.replace(/\//g, "-"), `${sessionId}.jsonl`);
110
+ if (!this.fs.existsSync(path)) return false;
111
+ return this.fs.readFileSync(path).trim().length > 0;
112
+ }
113
+ update(name, fields) {
114
+ const settings = this.store.read();
115
+ const profile = settings.profiles.find((p) => p.name === name);
116
+ if (!profile) throw new Error(`profile "${name}" not found`);
117
+ if (fields.channelId !== void 0) {
118
+ if (!settings.channels.some((c) => c.id === fields.channelId)) throw new Error(`channel id "${fields.channelId}" not found`);
119
+ profile.channelId = fields.channelId;
120
+ }
121
+ if (fields.path !== void 0) profile.path = fields.path;
122
+ if (fields.options !== void 0) profile.options = fields.options;
123
+ if (fields.env !== void 0) profile.env = fields.env;
124
+ if (fields.resume !== void 0) profile.resume = fields.resume;
125
+ this.store.write(settings);
126
+ }
127
+ };
128
+ //#endregion
129
+ export { FunnelProfiles as t };
@@ -0,0 +1,2 @@
1
+ import { t as FunnelProfiles } from "./profiles-f0mNmEyP.js";
2
+ export { FunnelProfiles };
@@ -0,0 +1,2 @@
1
+ import { t as FunnelProfiles } from "./profiles-wMRnjSid.js";
2
+ export { FunnelProfiles };
@@ -0,0 +1,31 @@
1
+ import { z } from "zod";
2
+ //#region lib/connectors/schedule-connector-schema.ts
3
+ /**
4
+ * Catch-up behavior when the daemon was down past one or more matching minutes.
5
+ *
6
+ * - `latest`: fire once with the most recent missed match (default; preserves prior behavior).
7
+ * - `all`: fire once per missed minute, oldest first (capped at 24 h).
8
+ * - `skip`: never fire missed matches; only fire when the current minute matches.
9
+ */
10
+ const scheduleCatchupPolicySchema = z.enum([
11
+ "latest",
12
+ "all",
13
+ "skip"
14
+ ]);
15
+ const scheduleEntrySchema = z.object({
16
+ id: z.string(),
17
+ cron: z.string(),
18
+ prompt: z.string(),
19
+ enabled: z.boolean().default(true),
20
+ catchupPolicy: scheduleCatchupPolicySchema.default("latest")
21
+ });
22
+ const scheduleConnectorSchema = z.object({
23
+ id: z.string(),
24
+ name: z.string(),
25
+ type: z.literal("schedule"),
26
+ entries: z.array(scheduleEntrySchema).default([]),
27
+ createdAt: z.string().datetime().optional(),
28
+ updatedAt: z.string().datetime().optional()
29
+ });
30
+ //#endregion
31
+ export { scheduleConnectorSchema as n, scheduleEntrySchema as r, scheduleCatchupPolicySchema as t };
@@ -1,6 +1,26 @@
1
- import { S as NotifyFn, b as FunnelLogger, o as ConnectorDiagnosticLog, x as FunnelConnectorListener } from "./connector-diagnostic-log-OPpPi9V9.js";
1
+ import { S as FunnelLogger, b as FunnelConnectorListener, o as ConnectorDiagnosticLog, x as NotifyFn } from "./connector-diagnostic-log-yTOojKUR.js";
2
+ import { n as FunnelFileSystem } from "./file-system-BeOKXjlV.js";
2
3
  import { z } from "zod";
3
4
 
5
+ //#region lib/connectors/schedule-state-store.d.ts
6
+ type Deps$1 = {
7
+ path: string;
8
+ fs?: FunnelFileSystem;
9
+ };
10
+ /**
11
+ * Per-connector lastFiredAt persistence for the schedule listener. The path is
12
+ * passed in by FunnelConnectorFactory so this store does not know about the
13
+ * funnel directory layout (`channels/<id>/connectors/<id>/state.json` lives
14
+ * outside this class).
15
+ */
16
+ declare class ScheduleStateStore {
17
+ private readonly path;
18
+ private readonly fs;
19
+ constructor(deps: Deps$1);
20
+ load(): Map<string, Date>;
21
+ save(state: Map<string, Date>): void;
22
+ }
23
+ //#endregion
4
24
  //#region lib/connectors/schedule-connector-schema.d.ts
5
25
  /**
6
26
  * Catch-up behavior when the daemon was down past one or more matching minutes.
@@ -47,50 +67,6 @@ declare const scheduleConnectorSchema: z.ZodObject<{
47
67
  }, z.core.$strip>;
48
68
  type ScheduleConnectorConfig = z.infer<typeof scheduleConnectorSchema>;
49
69
  //#endregion
50
- //#region lib/engine/fs/file-system.d.ts
51
- type FileStat = {
52
- mtimeMs: number; /** POSIX mode bits (e.g. 0o600). `null` when the underlying FS does not expose mode. */
53
- mode: number | null;
54
- };
55
- /**
56
- * Filesystem boundary used everywhere funnel reads or writes.
57
- * Default is NodeFunnelFileSystem (real `node:fs`); MemoryFunnelFileSystem
58
- * provides a sandbox for tests and embedded use.
59
- */
60
- declare abstract class FunnelFileSystem {
61
- abstract existsSync(path: string): boolean;
62
- abstract readFileSync(path: string): string;
63
- abstract writeFileSync(path: string, data: string): void;
64
- /** Write `data` and ensure the resulting file is owner-only (0600). Use for tokens and any file that may contain secrets. */
65
- abstract writeSecretFileSync(path: string, data: string): void;
66
- abstract appendFileSync(path: string, data: string): void;
67
- abstract unlink(path: string): void;
68
- abstract mkdirSync(path: string, options?: {
69
- recursive?: boolean;
70
- }): void;
71
- abstract readdirSync(path: string): string[];
72
- abstract statSync(path: string): FileStat;
73
- }
74
- //#endregion
75
- //#region lib/connectors/schedule-state-store.d.ts
76
- type Deps$1 = {
77
- path: string;
78
- fs?: FunnelFileSystem;
79
- };
80
- /**
81
- * Per-connector lastFiredAt persistence for the schedule listener. The path is
82
- * passed in by FunnelConnectorFactory so this store does not know about the
83
- * funnel directory layout (`channels/<id>/connectors/<id>/state.json` lives
84
- * outside this class).
85
- */
86
- declare class ScheduleStateStore {
87
- private readonly path;
88
- private readonly fs;
89
- constructor(deps: Deps$1);
90
- load(): Map<string, Date>;
91
- save(state: Map<string, Date>): void;
92
- }
93
- //#endregion
94
70
  //#region lib/connectors/schedule-listener.d.ts
95
71
  type ScheduleOnFired = (entry: ScheduleEntry, firedAt: Date) => void | Promise<void>;
96
72
  type Deps = {
@@ -133,4 +109,4 @@ declare class FunnelScheduleListener extends FunnelConnectorListener {
133
109
  private recordConnection;
134
110
  }
135
111
  //#endregion
136
- export { FunnelFileSystem as a, ScheduleEntry as c, scheduleEntrySchema as d, FileStat as i, scheduleCatchupPolicySchema as l, ScheduleOnFired as n, ScheduleCatchupPolicy as o, ScheduleStateStore as r, ScheduleConnectorConfig as s, FunnelScheduleListener as t, scheduleConnectorSchema as u };
112
+ export { ScheduleEntry as a, scheduleEntrySchema as c, ScheduleConnectorConfig as i, ScheduleStateStore as l, ScheduleOnFired as n, scheduleCatchupPolicySchema as o, ScheduleCatchupPolicy as r, scheduleConnectorSchema as s, FunnelScheduleListener as t };
@@ -1,7 +1,6 @@
1
- import { n as FunnelConnectorListener } from "./logger-Czli2OKh.js";
1
+ import { t as FunnelConnectorListener } from "./connector-listener-DU54DN-f.js";
2
+ import { t as NodeFunnelFileSystem } from "./node-file-system-BcrmWN9I.js";
2
3
  import { dirname } from "node:path";
3
- import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
4
- import { z } from "zod";
5
4
  //#region lib/connectors/match-cron.ts
6
5
  const parseField = (expr, min, max) => {
7
6
  const values = /* @__PURE__ */ new Set();
@@ -67,59 +66,6 @@ const matchCron = (expr, date) => {
67
66
  return true;
68
67
  };
69
68
  //#endregion
70
- //#region lib/engine/fs/file-system.ts
71
- /**
72
- * Filesystem boundary used everywhere funnel reads or writes.
73
- * Default is NodeFunnelFileSystem (real `node:fs`); MemoryFunnelFileSystem
74
- * provides a sandbox for tests and embedded use.
75
- */
76
- var FunnelFileSystem = class {};
77
- //#endregion
78
- //#region lib/engine/fs/node-file-system.ts
79
- const SECRET_MODE = 384;
80
- var NodeFunnelFileSystem = class extends FunnelFileSystem {
81
- constructor() {
82
- super();
83
- Object.freeze(this);
84
- }
85
- existsSync(path) {
86
- return existsSync(path);
87
- }
88
- readFileSync(path) {
89
- return readFileSync(path, "utf-8");
90
- }
91
- writeFileSync(path, data) {
92
- writeFileSync(path, data);
93
- }
94
- writeSecretFileSync(path, data) {
95
- writeFileSync(path, data, { mode: SECRET_MODE });
96
- try {
97
- chmodSync(path, SECRET_MODE);
98
- } catch {}
99
- }
100
- appendFileSync(path, data) {
101
- appendFileSync(path, data);
102
- }
103
- unlink(path) {
104
- try {
105
- unlinkSync(path);
106
- } catch {}
107
- }
108
- mkdirSync(path, options) {
109
- mkdirSync(path, { recursive: options?.recursive ?? false });
110
- }
111
- readdirSync(path) {
112
- return readdirSync(path);
113
- }
114
- statSync(path) {
115
- const stat = statSync(path);
116
- return {
117
- mtimeMs: stat.mtimeMs,
118
- mode: stat.mode & 511
119
- };
120
- }
121
- };
122
- //#endregion
123
69
  //#region lib/connectors/schedule-state-store.ts
124
70
  const defaultFs = new NodeFunnelFileSystem();
125
71
  /**
@@ -353,33 +299,4 @@ var FunnelScheduleListener = class extends FunnelConnectorListener {
353
299
  }
354
300
  };
355
301
  //#endregion
356
- //#region lib/connectors/schedule-connector-schema.ts
357
- /**
358
- * Catch-up behavior when the daemon was down past one or more matching minutes.
359
- *
360
- * - `latest`: fire once with the most recent missed match (default; preserves prior behavior).
361
- * - `all`: fire once per missed minute, oldest first (capped at 24 h).
362
- * - `skip`: never fire missed matches; only fire when the current minute matches.
363
- */
364
- const scheduleCatchupPolicySchema = z.enum([
365
- "latest",
366
- "all",
367
- "skip"
368
- ]);
369
- const scheduleEntrySchema = z.object({
370
- id: z.string(),
371
- cron: z.string(),
372
- prompt: z.string(),
373
- enabled: z.boolean().default(true),
374
- catchupPolicy: scheduleCatchupPolicySchema.default("latest")
375
- });
376
- const scheduleConnectorSchema = z.object({
377
- id: z.string(),
378
- name: z.string(),
379
- type: z.literal("schedule"),
380
- entries: z.array(scheduleEntrySchema).default([]),
381
- createdAt: z.string().datetime().optional(),
382
- updatedAt: z.string().datetime().optional()
383
- });
384
- //#endregion
385
- export { ScheduleStateStore as a, matchCron as c, FunnelScheduleListener as i, scheduleConnectorSchema as n, NodeFunnelFileSystem as o, scheduleEntrySchema as r, FunnelFileSystem as s, scheduleCatchupPolicySchema as t };
302
+ export { ScheduleStateStore as n, matchCron as r, FunnelScheduleListener as t };