@gamalan/pi-gateway 1.0.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 +366 -0
- package/config/config.default.json +51 -0
- package/dist/adapters/base.d.ts +85 -0
- package/dist/adapters/base.js +34 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/discord.d.ts +53 -0
- package/dist/adapters/discord.js +224 -0
- package/dist/adapters/discord.js.map +1 -0
- package/dist/adapters/index.d.ts +13 -0
- package/dist/adapters/index.js +8 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/slack.d.ts +49 -0
- package/dist/adapters/slack.js +231 -0
- package/dist/adapters/slack.js.map +1 -0
- package/dist/adapters/telegram.d.ts +64 -0
- package/dist/adapters/telegram.js +274 -0
- package/dist/adapters/telegram.js.map +1 -0
- package/dist/adapters/twitch.d.ts +75 -0
- package/dist/adapters/twitch.js +222 -0
- package/dist/adapters/twitch.js.map +1 -0
- package/dist/adapters/websocket.d.ts +30 -0
- package/dist/adapters/websocket.js +132 -0
- package/dist/adapters/websocket.js.map +1 -0
- package/dist/adapters/whatsapp.d.ts +49 -0
- package/dist/adapters/whatsapp.js +251 -0
- package/dist/adapters/whatsapp.js.map +1 -0
- package/dist/background/index.d.ts +1 -0
- package/dist/background/index.js +2 -0
- package/dist/background/index.js.map +1 -0
- package/dist/background/manager.d.ts +70 -0
- package/dist/background/manager.js +291 -0
- package/dist/background/manager.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1275 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +12 -0
- package/dist/logger.js +39 -0
- package/dist/logger.js.map +1 -0
- package/dist/paths.d.ts +14 -0
- package/dist/paths.js +32 -0
- package/dist/paths.js.map +1 -0
- package/dist/security/auth.d.ts +91 -0
- package/dist/security/auth.js +316 -0
- package/dist/security/auth.js.map +1 -0
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.js +2 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/tool-policy.d.ts +66 -0
- package/dist/security/tool-policy.js +467 -0
- package/dist/security/tool-policy.js.map +1 -0
- package/dist/sessions/index.d.ts +1 -0
- package/dist/sessions/index.js +2 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/sessions/store.d.ts +64 -0
- package/dist/sessions/store.js +247 -0
- package/dist/sessions/store.js.map +1 -0
- package/package.json +57 -0
- package/src/adapters/base.ts +124 -0
- package/src/adapters/discord.ts +270 -0
- package/src/adapters/index.ts +13 -0
- package/src/adapters/slack.ts +313 -0
- package/src/adapters/telegram.ts +394 -0
- package/src/adapters/twitch.ts +316 -0
- package/src/adapters/websocket.ts +158 -0
- package/src/adapters/whatsapp.ts +296 -0
- package/src/background/index.ts +1 -0
- package/src/background/manager.ts +395 -0
- package/src/index.ts +1665 -0
- package/src/logger.ts +43 -0
- package/src/paths.ts +40 -0
- package/src/security/auth.ts +458 -0
- package/src/security/index.ts +1 -0
- package/src/security/tool-policy.ts +568 -0
- package/src/sessions/index.ts +1 -0
- package/src/sessions/store.ts +360 -0
package/README.md
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# pi-gateway
|
|
2
|
+
|
|
3
|
+
Hermes-style messaging gateway for pi — a multi-platform agent with per-chat sessions, background task support, and allowlist security.
|
|
4
|
+
|
|
5
|
+
> Fork of [0xKobold/pi-gateway](https://github.com/0xKobold/pi-gateway) with config-based UID allowlisting.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Multi-platform adapters** — Discord, Telegram, Slack, Twitch, WhatsApp, WebSocket
|
|
10
|
+
- **Per-chat sessions** — isolated conversations with configurable reset policies (daily / idle)
|
|
11
|
+
- **Background tasks** — spawn async work from chats, results delivered when ready
|
|
12
|
+
- **Allowlist security** — DB-based and config-file pre-approved UIDs, optional DM pairing flow
|
|
13
|
+
- **HTTP + WebSocket API** — connect external clients, send prompts, receive streaming responses
|
|
14
|
+
- **pi-native** — runs as a pi extension with `/gateway` slash commands and registered tools
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Pi supports installing extensions directly from GitHub:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pi install git:github.com/gamalan/pi-gateway
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or clone and build manually:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/gamalan/pi-gateway.git
|
|
28
|
+
cd pi-gateway
|
|
29
|
+
bun install && bun run build
|
|
30
|
+
pi install .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requires pi coding agent (`@earendil-works/pi-coding-agent >= 0.80.3`) and `@sinclair/typebox >= 0.32.0`.
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Start the gateway
|
|
39
|
+
/gateway start
|
|
40
|
+
|
|
41
|
+
# Check status
|
|
42
|
+
/gateway status
|
|
43
|
+
|
|
44
|
+
# Stop
|
|
45
|
+
/gateway stop
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The gateway starts on `http://localhost:3847` by default. See `/gateway config` for current settings.
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
Configuration lives at `~/.pi/gateway/config.json`:
|
|
53
|
+
|
|
54
|
+
```jsonc
|
|
55
|
+
{
|
|
56
|
+
"port": 3847,
|
|
57
|
+
"host": "localhost",
|
|
58
|
+
"tokens": [], // Bearer tokens for API auth (empty = allow all)
|
|
59
|
+
"corsOrigins": ["*"],
|
|
60
|
+
"enableWebSocket": true,
|
|
61
|
+
"enableHttp": true,
|
|
62
|
+
"security": {
|
|
63
|
+
"allowAll": true, // false = enforce allowlist
|
|
64
|
+
"requirePairing": false,
|
|
65
|
+
"allowedUids": {}, // pre-approved users (see Security)
|
|
66
|
+
"adminUids": {}, // users with full access (see Admin Users)
|
|
67
|
+
"rateLimit": {
|
|
68
|
+
"maxRequests": 60,
|
|
69
|
+
"windowMs": 60000
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"sessions": {
|
|
73
|
+
"resetPolicy": "idle", // "daily" | "idle" | "both"
|
|
74
|
+
"dailyHour": 4, // hour (0-23) for daily reset
|
|
75
|
+
"idleMinutes": 1440 // minutes before idle reset
|
|
76
|
+
},
|
|
77
|
+
"platforms": {
|
|
78
|
+
"discord": {
|
|
79
|
+
"enabled": true,
|
|
80
|
+
"botToken": "your-token",
|
|
81
|
+
"guildId": "optional-guild-id"
|
|
82
|
+
},
|
|
83
|
+
"twitch": {
|
|
84
|
+
"enabled": true,
|
|
85
|
+
"clientId": "your-client-id",
|
|
86
|
+
"clientSecret": "your-secret",
|
|
87
|
+
"channels": ["channel-name"]
|
|
88
|
+
},
|
|
89
|
+
"telegram": {
|
|
90
|
+
"enabled": true,
|
|
91
|
+
"token": "your-bot-token",
|
|
92
|
+
"webhookUrl": "https://..." // omit for long polling
|
|
93
|
+
},
|
|
94
|
+
"slack": {
|
|
95
|
+
"enabled": true,
|
|
96
|
+
"webhookUrl": "https://...",
|
|
97
|
+
"botToken": "optional-bot-token"
|
|
98
|
+
},
|
|
99
|
+
"whatsapp": {
|
|
100
|
+
"enabled": true,
|
|
101
|
+
"sessionPath": "~/.pi/whatsapp-session",
|
|
102
|
+
"printQr": true
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
When the gateway starts for the first time with no config file, it
|
|
109
|
+
automatically seeds `~/.pi/gateway/config.json` from the default
|
|
110
|
+
template shipped with the package. You can also find it at
|
|
111
|
+
`node_modules/pi-gateway/config/config.default.json`.
|
|
112
|
+
|
|
113
|
+
### Telegram: webhook vs long polling
|
|
114
|
+
|
|
115
|
+
The gateway auto-detects the mode based on whether `webhookUrl` is set:
|
|
116
|
+
|
|
117
|
+
| `webhookUrl` | Mode | How it works |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| Set | **Webhook** | Telegram POSTs updates to `/webhook/telegram` on the gateway's HTTP server. Lowest latency, requires a public URL. |
|
|
120
|
+
| Omitted | **Long polling** | The gateway opens a persistent connection to Telegram's `getUpdates` endpoint (30s timeout). Telegram holds it open and returns immediately when a message arrives — near-real-time, no public URL needed. |
|
|
121
|
+
|
|
122
|
+
Both modes are real-time. Long polling is NOT interval-based — it keeps one connection alive at all times.
|
|
123
|
+
|
|
124
|
+
## Security
|
|
125
|
+
|
|
126
|
+
### Allowlist (DB)
|
|
127
|
+
|
|
128
|
+
Manage users at runtime via `/gateway` commands:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# List allowlisted users
|
|
132
|
+
/gateway allow
|
|
133
|
+
|
|
134
|
+
# Add a user
|
|
135
|
+
/gateway allow discord 123456789
|
|
136
|
+
|
|
137
|
+
# Revoke a user (via tool or directly)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Config-file pre-approved UIDs
|
|
141
|
+
|
|
142
|
+
Skip pairing entirely by listing UIDs in the `security` block of `config.json`:
|
|
143
|
+
|
|
144
|
+
```jsonc
|
|
145
|
+
{
|
|
146
|
+
"security": {
|
|
147
|
+
"allowAll": false,
|
|
148
|
+
"allowedUids": {
|
|
149
|
+
"discord": ["123456789", "987654321"],
|
|
150
|
+
"telegram": ["1234567890"],
|
|
151
|
+
"*": ["cross-platform-admin"]
|
|
152
|
+
},
|
|
153
|
+
"adminUids": {
|
|
154
|
+
"discord": ["123456789"],
|
|
155
|
+
"*": ["cross-platform-admin-uid"]
|
|
156
|
+
},
|
|
157
|
+
"rateLimit": {
|
|
158
|
+
"maxRequests": 60,
|
|
159
|
+
"windowMs": 60000
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- Platform-specific keys match that platform only
|
|
166
|
+
- The `"*"` wildcard matches any platform
|
|
167
|
+
- Users in this list are auto-allowed on first contact — no pairing code needed
|
|
168
|
+
- `adminUids` grants full unrestricted access (bypasses all tool policies)
|
|
169
|
+
- All security settings live in the main `config.json` — no separate security file
|
|
170
|
+
|
|
171
|
+
### Admin Users
|
|
172
|
+
|
|
173
|
+
Admin users have **full unrestricted access** — they bypass all tool policies and can use every tool pi offers (bash, write, edit, subagent, etc.).
|
|
174
|
+
|
|
175
|
+
Admins can be set at runtime or via config's `security.adminUids` block:
|
|
176
|
+
|
|
177
|
+
```jsonc
|
|
178
|
+
{
|
|
179
|
+
"security": {
|
|
180
|
+
"adminUids": {
|
|
181
|
+
"discord": ["123456789"],
|
|
182
|
+
"*": ["cross-platform-admin-uid"]
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# List all admins (DB + config)
|
|
190
|
+
/gateway admin list
|
|
191
|
+
|
|
192
|
+
# Grant admin to a user on a specific platform
|
|
193
|
+
/gateway admin add discord 123456789
|
|
194
|
+
|
|
195
|
+
# Grant admin on ALL platforms
|
|
196
|
+
/gateway admin add * 123456789
|
|
197
|
+
|
|
198
|
+
# Revoke admin
|
|
199
|
+
/gateway admin remove discord 123456789
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Admin users are listed in the status panel and their messages carry a "FULL ACCESS" guard.
|
|
203
|
+
|
|
204
|
+
### Tool Policy
|
|
205
|
+
|
|
206
|
+
By default, external users are **restricted to read-only tools** when their messages reach pi: they can search code, inspect files, and ask questions, but cannot write files, execute shell commands, spawn subagents, or modify system state.
|
|
207
|
+
|
|
208
|
+
The policy is enforced via a system directive prepended to every forwarded message. It is tunable per platform, per user, or globally.
|
|
209
|
+
|
|
210
|
+
**Default allowed tools:** `read`, `web_search`, `fetch_content`, `fffind`, `ffgrep`, `module_report`, `read_symbol`, code search tools, `lsp_diagnostics`, `lsp_navigation`, `image_generate`, `gateway_*`
|
|
211
|
+
|
|
212
|
+
**Default denied tools:** `bash`, `write`, `edit`, `subagent`, `todo`, `goal_complete`, `mcp`, `ast_grep_replace`, `agent_browser`, `ask_user_question`, `wait`, `intercom`, `wiki_*`, `lens_diagnostics`
|
|
213
|
+
|
|
214
|
+
#### Managing Policies
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# List all explicit policies
|
|
218
|
+
/gateway tool-policy list
|
|
219
|
+
|
|
220
|
+
# See the default baseline
|
|
221
|
+
/gateway tool-policy defaults
|
|
222
|
+
|
|
223
|
+
# Allow bash for a specific user
|
|
224
|
+
/gateway tool-policy set discord U123456 bash allow
|
|
225
|
+
|
|
226
|
+
# Deny write for all users on Discord
|
|
227
|
+
/gateway tool-policy set discord * write deny
|
|
228
|
+
|
|
229
|
+
# Allow everything for an admin user (glob)
|
|
230
|
+
/gateway tool-policy set * admin-uid * allow
|
|
231
|
+
|
|
232
|
+
# Remove a policy by ID
|
|
233
|
+
/gateway tool-policy remove 3
|
|
234
|
+
|
|
235
|
+
# Reset all custom policies to defaults
|
|
236
|
+
/gateway tool-policy reset
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Policies can also be managed from within pi sessions via the `gateway_tool_policy` tool.
|
|
240
|
+
|
|
241
|
+
**Resolution order** (highest wins): user-specific > platform-specific > global. Ties break deny-first (secure by default). The `*` glob matches any tool name. Admin users always bypass all restrictions.
|
|
242
|
+
|
|
243
|
+
> **Note:** All security configuration (allowlist, admin UIDs, rate limits)
|
|
244
|
+
> lives in the `security` block of `~/.pi/gateway/config.json`. There is
|
|
245
|
+
> no separate security config file. On first run, the gateway
|
|
246
|
+
> auto-seeds a complete default config so you don't have to write one
|
|
247
|
+
> from scratch.
|
|
248
|
+
|
|
249
|
+
### Pairing Flow
|
|
250
|
+
|
|
251
|
+
When `requirePairing` is enabled and a user is not in the allowlist:
|
|
252
|
+
|
|
253
|
+
1. User sends a message → blocked, receives a pairing code
|
|
254
|
+
2. Admin approves with `/gateway pair <code>`
|
|
255
|
+
3. User is added to the DB allowlist
|
|
256
|
+
|
|
257
|
+
## Commands
|
|
258
|
+
|
|
259
|
+
| Command | Description |
|
|
260
|
+
|---------|-------------|
|
|
261
|
+
| `/gateway start [port]` | Start the gateway |
|
|
262
|
+
| `/gateway stop` | Stop the gateway |
|
|
263
|
+
| `/gateway restart` | Restart the gateway |
|
|
264
|
+
| `/gateway status` | Show running status, platforms, sessions |
|
|
265
|
+
| `/gateway pair [code]` | List pending codes or approve one |
|
|
266
|
+
| `/gateway allow [platform] [userId]` | List allowlist or add a user |
|
|
267
|
+
| `/gateway revoke <platform> <userId>` | Remove a user from the DB allowlist |
|
|
268
|
+
| `/gateway sessions` | List active chat sessions |
|
|
269
|
+
| `/gateway tasks` | List background tasks |
|
|
270
|
+
| `/gateway config` | Show current configuration |
|
|
271
|
+
| `/gateway admin list` | List admin users (DB + config) |
|
|
272
|
+
| `/gateway admin add <p\|*> <uid>` | Grant admin privileges |
|
|
273
|
+
| `/gateway admin remove <p\|*> <uid>` | Revoke admin |
|
|
274
|
+
| `/gateway admin list` | List admin users |
|
|
275
|
+
| `/gateway admin add <p\|*> <uid>` | Grant admin privileges |
|
|
276
|
+
| `/gateway admin remove <p\|*> <uid>` | Revoke admin |
|
|
277
|
+
| `/gateway tool-policy list` | List explicit tool policies |
|
|
278
|
+
| `/gateway tool-policy defaults` | Show default policy baseline |
|
|
279
|
+
| `/gateway tool-policy set <p> <u> <t> allow\|deny` | Add/update a tool policy |
|
|
280
|
+
| `/gateway tool-policy remove <id>` | Delete a policy |
|
|
281
|
+
| `/gateway tool-policy reset` | Clear all, back to defaults |
|
|
282
|
+
|
|
283
|
+
## HTTP API
|
|
284
|
+
|
|
285
|
+
Available when the gateway is running:
|
|
286
|
+
|
|
287
|
+
| Endpoint | Method | Description |
|
|
288
|
+
|----------|--------|-------------|
|
|
289
|
+
| `/api/status` | GET | Gateway status (running, adapters, clients, sessions) |
|
|
290
|
+
| `/api/sessions` | GET | Active sessions |
|
|
291
|
+
| `/api/background` | GET | Background tasks |
|
|
292
|
+
| `/api/allowlist` | GET | Allowlisted users |
|
|
293
|
+
| `/api/pairing` | GET | Pending pairing codes |
|
|
294
|
+
|
|
295
|
+
Authenticate with `Authorization: Bearer <token>` if tokens are configured.
|
|
296
|
+
|
|
297
|
+
## WebSocket API
|
|
298
|
+
|
|
299
|
+
Connect to `ws://localhost:3847`. Messages are JSON:
|
|
300
|
+
|
|
301
|
+
```jsonc
|
|
302
|
+
// Send a prompt
|
|
303
|
+
{ "type": "prompt", "data": { "message": "Hello" } }
|
|
304
|
+
|
|
305
|
+
// Start a background task
|
|
306
|
+
{ "type": "background", "data": { "sessionId": "...", "command": "..." } }
|
|
307
|
+
|
|
308
|
+
// Ping
|
|
309
|
+
{ "type": "ping" }
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Receive responses, background task updates, and agent events as server-pushed messages.
|
|
313
|
+
|
|
314
|
+
## Registered Tools
|
|
315
|
+
|
|
316
|
+
The extension registers these tools for use in pi sessions:
|
|
317
|
+
|
|
318
|
+
- **`gateway_status`** — Check if gateway is running and which adapters are active
|
|
319
|
+
- **`gateway_sessions`** — List active chat sessions
|
|
320
|
+
- **`gateway_background_tasks`** — List and manage background tasks
|
|
321
|
+
- **`gateway_pairing`** — Generate or approve pairing codes
|
|
322
|
+
- **`gateway_tool_policy`** — Manage tool access policies for external users
|
|
323
|
+
|
|
324
|
+
## Sessions
|
|
325
|
+
|
|
326
|
+
Each chat gets an isolated session with configurable reset policies:
|
|
327
|
+
|
|
328
|
+
- **daily** — Session resets at a specific hour each day (default: 4 AM)
|
|
329
|
+
- **idle** — Session resets after N minutes of inactivity
|
|
330
|
+
- **both** — Whichever triggers first
|
|
331
|
+
|
|
332
|
+
Sessions persist across gateway restarts in `~/.pi/gateway-sessions.db`.
|
|
333
|
+
|
|
334
|
+
## Background Tasks
|
|
335
|
+
|
|
336
|
+
Long-running work is spawned in isolated background sessions. Results are delivered back to the parent chat when complete. Managed via `/gateway tasks`.
|
|
337
|
+
|
|
338
|
+
## Architecture
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
┌─────────────┐ ┌─────────────────────────────┐
|
|
342
|
+
│ pi agent │◄───►│ pi-gateway │
|
|
343
|
+
│ (RPC) │ │ │
|
|
344
|
+
└─────────────┘ │ ┌─────────────────────┐ │
|
|
345
|
+
│ │ Platform Adapters │ │
|
|
346
|
+
┌─────────────┐ │ │ Discord · Telegram │ │
|
|
347
|
+
│ HTTP / WS │────►│ │ Slack · Twitch │ │
|
|
348
|
+
│ Clients │ │ │ WhatsApp · WebSocket │ │
|
|
349
|
+
└─────────────┘ │ └─────────────────────┘ │
|
|
350
|
+
│ │
|
|
351
|
+
│ ┌─────────────────────┐ │
|
|
352
|
+
│ │ Sessions Store │ │
|
|
353
|
+
│ │ Background Manager │ │
|
|
354
|
+
│ │ Security Layer │ │
|
|
355
|
+
│ └─────────────────────┘ │
|
|
356
|
+
└─────────────────────────────┘
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
- **Platform adapters** translate incoming platform messages into a unified format
|
|
360
|
+
- **Sessions store** (`SQLite`) persists per-chat state with reset policies
|
|
361
|
+
- **Background manager** spawns async child processes, delivers results via chat
|
|
362
|
+
- **Security layer** checks allowlists, manages pairing codes, and enforces rate limits
|
|
363
|
+
|
|
364
|
+
## License
|
|
365
|
+
|
|
366
|
+
MIT
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"port": 3847,
|
|
3
|
+
"host": "localhost",
|
|
4
|
+
"tokens": [],
|
|
5
|
+
"corsOrigins": ["*"],
|
|
6
|
+
"enableWebSocket": true,
|
|
7
|
+
"enableHttp": true,
|
|
8
|
+
"security": {
|
|
9
|
+
"allowAll": true,
|
|
10
|
+
"requirePairing": false,
|
|
11
|
+
"allowedUids": {},
|
|
12
|
+
"adminUids": {},
|
|
13
|
+
"rateLimit": {
|
|
14
|
+
"maxRequests": 60,
|
|
15
|
+
"windowMs": 60000
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"sessions": {
|
|
19
|
+
"resetPolicy": "idle",
|
|
20
|
+
"dailyHour": 4,
|
|
21
|
+
"idleMinutes": 1440
|
|
22
|
+
},
|
|
23
|
+
"platforms": {
|
|
24
|
+
"discord": {
|
|
25
|
+
"enabled": false,
|
|
26
|
+
"botToken": "",
|
|
27
|
+
"guildId": ""
|
|
28
|
+
},
|
|
29
|
+
"twitch": {
|
|
30
|
+
"enabled": false,
|
|
31
|
+
"clientId": "",
|
|
32
|
+
"clientSecret": "",
|
|
33
|
+
"channels": []
|
|
34
|
+
},
|
|
35
|
+
"telegram": {
|
|
36
|
+
"enabled": false,
|
|
37
|
+
"token": "",
|
|
38
|
+
"webhookUrl": ""
|
|
39
|
+
},
|
|
40
|
+
"slack": {
|
|
41
|
+
"enabled": false,
|
|
42
|
+
"webhookUrl": "",
|
|
43
|
+
"botToken": ""
|
|
44
|
+
},
|
|
45
|
+
"whatsapp": {
|
|
46
|
+
"enabled": false,
|
|
47
|
+
"sessionPath": "~/.pi/whatsapp-session",
|
|
48
|
+
"printQr": true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform Adapter Base - Interface for Hermes-style platform adapters
|
|
3
|
+
*/
|
|
4
|
+
export interface PlatformMessage {
|
|
5
|
+
id: string;
|
|
6
|
+
platform: string;
|
|
7
|
+
channelId: string;
|
|
8
|
+
userId: string;
|
|
9
|
+
content: string;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
metadata?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export interface PlatformConfig {
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
platform: string;
|
|
16
|
+
token?: string;
|
|
17
|
+
botToken?: string;
|
|
18
|
+
webhookSecret?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface AdapterCallbacks {
|
|
21
|
+
onMessage: (message: PlatformMessage) => Promise<void>;
|
|
22
|
+
onTyping?: (userId: string, isTyping: boolean) => void;
|
|
23
|
+
onDisconnect?: () => void;
|
|
24
|
+
}
|
|
25
|
+
export interface PlatformAdapter {
|
|
26
|
+
readonly platform: string;
|
|
27
|
+
readonly config: PlatformConfig;
|
|
28
|
+
/**
|
|
29
|
+
* Initialize the adapter
|
|
30
|
+
*/
|
|
31
|
+
initialize(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Start listening for messages
|
|
34
|
+
*/
|
|
35
|
+
start(callbacks: AdapterCallbacks): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Stop listening
|
|
38
|
+
*/
|
|
39
|
+
stop(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Send a message to a channel
|
|
42
|
+
*/
|
|
43
|
+
sendMessage(channelId: string, content: string): Promise<string>;
|
|
44
|
+
/**
|
|
45
|
+
* Edit an existing message
|
|
46
|
+
*/
|
|
47
|
+
editMessage(channelId: string, messageId: string, content: string): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Delete a message
|
|
50
|
+
*/
|
|
51
|
+
deleteMessage(channelId: string, messageId: string): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Set typing indicator
|
|
54
|
+
*/
|
|
55
|
+
setTyping(channelId: string, isTyping: boolean): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Get adapter health status
|
|
58
|
+
*/
|
|
59
|
+
getStatus(): Promise<{
|
|
60
|
+
connected: boolean;
|
|
61
|
+
latency?: number;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Abstract base class for adapters
|
|
66
|
+
*/
|
|
67
|
+
export declare abstract class BaseAdapter implements PlatformAdapter {
|
|
68
|
+
abstract readonly platform: string;
|
|
69
|
+
abstract config: PlatformConfig;
|
|
70
|
+
protected callbacks: AdapterCallbacks | null;
|
|
71
|
+
protected running: boolean;
|
|
72
|
+
initialize(): Promise<void>;
|
|
73
|
+
start(callbacks: AdapterCallbacks): Promise<void>;
|
|
74
|
+
stop(): Promise<void>;
|
|
75
|
+
abstract sendMessage(channelId: string, content: string): Promise<string>;
|
|
76
|
+
abstract editMessage(channelId: string, messageId: string, content: string): Promise<void>;
|
|
77
|
+
abstract deleteMessage(channelId: string, messageId: string): Promise<void>;
|
|
78
|
+
abstract setTyping(channelId: string, isTyping: boolean): Promise<void>;
|
|
79
|
+
abstract getStatus(): Promise<{
|
|
80
|
+
connected: boolean;
|
|
81
|
+
latency?: number;
|
|
82
|
+
}>;
|
|
83
|
+
protected emitMessage(message: PlatformMessage): Promise<void>;
|
|
84
|
+
protected generateMessageId(): string;
|
|
85
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform Adapter Base - Interface for Hermes-style platform adapters
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base class for adapters
|
|
6
|
+
*/
|
|
7
|
+
export class BaseAdapter {
|
|
8
|
+
callbacks = null;
|
|
9
|
+
running = false;
|
|
10
|
+
async initialize() {
|
|
11
|
+
// Override in subclass
|
|
12
|
+
}
|
|
13
|
+
async start(callbacks) {
|
|
14
|
+
this.callbacks = callbacks;
|
|
15
|
+
this.running = true;
|
|
16
|
+
}
|
|
17
|
+
async stop() {
|
|
18
|
+
this.running = false;
|
|
19
|
+
this.callbacks = null;
|
|
20
|
+
}
|
|
21
|
+
emitMessage(message) {
|
|
22
|
+
if (this.callbacks?.onMessage) {
|
|
23
|
+
return this.callbacks.onMessage(message).catch((err) => {
|
|
24
|
+
// Log but don't crash the adapter loop
|
|
25
|
+
console.error(`[${this.platform}] Error in onMessage callback:`, err);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
generateMessageId() {
|
|
31
|
+
return `${this.platform}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2EH;;GAEG;AACH,MAAM,OAAgB,WAAW;IAGtB,SAAS,GAA4B,IAAI,CAAC;IAC1C,OAAO,GAAG,KAAK,CAAC;IAE1B,KAAK,CAAC,UAAU;QACf,uBAAuB;IACxB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAA2B;QACtC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,IAAI;QACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACvB,CAAC;IAYS,WAAW,CAAC,OAAwB;QAC7C,IAAI,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACtD,uCAAuC;gBACvC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,gCAAgC,EAAE,GAAG,CAAC,CAAC;YACvE,CAAC,CAAkB,CAAC;QACrB,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IAES,iBAAiB;QAC1B,OAAO,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IACnF,CAAC;CACD"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord Adapter - Hermes-style Discord platform adapter
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - DM and guild channel support
|
|
6
|
+
* - Slash command registration
|
|
7
|
+
* - Typing indicators
|
|
8
|
+
* - Message editing/deletion
|
|
9
|
+
* - Rate limit handling
|
|
10
|
+
*/
|
|
11
|
+
import { BaseAdapter, type PlatformConfig } from "./base.js";
|
|
12
|
+
export interface DiscordConfig extends PlatformConfig {
|
|
13
|
+
platform: "discord";
|
|
14
|
+
botToken: string;
|
|
15
|
+
guildId?: string;
|
|
16
|
+
allowedChannels?: string[];
|
|
17
|
+
allowedRoles?: string[];
|
|
18
|
+
requireMention?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare class DiscordAdapter extends BaseAdapter {
|
|
21
|
+
readonly platform: "discord";
|
|
22
|
+
config: DiscordConfig;
|
|
23
|
+
private httpClient;
|
|
24
|
+
private wsConnection;
|
|
25
|
+
private heartbeatInterval;
|
|
26
|
+
private sequence;
|
|
27
|
+
private sessionId;
|
|
28
|
+
private intents;
|
|
29
|
+
constructor(config: DiscordConfig);
|
|
30
|
+
initialize(): Promise<void>;
|
|
31
|
+
private apiRequest;
|
|
32
|
+
start(callbacks: any): Promise<void>;
|
|
33
|
+
private handleGatewayMessage;
|
|
34
|
+
private startHeartbeat;
|
|
35
|
+
private identify;
|
|
36
|
+
private handleDispatch;
|
|
37
|
+
private handleMessage;
|
|
38
|
+
private getBotId;
|
|
39
|
+
sendMessage(channelId: string, content: string): Promise<string>;
|
|
40
|
+
editMessage(channelId: string, messageId: string, content: string): Promise<void>;
|
|
41
|
+
deleteMessage(channelId: string, messageId: string): Promise<void>;
|
|
42
|
+
setTyping(channelId: string, isTyping: boolean): Promise<void>;
|
|
43
|
+
getStatus(): Promise<{
|
|
44
|
+
connected: boolean;
|
|
45
|
+
latency?: number;
|
|
46
|
+
}>;
|
|
47
|
+
stop(): Promise<void>;
|
|
48
|
+
registerSlashCommands(commands: Array<{
|
|
49
|
+
name: string;
|
|
50
|
+
description: string;
|
|
51
|
+
options?: any[];
|
|
52
|
+
}>): Promise<void>;
|
|
53
|
+
}
|