myceliumail 1.0.2 → 1.0.3
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/.context7 +87 -0
- package/.eslintrc.json +29 -0
- package/COMPLETE.md +51 -0
- package/MYCELIUMAIL_STARTER_KIT.md +603 -0
- package/NEXT_STEPS.md +96 -0
- package/desktop/README.md +102 -0
- package/desktop/assets/icon.icns +0 -0
- package/desktop/assets/icon.iconset/icon_128x128.png +0 -0
- package/desktop/assets/icon.iconset/icon_128x128@2x.png +0 -0
- package/desktop/assets/icon.iconset/icon_16x16.png +0 -0
- package/desktop/assets/icon.iconset/icon_16x16@2x.png +0 -0
- package/desktop/assets/icon.iconset/icon_256x256.png +0 -0
- package/desktop/assets/icon.iconset/icon_256x256@2x.png +0 -0
- package/desktop/assets/icon.iconset/icon_32x32.png +0 -0
- package/desktop/assets/icon.iconset/icon_32x32@2x.png +0 -0
- package/desktop/assets/icon.iconset/icon_512x512.png +0 -0
- package/desktop/assets/icon.iconset/icon_512x512@2x.png +0 -0
- package/desktop/assets/icon.png +0 -0
- package/desktop/assets/tray-icon.png +0 -0
- package/desktop/main.js +257 -0
- package/desktop/package-lock.json +4198 -0
- package/desktop/package.json +48 -0
- package/desktop/preload.js +11 -0
- package/dist/bin/myceliumail.js +2 -0
- package/dist/bin/myceliumail.js.map +1 -1
- package/dist/commands/key-announce.d.ts +6 -0
- package/dist/commands/key-announce.d.ts.map +1 -0
- package/dist/commands/key-announce.js +63 -0
- package/dist/commands/key-announce.js.map +1 -0
- package/docs/20251215_Treebird-Ecosystem_Knowledge-Base_v2.md +292 -0
- package/docs/20251215_Treebird-Ecosystem_Project-Instructions_v2.md +176 -0
- package/docs/AGENT_DELEGATION_WORKFLOW.md +453 -0
- package/docs/AGENT_STARTER_KIT.md +145 -0
- package/docs/ANNOUNCEMENT_DRAFTS.md +55 -0
- package/docs/DASHBOARD_AGENT_HANDOFF.md +429 -0
- package/docs/DASHBOARD_AGENT_PROMPT.md +32 -0
- package/docs/DASHBOARD_BUILD_ROADMAP.md +61 -0
- package/docs/DEPLOYMENT.md +59 -0
- package/docs/LESSONS_LEARNED.md +127 -0
- package/docs/MCP_PUBLISHING_ROADMAP.md +113 -0
- package/docs/MCP_STARTER_KIT.md +117 -0
- package/docs/SSAN_MESSAGES_SUMMARY.md +92 -0
- package/docs/STORAGE_ARCHITECTURE.md +114 -0
- package/mcp-server/README.md +143 -0
- package/mcp-server/assets/icon.png +0 -0
- package/mcp-server/myceliumail-mcp-1.0.0.tgz +0 -0
- package/mcp-server/package-lock.json +1142 -0
- package/mcp-server/package.json +49 -0
- package/mcp-server/src/lib/config.ts +55 -0
- package/mcp-server/src/lib/crypto.ts +150 -0
- package/mcp-server/src/lib/storage.ts +267 -0
- package/mcp-server/src/server.ts +387 -0
- package/mcp-server/tsconfig.json +26 -0
- package/package.json +3 -3
- package/src/bin/myceliumail.ts +54 -0
- package/src/commands/broadcast.ts +70 -0
- package/src/commands/dashboard.ts +19 -0
- package/src/commands/inbox.ts +75 -0
- package/src/commands/key-announce.ts +70 -0
- package/src/commands/key-import.ts +35 -0
- package/src/commands/keygen.ts +44 -0
- package/src/commands/keys.ts +55 -0
- package/src/commands/read.ts +97 -0
- package/src/commands/send.ts +89 -0
- package/src/commands/watch.ts +101 -0
- package/src/dashboard/public/app.js +523 -0
- package/src/dashboard/public/index.html +75 -0
- package/src/dashboard/public/styles.css +68 -0
- package/src/dashboard/routes.ts +128 -0
- package/src/dashboard/server.ts +33 -0
- package/src/lib/config.ts +104 -0
- package/src/lib/crypto.ts +210 -0
- package/src/lib/realtime.ts +109 -0
- package/src/storage/local.ts +209 -0
- package/src/storage/supabase.ts +336 -0
- package/src/types/index.ts +53 -0
- package/supabase/migrations/000_myceliumail_setup.sql +93 -0
- package/supabase/migrations/001_enable_realtime.sql +10 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# 🍄 Myceliumail Web Dashboard - Agent Handoff Brief
|
|
2
|
+
|
|
3
|
+
> **For a fresh Gemini agent to build the web dashboard**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Your Mission
|
|
8
|
+
|
|
9
|
+
Build a **local web dashboard** (http://localhost:3737) to view Myceliumail messages with:
|
|
10
|
+
- Unified inbox (local JSON + Supabase)
|
|
11
|
+
- Auto-decrypt encrypted messages
|
|
12
|
+
- Mark as read/archive
|
|
13
|
+
- Beautiful, clean UI
|
|
14
|
+
|
|
15
|
+
**Estimated time:** 4-6 hours
|
|
16
|
+
**Complexity:** Medium-Low
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## What's Already Built (You Can Use)
|
|
21
|
+
|
|
22
|
+
### Existing Modules in `src/`
|
|
23
|
+
|
|
24
|
+
| Module | Path | What It Does |
|
|
25
|
+
|--------|------|--------------|
|
|
26
|
+
| **Storage** | `storage/local.ts` | Read/write local JSON messages |
|
|
27
|
+
| **Storage** | `storage/supabase.ts` | Read/write Supabase messages |
|
|
28
|
+
| **Crypto** | `lib/crypto.ts` | Encrypt/decrypt with NaCl |
|
|
29
|
+
| **Config** | `lib/config.ts` | Load agent ID, credentials |
|
|
30
|
+
| **Types** | `types/index.ts` | TypeScript interfaces |
|
|
31
|
+
|
|
32
|
+
**Key functions you'll use:**
|
|
33
|
+
```typescript
|
|
34
|
+
// From storage/local.ts or storage/supabase.ts
|
|
35
|
+
import { getInbox, getMessage, markAsRead, archiveMessage } from '../storage/supabase.js';
|
|
36
|
+
|
|
37
|
+
// From crypto.ts
|
|
38
|
+
import { loadKeyPair, decryptMessage } from '../lib/crypto.js';
|
|
39
|
+
|
|
40
|
+
// From config.ts
|
|
41
|
+
import { loadConfig } from '../lib/config.js';
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Project Structure (Create These)
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
src/dashboard/
|
|
50
|
+
├── server.ts # Fastify HTTP server
|
|
51
|
+
├── routes.ts # API endpoints
|
|
52
|
+
└── public/
|
|
53
|
+
├── index.html # Dashboard UI
|
|
54
|
+
├── app.js # Frontend logic
|
|
55
|
+
└── styles.css # Styling
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Roadmap: Step-by-Step
|
|
61
|
+
|
|
62
|
+
### Step 1: Install Dependencies (15 min)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
cd myceliumail # or your project directory
|
|
66
|
+
npm install fastify @fastify/static @fastify/cors
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Step 2: Create Basic Server (30 min)
|
|
70
|
+
|
|
71
|
+
**File:** `src/dashboard/server.ts`
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import Fastify from 'fastify';
|
|
75
|
+
import fastifyStatic from '@fastify/static';
|
|
76
|
+
import { join, dirname } from 'path';
|
|
77
|
+
import { fileURLToPath } from 'url';
|
|
78
|
+
|
|
79
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
80
|
+
|
|
81
|
+
export async function startDashboard(port = 3737) {
|
|
82
|
+
const fastify = Fastify({ logger: true });
|
|
83
|
+
|
|
84
|
+
// Serve static files
|
|
85
|
+
await fastify.register(fastifyStatic, {
|
|
86
|
+
root: join(__dirname, 'public'),
|
|
87
|
+
prefix: '/'
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Start server
|
|
91
|
+
await fastify.listen({ port, host: '127.0.0.1' });
|
|
92
|
+
console.log(`🍄 Dashboard running on http://localhost:${port}`);
|
|
93
|
+
|
|
94
|
+
return fastify;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Test:** Run `node -r esbuild-register src/dashboard/server.ts` → should start server
|
|
99
|
+
|
|
100
|
+
### Step 3: Create API Routes (1-2 hours)
|
|
101
|
+
|
|
102
|
+
**File:** `src/dashboard/routes.ts`
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import type { FastifyInstance } from 'fastify';
|
|
106
|
+
import { loadConfig } from '../lib/config.js';
|
|
107
|
+
import * as storage from '../storage/supabase.js';
|
|
108
|
+
import { loadKeyPair, decryptMessage } from '../lib/crypto.js';
|
|
109
|
+
|
|
110
|
+
export async function registerRoutes(fastify: FastifyInstance) {
|
|
111
|
+
const config = loadConfig();
|
|
112
|
+
const agentId = config.agentId;
|
|
113
|
+
|
|
114
|
+
// GET /api/inbox
|
|
115
|
+
fastify.get('/api/inbox', async (request, reply) => {
|
|
116
|
+
const messages = await storage.getInbox(agentId, { limit: 100 });
|
|
117
|
+
|
|
118
|
+
// Decrypt encrypted messages
|
|
119
|
+
const keyPair = loadKeyPair(agentId);
|
|
120
|
+
const decrypted = messages.map(msg => {
|
|
121
|
+
if (msg.encrypted && keyPair && msg.ciphertext) {
|
|
122
|
+
try {
|
|
123
|
+
const decryptedText = decryptMessage({
|
|
124
|
+
ciphertext: msg.ciphertext,
|
|
125
|
+
nonce: msg.nonce!,
|
|
126
|
+
senderPublicKey: msg.senderPublicKey!
|
|
127
|
+
}, keyPair);
|
|
128
|
+
|
|
129
|
+
if (decryptedText) {
|
|
130
|
+
const parsed = JSON.parse(decryptedText);
|
|
131
|
+
return { ...msg, subject: parsed.subject, body: parsed.body, decrypted: true };
|
|
132
|
+
}
|
|
133
|
+
} catch {}
|
|
134
|
+
}
|
|
135
|
+
return msg;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return { messages: decrypted, total: decrypted.length };
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// GET /api/message/:id
|
|
142
|
+
fastify.get('/api/message/:id', async (request, reply) => {
|
|
143
|
+
const { id } = request.params as { id: string };
|
|
144
|
+
const message = await storage.getMessage(id);
|
|
145
|
+
|
|
146
|
+
if (!message) {
|
|
147
|
+
return reply.code(404).send({ error: 'Message not found' });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Decrypt if needed
|
|
151
|
+
const keyPair = loadKeyPair(agentId);
|
|
152
|
+
if (message.encrypted && keyPair && message.ciphertext) {
|
|
153
|
+
try {
|
|
154
|
+
const decryptedText = decryptMessage({
|
|
155
|
+
ciphertext: message.ciphertext,
|
|
156
|
+
nonce: message.nonce!,
|
|
157
|
+
senderPublicKey: message.senderPublicKey!
|
|
158
|
+
}, keyPair);
|
|
159
|
+
|
|
160
|
+
if (decryptedText) {
|
|
161
|
+
const parsed = JSON.parse(decryptedText);
|
|
162
|
+
return { ...message, subject: parsed.subject, body: parsed.body, decrypted: true };
|
|
163
|
+
}
|
|
164
|
+
} catch {}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return message;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// POST /api/message/:id/read
|
|
171
|
+
fastify.post('/api/message/:id/read', async (request, reply) => {
|
|
172
|
+
const { id } = request.params as { id: string };
|
|
173
|
+
await storage.markAsRead(id);
|
|
174
|
+
return { success: true };
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// POST /api/message/:id/archive
|
|
178
|
+
fastify.post('/api/message/:id/archive', async (request, reply) => {
|
|
179
|
+
const { id } = request.params as { id: string };
|
|
180
|
+
await storage.archiveMessage(id);
|
|
181
|
+
return { success: true };
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// GET /api/stats
|
|
185
|
+
fastify.get('/api/stats', async (request, reply) => {
|
|
186
|
+
const messages = await storage.getInbox(agentId);
|
|
187
|
+
const unread = messages.filter(m => !m.read).length;
|
|
188
|
+
return {
|
|
189
|
+
total: messages.length,
|
|
190
|
+
unread,
|
|
191
|
+
encrypted: messages.filter(m => m.encrypted).length
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Update server.ts to use routes:**
|
|
198
|
+
```typescript
|
|
199
|
+
import { registerRoutes } from './routes.js';
|
|
200
|
+
|
|
201
|
+
// After creating fastify instance:
|
|
202
|
+
await registerRoutes(fastify);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Step 4: Create Frontend UI (2-3 hours)
|
|
206
|
+
|
|
207
|
+
**File:** `src/dashboard/public/index.html`
|
|
208
|
+
|
|
209
|
+
```html
|
|
210
|
+
<!DOCTYPE html>
|
|
211
|
+
<html lang="en">
|
|
212
|
+
<head>
|
|
213
|
+
<meta charset="UTF-8">
|
|
214
|
+
<title>🍄 Myceliumail Dashboard</title>
|
|
215
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
216
|
+
<link rel="stylesheet" href="/styles.css">
|
|
217
|
+
</head>
|
|
218
|
+
<body class="bg-gray-900 text-gray-100">
|
|
219
|
+
<div class="container mx-auto p-6">
|
|
220
|
+
<!-- Header -->
|
|
221
|
+
<header class="flex justify-between items-center mb-8">
|
|
222
|
+
<h1 class="text-3xl font-bold">🍄 Myceliumail Dashboard</h1>
|
|
223
|
+
<div id="stats" class="text-sm text-gray-400"></div>
|
|
224
|
+
</header>
|
|
225
|
+
|
|
226
|
+
<!-- Inbox List -->
|
|
227
|
+
<div class="grid grid-cols-3 gap-6">
|
|
228
|
+
<div class="col-span-1 bg-gray-800 rounded-lg p-4">
|
|
229
|
+
<h2 class="text-xl font-semibold mb-4">Inbox</h2>
|
|
230
|
+
<div id="inbox-list" class="space-y-2"></div>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<!-- Message Detail -->
|
|
234
|
+
<div class="col-span-2 bg-gray-800 rounded-lg p-6">
|
|
235
|
+
<div id="message-detail">
|
|
236
|
+
<p class="text-gray-500">Select a message to view</p>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<script src="/app.js"></script>
|
|
243
|
+
</body>
|
|
244
|
+
</html>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**File:** `src/dashboard/public/app.js`
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
// Load inbox on page load
|
|
251
|
+
async function loadInbox() {
|
|
252
|
+
const res = await fetch('/api/inbox');
|
|
253
|
+
const data = await res.json();
|
|
254
|
+
|
|
255
|
+
const list = document.getElementById('inbox-list');
|
|
256
|
+
list.innerHTML = data.messages.map(msg => `
|
|
257
|
+
<div class="message-item p-3 rounded cursor-pointer hover:bg-gray-700 ${msg.read ? '' : 'font-bold'}"
|
|
258
|
+
onclick="viewMessage('${msg.id}')">
|
|
259
|
+
<div class="flex items-center gap-2">
|
|
260
|
+
${msg.encrypted ? '🔐' : '📨'}
|
|
261
|
+
${!msg.read ? '●' : ''}
|
|
262
|
+
<span class="text-sm">${msg.sender}</span>
|
|
263
|
+
</div>
|
|
264
|
+
<div class="text-sm text-gray-400 truncate">${msg.subject || '(no subject)'}</div>
|
|
265
|
+
</div>
|
|
266
|
+
`).join('');
|
|
267
|
+
|
|
268
|
+
updateStats(data);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function viewMessage(id) {
|
|
272
|
+
const res = await fetch(`/api/message/${id}`);
|
|
273
|
+
const msg = await res.json();
|
|
274
|
+
|
|
275
|
+
const detail = document.getElementById('message-detail');
|
|
276
|
+
detail.innerHTML = `
|
|
277
|
+
<div class="mb-4">
|
|
278
|
+
<div class="text-sm text-gray-400">From: ${msg.sender}</div>
|
|
279
|
+
<h2 class="text-2xl font-bold">${msg.subject}</h2>
|
|
280
|
+
<div class="text-sm text-gray-400">${new Date(msg.createdAt).toLocaleString()}</div>
|
|
281
|
+
${msg.encrypted ? '<div class="text-sm text-green-400">🔐 Encrypted (decrypted ✅)</div>' : ''}
|
|
282
|
+
</div>
|
|
283
|
+
<div class="prose prose-invert max-w-none">
|
|
284
|
+
<p class="whitespace-pre-wrap">${msg.body}</p>
|
|
285
|
+
</div>
|
|
286
|
+
<div class="mt-6 flex gap-4">
|
|
287
|
+
<button onclick="archiveMessage('${msg.id}')"
|
|
288
|
+
class="px-4 py-2 bg-gray-700 rounded hover:bg-gray-600">
|
|
289
|
+
Archive
|
|
290
|
+
</button>
|
|
291
|
+
</div>
|
|
292
|
+
`;
|
|
293
|
+
|
|
294
|
+
// Mark as read
|
|
295
|
+
await fetch(`/api/message/${id}/read`, { method: 'POST' });
|
|
296
|
+
loadInbox(); // Refresh
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async function archiveMessage(id) {
|
|
300
|
+
await fetch(`/api/message/${id}/archive`, { method: 'POST' });
|
|
301
|
+
loadInbox();
|
|
302
|
+
document.getElementById('message-detail').innerHTML = '<p class="text-gray-500">Select a message to view</p>';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function updateStats(data) {
|
|
306
|
+
document.getElementById('stats').innerHTML = `
|
|
307
|
+
Total: ${data.total} | Unread: ${data.messages.filter(m => !m.read).length}
|
|
308
|
+
`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Load on page load
|
|
312
|
+
loadInbox();
|
|
313
|
+
|
|
314
|
+
// Auto-refresh every 10 seconds
|
|
315
|
+
setInterval(loadInbox, 10000);
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**File:** `src/dashboard/public/styles.css`
|
|
319
|
+
|
|
320
|
+
```css
|
|
321
|
+
body {
|
|
322
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.message-item {
|
|
326
|
+
transition: all 0.2s;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.message-item:hover {
|
|
330
|
+
transform: translateX(4px);
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Step 5: Add CLI Command (30 min)
|
|
335
|
+
|
|
336
|
+
**File:** `src/commands/dashboard.ts`
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { Command } from 'commander';
|
|
340
|
+
import { startDashboard } from '../dashboard/server.js';
|
|
341
|
+
|
|
342
|
+
export function createDashboardCommand(): Command {
|
|
343
|
+
return new Command('dashboard')
|
|
344
|
+
.description('Start web dashboard on localhost:3737')
|
|
345
|
+
.option('-p, --port <port>', 'Port to run on', '3737')
|
|
346
|
+
.action(async (options) => {
|
|
347
|
+
const port = parseInt(options.port, 10);
|
|
348
|
+
await startDashboard(port);
|
|
349
|
+
console.log(`\n🍄 Dashboard running: http://localhost:${port}\n`);
|
|
350
|
+
console.log('Press Ctrl+C to stop\n');
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Add to** `src/bin/myceliumail.ts`:
|
|
356
|
+
```typescript
|
|
357
|
+
import { createDashboardCommand } from '../commands/dashboard.js';
|
|
358
|
+
|
|
359
|
+
// Register with other commands
|
|
360
|
+
program.addCommand(createDashboardCommand());
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Step 6: Build & Test (30 min)
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
# Build
|
|
367
|
+
npm run build
|
|
368
|
+
|
|
369
|
+
# Test
|
|
370
|
+
mycmail dashboard
|
|
371
|
+
|
|
372
|
+
# Open browser
|
|
373
|
+
open http://localhost:3737
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Acceptance Criteria
|
|
379
|
+
|
|
380
|
+
✅ Dashboard loads on http://localhost:3737
|
|
381
|
+
✅ Shows all messages from inbox
|
|
382
|
+
✅ Encrypted messages are decrypted automatically
|
|
383
|
+
✅ Clicking message shows full content
|
|
384
|
+
✅ Mark as read works
|
|
385
|
+
✅ Archive works
|
|
386
|
+
✅ Stats show correct counts
|
|
387
|
+
✅ Auto-refresh every 10 seconds
|
|
388
|
+
✅ Dark mode, clean UI
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## Testing Scenarios
|
|
393
|
+
|
|
394
|
+
1. **Empty inbox** → Shows "No messages"
|
|
395
|
+
2. **Encrypted message** → Decrypts and displays ✅
|
|
396
|
+
3. **Unread message** → Shows bold, has ● indicator
|
|
397
|
+
4. **Archive** → Message disappears from list
|
|
398
|
+
5. **Multiple agents** → All messages visible
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Troubleshooting
|
|
403
|
+
|
|
404
|
+
**Port already in use:**
|
|
405
|
+
```bash
|
|
406
|
+
mycmail dashboard --port 3738
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**Build errors:**
|
|
410
|
+
```bash
|
|
411
|
+
npm install @types/node fastify @fastify/static
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**Messages not showing:**
|
|
415
|
+
- Check storage: `cat ~/.myceliumail/data/messages.json`
|
|
416
|
+
- Check config: `echo $MYCELIUMAIL_AGENT_ID`
|
|
417
|
+
- Check logs in terminal
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Resources
|
|
422
|
+
|
|
423
|
+
- **Fastify docs:** https://fastify.dev/
|
|
424
|
+
- **Tailwind CSS:** https://tailwindcss.com/docs
|
|
425
|
+
- **Existing code:** `src/`
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
**Ready to build! Start with Step 1.** 🍄
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Quick Start Prompt for Fresh Agent
|
|
2
|
+
|
|
3
|
+
**Copy-paste this to a new Gemini agent:**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Hi! I need you to build a web dashboard for Myceliumail.
|
|
8
|
+
|
|
9
|
+
**Context:**
|
|
10
|
+
- Project: `./` (clone from GitHub)
|
|
11
|
+
- Read this first: `docs/DASHBOARD_AGENT_HANDOFF.md`
|
|
12
|
+
- Follow roadmap: `docs/DASHBOARD_BUILD_ROADMAP.md`
|
|
13
|
+
|
|
14
|
+
**Your mission:**
|
|
15
|
+
Build a local web dashboard (http://localhost:3737) to view Myceliumail messages.
|
|
16
|
+
|
|
17
|
+
**Key requirements:**
|
|
18
|
+
1. Use existing modules in `src/storage/`, `src/lib/crypto.ts`, `src/lib/config.ts`
|
|
19
|
+
2. Build with Fastify + Tailwind CSS
|
|
20
|
+
3. Auto-decrypt encrypted messages
|
|
21
|
+
4. Dark mode UI, clean design
|
|
22
|
+
5. Add `mycmail dashboard` CLI command
|
|
23
|
+
|
|
24
|
+
**Time estimate:** 4-6 hours
|
|
25
|
+
|
|
26
|
+
**Acceptance criteria:** User can run `mycmail dashboard`, open browser, see all messages (encrypted ones decrypted), archive/mark as read.
|
|
27
|
+
|
|
28
|
+
Start with Phase 1 in the roadmap!
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
**That's it! Hand this to a new agent and they're ready to build.**
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Dashboard Build Roadmap
|
|
2
|
+
|
|
3
|
+
> Quick reference for agent building the web dashboard
|
|
4
|
+
|
|
5
|
+
## Phase 1: Setup (15 min)
|
|
6
|
+
- [ ] Install: `npm install fastify @fastify/static @fastify/cors`
|
|
7
|
+
- [ ] Create: `src/dashboard/` directory
|
|
8
|
+
- [ ] Create: `src/dashboard/public/` directory
|
|
9
|
+
|
|
10
|
+
## Phase 2: Backend (2 hours)
|
|
11
|
+
- [ ] Create `src/dashboard/server.ts` (Fastify server)
|
|
12
|
+
- [ ] Create `src/dashboard/routes.ts` (5 API endpoints)
|
|
13
|
+
- [ ] Test: `GET /api/inbox` returns messages
|
|
14
|
+
- [ ] Test: `GET /api/message/:id` decrypts encrypted messages
|
|
15
|
+
- [ ] Test: `POST /api/message/:id/read` marks as read
|
|
16
|
+
|
|
17
|
+
## Phase 3: Frontend (2-3 hours)
|
|
18
|
+
- [ ] Create `src/dashboard/public/index.html` (layout + Tailwind)
|
|
19
|
+
- [ ] Create `src/dashboard/public/app.js` (fetch API, render messages)
|
|
20
|
+
- [ ] Create `src/dashboard/public/styles.css` (custom styles)
|
|
21
|
+
- [ ] Test: Messages load in browser
|
|
22
|
+
- [ ] Test: Click message → shows detail
|
|
23
|
+
- [ ] Test: Archive button works
|
|
24
|
+
|
|
25
|
+
## Phase 4: CLI Integration (30 min)
|
|
26
|
+
- [ ] Create `src/commands/dashboard.ts`
|
|
27
|
+
- [ ] Register in `src/bin/myceliumail.ts`
|
|
28
|
+
- [ ] Build: `npm run build`
|
|
29
|
+
- [ ] Test: `mycmail dashboard` starts server
|
|
30
|
+
|
|
31
|
+
## Phase 5: Polish (1 hour)
|
|
32
|
+
- [ ] Add auto-refresh (10 sec interval)
|
|
33
|
+
- [ ] Add unread indicators (bold + ●)
|
|
34
|
+
- [ ] Add stats header (total, unread)
|
|
35
|
+
- [ ] Add keyboard shortcuts (optional)
|
|
36
|
+
- [ ] Dark mode polish
|
|
37
|
+
|
|
38
|
+
## Acceptance Tests
|
|
39
|
+
- [ ] Can view all local messages
|
|
40
|
+
- [ ] Can view Supabase messages (if configured)
|
|
41
|
+
- [ ] Encrypted messages decrypt automatically
|
|
42
|
+
- [ ] Mark as read updates UI
|
|
43
|
+
- [ ] Archive removes from inbox
|
|
44
|
+
- [ ] UI is responsive and clean
|
|
45
|
+
|
|
46
|
+
## Time Budget
|
|
47
|
+
- Setup: 15 min
|
|
48
|
+
- Backend: 2 hours
|
|
49
|
+
- Frontend: 2-3 hours
|
|
50
|
+
- CLI: 30 min
|
|
51
|
+
- Polish: 1 hour
|
|
52
|
+
**Total: 4-6 hours**
|
|
53
|
+
|
|
54
|
+
## Key Files to Reference
|
|
55
|
+
- Storage API: `src/storage/supabase.ts`
|
|
56
|
+
- Crypto: `src/lib/crypto.ts`
|
|
57
|
+
- Config: `src/lib/config.ts`
|
|
58
|
+
- Types: `src/types/index.ts`
|
|
59
|
+
|
|
60
|
+
## Success =
|
|
61
|
+
User runs `mycmail dashboard`, opens browser, sees beautiful inbox with all messages (encrypted ones decrypted), can archive/read, feels 🍄 magical!
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Myceliumail Deployment Checklist
|
|
2
|
+
|
|
3
|
+
## Pre-Deployment
|
|
4
|
+
|
|
5
|
+
### 1. Environment Setup
|
|
6
|
+
- [ ] Set `MYCELIUMAIL_STORAGE=supabase` in `.env`
|
|
7
|
+
- [ ] Set production Supabase credentials:
|
|
8
|
+
```bash
|
|
9
|
+
SUPABASE_URL=https://your-project.supabase.co
|
|
10
|
+
SUPABASE_ANON_KEY=your-production-key
|
|
11
|
+
```
|
|
12
|
+
- [ ] Verify agent ID: `MYCELIUMAIL_AGENT_ID=your-agent`
|
|
13
|
+
|
|
14
|
+
### 2. Database
|
|
15
|
+
- [ ] Run migrations on production Supabase
|
|
16
|
+
- [ ] Verify `agent_messages` table exists with correct schema
|
|
17
|
+
- [ ] Check RLS policies are enabled
|
|
18
|
+
|
|
19
|
+
### 3. Build & Test
|
|
20
|
+
- [ ] Run `npm run build` - no errors
|
|
21
|
+
- [ ] Test send: `mycmail send test-agent "Test" -m "Hello"`
|
|
22
|
+
- [ ] Test inbox: `mycmail inbox`
|
|
23
|
+
- [ ] Test dashboard: `mycmail dashboard`
|
|
24
|
+
|
|
25
|
+
### 4. Security
|
|
26
|
+
- [ ] Ensure `.env` is in `.gitignore`
|
|
27
|
+
- [ ] No secrets committed to git
|
|
28
|
+
- [ ] Review public keys in `docs/AGENT_STARTER_KIT.md`
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Post-Deployment
|
|
33
|
+
|
|
34
|
+
### 1. Verify
|
|
35
|
+
- [ ] Send test message to known agent
|
|
36
|
+
- [ ] Check message appears in Supabase dashboard
|
|
37
|
+
- [ ] Verify encryption works: `mycmail send agent "Test" -m "Secret" --encrypt`
|
|
38
|
+
|
|
39
|
+
### 2. Update Agents
|
|
40
|
+
- [ ] Notify agents of production URL
|
|
41
|
+
- [ ] Share updated MCP config with Supabase credentials
|
|
42
|
+
- [ ] Confirm agents can send/receive
|
|
43
|
+
|
|
44
|
+
### 3. Monitor
|
|
45
|
+
- [ ] Check Supabase logs for errors
|
|
46
|
+
- [ ] Monitor message table growth
|
|
47
|
+
- [ ] Review API usage/limits
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Rollback
|
|
52
|
+
If issues occur:
|
|
53
|
+
```bash
|
|
54
|
+
# Switch back to local storage
|
|
55
|
+
MYCELIUMAIL_STORAGE=local mycmail inbox
|
|
56
|
+
|
|
57
|
+
# Or switch to staging
|
|
58
|
+
SUPABASE_URL=https://staging-project.supabase.co
|
|
59
|
+
```
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Lessons Learned: MCP Supabase Connection Debugging
|
|
2
|
+
|
|
3
|
+
## Date
|
|
4
|
+
December 17-18, 2025
|
|
5
|
+
|
|
6
|
+
## Issue Summary
|
|
7
|
+
Watson (Claude Desktop MCP user) could not see Supabase messages. The MCP server only showed locally cached messages instead of fetching from the cloud.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Root Causes Identified
|
|
12
|
+
|
|
13
|
+
### 1. Schema Mismatch (Code Bug)
|
|
14
|
+
**Problem:** The MCP server's `storage.ts` was using incorrect column names when querying Supabase:
|
|
15
|
+
- Used `recipient` instead of `to_agent`
|
|
16
|
+
- Used `sender` instead of `from_agent`
|
|
17
|
+
- Used `body` instead of `message`
|
|
18
|
+
|
|
19
|
+
**Symptom:** Supabase queries returned empty results because the columns didn't exist.
|
|
20
|
+
|
|
21
|
+
**Fix:** Updated all Supabase queries in `storage.ts` to use the correct column names matching the actual `agent_messages` table schema.
|
|
22
|
+
|
|
23
|
+
**Lesson:** Always verify that code column names match the actual database schema. Consider using generated types from Supabase to catch these mismatches at compile time.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
### 2. Silent Error Swallowing (Bad Pattern)
|
|
28
|
+
**Problem:** All Supabase operations had empty `catch` blocks:
|
|
29
|
+
```typescript
|
|
30
|
+
} catch {
|
|
31
|
+
// Fall through to local
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Symptom:** Errors were completely hidden. When Supabase failed, the code silently fell back to local storage without any indication of why.
|
|
36
|
+
|
|
37
|
+
**Fix:** Added proper error logging to all catch blocks:
|
|
38
|
+
```typescript
|
|
39
|
+
} catch (err) {
|
|
40
|
+
console.error('getInbox failed, falling back to local:', err);
|
|
41
|
+
// Fall through to local
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Lesson:** Never use empty catch blocks. Always log errors, even when recovering gracefully. Silent failures are debugging nightmares.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### 3. NPX Caching (Deployment Issue)
|
|
50
|
+
**Problem:** `npx myceliumail-mcp` was aggressively caching old versions of the package, even after publishing fixes to npm.
|
|
51
|
+
|
|
52
|
+
**Symptom:** Even after publishing v1.0.6 with fixes, Claude Desktop kept running the broken old version.
|
|
53
|
+
|
|
54
|
+
**Attempted Solutions:**
|
|
55
|
+
- `npx -y myceliumail-mcp@1.0.6` - Did not reliably bust cache
|
|
56
|
+
- `npm cache clean --force` - Not effective for npx cache
|
|
57
|
+
|
|
58
|
+
**Final Fix:** Configured Claude Desktop to run the local build directly:
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"command": "node",
|
|
62
|
+
"args": ["/path/to/dist/server.js"]
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Lesson:** For development and debugging, always use local builds with direct paths. Don't trust `npx` caching behavior during active development. For production users, consider adding version-specific instructions: `npx myceliumail-mcp@latest`.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
### 4. Missing Global Config Support (Feature Gap)
|
|
71
|
+
**Problem:** The MCP server only read Supabase credentials from environment variables, but Claude Desktop's MCP config doesn't support complex env var setup easily.
|
|
72
|
+
|
|
73
|
+
**Fix:** Added fallback to read from `~/.myceliumail/config.json`, same as the CLI tool.
|
|
74
|
+
|
|
75
|
+
**Lesson:** MCP servers should support multiple config sources (env vars, config files) for flexibility across different deployment contexts.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Debugging Techniques Used
|
|
80
|
+
|
|
81
|
+
### 1. File-Based Debug Logging
|
|
82
|
+
When console output was being swallowed by the MCP runtime, we added file-based logging:
|
|
83
|
+
```typescript
|
|
84
|
+
const LOG_FILE = join(homedir(), '.myceliumail', 'debug.log');
|
|
85
|
+
appendFileSync(LOG_FILE, `[${timestamp}] ${msg}\n`);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This proved essential for diagnosing issues in environments where stderr isn't visible.
|
|
89
|
+
|
|
90
|
+
### 2. Reproduction Script
|
|
91
|
+
Created `debug-mcp-connection.js` to test the Supabase connection outside of the MCP context:
|
|
92
|
+
```javascript
|
|
93
|
+
const { hasSupabase } = require('./dist/lib/config.js');
|
|
94
|
+
const { getInbox } = require('./dist/lib/storage.js');
|
|
95
|
+
// Test directly
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This isolated whether the issue was in the code or the MCP runtime environment.
|
|
99
|
+
|
|
100
|
+
### 3. Direct Supabase Verification
|
|
101
|
+
Used the Supabase MCP tool to verify data existed:
|
|
102
|
+
```sql
|
|
103
|
+
SELECT * FROM agent_messages WHERE to_agent = 'watson';
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
This confirmed the data was in the cloud and the issue was in fetching, not storage.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Prevention Guidelines
|
|
111
|
+
|
|
112
|
+
1. **Schema Validation:** Use TypeScript generated types from Supabase CLI to catch column name mismatches at compile time.
|
|
113
|
+
|
|
114
|
+
2. **Error Visibility:** Always log errors, never swallow them silently. Use a consistent error logging pattern.
|
|
115
|
+
|
|
116
|
+
3. **Multiple Config Sources:** Support env vars AND config files for flexibility.
|
|
117
|
+
|
|
118
|
+
4. **Local Development:** Always test with local builds during debugging. Don't rely on npm/npx for rapid iteration.
|
|
119
|
+
|
|
120
|
+
5. **Integration Tests:** Add tests that actually hit Supabase (in a test project) to catch schema drift.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Related Files Modified
|
|
125
|
+
- `mcp-server/src/lib/config.ts` - Added config file support
|
|
126
|
+
- `mcp-server/src/lib/storage.ts` - Fixed column names, added error logging
|
|
127
|
+
- `mcp-server/package.json` - Version bumps
|