@thesammykins/tether 1.0.1 → 1.2.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/.env.example +14 -0
- package/README.md +25 -283
- package/bin/tether.ts +366 -28
- package/docs/agents.md +205 -0
- package/docs/architecture.md +117 -0
- package/docs/cli.md +277 -0
- package/docs/configuration.md +158 -0
- package/docs/discord-setup.md +122 -0
- package/docs/installation.md +151 -0
- package/docs/troubleshooting.md +74 -0
- package/package.json +2 -1
- package/src/api.ts +89 -6
- package/src/bot.ts +47 -9
- package/src/config.ts +390 -0
- package/src/db.ts +6 -0
- package/src/features/pause-resume.ts +3 -3
- package/src/queue.ts +1 -0
- package/src/worker.ts +14 -2
- package/src/spawner.ts +0 -110
package/.env.example
CHANGED
|
@@ -12,6 +12,20 @@ AGENT_TYPE=claude
|
|
|
12
12
|
# Optional - Working directory for agent sessions
|
|
13
13
|
CLAUDE_WORKING_DIR=/path/to/your/project
|
|
14
14
|
|
|
15
|
+
# Optional - HTTP API server configuration
|
|
16
|
+
TETHER_API_HOST=127.0.0.1
|
|
17
|
+
# Bind address for the HTTP API server (default: 127.0.0.1)
|
|
18
|
+
# Use 0.0.0.0 to allow external connections
|
|
19
|
+
|
|
20
|
+
TETHER_API_PORT=2643
|
|
21
|
+
# Port for the HTTP API server (default: 2643)
|
|
22
|
+
|
|
23
|
+
API_TOKEN=
|
|
24
|
+
# Optional - API authentication token
|
|
25
|
+
# If set, all API requests (except /health) must include:
|
|
26
|
+
# Authorization: Bearer <token>
|
|
27
|
+
# If not set, no authentication is required (backward compatible)
|
|
28
|
+
|
|
15
29
|
# Optional - Direct Messages (disabled by default)
|
|
16
30
|
ENABLE_DMS=false
|
|
17
31
|
# Set to "true" to allow the bot to respond to DMs.
|
package/README.md
CHANGED
|
@@ -9,269 +9,45 @@ Fork of [cord](https://github.com/alexknowshtml/cord) with multi-agent support a
|
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
12
|
-
- **Multi-agent support** — Claude Code, OpenCode, Codex (switch via
|
|
13
|
-
- **BRB mode** — Agent asks questions via Discord buttons
|
|
14
|
-
- **Direct messages** — Chat with the bot privately via DMs
|
|
12
|
+
- **Multi-agent support** — Claude Code, OpenCode, Codex (switch via config)
|
|
13
|
+
- **BRB mode** — Agent asks questions via Discord buttons when you're away
|
|
14
|
+
- **Direct messages** — Chat with the bot privately via DMs
|
|
15
|
+
- **Encrypted config** — `tether config` stores tokens with AES-256-GCM encryption
|
|
15
16
|
- **Access control** — User, role, and channel allowlists
|
|
16
17
|
- **Rate limiting** — Per-user sliding window
|
|
17
18
|
- **Session management** — Turn limits, duration limits, pause/resume
|
|
18
19
|
- **Smart threads** — Auto-naming, channel context for new conversations
|
|
19
|
-
- **Acknowledgment** — 👀 reaction on incoming messages
|
|
20
20
|
- **Resilient** — Exponential backoff on connection failures
|
|
21
21
|
|
|
22
22
|
## Quick Start
|
|
23
23
|
|
|
24
|
-
### Prerequisites
|
|
25
|
-
|
|
26
|
-
- [Bun](https://bun.sh) runtime
|
|
27
|
-
- [Redis](https://redis.io) (for BullMQ job queue)
|
|
28
|
-
- A Discord bot token ([setup guide below](#discord-bot-setup))
|
|
29
|
-
- An AI agent CLI installed on PATH (`claude`, `opencode`, or `codex`)
|
|
30
|
-
|
|
31
|
-
### Install
|
|
32
|
-
|
|
33
|
-
From the npm registry (requires [Bun](https://bun.sh)):
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
bun add @thesammykins/tether
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Or clone from source:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
git clone https://github.com/thesammykins/tether.git
|
|
43
|
-
cd tether
|
|
44
|
-
bun install
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Configure
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
cp .env.example .env
|
|
51
|
-
# Edit .env with your DISCORD_BOT_TOKEN and settings
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Run
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
bun run start # Start bot + worker via CLI
|
|
58
|
-
# or run separately:
|
|
59
|
-
bun run bot # Discord gateway
|
|
60
|
-
bun run worker # Job processor
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
---
|
|
64
|
-
|
|
65
|
-
## Discord Bot Setup
|
|
66
|
-
|
|
67
|
-
Step-by-step guide to creating and configuring your Discord bot.
|
|
68
|
-
|
|
69
|
-
### 1. Create a Discord Application
|
|
70
|
-
|
|
71
|
-
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
|
|
72
|
-
2. Click **New Application** and give it a name (e.g. "Tether")
|
|
73
|
-
3. Note the **Application ID** — you'll need it for the invite link
|
|
74
|
-
|
|
75
|
-
### 2. Create the Bot User
|
|
76
|
-
|
|
77
|
-
1. In your application, go to the **Bot** tab in the left sidebar
|
|
78
|
-
2. Click **Add Bot** (if not already created)
|
|
79
|
-
3. Under **Token**, click **Reset Token** and copy it — this is your `DISCORD_BOT_TOKEN`
|
|
80
|
-
4. **Store this token securely** — you won't be able to see it again
|
|
81
|
-
|
|
82
|
-
### 3. Configure Bot Permissions
|
|
83
|
-
|
|
84
|
-
Under the **Bot** tab, configure these settings:
|
|
85
|
-
|
|
86
|
-
**Privileged Gateway Intents** (toggle ON):
|
|
87
|
-
|
|
88
|
-
| Intent | Why |
|
|
89
|
-
|--------|-----|
|
|
90
|
-
| **Message Content Intent** | Required to read message text (not just metadata) |
|
|
91
|
-
|
|
92
|
-
> Without Message Content Intent enabled, the bot will connect but never see message contents.
|
|
93
|
-
|
|
94
|
-
**Bot Permissions** — the bot needs these permissions in your server:
|
|
95
|
-
|
|
96
|
-
| Permission | Why |
|
|
97
|
-
|------------|-----|
|
|
98
|
-
| Send Messages | Reply to users |
|
|
99
|
-
| Create Public Threads | Create conversation threads |
|
|
100
|
-
| Send Messages in Threads | Respond in threads |
|
|
101
|
-
| Manage Threads | Rename threads |
|
|
102
|
-
| Read Message History | Fetch channel context for new conversations |
|
|
103
|
-
| Add Reactions | 👀 acknowledgment emoji |
|
|
104
|
-
| Use Slash Commands | `/cord config` command |
|
|
105
|
-
|
|
106
|
-
### 4. Generate the Invite Link
|
|
107
|
-
|
|
108
|
-
1. Go to the **OAuth2** tab → **URL Generator**
|
|
109
|
-
2. Under **Scopes**, select:
|
|
110
|
-
- `bot`
|
|
111
|
-
- `applications.commands`
|
|
112
|
-
3. Under **Bot Permissions**, select the permissions listed above, or use the permission integer `326417588288` which includes all required permissions
|
|
113
|
-
4. Copy the generated URL and open it in your browser
|
|
114
|
-
5. Select the server you want to add the bot to and click **Authorize**
|
|
115
|
-
|
|
116
|
-
### 5. Enable DMs (Optional)
|
|
117
|
-
|
|
118
|
-
If you want users to be able to DM the bot directly:
|
|
119
|
-
|
|
120
|
-
1. In `.env`, set `ENABLE_DMS=true`
|
|
121
|
-
2. In the Discord Developer Portal → **Bot** tab, make sure **Allow DMs** is not disabled
|
|
122
|
-
|
|
123
|
-
DM behavior:
|
|
124
|
-
- Any message sent to the bot in DMs starts or continues a conversation
|
|
125
|
-
- No `@mention` needed — every DM is treated as a prompt
|
|
126
|
-
- Sessions persist per-user until manually reset
|
|
127
|
-
- Send `!reset` in DMs to start a fresh session
|
|
128
|
-
- Only `ALLOWED_USERS` is checked for DMs (roles and channels don't apply)
|
|
129
|
-
|
|
130
|
-
### 6. Start the Bot
|
|
131
|
-
|
|
132
24
|
```bash
|
|
133
|
-
#
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
# Start tether
|
|
137
|
-
bun run start
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
You should see:
|
|
141
|
-
```
|
|
142
|
-
[bot] Connecting to Discord gateway (attempt 1)...
|
|
143
|
-
[bot] Logged in as YourBot#1234
|
|
144
|
-
[worker] Worker started, waiting for jobs...
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### 7. Test It
|
|
148
|
-
|
|
149
|
-
In your Discord server, `@mention` the bot in any channel:
|
|
150
|
-
|
|
151
|
-
```
|
|
152
|
-
@Tether what time is it?
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
The bot will:
|
|
156
|
-
1. React with 👀
|
|
157
|
-
2. Create a thread with an auto-generated name
|
|
158
|
-
3. Post a "Processing..." status message
|
|
159
|
-
4. Forward the prompt to your AI agent
|
|
160
|
-
5. Post the response in the thread
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## Configuration
|
|
165
|
-
|
|
166
|
-
| Variable | Default | Description |
|
|
167
|
-
|----------|---------|-------------|
|
|
168
|
-
| `DISCORD_BOT_TOKEN` | required | Discord bot token |
|
|
169
|
-
| `AGENT_TYPE` | `claude` | Agent backend: `claude`, `opencode`, `codex` |
|
|
170
|
-
| `ENABLE_DMS` | `false` | Allow bot to respond to direct messages |
|
|
171
|
-
| `REDIS_HOST` | `localhost` | Redis host |
|
|
172
|
-
| `REDIS_PORT` | `6379` | Redis port |
|
|
173
|
-
| `ALLOWED_USERS` | (empty=all) | Comma-separated Discord user IDs |
|
|
174
|
-
| `ALLOWED_ROLES` | (empty=all) | Comma-separated role IDs (guild only) |
|
|
175
|
-
| `ALLOWED_CHANNELS` | (empty=all) | Comma-separated channel IDs (guild only) |
|
|
176
|
-
| `RATE_LIMIT_REQUESTS` | `5` | Max requests per window |
|
|
177
|
-
| `RATE_LIMIT_WINDOW_MS` | `60000` | Rate limit window (ms) |
|
|
178
|
-
| `MAX_TURNS_PER_SESSION` | `50` | Max turns per thread/DM session |
|
|
179
|
-
| `MAX_SESSION_DURATION_MS` | `3600000` | Max session duration (ms) |
|
|
180
|
-
| `CLAUDE_WORKING_DIR` | cwd | Default working directory for agents |
|
|
181
|
-
| `DB_PATH` | `./data/threads.db` | SQLite database path |
|
|
182
|
-
| `CORD_ALLOWED_DIRS` | (empty=any) | Comma-separated allowed working directories |
|
|
183
|
-
|
|
184
|
-
### Finding Discord IDs
|
|
185
|
-
|
|
186
|
-
To get user, role, or channel IDs:
|
|
187
|
-
1. Enable **Developer Mode** in Discord: Settings → App Settings → Advanced → Developer Mode
|
|
188
|
-
2. Right-click a user, role, or channel and select **Copy ID**
|
|
189
|
-
|
|
190
|
-
## Agent CLI Requirements
|
|
191
|
-
|
|
192
|
-
Whichever agent you choose must be installed and available on PATH:
|
|
193
|
-
- **Claude Code**: `claude` CLI — [Install guide](https://docs.anthropic.com/en/docs/claude-code)
|
|
194
|
-
- **OpenCode**: `opencode` CLI
|
|
195
|
-
- **Codex**: `codex` CLI
|
|
196
|
-
|
|
197
|
-
## Architecture
|
|
198
|
-
|
|
199
|
-
Tether uses a queue-based architecture for reliable message processing:
|
|
200
|
-
|
|
201
|
-
```
|
|
202
|
-
Discord Bot → BullMQ Queue → Agent Worker
|
|
203
|
-
(gateway) (Redis) (spawns CLI)
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Key Components
|
|
207
|
-
|
|
208
|
-
- **Bot** (`src/bot.ts`) — Discord gateway with middleware pipeline
|
|
209
|
-
- **Worker** (`src/worker.ts`) — Job processor using adapter registry
|
|
210
|
-
- **Adapters** (`src/adapters/`) — AgentAdapter interface for Claude/OpenCode/Codex
|
|
211
|
-
- **Middleware** (`src/middleware/`) — Allowlist, rate-limiter
|
|
212
|
-
- **Features** (`src/features/`) — Ack, channel-context, thread-naming, session-limits, pause-resume
|
|
213
|
-
- **Queue** (`src/queue.ts`) — BullMQ job queue
|
|
214
|
-
- **DB** (`src/db.ts`) — SQLite for thread→session mapping
|
|
215
|
-
|
|
216
|
-
### Message Flow
|
|
25
|
+
# Install
|
|
26
|
+
bun add -g @thesammykins/tether
|
|
217
27
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
3. Bot creates thread with auto-generated name
|
|
222
|
-
4. Bot adds job to BullMQ queue
|
|
223
|
-
5. Worker pulls job and spawns agent adapter
|
|
224
|
-
6. Adapter runs CLI (`claude`/`opencode`/`codex`) with prompt
|
|
225
|
-
7. Worker posts response back to Discord thread
|
|
28
|
+
# Configure
|
|
29
|
+
tether config set DISCORD_BOT_TOKEN # paste token (hidden input)
|
|
30
|
+
tether config set AGENT_TYPE claude # or: opencode, codex
|
|
226
31
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
3. Bot creates or resumes session for this user
|
|
231
|
-
4. Bot adds job to BullMQ queue
|
|
232
|
-
5. Worker posts response back to the DM channel
|
|
233
|
-
|
|
234
|
-
### Middleware Pipeline
|
|
235
|
-
|
|
236
|
-
Messages pass through middleware in this order:
|
|
237
|
-
|
|
238
|
-
1. **Allowlist** — Block unauthorized users/channels/roles (DMs: user-only check)
|
|
239
|
-
2. **Rate Limiter** — Sliding window per-user rate limit
|
|
240
|
-
3. **Pause/Resume** — Hold messages if thread is paused (guild threads only)
|
|
241
|
-
|
|
242
|
-
### Session Management
|
|
243
|
-
|
|
244
|
-
- **Turn limits** — Max messages per thread/DM session (prevents infinite loops)
|
|
245
|
-
- **Duration limits** — Max session lifetime
|
|
246
|
-
- **Pause/Resume** — Type `pause` to hold messages, `resume` to continue (threads only)
|
|
247
|
-
- **DM Reset** — Type `!reset` in DMs to start a new session
|
|
248
|
-
- **Auto-completion** — React with ✅ on last message to mark "Done" (threads only)
|
|
249
|
-
|
|
250
|
-
## Working Directory Configuration
|
|
251
|
-
|
|
252
|
-
Tether supports per-channel working directories so agents operate in the correct project context.
|
|
253
|
-
|
|
254
|
-
**Channel-level configuration** (persists for all conversations in that channel):
|
|
255
|
-
```
|
|
256
|
-
/cord config dir ~/Code/myproject
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
**Per-message override** (one-time, just for this conversation):
|
|
32
|
+
# Start Redis + Tether
|
|
33
|
+
redis-server &
|
|
34
|
+
tether start
|
|
260
35
|
```
|
|
261
|
-
@bot [/other/project] what files are here?
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
**Fallback chain**: Message override → Channel config → `CLAUDE_WORKING_DIR` env → `process.cwd()`
|
|
265
|
-
|
|
266
|
-
### Security: Directory Allowlist
|
|
267
36
|
|
|
268
|
-
|
|
37
|
+
Then `@mention` the bot in your Discord server.
|
|
269
38
|
|
|
270
|
-
|
|
271
|
-
CORD_ALLOWED_DIRS=/home/projects,/var/code
|
|
272
|
-
```
|
|
39
|
+
## Documentation
|
|
273
40
|
|
|
274
|
-
|
|
41
|
+
| Guide | Description |
|
|
42
|
+
|-------|-------------|
|
|
43
|
+
| **[Installation](docs/installation.md)** | Prerequisites, install methods, quick setup |
|
|
44
|
+
| **[Discord Setup](docs/discord-setup.md)** | Bot creation, permissions, intents, invite link |
|
|
45
|
+
| **[Agent Setup](docs/agents.md)** | Claude Code / OpenCode / Codex install and auth |
|
|
46
|
+
| **[Configuration](docs/configuration.md)** | All config keys, encrypted secrets, resolution chain |
|
|
47
|
+
| **[CLI Reference](docs/cli.md)** | Every `tether` command with examples |
|
|
48
|
+
| **[Architecture](docs/architecture.md)** | System design, message flow, middleware pipeline |
|
|
49
|
+
| **[Troubleshooting](docs/troubleshooting.md)** | Common problems and solutions |
|
|
50
|
+
| **[HTTP API](skills/tether/HTTP-API.md)** | REST API for external scripts and webhooks |
|
|
275
51
|
|
|
276
52
|
## Testing
|
|
277
53
|
|
|
@@ -280,45 +56,11 @@ bun test # Run all tests
|
|
|
280
56
|
bun test tests/adapters/ # Adapter tests only
|
|
281
57
|
bun test tests/middleware/ # Middleware tests only
|
|
282
58
|
bun test tests/features/ # Feature tests only
|
|
283
|
-
bun test tests/integration/ # Integration tests only
|
|
284
59
|
```
|
|
285
60
|
|
|
286
|
-
## CLI Commands
|
|
287
|
-
|
|
288
|
-
Quick reference:
|
|
289
|
-
- `tether start` — Start bot + worker
|
|
290
|
-
- `tether stop` — Stop all processes
|
|
291
|
-
- `tether status` — Show running status
|
|
292
|
-
- `tether send <channel> "message"` — Send text message
|
|
293
|
-
- `tether embed <channel> "text" --title "T"` — Send formatted embed
|
|
294
|
-
- `tether file <channel> ./file.txt` — Send file attachment
|
|
295
|
-
- `tether buttons <channel> "prompt" --button label="Yes" id="yes"` — Send interactive buttons
|
|
296
|
-
- `tether state <channel> <msgId> done` — Update status message
|
|
297
|
-
- `tether dm <user-id> "message"` — Send a DM to a user (proactive outreach)
|
|
298
|
-
- `tether dm <user-id> --embed "text" --title "T"` — Send an embed DM
|
|
299
|
-
- `tether dm <user-id> --file ./report.md` — Send a file via DM
|
|
300
|
-
- `tether ask <channel> "question" --button "Yes" --button "No"` — Ask a question via buttons, blocks until answered
|
|
301
|
-
|
|
302
|
-
## HTTP API
|
|
303
|
-
|
|
304
|
-
Tether exposes an HTTP API on port 2643 for external scripts and webhooks. See the [API documentation](./skills/tether/HTTP-API.md) for details.
|
|
305
|
-
|
|
306
|
-
## Troubleshooting
|
|
307
|
-
|
|
308
|
-
| Problem | Solution |
|
|
309
|
-
|---------|----------|
|
|
310
|
-
| Bot connects but doesn't respond | Enable **Message Content Intent** in Developer Portal → Bot tab |
|
|
311
|
-
| "TokenInvalid" error | Regenerate your bot token in the Developer Portal |
|
|
312
|
-
| "DisallowedIntents" error | Enable the required intents in Developer Portal → Bot tab |
|
|
313
|
-
| Bot doesn't receive DMs | Set `ENABLE_DMS=true` in `.env` |
|
|
314
|
-
| "Rate limit exceeded" | Adjust `RATE_LIMIT_REQUESTS` / `RATE_LIMIT_WINDOW_MS` |
|
|
315
|
-
| Agent command not found | Ensure `claude`/`opencode`/`codex` is installed and on PATH |
|
|
316
|
-
| Redis connection refused | Start Redis: `redis-server` |
|
|
317
|
-
| Bot can't create threads | Check bot has **Create Public Threads** permission in your server |
|
|
318
|
-
|
|
319
61
|
## Privacy
|
|
320
62
|
|
|
321
|
-
|
|
63
|
+
No message content or user data is stored. Only thread-to-session mappings and channel config persist. Messages pass through Redis transiently and are discarded after processing.
|
|
322
64
|
|
|
323
65
|
## License
|
|
324
66
|
|