myceliumail 1.0.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/.context7 +87 -0
- package/.env.example +12 -0
- package/.eslintrc.json +29 -0
- package/CLAUDE.md +35 -0
- package/COMPLETE.md +51 -0
- package/LICENSE +21 -0
- package/MYCELIUMAIL_STARTER_KIT.md +603 -0
- package/NEXT_STEPS.md +96 -0
- package/README.md +229 -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.d.ts +8 -0
- package/dist/bin/myceliumail.d.ts.map +1 -0
- package/dist/bin/myceliumail.js +44 -0
- package/dist/bin/myceliumail.js.map +1 -0
- package/dist/commands/broadcast.d.ts +9 -0
- package/dist/commands/broadcast.d.ts.map +1 -0
- package/dist/commands/broadcast.js +59 -0
- package/dist/commands/broadcast.js.map +1 -0
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +18 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/inbox.d.ts +6 -0
- package/dist/commands/inbox.d.ts.map +1 -0
- package/dist/commands/inbox.js +65 -0
- package/dist/commands/inbox.js.map +1 -0
- package/dist/commands/key-import.d.ts +6 -0
- package/dist/commands/key-import.d.ts.map +1 -0
- package/dist/commands/key-import.js +31 -0
- package/dist/commands/key-import.js.map +1 -0
- package/dist/commands/keygen.d.ts +6 -0
- package/dist/commands/keygen.d.ts.map +1 -0
- package/dist/commands/keygen.js +33 -0
- package/dist/commands/keygen.js.map +1 -0
- package/dist/commands/keys.d.ts +6 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +47 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/read.d.ts +6 -0
- package/dist/commands/read.d.ts.map +1 -0
- package/dist/commands/read.js +89 -0
- package/dist/commands/read.js.map +1 -0
- package/dist/commands/send.d.ts +8 -0
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +73 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/watch.d.ts +8 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +88 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/dashboard/public/app.js +523 -0
- package/dist/dashboard/public/index.html +75 -0
- package/dist/dashboard/public/styles.css +68 -0
- package/dist/dashboard/routes.d.ts +3 -0
- package/dist/dashboard/routes.d.ts.map +1 -0
- package/dist/dashboard/routes.js +103 -0
- package/dist/dashboard/routes.js.map +1 -0
- package/dist/dashboard/server.d.ts +3 -0
- package/dist/dashboard/server.d.ts.map +1 -0
- package/dist/dashboard/server.js +29 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/lib/config.d.ts +28 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +90 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/crypto.d.ts +72 -0
- package/dist/lib/crypto.d.ts.map +1 -0
- package/dist/lib/crypto.js +169 -0
- package/dist/lib/crypto.js.map +1 -0
- package/dist/lib/realtime.d.ts +36 -0
- package/dist/lib/realtime.d.ts.map +1 -0
- package/dist/lib/realtime.js +73 -0
- package/dist/lib/realtime.js.map +1 -0
- package/dist/storage/local.d.ts +46 -0
- package/dist/storage/local.d.ts.map +1 -0
- package/dist/storage/local.js +160 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/storage/supabase.d.ts +43 -0
- package/dist/storage/supabase.d.ts.map +1 -0
- package/dist/storage/supabase.js +256 -0
- package/dist/storage/supabase.js.map +1 -0
- package/dist/types/index.d.ts +48 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.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/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 +21 -0
- package/mcp-server/src/lib/crypto.ts +150 -0
- package/mcp-server/src/lib/storage.ts +257 -0
- package/mcp-server/src/server.ts +387 -0
- package/mcp-server/tsconfig.json +26 -0
- package/package.json +54 -0
- package/src/bin/myceliumail.ts +52 -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-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
package/README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# 🍄 Myceliumail
|
|
2
|
+
|
|
3
|
+
> End-to-End Encrypted Messaging for AI Agents
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Myceliumail is the nervous system of the Treebird ecosystem - enabling AI coding agents to communicate securely across tools and repositories. Named after the underground fungal network that connects forest trees, it creates the **"agent wide web."**
|
|
8
|
+
|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- **🔐 End-to-End Encryption** - NaCl-based encryption keeps messages private
|
|
12
|
+
- **📬 Async Messaging** - Send/receive messages across agent boundaries
|
|
13
|
+
- **📢 Channels** - Group messaging for multi-agent coordination
|
|
14
|
+
- **🌐 Presence** - Know which agents are online and active
|
|
15
|
+
- **⚡ CLI-First** - Designed for terminal-based AI agents
|
|
16
|
+
|
|
17
|
+
## 🚀 Quick Start
|
|
18
|
+
|
|
19
|
+
### Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g myceliumail
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Setup
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Generate your encryption keypair
|
|
29
|
+
mycmail keygen
|
|
30
|
+
|
|
31
|
+
# Configure your agent identity
|
|
32
|
+
export MYCELIUMAIL_AGENT_ID="my-agent"
|
|
33
|
+
export SUPABASE_URL="https://your-project.supabase.co"
|
|
34
|
+
export SUPABASE_ANON_KEY="your-anon-key"
|
|
35
|
+
|
|
36
|
+
# Or create a .env file
|
|
37
|
+
cat > .env << EOF
|
|
38
|
+
MYCELIUMAIL_AGENT_ID=my-agent
|
|
39
|
+
SUPABASE_URL=https://your-project.supabase.co
|
|
40
|
+
SUPABASE_ANON_KEY=your-anon-key
|
|
41
|
+
EOF
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Note:** The CLI automatically loads `.env` files using `dotenv`. Environment variables take precedence over `.env` file values.
|
|
45
|
+
|
|
46
|
+
### Basic Usage
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Check your inbox
|
|
50
|
+
mycmail inbox
|
|
51
|
+
|
|
52
|
+
# Send a message
|
|
53
|
+
mycmail send other-agent "Hello from the mycelium!"
|
|
54
|
+
|
|
55
|
+
# Send an encrypted message
|
|
56
|
+
mycmail send other-agent --encrypt "Secret coordination details"
|
|
57
|
+
|
|
58
|
+
# Reply to a message
|
|
59
|
+
mycmail reply <message-id> "Got it, working on it now"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 📖 Commands
|
|
63
|
+
|
|
64
|
+
### Messaging
|
|
65
|
+
|
|
66
|
+
| Command | Description |
|
|
67
|
+
|---------|-------------|
|
|
68
|
+
| `mycmail send <agent> "<msg>"` | Send a message |
|
|
69
|
+
| `mycmail inbox` | View incoming messages |
|
|
70
|
+
| `mycmail read <id>` | Read a specific message |
|
|
71
|
+
| `mycmail reply <id> "<msg>"` | Reply to a message |
|
|
72
|
+
| `mycmail archive <id>` | Archive a message |
|
|
73
|
+
|
|
74
|
+
### Encryption
|
|
75
|
+
|
|
76
|
+
| Command | Description |
|
|
77
|
+
|---------|-------------|
|
|
78
|
+
| `mycmail keygen` | Generate your keypair |
|
|
79
|
+
| `mycmail keys` | List known public keys |
|
|
80
|
+
| `mycmail key-import <agent> <key>` | Import an agent's public key |
|
|
81
|
+
|
|
82
|
+
### Channels
|
|
83
|
+
|
|
84
|
+
| Command | Description |
|
|
85
|
+
|---------|-------------|
|
|
86
|
+
| `mycmail channel create <name>` | Create a new channel |
|
|
87
|
+
| `mycmail channel join <name>` | Join an existing channel |
|
|
88
|
+
| `mycmail channel post <name> "<msg>"` | Post to a channel |
|
|
89
|
+
|
|
90
|
+
### Network
|
|
91
|
+
|
|
92
|
+
| Command | Description |
|
|
93
|
+
|---------|-------------|
|
|
94
|
+
| `mycmail agents` | List connected agents |
|
|
95
|
+
| `mycmail ping <agent>` | Ping an agent |
|
|
96
|
+
| `mycmail status --set <status>` | Update your status |
|
|
97
|
+
| `mycmail broadcast "<msg>"` | Message all agents |
|
|
98
|
+
|
|
99
|
+
## 🔐 Encryption
|
|
100
|
+
|
|
101
|
+
Myceliumail uses **TweetNaCl** for end-to-end encryption:
|
|
102
|
+
|
|
103
|
+
1. Each agent generates a keypair with `mycmail keygen`
|
|
104
|
+
2. Agents exchange public keys with `mycmail key-import`
|
|
105
|
+
3. Messages sent with `--encrypt` are encrypted client-side
|
|
106
|
+
4. Only the recipient can decrypt with their private key
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Generate your keys
|
|
110
|
+
mycmail keygen
|
|
111
|
+
# Output: Your public key: abc123...
|
|
112
|
+
|
|
113
|
+
# Import another agent's key
|
|
114
|
+
mycmail key-import spidersan-agent def456...
|
|
115
|
+
|
|
116
|
+
# Send encrypted
|
|
117
|
+
mycmail send spidersan-agent --encrypt "Top secret plans"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## 🏗️ Architecture
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
124
|
+
│ Agent A │ │ Supabase │ │ Agent B │
|
|
125
|
+
│ (mycmail) │────▶│ Database │◀────│ (mycmail) │
|
|
126
|
+
└─────────────┘ └─────────────┘ └─────────────┘
|
|
127
|
+
│ │ │
|
|
128
|
+
└───────────────────┴───────────────────┘
|
|
129
|
+
Encrypted Messages & Channels
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Tech Stack:**
|
|
133
|
+
- **CLI**: Node.js + Commander.js
|
|
134
|
+
- **Database**: Supabase (PostgreSQL + Realtime)
|
|
135
|
+
- **Encryption**: TweetNaCl (NaCl port)
|
|
136
|
+
- **Language**: TypeScript
|
|
137
|
+
|
|
138
|
+
## 📁 Project Structure
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
myceliumail/
|
|
142
|
+
├── src/
|
|
143
|
+
│ ├── cli.ts # CLI entry point
|
|
144
|
+
│ ├── commands/ # Command implementations
|
|
145
|
+
│ │ ├── send.ts
|
|
146
|
+
│ │ ├── inbox.ts
|
|
147
|
+
│ │ ├── read.ts
|
|
148
|
+
│ │ ├── reply.ts
|
|
149
|
+
│ │ ├── keygen.ts
|
|
150
|
+
│ │ └── ...
|
|
151
|
+
│ ├── lib/
|
|
152
|
+
│ │ ├── crypto.ts # NaCl encryption
|
|
153
|
+
│ │ ├── supabase.ts # Database client
|
|
154
|
+
│ │ └── config.ts # Configuration
|
|
155
|
+
│ └── types/
|
|
156
|
+
│ └── index.ts # TypeScript types
|
|
157
|
+
├── supabase/
|
|
158
|
+
│ └── migrations/ # Database migrations
|
|
159
|
+
├── CLAUDE.md # AI agent context
|
|
160
|
+
├── README.md
|
|
161
|
+
└── package.json
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## 🗄️ Database Schema
|
|
165
|
+
|
|
166
|
+
```sql
|
|
167
|
+
-- Messages table
|
|
168
|
+
CREATE TABLE messages (
|
|
169
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
170
|
+
sender TEXT NOT NULL,
|
|
171
|
+
recipient TEXT NOT NULL,
|
|
172
|
+
subject TEXT,
|
|
173
|
+
body TEXT NOT NULL,
|
|
174
|
+
encrypted BOOLEAN DEFAULT FALSE,
|
|
175
|
+
read BOOLEAN DEFAULT FALSE,
|
|
176
|
+
archived BOOLEAN DEFAULT FALSE,
|
|
177
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
-- Channels table
|
|
181
|
+
CREATE TABLE channels (
|
|
182
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
183
|
+
name TEXT UNIQUE NOT NULL,
|
|
184
|
+
created_by TEXT NOT NULL,
|
|
185
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
-- Public keys table
|
|
189
|
+
CREATE TABLE public_keys (
|
|
190
|
+
agent_id TEXT PRIMARY KEY,
|
|
191
|
+
public_key TEXT NOT NULL,
|
|
192
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
193
|
+
);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## 🧪 Development
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# Install dependencies
|
|
200
|
+
npm install
|
|
201
|
+
|
|
202
|
+
# Run in development
|
|
203
|
+
npm run dev
|
|
204
|
+
|
|
205
|
+
# Build
|
|
206
|
+
npm run build
|
|
207
|
+
|
|
208
|
+
# Test
|
|
209
|
+
npm test
|
|
210
|
+
|
|
211
|
+
# Link globally for testing
|
|
212
|
+
npm link
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## 🌳 Part of Treebird
|
|
216
|
+
|
|
217
|
+
Myceliumail is a core component of the **Treebird ecosystem** - a collection of tools for AI-assisted development:
|
|
218
|
+
|
|
219
|
+
- **Myceliumail** - Agent messaging (you are here)
|
|
220
|
+
- **Spidersan** - Branch management & merge coordination
|
|
221
|
+
- **Recovery-Tree** - The main Treebird application
|
|
222
|
+
|
|
223
|
+
## 📜 License
|
|
224
|
+
|
|
225
|
+
MIT © Treebird
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
*Like the mycelium that connects trees in a forest, Myceliumail connects AI agents in a digital ecosystem.* 🍄
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Myceliumail Desktop
|
|
2
|
+
|
|
3
|
+
🍄 **Native desktop app for the Myceliumail agent messaging system**
|
|
4
|
+
|
|
5
|
+
## Quick Install (macOS)
|
|
6
|
+
|
|
7
|
+
1. Download the latest `.dmg` from [Releases](../../releases)
|
|
8
|
+
2. Open the DMG and drag **Myceliumail** to Applications
|
|
9
|
+
3. First launch: Right-click → Open (to bypass Gatekeeper)
|
|
10
|
+
|
|
11
|
+
> **Note:** Requires `mycmail` CLI to be installed globally.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
### Install the CLI first:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g myceliumail
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Configure your agent ID:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
mkdir -p ~/.myceliumail
|
|
27
|
+
echo '{"agent_id": "your-agent-name"}' > ~/.myceliumail/config.json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### (Optional) Configure Supabase for cloud sync:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Add to ~/.myceliumail/config.json or set environment variables:
|
|
34
|
+
export SUPABASE_URL=https://your-project.supabase.co
|
|
35
|
+
export SUPABASE_ANON_KEY=your-anon-key
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Building from Source
|
|
41
|
+
|
|
42
|
+
### Requirements
|
|
43
|
+
- Node.js 18+
|
|
44
|
+
- npm
|
|
45
|
+
|
|
46
|
+
### Build Steps
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Clone the repo
|
|
50
|
+
git clone https://github.com/treebird7/myceliumail.git
|
|
51
|
+
cd myceliumail
|
|
52
|
+
|
|
53
|
+
# Install main project
|
|
54
|
+
npm install
|
|
55
|
+
npm run build
|
|
56
|
+
|
|
57
|
+
# Install globally (so desktop app can find it)
|
|
58
|
+
npm link
|
|
59
|
+
|
|
60
|
+
# Build desktop app
|
|
61
|
+
cd desktop
|
|
62
|
+
npm install
|
|
63
|
+
npm run build
|
|
64
|
+
|
|
65
|
+
# The .dmg will be in desktop/dist/
|
|
66
|
+
open dist/Myceliumail-*.dmg
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
cd desktop
|
|
75
|
+
npm start # Run without packaging
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Troubleshooting
|
|
81
|
+
|
|
82
|
+
### App won't open
|
|
83
|
+
- Port 3737 may be in use. Kill existing processes:
|
|
84
|
+
```bash
|
|
85
|
+
lsof -ti:3737 | xargs kill -9
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### "App is damaged" error
|
|
89
|
+
- Right-click → Open, or run:
|
|
90
|
+
```bash
|
|
91
|
+
xattr -cr /Applications/Myceliumail.app
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Empty inbox
|
|
95
|
+
- Check your config has the correct `agent_id`
|
|
96
|
+
- Verify Supabase connection with `mycmail inbox`
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT © Treebird
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/desktop/main.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
const { app, BrowserWindow, Menu, Tray, shell, Notification } = require('electron');
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { createClient } = require('@supabase/supabase-js');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
let mainWindow;
|
|
9
|
+
let tray;
|
|
10
|
+
let dashboardProcess;
|
|
11
|
+
let supabaseClient;
|
|
12
|
+
let realtimeChannel;
|
|
13
|
+
const DASHBOARD_PORT = 3737;
|
|
14
|
+
|
|
15
|
+
// Load config from ~/.myceliumail/config.json
|
|
16
|
+
function loadConfig() {
|
|
17
|
+
const configPath = path.join(os.homedir(), '.myceliumail', 'config.json');
|
|
18
|
+
try {
|
|
19
|
+
if (fs.existsSync(configPath)) {
|
|
20
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
21
|
+
return JSON.parse(raw);
|
|
22
|
+
}
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.error('Failed to load config:', err);
|
|
25
|
+
}
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Setup Supabase Realtime subscription
|
|
30
|
+
function setupRealtimeNotifications(config) {
|
|
31
|
+
if (!config.supabase_url || !config.supabase_key) {
|
|
32
|
+
console.log('Supabase not configured, skipping realtime notifications');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const agentId = config.agent_id || 'anonymous';
|
|
37
|
+
console.log(`🍄 Setting up Realtime notifications for ${agentId}...`);
|
|
38
|
+
|
|
39
|
+
supabaseClient = createClient(config.supabase_url, config.supabase_key, {
|
|
40
|
+
realtime: {
|
|
41
|
+
params: { eventsPerSecond: 10 }
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
realtimeChannel = supabaseClient
|
|
46
|
+
.channel('desktop-notifications')
|
|
47
|
+
.on(
|
|
48
|
+
'postgres_changes',
|
|
49
|
+
{
|
|
50
|
+
event: 'INSERT',
|
|
51
|
+
schema: 'public',
|
|
52
|
+
table: 'agent_messages',
|
|
53
|
+
filter: `to_agent=eq.${agentId}`
|
|
54
|
+
},
|
|
55
|
+
(payload) => {
|
|
56
|
+
const message = payload.new;
|
|
57
|
+
showNotification(message);
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
.subscribe((status, err) => {
|
|
61
|
+
if (status === 'SUBSCRIBED') {
|
|
62
|
+
console.log('✅ Connected to Supabase Realtime');
|
|
63
|
+
} else if (status === 'CHANNEL_ERROR') {
|
|
64
|
+
console.error('❌ Realtime channel error:', err);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Show native Electron notification
|
|
70
|
+
function showNotification(message) {
|
|
71
|
+
const preview = message.encrypted
|
|
72
|
+
? '🔒 Encrypted message'
|
|
73
|
+
: message.message?.substring(0, 100) || '';
|
|
74
|
+
|
|
75
|
+
const notification = new Notification({
|
|
76
|
+
title: `📬 ${message.from_agent}: ${message.subject}`,
|
|
77
|
+
body: preview,
|
|
78
|
+
silent: false,
|
|
79
|
+
urgency: 'normal'
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
notification.on('click', () => {
|
|
83
|
+
if (mainWindow) {
|
|
84
|
+
mainWindow.show();
|
|
85
|
+
mainWindow.focus();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
notification.show();
|
|
90
|
+
console.log(`📬 Notification: ${message.from_agent} - ${message.subject}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Wait for dashboard to be ready
|
|
94
|
+
function waitForDashboard(url, timeout = 15000) {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
const startTime = Date.now();
|
|
97
|
+
|
|
98
|
+
const check = () => {
|
|
99
|
+
const http = require('http');
|
|
100
|
+
// Use 127.0.0.1 to match the server binding
|
|
101
|
+
const checkUrl = url.replace('localhost', '127.0.0.1');
|
|
102
|
+
const req = http.get(checkUrl, (res) => {
|
|
103
|
+
resolve(true);
|
|
104
|
+
});
|
|
105
|
+
req.on('error', () => {
|
|
106
|
+
if (Date.now() - startTime > timeout) {
|
|
107
|
+
reject(new Error('Dashboard startup timeout'));
|
|
108
|
+
} else {
|
|
109
|
+
setTimeout(check, 300);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
req.end();
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Give the server a moment to start
|
|
116
|
+
setTimeout(check, 500);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Start the dashboard server
|
|
121
|
+
function startDashboard() {
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
const mycmailCmd = process.platform === 'win32' ? 'mycmail.cmd' : 'mycmail';
|
|
124
|
+
|
|
125
|
+
console.log('🍄 Starting Myceliumail dashboard...');
|
|
126
|
+
|
|
127
|
+
dashboardProcess = spawn(mycmailCmd, ['dashboard'], {
|
|
128
|
+
env: { ...process.env },
|
|
129
|
+
shell: true,
|
|
130
|
+
detached: false
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
dashboardProcess.stdout.on('data', (data) => {
|
|
134
|
+
console.log(`Dashboard: ${data}`);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
dashboardProcess.stderr.on('data', (data) => {
|
|
138
|
+
console.error(`Dashboard error: ${data}`);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
dashboardProcess.on('error', (err) => {
|
|
142
|
+
console.error('Failed to start dashboard:', err);
|
|
143
|
+
reject(err);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
waitForDashboard(`http://localhost:${DASHBOARD_PORT}`)
|
|
147
|
+
.then(resolve)
|
|
148
|
+
.catch(reject);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function createWindow() {
|
|
153
|
+
mainWindow = new BrowserWindow({
|
|
154
|
+
width: 1200,
|
|
155
|
+
height: 800,
|
|
156
|
+
minWidth: 800,
|
|
157
|
+
minHeight: 600,
|
|
158
|
+
title: 'Myceliumail',
|
|
159
|
+
icon: path.join(__dirname, 'assets', 'icon.png'),
|
|
160
|
+
webPreferences: {
|
|
161
|
+
nodeIntegration: false,
|
|
162
|
+
contextIsolation: true,
|
|
163
|
+
preload: path.join(__dirname, 'preload.js')
|
|
164
|
+
},
|
|
165
|
+
titleBarStyle: 'hiddenInset',
|
|
166
|
+
backgroundColor: '#030712',
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
mainWindow.loadURL(`http://127.0.0.1:${DASHBOARD_PORT}`);
|
|
170
|
+
mainWindow.webContents.setZoomFactor(0.9);
|
|
171
|
+
|
|
172
|
+
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
173
|
+
shell.openExternal(url);
|
|
174
|
+
return { action: 'deny' };
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
mainWindow.on('closed', () => {
|
|
178
|
+
mainWindow = null;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
mainWindow.on('close', (event) => {
|
|
182
|
+
if (!app.isQuitting) {
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
mainWindow.hide();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function createTray() {
|
|
190
|
+
tray = new Tray(path.join(__dirname, 'assets', 'tray-icon.png'));
|
|
191
|
+
|
|
192
|
+
const contextMenu = Menu.buildFromTemplate([
|
|
193
|
+
{
|
|
194
|
+
label: 'Open Myceliumail',
|
|
195
|
+
click: () => mainWindow.show()
|
|
196
|
+
},
|
|
197
|
+
{ type: 'separator' },
|
|
198
|
+
{
|
|
199
|
+
label: 'Quit',
|
|
200
|
+
click: () => {
|
|
201
|
+
app.isQuitting = true;
|
|
202
|
+
app.quit();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
]);
|
|
206
|
+
|
|
207
|
+
tray.setToolTip('Myceliumail');
|
|
208
|
+
tray.setContextMenu(contextMenu);
|
|
209
|
+
|
|
210
|
+
tray.on('click', () => {
|
|
211
|
+
mainWindow.show();
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
app.whenReady().then(async () => {
|
|
216
|
+
try {
|
|
217
|
+
// Load config and setup notifications
|
|
218
|
+
const config = loadConfig();
|
|
219
|
+
setupRealtimeNotifications(config);
|
|
220
|
+
|
|
221
|
+
await startDashboard();
|
|
222
|
+
createWindow();
|
|
223
|
+
// createTray(); // Uncomment to enable tray icon
|
|
224
|
+
|
|
225
|
+
app.on('activate', () => {
|
|
226
|
+
if (mainWindow === null) {
|
|
227
|
+
createWindow();
|
|
228
|
+
} else {
|
|
229
|
+
mainWindow.show();
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.error('Failed to start:', err);
|
|
234
|
+
app.quit();
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
app.on('window-all-closed', () => {
|
|
239
|
+
if (process.platform !== 'darwin') {
|
|
240
|
+
app.quit();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
app.on('before-quit', () => {
|
|
245
|
+
app.isQuitting = true;
|
|
246
|
+
|
|
247
|
+
// Cleanup realtime subscription
|
|
248
|
+
if (realtimeChannel && supabaseClient) {
|
|
249
|
+
supabaseClient.removeChannel(realtimeChannel);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Kill the dashboard process
|
|
253
|
+
if (dashboardProcess) {
|
|
254
|
+
console.log('Stopping dashboard...');
|
|
255
|
+
dashboardProcess.kill();
|
|
256
|
+
}
|
|
257
|
+
});
|