@interactive-inc/claude-funnel 0.28.0 → 0.30.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 +19 -21
- package/dist/bin.js +875 -880
- package/dist/gateway/daemon.js +225 -260
- package/dist/index.d.ts +39 -71
- package/dist/index.js +89 -107
- package/funnel.schema.json +3 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ Connector — a single attachment from a channel to an external source. Four typ
|
|
|
42
42
|
|
|
43
43
|
Profile sits on top of that model as a launch convenience, not part of it — you never need one to run an agent (`fnl claude --channel <name>` is enough). It is a saved launch preset bundling `{ path, channelId, options, env, resume }` so `fnl claude --profile cto` reproduces a known setup: which directory to launch from, which channel to bind, and the launch recipe (args prepended to the claude argv, env layered under the process, session reuse). A profile carries a stable uuid `id` (the unit the PID file and the resumable session key off, so renaming it strands neither); `name` is just the handle you type. The first profile in the list is the default. Because a profile already binds a channel, `--profile` and `--channel` cannot be combined.
|
|
44
44
|
|
|
45
|
-
The daemon is where all external connections live. It runs on port 9742, supervises connectors with auto-restart, broadcasts events to subscribed agent sessions over WebSocket, and serves the reply API that MCP calls. Starting or stopping an agent never starts or stops external connections.
|
|
45
|
+
The daemon is where all external connections live. It runs on port 9742, bound to loopback (`127.0.0.1`) only so it is never reachable off-box; set `FUNNEL_HOST=0.0.0.0` to expose it deliberately (every privileged endpoint still requires the bearer token regardless). It supervises connectors with auto-restart, broadcasts events to subscribed agent sessions over WebSocket, and serves the reply API that MCP calls. Starting or stopping an agent never starts or stops external connections.
|
|
46
46
|
|
|
47
47
|
The MCP layer is a thin bridge into the agent. It subscribes to the bound channel over WebSocket (the daemon does the work) and exposes one tool per callable connector so the agent can reply back out.
|
|
48
48
|
|
|
@@ -102,11 +102,7 @@ Or drop a `funnel.json` in the repo and `fnl claude` (no args) inside the repo w
|
|
|
102
102
|
"connectors": [
|
|
103
103
|
{
|
|
104
104
|
"type": "slack",
|
|
105
|
-
"name": "my-slack"
|
|
106
|
-
"env": {
|
|
107
|
-
"botToken": "SLACK_BOT_TOKEN",
|
|
108
|
-
"appToken": "SLACK_APP_TOKEN"
|
|
109
|
-
}
|
|
105
|
+
"name": "my-slack"
|
|
110
106
|
}
|
|
111
107
|
]
|
|
112
108
|
},
|
|
@@ -132,21 +128,18 @@ Or drop a `funnel.json` in the repo and `fnl claude` (no args) inside the repo w
|
|
|
132
128
|
|
|
133
129
|
A channel declares only transport — its `connectors` and delivery mode. The launch recipe lives on `profiles[]`: each profile binds to a channel by name and carries `options` (prepended to the claude argv before user-supplied CLI args, which still come last — use it for flags like `--brief`, `--agent <name>`, `--model <name>`), `env` (layered under the launched claude process — `process.env` from the launching shell wins on collision), and `resume`. `fnl claude` applies the first profile bound to the chosen channel.
|
|
134
130
|
|
|
135
|
-
The optional `connectors` array on a channel is the source of truth for that channel: missing connectors are created
|
|
131
|
+
The optional `connectors` array on a channel is the source of truth for that channel: missing connectors are created and connectors not declared are removed on launch. Connectors are matched by name. An absent `connectors` field leaves existing connectors alone.
|
|
136
132
|
|
|
137
|
-
On launch, the chosen channel's transport (`connectors`, delivery) is materialized into
|
|
133
|
+
A repo with a `funnel.json` is scoped to itself. On first launch funnel writes a stable `id` (uuid) to the top of `funnel.json` and keeps every byte of funnel state under `~/.funnel/projects/<id>/`, never touching the global `~/.funnel` — only the event log and temp files under `/tmp/funnel/` are shared. This scoping applies to every CLI command run in the repo, not just `fnl claude`. On launch the chosen channel's transport (`connectors`, delivery) is materialized into `~/.funnel/projects/<id>/settings.json`; the profile recipe is passed straight to the launcher and is not persisted there. Raw launches (`fnl claude --channel <name>` without funnel.json) bind transport only against the global `~/.funnel` and carry no recipe.
|
|
138
134
|
|
|
139
135
|
The optional top-level `$schema` points at the JSON Schema so editors can validate and autocomplete the file. The recommended reference for repos with a local install is `./node_modules/@interactive-inc/claude-funnel/funnel.schema.json` — it works without a network round-trip and editors do not need to prompt for trust. The same file is also published at `https://interactive-inc.github.io/open-claude-funnel/funnel.schema.json` (editors usually require explicit trust on first use), and `fnl schema > funnel.schema.json` regenerates a local copy on demand.
|
|
140
136
|
|
|
141
|
-
|
|
137
|
+
Connectors in `funnel.json` carry no tokens. A connector's token is set out of band and stored only in the repo-scoped settings:
|
|
142
138
|
|
|
143
|
-
-
|
|
144
|
-
-
|
|
145
|
-
- field omitted everywhere — `fnl claude` prompts on a TTY and writes the answer to `~/.funnel/settings.json`; on non-TTY stdin the launch fails so CI / agent-spawned-agent runs do not hang
|
|
139
|
+
- run `fnl channels <ch> connectors set <c> --bot-token=...` (etc.) in the repo to write it to `~/.funnel/projects/<id>/settings.json`
|
|
140
|
+
- or omit it — `fnl claude` prompts on a TTY at launch and saves the answer there; it carries over so later launches do not re-prompt. On non-TTY stdin the launch fails so CI / agent-spawned-agent runs do not hang
|
|
146
141
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
`funnel.json` itself is never written to — secrets stay in env vars, `.env.local`, or `~/.funnel`, never in the committed file.
|
|
142
|
+
Only the `id` is ever written back to `funnel.json`; tokens never live in the repo.
|
|
150
143
|
|
|
151
144
|
Cron-driven agent runs:
|
|
152
145
|
|
|
@@ -271,21 +264,23 @@ Profile = { id, name, path, channelId, options[], env, resume, sessionId? }
|
|
|
271
264
|
name is the CLI handle. sessionId is execution state, not config — the claude session this
|
|
272
265
|
profile last launched, written by the launcher and read back on the next resume.
|
|
273
266
|
|
|
274
|
-
LocalConfig = { channels: ChannelSpec[], profiles?: ProfileSpec[] }
|
|
267
|
+
LocalConfig = { id?, channels: ChannelSpec[], profiles?: ProfileSpec[] }
|
|
275
268
|
per-repo file (funnel.json). channels[] required; first entry is default, --channel selects.
|
|
269
|
+
id (uuid) is written back on first launch; this repo's funnel state lives under
|
|
270
|
+
~/.funnel/projects/<id>/ and the global ~/.funnel is never touched.
|
|
276
271
|
|
|
277
272
|
ChannelSpec = { name, connectors? }
|
|
278
273
|
transport declaration (no id, since funnel.json declares by name). connectors materialize into
|
|
279
|
-
the matching Channel in ~/.funnel/settings.json on launch.
|
|
280
|
-
|
|
281
|
-
omission for a TTY prompt persisted to ~/.funnel.
|
|
274
|
+
the matching Channel in ~/.funnel/projects/<id>/settings.json on launch. Connectors carry no
|
|
275
|
+
tokens; a token is set via the CLI or prompted on a TTY at launch and saved to that scoped settings.
|
|
282
276
|
|
|
283
277
|
ProfileSpec = { channel, options?, env?, resume? }
|
|
284
278
|
launch recipe bound to a channel by name. applied inline on launch (the first spec bound to the
|
|
285
279
|
chosen channel — selected by its channel binding, not by name); not persisted into the global
|
|
286
280
|
profiles[] list.
|
|
287
281
|
|
|
288
|
-
Settings = { channels[], profiles[] } → ~/.funnel/settings.json
|
|
282
|
+
Settings = { channels[], profiles[] } → ~/.funnel/settings.json (global)
|
|
283
|
+
or ~/.funnel/projects/<id>/settings.json (per-repo funnel.json)
|
|
289
284
|
```
|
|
290
285
|
|
|
291
286
|
## File layout
|
|
@@ -294,7 +289,10 @@ Persistent state lives under `~/.funnel/`. Volatile logs and the event log live
|
|
|
294
289
|
|
|
295
290
|
```
|
|
296
291
|
~/.funnel/
|
|
297
|
-
├── settings.json channels[] with nested connectors, profiles[]
|
|
292
|
+
├── settings.json global channels[] with nested connectors, profiles[]
|
|
293
|
+
├── projects/
|
|
294
|
+
│ └── <id>/ per-repo state for a repo with funnel.json
|
|
295
|
+
│ └── settings.json, gateway.token, claude/, ... (same layout as the global root, scoped by funnel.json id)
|
|
298
296
|
├── gateway.pid daemon PID
|
|
299
297
|
├── gateway.token Bearer token for daemon HTTP / WS
|
|
300
298
|
├── claude/
|