aibroker 0.2.6 → 0.6.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/README.md +164 -4
- package/dist/adapters/iterm/core.d.ts +2 -0
- package/dist/adapters/iterm/core.d.ts.map +1 -1
- package/dist/adapters/iterm/core.js +13 -5
- package/dist/adapters/iterm/core.js.map +1 -1
- package/dist/adapters/iterm/iterm2-api.d.ts +20 -0
- package/dist/adapters/iterm/iterm2-api.d.ts.map +1 -0
- package/dist/adapters/iterm/iterm2-api.js +244 -0
- package/dist/adapters/iterm/iterm2-api.js.map +1 -0
- package/dist/adapters/iterm/sessions.d.ts.map +1 -1
- package/dist/adapters/iterm/sessions.js +3 -2
- package/dist/adapters/iterm/sessions.js.map +1 -1
- package/dist/adapters/kokoro/media.d.ts +2 -1
- package/dist/adapters/kokoro/media.d.ts.map +1 -1
- package/dist/adapters/kokoro/media.js +53 -5
- package/dist/adapters/kokoro/media.js.map +1 -1
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-13-56).d.ts +49 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-13-56).d.ts.map +1 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-13-56).js +632 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-13-56).js.map +1 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-13-59).js +632 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-15-36).d.ts +49 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-15-36).d.ts.map +1 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-15-36).js +614 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-15-36).js.map +1 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-15-46).js +614 -0
- package/dist/adapters/pailot/gateway.d.ts +48 -0
- package/dist/adapters/pailot/gateway.d.ts (SFConflict mnott 2026-03-06-21-13-59).map +1 -0
- package/dist/adapters/pailot/gateway.d.ts (SFConflict mnott 2026-03-06-21-15-46).map +1 -0
- package/dist/adapters/pailot/gateway.d.ts.map +1 -0
- package/dist/adapters/pailot/gateway.js +828 -0
- package/dist/adapters/pailot/gateway.js (SFConflict mnott 2026-03-06-21-13-59).map +1 -0
- package/dist/adapters/pailot/gateway.js (SFConflict mnott 2026-03-06-21-15-46).map +1 -0
- package/dist/adapters/pailot/gateway.js.map +1 -0
- package/dist/backend/api.d.ts +5 -1
- package/dist/backend/api.d.ts.map +1 -1
- package/dist/backend/api.js +74 -3
- package/dist/backend/api.js.map +1 -1
- package/dist/core/hybrid.d.ts +7 -0
- package/dist/core/hybrid.d.ts.map +1 -1
- package/dist/core/hybrid.js +33 -0
- package/dist/core/hybrid.js.map +1 -1
- package/dist/core/state.d.ts +3 -0
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +4 -0
- package/dist/core/state.js.map +1 -1
- package/dist/core/status-cache.d.ts +51 -0
- package/dist/core/status-cache.d.ts.map +1 -0
- package/dist/core/status-cache.js +62 -0
- package/dist/core/status-cache.js.map +1 -0
- package/dist/daemon/adapter-registry (SFConflict mnott 2026-03-06-21-15-36).d.ts +63 -0
- package/dist/daemon/adapter-registry (SFConflict mnott 2026-03-06-21-15-36).d.ts.map +1 -0
- package/dist/daemon/adapter-registry (SFConflict mnott 2026-03-06-21-15-36).js +229 -0
- package/dist/daemon/adapter-registry (SFConflict mnott 2026-03-06-21-15-36).js.map +1 -0
- package/dist/daemon/adapter-registry.d.ts +63 -0
- package/dist/daemon/adapter-registry.d.ts.map +1 -0
- package/dist/daemon/adapter-registry.js +240 -0
- package/dist/daemon/adapter-registry.js.map +1 -0
- package/dist/daemon/cli.d.ts +14 -0
- package/dist/daemon/cli.d.ts.map +1 -0
- package/dist/daemon/cli.js +150 -0
- package/dist/daemon/cli.js.map +1 -0
- package/dist/daemon/command-context.d.ts +24 -0
- package/dist/daemon/command-context.d.ts.map +1 -0
- package/dist/daemon/command-context.js +13 -0
- package/dist/daemon/command-context.js.map +1 -0
- package/dist/daemon/commands.d.ts +22 -0
- package/dist/daemon/commands.d.ts.map +1 -0
- package/dist/daemon/commands.js +632 -0
- package/dist/daemon/commands.js.map +1 -0
- package/dist/daemon/core-handlers.d.ts +24 -0
- package/dist/daemon/core-handlers.d.ts.map +1 -0
- package/dist/daemon/core-handlers.js +640 -0
- package/dist/daemon/core-handlers.js.map +1 -0
- package/dist/daemon/create-adapter.d.ts +22 -0
- package/dist/daemon/create-adapter.d.ts.map +1 -0
- package/dist/daemon/create-adapter.js +153 -0
- package/dist/daemon/create-adapter.js.map +1 -0
- package/dist/daemon/image-gen.d.ts +28 -0
- package/dist/daemon/image-gen.d.ts.map +1 -0
- package/dist/daemon/image-gen.js +97 -0
- package/dist/daemon/image-gen.js.map +1 -0
- package/dist/daemon/index.d.ts +12 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +184 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/pai-projects.d.ts +68 -0
- package/dist/daemon/pai-projects.d.ts.map +1 -0
- package/dist/daemon/pai-projects.js +174 -0
- package/dist/daemon/pai-projects.js.map +1 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-13-56).d.ts +12 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-13-56).d.ts.map +1 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-13-56).js +252 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-13-56).js.map +1 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-13-59).js +252 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-36).d.ts +12 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-36).d.ts.map +1 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-36).js +240 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-36).js.map +1 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-46).js +240 -0
- package/dist/daemon/screenshot.d.ts +12 -0
- package/dist/daemon/screenshot.d.ts (SFConflict mnott 2026-03-06-21-13-59).map +1 -0
- package/dist/daemon/screenshot.d.ts (SFConflict mnott 2026-03-06-21-15-46).map +1 -0
- package/dist/daemon/screenshot.d.ts.map +1 -0
- package/dist/daemon/screenshot.js +252 -0
- package/dist/daemon/screenshot.js (SFConflict mnott 2026-03-06-21-13-59).map +1 -0
- package/dist/daemon/screenshot.js (SFConflict mnott 2026-03-06-21-15-46).map +1 -0
- package/dist/daemon/screenshot.js.map +1 -0
- package/dist/daemon/session-content.d.ts +27 -0
- package/dist/daemon/session-content.d.ts.map +1 -0
- package/dist/daemon/session-content.js +76 -0
- package/dist/daemon/session-content.js.map +1 -0
- package/dist/daemon/vision.d.ts +46 -0
- package/dist/daemon/vision.d.ts.map +1 -0
- package/dist/daemon/vision.js +176 -0
- package/dist/daemon/vision.js.map +1 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/ipc/client.d.ts +4 -1
- package/dist/ipc/client.d.ts.map +1 -1
- package/dist/ipc/client.js +10 -1
- package/dist/ipc/client.js.map +1 -1
- package/dist/ipc/validate.d.ts +52 -0
- package/dist/ipc/validate.d.ts.map +1 -0
- package/dist/ipc/validate.js +129 -0
- package/dist/ipc/validate.js.map +1 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-13-56).d.ts +23 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-13-56).d.ts.map +1 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-13-56).js +595 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-13-56).js.map +1 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-13-59).js +595 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-15-36).d.ts +23 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-15-36).d.ts.map +1 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-15-36).js +592 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-15-36).js.map +1 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-15-46).js +592 -0
- package/dist/mcp/index.d.ts +23 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +660 -0
- package/dist/mcp/index.js (SFConflict mnott 2026-03-06-21-13-59).map +1 -0
- package/dist/mcp/index.js (SFConflict mnott 2026-03-06-21-15-46).map +1 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/types/adapter.d.ts +41 -0
- package/dist/types/adapter.d.ts.map +1 -0
- package/dist/types/adapter.js +2 -0
- package/dist/types/adapter.js.map +1 -0
- package/dist/types/backend.d.ts +29 -1
- package/dist/types/backend.d.ts.map +1 -1
- package/dist/types/broker.d.ts +47 -0
- package/dist/types/broker.d.ts.map +1 -0
- package/dist/types/broker.js +21 -0
- package/dist/types/broker.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +12 -2
- package/templates/adapter/ONBOARDING_PROMPT.md +309 -0
- package/templates/adapter/README.md.tmpl +81 -0
- package/templates/adapter/package.json.tmpl +23 -0
- package/templates/adapter/src/watcher/cli.ts.tmpl +12 -0
- package/templates/adapter/src/watcher/commands.ts.tmpl +44 -0
- package/templates/adapter/src/watcher/connection.ts.tmpl +59 -0
- package/templates/adapter/src/watcher/index.ts.tmpl +201 -0
- package/templates/adapter/src/watcher/ipc-server.ts.tmpl +250 -0
- package/templates/adapter/src/watcher/send.ts.tmpl +62 -0
- package/templates/adapter/src/watcher/state.ts.tmpl +39 -0
- package/templates/adapter/tsconfig.json.tmpl +14 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# AIBroker Adapter Onboarding — AI-Guided Creation
|
|
2
|
+
|
|
3
|
+
You are helping a developer create a new AIBroker messaging adapter. Your job is to ask up to 5 targeted questions, then generate a complete, working adapter from the scaffold templates.
|
|
4
|
+
|
|
5
|
+
Work through the following phases in order.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Phase 1: Interview (5 questions max)
|
|
10
|
+
|
|
11
|
+
Ask only what you need. If the answer is obvious from context (e.g. the user says "Discord"), skip the question. Combine questions where sensible. Never ask more than 5.
|
|
12
|
+
|
|
13
|
+
**The 5 questions:**
|
|
14
|
+
|
|
15
|
+
1. **Service name** — What service are you connecting? (e.g. Signal, Discord, Slack, IRC, Matrix)
|
|
16
|
+
2. **Package name** — What npm package should be used? (If you don't know, say "search for me" — you will search npm.)
|
|
17
|
+
3. **Auth method** — How does the SDK authenticate? (QR code scan, phone number + OTP, API key, OAuth, bot token, username/password)
|
|
18
|
+
4. **Message model** — Does the service have the concept of channels/rooms/groups, or is it always a 1:1 direct chat? Who is the default recipient?
|
|
19
|
+
5. **Voice support** — Does the service support sending voice/audio messages? If yes, what format does it accept? (Most accept OGG Opus or MP3.)
|
|
20
|
+
|
|
21
|
+
If the user already answered any of these in their initial message, skip those questions.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Phase 2: npm Research
|
|
26
|
+
|
|
27
|
+
Before generating code, search npm for the best library for this service:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Search npm for available packages
|
|
31
|
+
npm search <service-name> --json | head -20
|
|
32
|
+
|
|
33
|
+
# Read the top candidate's README
|
|
34
|
+
npm show <package-name> readme
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Pick the most maintained package (high weekly downloads, recent publish date, active repo). If the user specified a package, verify it exists and read its docs anyway.
|
|
38
|
+
|
|
39
|
+
Read the package README for:
|
|
40
|
+
- Connection/auth pattern (how to establish a session)
|
|
41
|
+
- How to receive incoming messages (event name, callback signature)
|
|
42
|
+
- How to send a text message (method name, arguments)
|
|
43
|
+
- How to send a file/audio (if applicable)
|
|
44
|
+
- Session persistence (does it save credentials to disk?)
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Phase 3: Generate the Adapter
|
|
49
|
+
|
|
50
|
+
Generate all files listed below. Use the scaffold templates in `templates/adapter/` as the starting point and replace all `{{ADAPTER_NAME}}` and `{{DISPLAY_NAME}}` placeholders.
|
|
51
|
+
|
|
52
|
+
**Variable substitution:**
|
|
53
|
+
- `{{ADAPTER_NAME}}` — lowercase hyphenated package name (e.g. `signal-bridge`, `discord-adapter`)
|
|
54
|
+
- `{{DISPLAY_NAME}}` — human-readable service name (e.g. `Signal`, `Discord`)
|
|
55
|
+
|
|
56
|
+
### Files to generate
|
|
57
|
+
|
|
58
|
+
**`package.json`** — from `templates/adapter/package.json.tmpl`
|
|
59
|
+
- Fill in name, description
|
|
60
|
+
- Add the chosen npm library to `dependencies`
|
|
61
|
+
|
|
62
|
+
**`tsconfig.json`** — from `templates/adapter/tsconfig.json.tmpl`
|
|
63
|
+
- No changes needed
|
|
64
|
+
|
|
65
|
+
**`src/watcher/state.ts`** — from `templates/adapter/src/watcher/state.ts.tmpl`
|
|
66
|
+
- Replace placeholders only
|
|
67
|
+
- The socket path must be `/tmp/{{ADAPTER_NAME}}-watcher.sock`
|
|
68
|
+
|
|
69
|
+
**`src/watcher/connection.ts`** — from `templates/adapter/src/watcher/connection.ts.tmpl`
|
|
70
|
+
- THIS IS THE CORE IMPLEMENTATION FILE — replace the stub with real SDK code
|
|
71
|
+
- Implement `connectWatcher()` using the library you researched
|
|
72
|
+
- The function must:
|
|
73
|
+
1. Load credentials from `appDir` (use `getAppDir()` from aibroker if available, or `os.homedir()` + `.{{ADAPTER_NAME}}/auth/`)
|
|
74
|
+
2. Establish connection to the service
|
|
75
|
+
3. Subscribe to incoming messages, calling `onMessage(text, timestamp)` for each
|
|
76
|
+
4. Return `cleanup()` to disconnect gracefully
|
|
77
|
+
5. Return `triggerLogin()` to start a fresh auth flow (QR, OTP, etc.)
|
|
78
|
+
- Include all necessary imports from the chosen npm package
|
|
79
|
+
- Handle reconnection if the SDK supports it
|
|
80
|
+
- Log connection events with `log()` from aibroker
|
|
81
|
+
|
|
82
|
+
**`src/watcher/send.ts`** — from `templates/adapter/src/watcher/send.ts.tmpl`
|
|
83
|
+
- Replace stubs with real SDK delivery calls
|
|
84
|
+
- `sendText(text, recipient?)` — send plain text
|
|
85
|
+
- `sendVoice(audioPath, recipient?)` — send OGG Opus audio file (skip with a clear comment if service does not support voice)
|
|
86
|
+
- `sendFile(filePath, caption?, mimetype?, recipient?)` — send document attachment
|
|
87
|
+
|
|
88
|
+
**`src/watcher/commands.ts`** — from `templates/adapter/src/watcher/commands.ts.tmpl`
|
|
89
|
+
- Replace placeholders only
|
|
90
|
+
- This is a thin handler — only /restart and /login are local
|
|
91
|
+
- All other commands are forwarded to the hub by index.ts
|
|
92
|
+
|
|
93
|
+
**`src/watcher/ipc-server.ts`** — from `templates/adapter/src/watcher/ipc-server.ts.tmpl`
|
|
94
|
+
- Replace placeholders only
|
|
95
|
+
- The required IPC handlers are already present: `deliver`, `health`, `connection_status`, `login`
|
|
96
|
+
|
|
97
|
+
**`src/watcher/index.ts`** — from `templates/adapter/src/watcher/index.ts.tmpl`
|
|
98
|
+
- Replace placeholders only
|
|
99
|
+
- The hub-required pattern (connectToHub with retry, heartbeat, re-registration) is built in
|
|
100
|
+
|
|
101
|
+
**`src/watcher/cli.ts`** — from `templates/adapter/src/watcher/cli.ts.tmpl`
|
|
102
|
+
- Replace placeholders only
|
|
103
|
+
|
|
104
|
+
**`src/index.ts`** — MCP server entry point
|
|
105
|
+
- Model this on Whazaa's `src/index.ts` (the reference implementation)
|
|
106
|
+
- Expose MCP tools named `<service>_send`, `<service>_status`, `<service>_tts` (voice, if supported), `<service>_send_file`
|
|
107
|
+
- Wire each tool to the corresponding IPC handler via `WatcherClient`
|
|
108
|
+
- Socket path: `/tmp/{{ADAPTER_NAME}}-watcher.sock`
|
|
109
|
+
|
|
110
|
+
**`README.md`** — from `templates/adapter/README.md.tmpl`
|
|
111
|
+
- Fill in service name, auth instructions, and any service-specific setup steps
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Phase 4: Wire into AIBroker Config
|
|
116
|
+
|
|
117
|
+
After generating the adapter files, register the adapter with the AIBroker hub:
|
|
118
|
+
|
|
119
|
+
1. **Check if `~/.aibroker/config.json` exists.** If yes, add an entry to the `adapters` array:
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"name": "{{ADAPTER_NAME}}",
|
|
123
|
+
"socketPath": "/tmp/{{ADAPTER_NAME}}-watcher.sock",
|
|
124
|
+
"autoStart": false
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
2. **Register as an MCP server** in `~/.claude.json` under `mcpServers`:
|
|
129
|
+
```json
|
|
130
|
+
"{{ADAPTER_NAME}}": {
|
|
131
|
+
"type": "stdio",
|
|
132
|
+
"command": "node",
|
|
133
|
+
"args": ["/path/to/{{ADAPTER_NAME}}/dist/index.js"],
|
|
134
|
+
"description": "{{DISPLAY_NAME}} MCP adapter"
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
3. **Add permission** in `~/.claude/settings.json` under `permissions.allow`:
|
|
139
|
+
```json
|
|
140
|
+
"mcp__{{ADAPTER_NAME}}"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Phase 5: Test the Connection
|
|
146
|
+
|
|
147
|
+
After generating all files:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Ensure the AIBroker hub is running
|
|
151
|
+
aibroker start
|
|
152
|
+
|
|
153
|
+
# Build and start the adapter
|
|
154
|
+
cd /path/to/{{ADAPTER_NAME}}
|
|
155
|
+
npm install
|
|
156
|
+
npm run build
|
|
157
|
+
node dist/watcher/cli.js watch
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The adapter will:
|
|
161
|
+
1. Connect to the AIBroker hub (fails if hub is not running)
|
|
162
|
+
2. Connect to the upstream service
|
|
163
|
+
3. Register with the hub
|
|
164
|
+
4. Start its IPC server
|
|
165
|
+
|
|
166
|
+
Verify the IPC server is responding:
|
|
167
|
+
```bash
|
|
168
|
+
node -e "
|
|
169
|
+
import { WatcherClient } from 'aibroker';
|
|
170
|
+
const c = new WatcherClient('/tmp/{{ADAPTER_NAME}}-watcher.sock');
|
|
171
|
+
c.call_raw('health', {}).then(r => console.log(JSON.stringify(r, null, 2)));
|
|
172
|
+
"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Expected health response:
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"ok": true,
|
|
179
|
+
"result": {
|
|
180
|
+
"status": "ok",
|
|
181
|
+
"connectionStatus": "connected",
|
|
182
|
+
"stats": { "messagesReceived": 0, "messagesSent": 0, "errors": 0 },
|
|
183
|
+
"lastMessageAgo": null
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
If `status` is `"down"` or `connectionStatus` is not `"connected"`, check the watcher logs for auth/connection errors and run `triggerLogin()` via the `login` IPC handler.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Interface Contracts (Reference)
|
|
193
|
+
|
|
194
|
+
### MessengerAdapter interface
|
|
195
|
+
|
|
196
|
+
Every adapter fulfills this interface structurally via IPC handlers:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
interface MessengerAdapter {
|
|
200
|
+
// Lifecycle
|
|
201
|
+
start(config: AdapterConfig): Promise<void>;
|
|
202
|
+
stop(): Promise<void>;
|
|
203
|
+
health(): Promise<AdapterHealth>; // IPC: "health"
|
|
204
|
+
|
|
205
|
+
// Auth
|
|
206
|
+
login(): Promise<string>; // IPC: "login"
|
|
207
|
+
connectionStatus(): Promise<AdapterConnectionStatus>; // IPC: "connection_status"
|
|
208
|
+
|
|
209
|
+
// Outbound
|
|
210
|
+
sendText(text, recipient?): Promise<void>; // IPC: "send"
|
|
211
|
+
sendVoice(audioPath, recipient?): Promise<void>; // IPC: "send_voice"
|
|
212
|
+
sendFile(filePath, caption?, mimetype?, recipient?): Promise<void>; // IPC: "send_file"
|
|
213
|
+
sendImage(imagePath, caption?, recipient?): Promise<void>; // IPC: "send_image"
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### AdapterHealth contract
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
interface AdapterHealth {
|
|
221
|
+
status: "ok" | "degraded" | "down";
|
|
222
|
+
connectionStatus: "connected" | "connecting" | "disconnected" | "error";
|
|
223
|
+
stats: {
|
|
224
|
+
messagesReceived: number;
|
|
225
|
+
messagesSent: number;
|
|
226
|
+
errors: number;
|
|
227
|
+
};
|
|
228
|
+
lastMessageAgo: number | null;
|
|
229
|
+
detail?: string;
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### BrokerMessage (hub envelope)
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
interface BrokerMessage {
|
|
237
|
+
id: string; // UUID
|
|
238
|
+
timestamp: number; // epoch ms
|
|
239
|
+
source: string; // adapter name that sent it
|
|
240
|
+
target?: string; // target adapter name (omit for default routing)
|
|
241
|
+
type: "text" | "voice" | "file" | "image" | "command" | "status";
|
|
242
|
+
payload: {
|
|
243
|
+
text?: string;
|
|
244
|
+
filePath?: string;
|
|
245
|
+
audioPath?: string;
|
|
246
|
+
buffer?: string; // base64-encoded binary data (images, voice)
|
|
247
|
+
mimetype?: string;
|
|
248
|
+
caption?: string;
|
|
249
|
+
recipient?: string;
|
|
250
|
+
channel?: string;
|
|
251
|
+
metadata?: Record<string, unknown>;
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Required IPC handlers
|
|
257
|
+
|
|
258
|
+
| Handler | Called by | Must return |
|
|
259
|
+
|---------|-----------|-------------|
|
|
260
|
+
| `deliver` | Hub, when routing a BrokerMessage to this adapter | `{ ok: true, result: { delivered: true } }` or `{ ok: false, error: string }` |
|
|
261
|
+
| `health` | Hub health polling (every 10s) | `{ ok: true, result: AdapterHealth }` |
|
|
262
|
+
| `connection_status` | Hub, MCP tools | `{ ok: true, result: { status: AdapterConnectionStatus } }` |
|
|
263
|
+
| `login` | User, via MCP tool | `{ ok: true, result: { message: string } }` |
|
|
264
|
+
| `send` | MCP tool | `{ ok: true, result: { sent: true } }` |
|
|
265
|
+
| `send_voice` | MCP tool | `{ ok: true, result: { sent: true } }` |
|
|
266
|
+
| `send_file` | MCP tool | `{ ok: true, result: { sent: true } }` |
|
|
267
|
+
| `status` | MCP tool | Human-readable status object |
|
|
268
|
+
|
|
269
|
+
### Hub-required architecture
|
|
270
|
+
|
|
271
|
+
The adapter **requires** the AIBroker hub daemon to be running:
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
Adapter startup:
|
|
275
|
+
1. connectToHub() — probe hub via "ping", retry 3x, fail if unreachable
|
|
276
|
+
2. connectWatcher() — establish upstream service connection
|
|
277
|
+
3. startIpcServer() — start adapter IPC server on its socket
|
|
278
|
+
4. register_adapter — announce to hub so it can push outbound messages
|
|
279
|
+
5. heartbeat loop — ping hub every 30s, re-register if it restarts
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Adapters do NOT:
|
|
283
|
+
- Create their own APIBackend or HybridSessionManager
|
|
284
|
+
- Handle commands locally (except /restart and /login)
|
|
285
|
+
- Run TTS, STT, screenshots, or image generation
|
|
286
|
+
- Manage sessions
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Reference Implementation
|
|
291
|
+
|
|
292
|
+
**Whazaa** is the canonical reference adapter. When in doubt, model behaviour on Whazaa:
|
|
293
|
+
|
|
294
|
+
- Repo: `~/dev/ai/Whazaa/`
|
|
295
|
+
- Key file — connection: `src/watcher/baileys.ts` (WhatsApp-specific connection)
|
|
296
|
+
- Key file — send: `src/watcher/send.ts`
|
|
297
|
+
- Key file — IPC: `src/watcher/ipc-server.ts`
|
|
298
|
+
- Key file — MCP tools: `src/index.ts`
|
|
299
|
+
- Key file — watcher: `src/watcher/index.ts` (thin hub-required adapter pattern)
|
|
300
|
+
- Key pattern — Baileys saves auth state to `~/.whazaa/auth/` via `useMultiFileAuthState()`
|
|
301
|
+
|
|
302
|
+
The Whazaa pattern for `connectWatcher()` is:
|
|
303
|
+
1. Call `useMultiFileAuthState(authDir)` (or equivalent for your SDK)
|
|
304
|
+
2. Create the client instance with the auth state
|
|
305
|
+
3. Set up message event listener calling `onMessage(text, timestamp)`
|
|
306
|
+
4. Call `client.connect()` or equivalent
|
|
307
|
+
5. Return `cleanup` (calls `client.logout()` or `client.disconnect()`) and `triggerLogin` (generates new QR/pairing)
|
|
308
|
+
|
|
309
|
+
Follow this pattern exactly. The template scaffold maps cleanly onto it.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# {{ADAPTER_NAME}}
|
|
2
|
+
|
|
3
|
+
AIBroker adapter for {{DISPLAY_NAME}}.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This adapter bridges {{DISPLAY_NAME}} and the [AIBroker](https://github.com/mnott/AIBroker) hub, enabling bidirectional message routing between {{DISPLAY_NAME}} and AI backends (Claude Code, Anthropic API, etc.).
|
|
8
|
+
|
|
9
|
+
**Requires the AIBroker daemon to be running.** The adapter is a thin transport pipe — all command handling, session management, TTS, and media processing are owned by the hub.
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
### 1. Install dependencies
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 2. Implement the connection
|
|
20
|
+
|
|
21
|
+
Open `src/watcher/connection.ts` and implement `connectWatcher()` with your {{DISPLAY_NAME}} SDK.
|
|
22
|
+
|
|
23
|
+
The function must:
|
|
24
|
+
- Connect to {{DISPLAY_NAME}} using your credentials
|
|
25
|
+
- Call `onMessage(text, timestamp)` for each incoming message
|
|
26
|
+
- Return `cleanup()` (disconnect) and `triggerLogin()` (fresh auth flow)
|
|
27
|
+
|
|
28
|
+
### 3. Implement outbound delivery
|
|
29
|
+
|
|
30
|
+
Open `src/watcher/send.ts` and implement `sendText()`, `sendVoice()`, and `sendFile()`.
|
|
31
|
+
|
|
32
|
+
### 4. Build
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm run build
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 5. Run
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Start the AIBroker hub first
|
|
42
|
+
aibroker start
|
|
43
|
+
|
|
44
|
+
# Then start the adapter — it auto-registers with the hub
|
|
45
|
+
npm run watch
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Architecture
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
{{DISPLAY_NAME}} <── connectWatcher() ──> watcher/index.ts
|
|
52
|
+
│
|
|
53
|
+
forward to hub startIpcServer()
|
|
54
|
+
│ │
|
|
55
|
+
AIBroker hub ◄── deliver ─┘
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The adapter:
|
|
59
|
+
- Forwards all incoming messages to the hub via `route_message`
|
|
60
|
+
- Receives outbound messages from the hub via the `deliver` IPC handler
|
|
61
|
+
- Only handles `/restart` and `/login` locally
|
|
62
|
+
- Maintains a 30s heartbeat with the hub; re-registers if hub restarts
|
|
63
|
+
|
|
64
|
+
## IPC Methods
|
|
65
|
+
|
|
66
|
+
The adapter exposes these methods on its Unix socket (`/tmp/{{ADAPTER_NAME}}-watcher.sock`):
|
|
67
|
+
|
|
68
|
+
| Method | Description |
|
|
69
|
+
|--------|-------------|
|
|
70
|
+
| `deliver` | Hub pushes a BrokerMessage for outbound delivery |
|
|
71
|
+
| `health` | Returns adapter health (status, stats, lastMessageAgo) |
|
|
72
|
+
| `connection_status` | Returns upstream connection status string |
|
|
73
|
+
| `send` | Send a text message |
|
|
74
|
+
| `send_voice` | Send a voice note (OGG Opus path) |
|
|
75
|
+
| `send_file` | Send a file attachment |
|
|
76
|
+
| `login` | Trigger a fresh login / QR pairing flow |
|
|
77
|
+
| `status` | Human-readable adapter status summary |
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{ADAPTER_NAME}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AIBroker adapter for {{DISPLAY_NAME}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"{{ADAPTER_NAME}}": "dist/watcher/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"watch": "node dist/watcher/cli.js watch",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"aibroker": "^0.6.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^22.0.0",
|
|
21
|
+
"typescript": "^5.0.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { watch } from "./index.js";
|
|
3
|
+
|
|
4
|
+
const command = process.argv[2];
|
|
5
|
+
if (command === "watch" || !command) {
|
|
6
|
+
watch().catch((err) => {
|
|
7
|
+
console.error("Fatal:", err);
|
|
8
|
+
process.exit(1);
|
|
9
|
+
});
|
|
10
|
+
} else {
|
|
11
|
+
console.log(`Usage: {{ADAPTER_NAME}} watch`);
|
|
12
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands.ts — Thin adapter-local command handler for {{DISPLAY_NAME}}
|
|
3
|
+
*
|
|
4
|
+
* Only handles commands that require direct upstream service access
|
|
5
|
+
* (/restart, /login). All other commands are forwarded to the AIBroker
|
|
6
|
+
* hub daemon via route_message in index.ts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { log } from "aibroker";
|
|
10
|
+
import { sendText } from "./send.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create the local message handler for adapter-specific commands.
|
|
14
|
+
*
|
|
15
|
+
* Only /restart and /login are handled here. Everything else should have
|
|
16
|
+
* been routed to the hub by index.ts — this handler is the fallback for
|
|
17
|
+
* commands that need direct adapter access.
|
|
18
|
+
*/
|
|
19
|
+
export function createMessageHandler(): (text: string, timestamp: number) => void {
|
|
20
|
+
return function handleMessage(text: string, _timestamp: number): void {
|
|
21
|
+
const trimmed = text.trim();
|
|
22
|
+
|
|
23
|
+
if (trimmed === "/restart") {
|
|
24
|
+
log("/restart: watcher restart requested");
|
|
25
|
+
sendText("Restarting {{ADAPTER_NAME}} watcher...").catch(() => {});
|
|
26
|
+
setTimeout(() => {
|
|
27
|
+
log("/restart: exiting — process manager will restart us");
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}, 1500);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// /login is handled via the triggerLogin callback in index.ts,
|
|
34
|
+
// not here. This is just a safety net.
|
|
35
|
+
if (trimmed === "/login") {
|
|
36
|
+
sendText("Use the login flow — handled by the watcher.").catch(() => {});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Everything else should have gone to the hub. If we get here,
|
|
41
|
+
// something went wrong with routing.
|
|
42
|
+
log(`[{{ADAPTER_NAME}}] commands.ts: unexpected local message: ${trimmed.slice(0, 60)}`);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connection.ts — Connect to {{DISPLAY_NAME}}
|
|
3
|
+
*
|
|
4
|
+
* TODO: Implement the upstream service connection here.
|
|
5
|
+
* This is the adapter-specific part — use the SDK/library for your service.
|
|
6
|
+
*
|
|
7
|
+
* Replace this stub with a real connection using the appropriate SDK, e.g.:
|
|
8
|
+
* - @whiskeysockets/baileys for WhatsApp
|
|
9
|
+
* - gramjs / telegram for Telegram
|
|
10
|
+
* - discord.js for Discord
|
|
11
|
+
* - signal-cli for Signal
|
|
12
|
+
*
|
|
13
|
+
* The onMessage callback receives every incoming message text so the watcher
|
|
14
|
+
* can route it through the command handler / hub forwarding.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export interface ConnectionResult {
|
|
18
|
+
/** Call to cleanly disconnect from the upstream service. */
|
|
19
|
+
cleanup: () => void;
|
|
20
|
+
/**
|
|
21
|
+
* Trigger a new login flow (QR code, pairing, etc.) and return a
|
|
22
|
+
* human-readable status string to send back to the user.
|
|
23
|
+
*/
|
|
24
|
+
triggerLogin: () => Promise<string>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Establish the upstream connection.
|
|
29
|
+
*
|
|
30
|
+
* @param onMessage - Called for each incoming message the adapter should process.
|
|
31
|
+
* Receives the message text and the message timestamp (epoch ms).
|
|
32
|
+
*/
|
|
33
|
+
export async function connectWatcher(
|
|
34
|
+
onMessage: (text: string, timestamp: number) => void,
|
|
35
|
+
): Promise<ConnectionResult> {
|
|
36
|
+
// TODO: Replace this stub with your actual connection logic.
|
|
37
|
+
//
|
|
38
|
+
// Typical pattern:
|
|
39
|
+
// 1. Load credentials from appDir (set by setAppDir() in index.ts)
|
|
40
|
+
// 2. Connect to the upstream service
|
|
41
|
+
// 3. Subscribe to incoming messages, calling onMessage() for each
|
|
42
|
+
// 4. Return cleanup() and triggerLogin() implementations
|
|
43
|
+
//
|
|
44
|
+
console.log("[{{ADAPTER_NAME}}] Connection stub — implement connectWatcher()");
|
|
45
|
+
|
|
46
|
+
// Suppress unused-parameter warning in stub
|
|
47
|
+
void onMessage;
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
cleanup: () => {
|
|
51
|
+
// TODO: close WebSocket, stop polling, revoke auth, etc.
|
|
52
|
+
console.log("[{{ADAPTER_NAME}}] cleanup() called");
|
|
53
|
+
},
|
|
54
|
+
triggerLogin: async () => {
|
|
55
|
+
// TODO: start QR code / pairing flow, return status string
|
|
56
|
+
return "Login not yet implemented for {{DISPLAY_NAME}}";
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|