@ravi-hq/ravi 0.1.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/LICENSE +21 -0
- package/README.md +271 -0
- package/dist/channels/email.d.ts +221 -0
- package/dist/channels/email.d.ts.map +1 -0
- package/dist/channels/email.js +143 -0
- package/dist/channels/email.js.map +1 -0
- package/dist/channels/sms.d.ts +223 -0
- package/dist/channels/sms.d.ts.map +1 -0
- package/dist/channels/sms.js +141 -0
- package/dist/channels/sms.js.map +1 -0
- package/dist/cli.d.ts +24 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +390 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +174 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +289 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto.d.ts +143 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +310 -0
- package/dist/crypto.js.map +1 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +143 -0
- package/dist/index.js.map +1 -0
- package/dist/service.d.ts +98 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +130 -0
- package/dist/service.js.map +1 -0
- package/dist/sse.d.ts +110 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +269 -0
- package/dist/sse.js.map +1 -0
- package/dist/tools/email-send.d.ts +13 -0
- package/dist/tools/email-send.d.ts.map +1 -0
- package/dist/tools/email-send.js +60 -0
- package/dist/tools/email-send.js.map +1 -0
- package/dist/tools/feedback.d.ts +13 -0
- package/dist/tools/feedback.d.ts.map +1 -0
- package/dist/tools/feedback.js +43 -0
- package/dist/tools/feedback.js.map +1 -0
- package/dist/tools/identity.d.ts +43 -0
- package/dist/tools/identity.d.ts.map +1 -0
- package/dist/tools/identity.js +59 -0
- package/dist/tools/identity.js.map +1 -0
- package/dist/tools/inbox.d.ts +22 -0
- package/dist/tools/inbox.d.ts.map +1 -0
- package/dist/tools/inbox.js +166 -0
- package/dist/tools/inbox.js.map +1 -0
- package/dist/tools/passwords.d.ts +19 -0
- package/dist/tools/passwords.d.ts.map +1 -0
- package/dist/tools/passwords.js +165 -0
- package/dist/tools/passwords.js.map +1 -0
- package/dist/tools/sms-send.d.ts +11 -0
- package/dist/tools/sms-send.d.ts.map +1 -0
- package/dist/tools/sms-send.js +32 -0
- package/dist/tools/sms-send.js.map +1 -0
- package/dist/tools/vault.d.ts +15 -0
- package/dist/tools/vault.d.ts.map +1 -0
- package/dist/tools/vault.js +97 -0
- package/dist/tools/vault.js.map +1 -0
- package/dist/types.d.ts +137 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +18 -0
- package/dist/utils.js.map +1 -0
- package/openclaw.plugin.json +33 -0
- package/package.json +70 -0
- package/skills/CLAUDE.md +36 -0
- package/skills/ravi/SKILL.md +46 -0
- package/skills/ravi-email-send/SKILL.md +44 -0
- package/skills/ravi-feedback/SKILL.md +37 -0
- package/skills/ravi-identity/SKILL.md +50 -0
- package/skills/ravi-inbox/SKILL.md +58 -0
- package/skills/ravi-login/SKILL.md +42 -0
- package/skills/ravi-passwords/SKILL.md +48 -0
- package/skills/ravi-vault/SKILL.md +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ravi HQ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# @ravi-hq/openclaw-plugin
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@ravi-hq/openclaw-plugin)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
OpenClaw plugin for [Ravi](https://ravi.app) -- identity provider for AI agents.
|
|
7
|
+
Gives your agent a real email address, phone number, password manager, and
|
|
8
|
+
encrypted vault through a single plugin.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **2 messaging channels** -- real-time email and SMS via Server-Sent Events
|
|
13
|
+
- **17 agent tools** -- manage identities, inboxes, passwords, vault secrets, and more
|
|
14
|
+
- **E2E encryption** -- PIN-based Argon2id + NaCl SealedBox, compatible with Ravi CLI
|
|
15
|
+
- **DM policy controls** -- allowlist or open policy per channel account
|
|
16
|
+
- **Lazy crypto** -- encryption keys derived on first use, not at startup
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
openclaw plugins install @ravi-hq/openclaw-plugin
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Install and authenticate
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
openclaw plugins install @ravi-hq/openclaw-plugin
|
|
30
|
+
openclaw ravi login
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This opens a browser for device-code authentication. Copy the access token from the output.
|
|
34
|
+
|
|
35
|
+
### 2. Add your token and PIN to the plugin config
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
# In your OpenClaw config
|
|
39
|
+
plugins:
|
|
40
|
+
ravi:
|
|
41
|
+
token: "eyJhbGciOiJIUzI1NiIs..." # From 'openclaw ravi login'
|
|
42
|
+
pin: "123456" # Your 6-digit E2E encryption PIN
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 3. Run setup to configure channels
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
openclaw ravi setup
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This lists your identities and generates a channel configuration snippet you
|
|
52
|
+
can paste into your OpenClaw config.
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
plugins:
|
|
58
|
+
ravi:
|
|
59
|
+
# Ravi API base URL (default: https://ravi.app)
|
|
60
|
+
apiUrl: "https://ravi.app"
|
|
61
|
+
|
|
62
|
+
# JWT access token (required)
|
|
63
|
+
token: "your-access-token"
|
|
64
|
+
|
|
65
|
+
# 6-digit E2E encryption PIN (required)
|
|
66
|
+
pin: "123456"
|
|
67
|
+
|
|
68
|
+
# Channel accounts (from 'openclaw ravi setup')
|
|
69
|
+
channels:
|
|
70
|
+
ravi-email:
|
|
71
|
+
accounts:
|
|
72
|
+
"identity-uuid-here":
|
|
73
|
+
identityUuid: "identity-uuid-here"
|
|
74
|
+
identityName: "My Agent"
|
|
75
|
+
email: "agent@in.ravi.app"
|
|
76
|
+
dm:
|
|
77
|
+
policy: "allowlist" # "allowlist" or "open"
|
|
78
|
+
allowFrom:
|
|
79
|
+
- "you@gmail.com"
|
|
80
|
+
|
|
81
|
+
ravi-sms:
|
|
82
|
+
accounts:
|
|
83
|
+
"identity-uuid-here":
|
|
84
|
+
identityUuid: "identity-uuid-here"
|
|
85
|
+
identityName: "My Agent"
|
|
86
|
+
phone: "+15551234567"
|
|
87
|
+
dm:
|
|
88
|
+
policy: "allowlist" # "allowlist" or "open"
|
|
89
|
+
allowFrom:
|
|
90
|
+
- "+15559876543"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Channels
|
|
94
|
+
|
|
95
|
+
### Ravi Email
|
|
96
|
+
|
|
97
|
+
Real-time email channel powered by SSE events from the Ravi backend.
|
|
98
|
+
|
|
99
|
+
- **Threading model:** Each email thread maps 1:1 to an OpenClaw session. A new
|
|
100
|
+
`thread_id` creates a new session; subsequent messages in that thread resume it.
|
|
101
|
+
- **Session key format:** `agent:{agentId}:ravi-email:{accountId}:thread:{threadId}`
|
|
102
|
+
- **DM policy:** Set `policy: "allowlist"` to restrict incoming email to specific
|
|
103
|
+
sender addresses, or `"open"` to accept all senders.
|
|
104
|
+
- **Outbound:** Replies are sent via the Ravi API and threaded correctly in the
|
|
105
|
+
original email conversation.
|
|
106
|
+
|
|
107
|
+
### Ravi SMS
|
|
108
|
+
|
|
109
|
+
Real-time SMS channel powered by the same SSE stream.
|
|
110
|
+
|
|
111
|
+
- **Threading model:** All messages from one phone number form a single session.
|
|
112
|
+
A new `from_number` creates a new session; subsequent messages from that number
|
|
113
|
+
resume it.
|
|
114
|
+
- **Session key format:** `agent:{agentId}:ravi-sms:{accountId}:dm:{phoneNumber}`
|
|
115
|
+
- **DM policy:** Set `policy: "allowlist"` to restrict incoming SMS to specific
|
|
116
|
+
phone numbers (E.164 format), or `"open"` to accept all senders.
|
|
117
|
+
- **Outbound:** Replies are sent via the Ravi API as plain-text SMS.
|
|
118
|
+
|
|
119
|
+
## Agent Tools
|
|
120
|
+
|
|
121
|
+
The plugin registers 17 tools that agents can invoke.
|
|
122
|
+
|
|
123
|
+
### Identity
|
|
124
|
+
|
|
125
|
+
| Tool | Description | Parameters |
|
|
126
|
+
|------|-------------|------------|
|
|
127
|
+
| `ravi_identity_list` | List all identities for the authenticated user | -- |
|
|
128
|
+
| `ravi_identity_create` | Create a new identity (provisions email + phone + vault) | `name` (string, required) |
|
|
129
|
+
| `ravi_get_info` | Get the active identity's email and phone number | -- |
|
|
130
|
+
|
|
131
|
+
### Email Inbox
|
|
132
|
+
|
|
133
|
+
| Tool | Description | Parameters |
|
|
134
|
+
|------|-------------|------------|
|
|
135
|
+
| `ravi_inbox_email` | List email threads, optionally unread only | `unread` (boolean) |
|
|
136
|
+
| `ravi_read_email` | Read all messages in an email thread | `thread_id` (string, required) |
|
|
137
|
+
| `ravi_email_compose` | Compose and send a new email | `to`, `subject`, `body` (all required) |
|
|
138
|
+
| `ravi_email_reply` | Reply to an existing email | `message_id` (number, required), `body` (required), `reply_all` |
|
|
139
|
+
|
|
140
|
+
### SMS Inbox
|
|
141
|
+
|
|
142
|
+
| Tool | Description | Parameters |
|
|
143
|
+
|------|-------------|------------|
|
|
144
|
+
| `ravi_inbox_sms` | List SMS conversations, optionally unread only | `unread` (boolean) |
|
|
145
|
+
| `ravi_read_sms` | Read all messages in an SMS conversation | `conversation_id` (string, required) |
|
|
146
|
+
|
|
147
|
+
### Passwords
|
|
148
|
+
|
|
149
|
+
| Tool | Description | Parameters |
|
|
150
|
+
|------|-------------|------------|
|
|
151
|
+
| `ravi_passwords_list` | List all saved passwords (passwords hidden in list view) | -- |
|
|
152
|
+
| `ravi_passwords_get` | Get a specific password entry with decrypted password | `uuid` (string, required) |
|
|
153
|
+
| `ravi_passwords_create` | Create a new password (encrypted) | `domain` (required), `username`, `password`, `notes` |
|
|
154
|
+
| `ravi_passwords_delete` | Delete a password entry | `uuid` (string, required) |
|
|
155
|
+
|
|
156
|
+
### Vault
|
|
157
|
+
|
|
158
|
+
| Tool | Description | Parameters |
|
|
159
|
+
|------|-------------|------------|
|
|
160
|
+
| `ravi_vault_list` | List all secret keys in the vault | -- |
|
|
161
|
+
| `ravi_vault_get` | Get a secret by key (auto-decrypted) | `key` (string, required) |
|
|
162
|
+
| `ravi_vault_set` | Store a secret (auto-encrypted) | `key` (string, required), `value` (string, required) |
|
|
163
|
+
|
|
164
|
+
### Feedback
|
|
165
|
+
|
|
166
|
+
| Tool | Description | Parameters |
|
|
167
|
+
|------|-------------|------------|
|
|
168
|
+
| `ravi_feedback` | Submit feedback about Ravi | `message` (required), `category` (bug/feature/help/other) |
|
|
169
|
+
|
|
170
|
+
## CLI Commands
|
|
171
|
+
|
|
172
|
+
Run these via `openclaw ravi <command>`:
|
|
173
|
+
|
|
174
|
+
| Command | Description |
|
|
175
|
+
|---------|-------------|
|
|
176
|
+
| `login` | Authenticate via device-code flow (opens browser) |
|
|
177
|
+
| `status` | Show auth status and list available identities |
|
|
178
|
+
| `setup` | Generate channel configuration from your identities |
|
|
179
|
+
|
|
180
|
+
## E2E Encryption
|
|
181
|
+
|
|
182
|
+
All sensitive data (passwords, vault secrets, email content at rest) is encrypted
|
|
183
|
+
end-to-end using a 6-digit PIN.
|
|
184
|
+
|
|
185
|
+
- **Key derivation:** PIN + server-stored salt -> Argon2id (time=3, mem=64MB, threads=1) -> 32-byte seed
|
|
186
|
+
- **Encryption:** NaCl SealedBox (ephemeral X25519 keypair + Poly1305 MAC)
|
|
187
|
+
- **Ciphertext format:** `e2e::<base64>` -- same format used by the Ravi CLI
|
|
188
|
+
- **PIN verification:** On first use, the plugin derives the keypair and verifies
|
|
189
|
+
it against the server's stored verifier before proceeding
|
|
190
|
+
|
|
191
|
+
The crypto layer is lazy-initialized: keys are only derived when a tool actually
|
|
192
|
+
needs encryption or decryption.
|
|
193
|
+
|
|
194
|
+
## Architecture
|
|
195
|
+
|
|
196
|
+
```text
|
|
197
|
+
Agent (OpenClaw runtime)
|
|
198
|
+
|
|
|
199
|
+
+-- Channels (ravi-email, ravi-sms)
|
|
200
|
+
| '-- SSE stream --> GET /api/events/stream/
|
|
201
|
+
| Events arrive in plaintext (server decrypts before dispatch)
|
|
202
|
+
|
|
|
203
|
+
+-- Tools (17 agent tools)
|
|
204
|
+
| '-- REST calls --> GET/POST/DELETE /api/...
|
|
205
|
+
| Identity-scoped via X-Ravi-Identity header
|
|
206
|
+
|
|
|
207
|
+
'-- Crypto layer (lazy-initialized)
|
|
208
|
+
PIN + salt --> Argon2id --> NaCl SealedBox
|
|
209
|
+
Decrypts historical data from REST ("e2e::<base64>" fields)
|
|
210
|
+
Encrypts vault/password writes before sending to server
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Key points:
|
|
214
|
+
|
|
215
|
+
- **SSE events are plaintext** -- the server decrypts before pushing to the stream.
|
|
216
|
+
No client-side decryption needed for real-time channel messages.
|
|
217
|
+
- **REST data may be encrypted** -- historical email content, passwords, and vault
|
|
218
|
+
secrets can contain `"e2e::<base64>"` ciphertext that the plugin decrypts
|
|
219
|
+
client-side using the PIN-derived keypair.
|
|
220
|
+
- **Authentication** uses unbound JWTs. The `X-Ravi-Identity` header scopes each
|
|
221
|
+
API request to a specific identity.
|
|
222
|
+
|
|
223
|
+
## Development
|
|
224
|
+
|
|
225
|
+
### Prerequisites
|
|
226
|
+
|
|
227
|
+
- Node.js 18+
|
|
228
|
+
- npm
|
|
229
|
+
|
|
230
|
+
### Setup
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
npm install
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Scripts
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
npm run build # Compile TypeScript to dist/
|
|
240
|
+
npm test # Run tests (vitest, single run)
|
|
241
|
+
npm run test:watch # Run tests in watch mode
|
|
242
|
+
npm run lint # Type-check without emitting (tsc --noEmit)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Project structure
|
|
246
|
+
|
|
247
|
+
```text
|
|
248
|
+
src/
|
|
249
|
+
index.ts # Plugin entry point and registration
|
|
250
|
+
types.ts # TypeScript interfaces for API responses and SSE events
|
|
251
|
+
client.ts # RaviClient -- typed HTTP client for all REST endpoints
|
|
252
|
+
sse.ts # RaviSSEClient -- SSE with auto-reconnect and heartbeat
|
|
253
|
+
crypto.ts # E2E encryption (Argon2id + NaCl SealedBox)
|
|
254
|
+
service.ts # Background listener service (manages SSE connections)
|
|
255
|
+
cli.ts # CLI commands (login, status, setup)
|
|
256
|
+
channels/
|
|
257
|
+
email.ts # ravi-email channel definition
|
|
258
|
+
sms.ts # ravi-sms channel definition
|
|
259
|
+
tools/
|
|
260
|
+
identity.ts # Identity management tools
|
|
261
|
+
inbox.ts # Email and SMS inbox tools
|
|
262
|
+
email-send.ts # Email compose and reply tools
|
|
263
|
+
passwords.ts # Password manager tools
|
|
264
|
+
vault.ts # Vault secret tools
|
|
265
|
+
feedback.ts # Feedback tool
|
|
266
|
+
__tests__/ # Test files (*.test.ts)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## License
|
|
270
|
+
|
|
271
|
+
MIT
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import type { RaviClient } from "../client.js";
|
|
2
|
+
import type { EmailEvent } from "../types.js";
|
|
3
|
+
/** Configuration for a single email account within the ravi-email channel. */
|
|
4
|
+
export interface EmailAccountConfig {
|
|
5
|
+
/** The identity UUID this account is bound to. */
|
|
6
|
+
identityUuid: string;
|
|
7
|
+
/** Human-readable identity name for display purposes. */
|
|
8
|
+
identityName: string;
|
|
9
|
+
/** The email address provisioned for this identity. */
|
|
10
|
+
email: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Thread context passed alongside outbound messages so the channel
|
|
14
|
+
* can thread replies correctly in the original email conversation.
|
|
15
|
+
*/
|
|
16
|
+
export interface EmailThreadContext {
|
|
17
|
+
/** The email thread identifier (maps 1:1 to an OpenClaw session). */
|
|
18
|
+
threadId: string;
|
|
19
|
+
/** Numeric ID of the last message in the thread (used for reply targeting). */
|
|
20
|
+
lastMessageId: number;
|
|
21
|
+
/** Subject line of the email thread. */
|
|
22
|
+
subject: string;
|
|
23
|
+
}
|
|
24
|
+
/** Normalized inbound message envelope produced by {@link normalizeInbound}. */
|
|
25
|
+
export interface EmailInboundEnvelope {
|
|
26
|
+
/** Sender's email address (used as the sender ID). */
|
|
27
|
+
senderId: string;
|
|
28
|
+
/** Plain-text body extracted from the email (falls back to subject). */
|
|
29
|
+
text: string;
|
|
30
|
+
/** Thread ID that maps to the OpenClaw session key. */
|
|
31
|
+
threadId: string;
|
|
32
|
+
/** Additional email metadata passed through to the agent. */
|
|
33
|
+
metadata: {
|
|
34
|
+
senderType: string;
|
|
35
|
+
readOnly: boolean;
|
|
36
|
+
subject: string;
|
|
37
|
+
fromDisplayName: string;
|
|
38
|
+
htmlContent: string;
|
|
39
|
+
attachments: number;
|
|
40
|
+
inReplyTo: string | null;
|
|
41
|
+
lastMessageId: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates the ravi-email channel definition for OpenClaw.
|
|
46
|
+
*
|
|
47
|
+
* Each email thread maps to exactly one OpenClaw session (1:1):
|
|
48
|
+
* - A new `thread_id` triggers creation of a new session.
|
|
49
|
+
* - A known `thread_id` resumes the existing session.
|
|
50
|
+
*
|
|
51
|
+
* The channel is a plain object (not a class) following OpenClaw's
|
|
52
|
+
* declarative channel convention.
|
|
53
|
+
*
|
|
54
|
+
* @returns The ravi-email channel definition object.
|
|
55
|
+
*/
|
|
56
|
+
export declare function createEmailChannel(): {
|
|
57
|
+
id: "ravi-email";
|
|
58
|
+
meta: {
|
|
59
|
+
id: string;
|
|
60
|
+
label: string;
|
|
61
|
+
selectionLabel: string;
|
|
62
|
+
blurb: string;
|
|
63
|
+
aliases: string[];
|
|
64
|
+
};
|
|
65
|
+
capabilities: {
|
|
66
|
+
chatTypes: readonly ["direct"];
|
|
67
|
+
};
|
|
68
|
+
config: {
|
|
69
|
+
/**
|
|
70
|
+
* List all configured account IDs (identity UUIDs) for the ravi-email channel.
|
|
71
|
+
*
|
|
72
|
+
* @param cfg - The full OpenClaw plugin configuration object.
|
|
73
|
+
* @returns Array of identity UUID strings.
|
|
74
|
+
*/
|
|
75
|
+
listAccountIds: (cfg: Record<string, unknown>) => string[];
|
|
76
|
+
/**
|
|
77
|
+
* Resolve an account configuration by its ID (identity UUID).
|
|
78
|
+
*
|
|
79
|
+
* Returns a default empty config if the account is not found rather than
|
|
80
|
+
* throwing, so callers can handle missing accounts gracefully.
|
|
81
|
+
*
|
|
82
|
+
* @param cfg - The full OpenClaw plugin configuration object.
|
|
83
|
+
* @param accountId - The identity UUID to look up.
|
|
84
|
+
* @returns The resolved {@link EmailAccountConfig}.
|
|
85
|
+
*/
|
|
86
|
+
resolveAccount: (cfg: Record<string, unknown>, accountId: string) => EmailAccountConfig;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Normalize an inbound SSE email event into a channel message envelope.
|
|
90
|
+
*
|
|
91
|
+
* All messages are passed through — the backend classifies senders via
|
|
92
|
+
* `sender_type`. External senders are marked read-only so the agent
|
|
93
|
+
* treats them as context without responding or using write tools.
|
|
94
|
+
*
|
|
95
|
+
* @param event - The raw {@link EmailEvent} from the SSE stream.
|
|
96
|
+
* @param account - The {@link EmailAccountConfig} for the receiving identity.
|
|
97
|
+
* @returns A normalized {@link EmailInboundEnvelope} (never null).
|
|
98
|
+
*/
|
|
99
|
+
normalizeInbound(event: EmailEvent, account: EmailAccountConfig): EmailInboundEnvelope | null;
|
|
100
|
+
outbound: {
|
|
101
|
+
deliveryMode: "direct";
|
|
102
|
+
/**
|
|
103
|
+
* Send a reply to the email thread that triggered this session.
|
|
104
|
+
*
|
|
105
|
+
* Wraps the plain-text body in HTML and calls {@link RaviClient.replyToEmail}
|
|
106
|
+
* using the thread context to target the correct message for threading.
|
|
107
|
+
*
|
|
108
|
+
* @param params.text - Plain-text reply body.
|
|
109
|
+
* @param params.client - An authenticated {@link RaviClient} instance.
|
|
110
|
+
* @param params.threadContext - Thread context from the inbound event.
|
|
111
|
+
* @returns A result object indicating success or failure.
|
|
112
|
+
*/
|
|
113
|
+
sendText: (params: {
|
|
114
|
+
text: string;
|
|
115
|
+
client: RaviClient;
|
|
116
|
+
threadContext?: EmailThreadContext;
|
|
117
|
+
}) => Promise<{
|
|
118
|
+
ok: boolean;
|
|
119
|
+
error?: string;
|
|
120
|
+
}>;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Generate the session key for this channel.
|
|
124
|
+
*
|
|
125
|
+
* The key uniquely identifies a session by combining the agent, account,
|
|
126
|
+
* and thread. OpenClaw uses this to route inbound events to the correct
|
|
127
|
+
* session and persist session state.
|
|
128
|
+
*
|
|
129
|
+
* Format: `agent:{agentId}:ravi-email:{accountId}:thread:{threadId}`
|
|
130
|
+
*
|
|
131
|
+
* @param agentId - The OpenClaw agent identifier.
|
|
132
|
+
* @param accountId - The identity UUID (account ID).
|
|
133
|
+
* @param threadId - The email thread identifier.
|
|
134
|
+
* @returns The deterministic session key string.
|
|
135
|
+
*/
|
|
136
|
+
getSessionKey(agentId: string, accountId: string, threadId: string): string;
|
|
137
|
+
};
|
|
138
|
+
/** The singleton ravi-email channel instance. */
|
|
139
|
+
export declare const raviEmailChannel: {
|
|
140
|
+
id: "ravi-email";
|
|
141
|
+
meta: {
|
|
142
|
+
id: string;
|
|
143
|
+
label: string;
|
|
144
|
+
selectionLabel: string;
|
|
145
|
+
blurb: string;
|
|
146
|
+
aliases: string[];
|
|
147
|
+
};
|
|
148
|
+
capabilities: {
|
|
149
|
+
chatTypes: readonly ["direct"];
|
|
150
|
+
};
|
|
151
|
+
config: {
|
|
152
|
+
/**
|
|
153
|
+
* List all configured account IDs (identity UUIDs) for the ravi-email channel.
|
|
154
|
+
*
|
|
155
|
+
* @param cfg - The full OpenClaw plugin configuration object.
|
|
156
|
+
* @returns Array of identity UUID strings.
|
|
157
|
+
*/
|
|
158
|
+
listAccountIds: (cfg: Record<string, unknown>) => string[];
|
|
159
|
+
/**
|
|
160
|
+
* Resolve an account configuration by its ID (identity UUID).
|
|
161
|
+
*
|
|
162
|
+
* Returns a default empty config if the account is not found rather than
|
|
163
|
+
* throwing, so callers can handle missing accounts gracefully.
|
|
164
|
+
*
|
|
165
|
+
* @param cfg - The full OpenClaw plugin configuration object.
|
|
166
|
+
* @param accountId - The identity UUID to look up.
|
|
167
|
+
* @returns The resolved {@link EmailAccountConfig}.
|
|
168
|
+
*/
|
|
169
|
+
resolveAccount: (cfg: Record<string, unknown>, accountId: string) => EmailAccountConfig;
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Normalize an inbound SSE email event into a channel message envelope.
|
|
173
|
+
*
|
|
174
|
+
* All messages are passed through — the backend classifies senders via
|
|
175
|
+
* `sender_type`. External senders are marked read-only so the agent
|
|
176
|
+
* treats them as context without responding or using write tools.
|
|
177
|
+
*
|
|
178
|
+
* @param event - The raw {@link EmailEvent} from the SSE stream.
|
|
179
|
+
* @param account - The {@link EmailAccountConfig} for the receiving identity.
|
|
180
|
+
* @returns A normalized {@link EmailInboundEnvelope} (never null).
|
|
181
|
+
*/
|
|
182
|
+
normalizeInbound(event: EmailEvent, account: EmailAccountConfig): EmailInboundEnvelope | null;
|
|
183
|
+
outbound: {
|
|
184
|
+
deliveryMode: "direct";
|
|
185
|
+
/**
|
|
186
|
+
* Send a reply to the email thread that triggered this session.
|
|
187
|
+
*
|
|
188
|
+
* Wraps the plain-text body in HTML and calls {@link RaviClient.replyToEmail}
|
|
189
|
+
* using the thread context to target the correct message for threading.
|
|
190
|
+
*
|
|
191
|
+
* @param params.text - Plain-text reply body.
|
|
192
|
+
* @param params.client - An authenticated {@link RaviClient} instance.
|
|
193
|
+
* @param params.threadContext - Thread context from the inbound event.
|
|
194
|
+
* @returns A result object indicating success or failure.
|
|
195
|
+
*/
|
|
196
|
+
sendText: (params: {
|
|
197
|
+
text: string;
|
|
198
|
+
client: RaviClient;
|
|
199
|
+
threadContext?: EmailThreadContext;
|
|
200
|
+
}) => Promise<{
|
|
201
|
+
ok: boolean;
|
|
202
|
+
error?: string;
|
|
203
|
+
}>;
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* Generate the session key for this channel.
|
|
207
|
+
*
|
|
208
|
+
* The key uniquely identifies a session by combining the agent, account,
|
|
209
|
+
* and thread. OpenClaw uses this to route inbound events to the correct
|
|
210
|
+
* session and persist session state.
|
|
211
|
+
*
|
|
212
|
+
* Format: `agent:{agentId}:ravi-email:{accountId}:thread:{threadId}`
|
|
213
|
+
*
|
|
214
|
+
* @param agentId - The OpenClaw agent identifier.
|
|
215
|
+
* @param accountId - The identity UUID (account ID).
|
|
216
|
+
* @param threadId - The email thread identifier.
|
|
217
|
+
* @returns The deterministic session key string.
|
|
218
|
+
*/
|
|
219
|
+
getSessionKey(agentId: string, accountId: string, threadId: string): string;
|
|
220
|
+
};
|
|
221
|
+
//# sourceMappingURL=email.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/channels/email.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;CAEf;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,gFAAgF;AAChF,MAAM,WAAW,oBAAoB;IACnC,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,OAAO,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB;;;;;;;;;;;;;QAkB5B;;;;;WAKG;8BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,EAAE;QAOxD;;;;;;;;;WASG;8BAEI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,aACjB,MAAM,KAChB,kBAAkB;;IAgBvB;;;;;;;;;;OAUG;4BAEM,UAAU,WACR,kBAAkB,GAC1B,oBAAoB,GAAG,IAAI;;;QAuB5B;;;;;;;;;;WAUG;2BACsB;YACvB,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,UAAU,CAAC;YACnB,aAAa,CAAC,EAAE,kBAAkB,CAAC;SACpC,KAAG,OAAO,CAAC;YAAE,EAAE,EAAE,OAAO,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;;IAoB9C;;;;;;;;;;;;;OAaG;2BACoB,MAAM,aAAa,MAAM,YAAY,MAAM,GAAG,MAAM;EAI9E;AAED,iDAAiD;AACjD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;QAvIvB;;;;;WAKG;8BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,EAAE;QAOxD;;;;;;;;;WASG;8BAEI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,aACjB,MAAM,KAChB,kBAAkB;;IAgBvB;;;;;;;;;;OAUG;4BAEM,UAAU,WACR,kBAAkB,GAC1B,oBAAoB,GAAG,IAAI;;;QAuB5B;;;;;;;;;;WAUG;2BACsB;YACvB,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,UAAU,CAAC;YACnB,aAAa,CAAC,EAAE,kBAAkB,CAAC;SACpC,KAAG,OAAO,CAAC;YAAE,EAAE,EAAE,OAAO,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;;IAoB9C;;;;;;;;;;;;;OAaG;2BACoB,MAAM,aAAa,MAAM,YAAY,MAAM,GAAG,MAAM;CAO3B,CAAC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { wrapInHtml } from "../utils.js";
|
|
2
|
+
// ─── Channel Definition ──────────────────────────────────────────────────────
|
|
3
|
+
/**
|
|
4
|
+
* Creates the ravi-email channel definition for OpenClaw.
|
|
5
|
+
*
|
|
6
|
+
* Each email thread maps to exactly one OpenClaw session (1:1):
|
|
7
|
+
* - A new `thread_id` triggers creation of a new session.
|
|
8
|
+
* - A known `thread_id` resumes the existing session.
|
|
9
|
+
*
|
|
10
|
+
* The channel is a plain object (not a class) following OpenClaw's
|
|
11
|
+
* declarative channel convention.
|
|
12
|
+
*
|
|
13
|
+
* @returns The ravi-email channel definition object.
|
|
14
|
+
*/
|
|
15
|
+
export function createEmailChannel() {
|
|
16
|
+
return {
|
|
17
|
+
id: "ravi-email",
|
|
18
|
+
meta: {
|
|
19
|
+
id: "ravi-email",
|
|
20
|
+
label: "Ravi Email",
|
|
21
|
+
selectionLabel: "Ravi Email (AI Agent Identity)",
|
|
22
|
+
blurb: "Email channel via Ravi identity provider. Each email thread becomes a separate agent session.",
|
|
23
|
+
aliases: ["ravi-mail"],
|
|
24
|
+
},
|
|
25
|
+
capabilities: {
|
|
26
|
+
chatTypes: ["direct"],
|
|
27
|
+
},
|
|
28
|
+
config: {
|
|
29
|
+
/**
|
|
30
|
+
* List all configured account IDs (identity UUIDs) for the ravi-email channel.
|
|
31
|
+
*
|
|
32
|
+
* @param cfg - The full OpenClaw plugin configuration object.
|
|
33
|
+
* @returns Array of identity UUID strings.
|
|
34
|
+
*/
|
|
35
|
+
listAccountIds: (cfg) => {
|
|
36
|
+
const channels = cfg.channels;
|
|
37
|
+
return Object.keys(channels?.["ravi-email"]?.accounts ?? {});
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* Resolve an account configuration by its ID (identity UUID).
|
|
41
|
+
*
|
|
42
|
+
* Returns a default empty config if the account is not found rather than
|
|
43
|
+
* throwing, so callers can handle missing accounts gracefully.
|
|
44
|
+
*
|
|
45
|
+
* @param cfg - The full OpenClaw plugin configuration object.
|
|
46
|
+
* @param accountId - The identity UUID to look up.
|
|
47
|
+
* @returns The resolved {@link EmailAccountConfig}.
|
|
48
|
+
*/
|
|
49
|
+
resolveAccount: (cfg, accountId) => {
|
|
50
|
+
const channels = cfg.channels;
|
|
51
|
+
const found = channels?.["ravi-email"]?.accounts?.[accountId];
|
|
52
|
+
if (!found) {
|
|
53
|
+
console.warn(`[ravi] Email account ${accountId} not found in config — using empty defaults`);
|
|
54
|
+
}
|
|
55
|
+
return found ?? {
|
|
56
|
+
identityUuid: accountId,
|
|
57
|
+
identityName: "",
|
|
58
|
+
email: "",
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
/**
|
|
63
|
+
* Normalize an inbound SSE email event into a channel message envelope.
|
|
64
|
+
*
|
|
65
|
+
* All messages are passed through — the backend classifies senders via
|
|
66
|
+
* `sender_type`. External senders are marked read-only so the agent
|
|
67
|
+
* treats them as context without responding or using write tools.
|
|
68
|
+
*
|
|
69
|
+
* @param event - The raw {@link EmailEvent} from the SSE stream.
|
|
70
|
+
* @param account - The {@link EmailAccountConfig} for the receiving identity.
|
|
71
|
+
* @returns A normalized {@link EmailInboundEnvelope} (never null).
|
|
72
|
+
*/
|
|
73
|
+
normalizeInbound(event, account) {
|
|
74
|
+
const isReadOnly = event.sender_type !== "owner" && event.sender_type !== "self";
|
|
75
|
+
return {
|
|
76
|
+
senderId: event.from_email,
|
|
77
|
+
text: event.text_content || event.subject,
|
|
78
|
+
threadId: event.thread_id,
|
|
79
|
+
metadata: {
|
|
80
|
+
senderType: event.sender_type,
|
|
81
|
+
readOnly: isReadOnly,
|
|
82
|
+
subject: event.subject,
|
|
83
|
+
fromDisplayName: event.from_display_name,
|
|
84
|
+
htmlContent: event.html_content,
|
|
85
|
+
attachments: event.attachments,
|
|
86
|
+
inReplyTo: event.in_reply_to,
|
|
87
|
+
lastMessageId: Number(event.id) || 0,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
outbound: {
|
|
92
|
+
deliveryMode: "direct",
|
|
93
|
+
/**
|
|
94
|
+
* Send a reply to the email thread that triggered this session.
|
|
95
|
+
*
|
|
96
|
+
* Wraps the plain-text body in HTML and calls {@link RaviClient.replyToEmail}
|
|
97
|
+
* using the thread context to target the correct message for threading.
|
|
98
|
+
*
|
|
99
|
+
* @param params.text - Plain-text reply body.
|
|
100
|
+
* @param params.client - An authenticated {@link RaviClient} instance.
|
|
101
|
+
* @param params.threadContext - Thread context from the inbound event.
|
|
102
|
+
* @returns A result object indicating success or failure.
|
|
103
|
+
*/
|
|
104
|
+
sendText: async (params) => {
|
|
105
|
+
const { text, client, threadContext } = params;
|
|
106
|
+
if (!threadContext?.lastMessageId) {
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
error: "No email thread context — cannot reply without a message to reply to",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
await client.replyToEmail(threadContext.lastMessageId, wrapInHtml(text));
|
|
114
|
+
return { ok: true };
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
118
|
+
return { ok: false, error: `Failed to send email reply: ${message}` };
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
/**
|
|
123
|
+
* Generate the session key for this channel.
|
|
124
|
+
*
|
|
125
|
+
* The key uniquely identifies a session by combining the agent, account,
|
|
126
|
+
* and thread. OpenClaw uses this to route inbound events to the correct
|
|
127
|
+
* session and persist session state.
|
|
128
|
+
*
|
|
129
|
+
* Format: `agent:{agentId}:ravi-email:{accountId}:thread:{threadId}`
|
|
130
|
+
*
|
|
131
|
+
* @param agentId - The OpenClaw agent identifier.
|
|
132
|
+
* @param accountId - The identity UUID (account ID).
|
|
133
|
+
* @param threadId - The email thread identifier.
|
|
134
|
+
* @returns The deterministic session key string.
|
|
135
|
+
*/
|
|
136
|
+
getSessionKey(agentId, accountId, threadId) {
|
|
137
|
+
return `agent:${agentId}:ravi-email:${accountId}:thread:${threadId}`;
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** The singleton ravi-email channel instance. */
|
|
142
|
+
export const raviEmailChannel = createEmailChannel();
|
|
143
|
+
//# sourceMappingURL=email.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/channels/email.ts"],"names":[],"mappings":"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"}
|