myceliumail 1.0.1 → 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.
@@ -12,6 +12,7 @@ import { loadConfig } from '../lib/config.js';
12
12
  import { createKeygenCommand } from '../commands/keygen.js';
13
13
  import { createKeysCommand } from '../commands/keys.js';
14
14
  import { createKeyImportCommand } from '../commands/key-import.js';
15
+ import { createKeyAnnounceCommand } from '../commands/key-announce.js';
15
16
  import { createSendCommand } from '../commands/send.js';
16
17
  import { createInboxCommand } from '../commands/inbox.js';
17
18
  import { createReadCommand } from '../commands/read.js';
@@ -33,6 +34,7 @@ Config: ~/.myceliumail/config.json
33
34
  program.addCommand(createKeygenCommand());
34
35
  program.addCommand(createKeysCommand());
35
36
  program.addCommand(createKeyImportCommand());
37
+ program.addCommand(createKeyAnnounceCommand());
36
38
  program.addCommand(createSendCommand());
37
39
  program.addCommand(createInboxCommand());
38
40
  program.addCommand(createReadCommand());
@@ -1 +1 @@
1
- {"version":3,"file":"myceliumail.js","sourceRoot":"","sources":["../../src/bin/myceliumail.ts"],"names":[],"mappings":";AAEA;;;;GAIG;AAEH,4CAA4C;AAC5C,OAAO,eAAe,CAAC;AAEvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,kBAAkB;AAClB,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACF,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,+DAA+D,CAAC;KAC5E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,6BAA6B;AAC7B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;AAC5B,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE;iBACZ,MAAM,CAAC,OAAO;;CAE9B,CAAC,CAAC;AAEH,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;AAC1C,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACxC,OAAO,CAAC,UAAU,CAAC,sBAAsB,EAAE,CAAC,CAAC;AAC7C,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACxC,OAAO,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC;AACzC,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACxC,OAAO,CAAC,UAAU,CAAC,sBAAsB,EAAE,CAAC,CAAC;AAC7C,OAAO,CAAC,UAAU,CAAC,sBAAsB,EAAE,CAAC,CAAC;AAC7C,OAAO,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC;AAEzC,gBAAgB;AAChB,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"myceliumail.js","sourceRoot":"","sources":["../../src/bin/myceliumail.ts"],"names":[],"mappings":";AAEA;;;;GAIG;AAEH,4CAA4C;AAC5C,OAAO,eAAe,CAAC;AAEvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,kBAAkB;AAClB,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACF,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,+DAA+D,CAAC;KAC5E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,6BAA6B;AAC7B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;AAC5B,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE;iBACZ,MAAM,CAAC,OAAO;;CAE9B,CAAC,CAAC;AAEH,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;AAC1C,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACxC,OAAO,CAAC,UAAU,CAAC,sBAAsB,EAAE,CAAC,CAAC;AAC7C,OAAO,CAAC,UAAU,CAAC,wBAAwB,EAAE,CAAC,CAAC;AAC/C,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACxC,OAAO,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC;AACzC,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACxC,OAAO,CAAC,UAAU,CAAC,sBAAsB,EAAE,CAAC,CAAC;AAC7C,OAAO,CAAC,UAAU,CAAC,sBAAsB,EAAE,CAAC,CAAC;AAC7C,OAAO,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC;AAEzC,gBAAgB;AAChB,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * key-announce command - Publish your public key to Supabase
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare function createKeyAnnounceCommand(): Command;
6
+ //# sourceMappingURL=key-announce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-announce.d.ts","sourceRoot":"","sources":["../../src/commands/key-announce.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,wBAAwB,IAAI,OAAO,CA6DlD"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * key-announce command - Publish your public key to Supabase
3
+ */
4
+ import { Command } from 'commander';
5
+ import { loadConfig, hasSupabaseConfig } from '../lib/config.js';
6
+ import { hasKeyPair, loadKeyPair, getPublicKeyBase64 } from '../lib/crypto.js';
7
+ export function createKeyAnnounceCommand() {
8
+ return new Command('key-announce')
9
+ .description('Publish your public key to the network')
10
+ .action(async () => {
11
+ const config = loadConfig();
12
+ const agentId = config.agentId;
13
+ // Check for keypair
14
+ if (!hasKeyPair(agentId)) {
15
+ console.error(`❌ No keypair found for ${agentId}`);
16
+ console.log(' Run: mycmail keygen');
17
+ process.exit(1);
18
+ }
19
+ // Check for Supabase
20
+ if (!hasSupabaseConfig(config)) {
21
+ console.error('❌ Supabase not configured');
22
+ console.log(' Set supabase_url and supabase_key in ~/.myceliumail/config.json');
23
+ process.exit(1);
24
+ }
25
+ const keyPair = loadKeyPair(agentId);
26
+ if (!keyPair) {
27
+ console.error('❌ Failed to load keypair');
28
+ process.exit(1);
29
+ }
30
+ const publicKey = getPublicKeyBase64(keyPair);
31
+ try {
32
+ // Make direct Supabase request
33
+ const url = `${config.supabaseUrl}/rest/v1/agent_keys`;
34
+ const response = await fetch(url, {
35
+ method: 'POST',
36
+ headers: {
37
+ 'Content-Type': 'application/json',
38
+ 'apikey': config.supabaseKey,
39
+ 'Authorization': `Bearer ${config.supabaseKey}`,
40
+ 'Prefer': 'resolution=merge-duplicates',
41
+ },
42
+ body: JSON.stringify({
43
+ agent_id: agentId,
44
+ public_key: publicKey,
45
+ updated_at: new Date().toISOString(),
46
+ }),
47
+ });
48
+ if (!response.ok) {
49
+ const error = await response.text();
50
+ throw new Error(error);
51
+ }
52
+ console.log('📢 Public key announced successfully!\n');
53
+ console.log(`Agent ID: ${agentId}`);
54
+ console.log(`Public Key: ${publicKey}`);
55
+ console.log('\n✅ Other agents can now send you encrypted messages');
56
+ }
57
+ catch (error) {
58
+ console.error('❌ Failed to announce key:', error);
59
+ process.exit(1);
60
+ }
61
+ });
62
+ }
63
+ //# sourceMappingURL=key-announce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-announce.js","sourceRoot":"","sources":["../../src/commands/key-announce.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,MAAM,UAAU,wBAAwB;IACpC,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC;SAC7B,WAAW,CAAC,wCAAwC,CAAC;SACrD,MAAM,CAAC,KAAK,IAAI,EAAE;QACf,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAE/B,oBAAoB;QACpB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;YACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QAED,qBAAqB;QACrB,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC;YAClF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;YAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QAED,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAE9C,IAAI,CAAC;YACD,+BAA+B;YAC/B,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,WAAW,qBAAqB,CAAC;YACvD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC9B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACL,cAAc,EAAE,kBAAkB;oBAClC,QAAQ,EAAE,MAAM,CAAC,WAAY;oBAC7B,eAAe,EAAE,UAAU,MAAM,CAAC,WAAW,EAAE;oBAC/C,QAAQ,EAAE,6BAA6B;iBAC1C;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACjB,QAAQ,EAAE,OAAO;oBACjB,UAAU,EAAE,SAAS;oBACrB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBACvC,CAAC;aACL,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACpC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,EAAE,CAAC,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,eAAe,SAAS,EAAE,CAAC,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;QACxE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC,CAAC,CAAC;AACX,CAAC"}
@@ -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
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "myceliumail-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "myceliumail-mcp",
9
- "version": "1.0.0",
9
+ "version": "1.0.7",
10
10
  "dependencies": {
11
11
  "@modelcontextprotocol/sdk": "^1.0.0",
12
12
  "tweetnacl": "^1.0.3",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myceliumail-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.7",
4
4
  "description": "MCP server for Myceliumail - End-to-End Encrypted Messaging for AI Agents",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",
@@ -46,4 +46,4 @@
46
46
  "engines": {
47
47
  "node": ">=18.0.0"
48
48
  }
49
- }
49
+ }
@@ -1,19 +1,53 @@
1
1
  /**
2
2
  * Myceliumail MCP - Config Module
3
+ *
4
+ * Reads configuration from:
5
+ * 1. Environment variables (highest priority)
6
+ * 2. ~/.myceliumail/config.json (fallback)
3
7
  */
4
8
 
9
+ import { existsSync, readFileSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { homedir } from 'os';
12
+
13
+ const CONFIG_FILE = join(homedir(), '.myceliumail', 'config.json');
14
+
15
+ interface FileConfig {
16
+ agent_id?: string;
17
+ supabase_url?: string;
18
+ supabase_key?: string;
19
+ }
20
+
21
+ let cachedFileConfig: FileConfig | null = null;
22
+
23
+ function loadFileConfig(): FileConfig {
24
+ if (cachedFileConfig) return cachedFileConfig;
25
+
26
+ if (existsSync(CONFIG_FILE)) {
27
+ try {
28
+ const raw = readFileSync(CONFIG_FILE, 'utf-8');
29
+ cachedFileConfig = JSON.parse(raw);
30
+ return cachedFileConfig!;
31
+ } catch {
32
+ // Invalid config file - ignore
33
+ }
34
+ }
35
+ return {};
36
+ }
37
+
5
38
  export function getAgentId(): string {
6
39
  return process.env.MYCELIUMAIL_AGENT_ID ||
7
40
  process.env.MYCELIUMAIL_AGENT ||
41
+ loadFileConfig().agent_id ||
8
42
  'anonymous';
9
43
  }
10
44
 
11
45
  export function getSupabaseUrl(): string | undefined {
12
- return process.env.SUPABASE_URL;
46
+ return process.env.SUPABASE_URL || loadFileConfig().supabase_url;
13
47
  }
14
48
 
15
49
  export function getSupabaseKey(): string | undefined {
16
- return process.env.SUPABASE_ANON_KEY;
50
+ return process.env.SUPABASE_ANON_KEY || loadFileConfig().supabase_key;
17
51
  }
18
52
 
19
53
  export function hasSupabase(): boolean {
@@ -59,6 +59,7 @@ function toMessage(stored: StoredMessage): Message {
59
59
  // Supabase helpers
60
60
  async function supabaseRequest<T>(path: string, options: RequestInit = {}): Promise<T> {
61
61
  const url = `${getSupabaseUrl()}/rest/v1${path}`;
62
+
62
63
  const response = await fetch(url, {
63
64
  ...options,
64
65
  headers: {
@@ -69,7 +70,13 @@ async function supabaseRequest<T>(path: string, options: RequestInit = {}): Prom
69
70
  ...options.headers,
70
71
  },
71
72
  });
72
- if (!response.ok) throw new Error(await response.text());
73
+
74
+ if (!response.ok) {
75
+ const text = await response.text();
76
+ console.error(`Supabase request failed (${response.status}): ${text}`);
77
+ throw new Error(text);
78
+ }
79
+
73
80
  if (response.status === 204) return {} as T;
74
81
  return response.json() as Promise<T>;
75
82
  }
@@ -106,10 +113,10 @@ export async function sendMessage(
106
113
  const [result] = await supabaseRequest<StoredMessage[]>('/agent_messages', {
107
114
  method: 'POST',
108
115
  body: JSON.stringify({
109
- sender: newMessage.sender,
110
- recipient: newMessage.recipient,
116
+ from_agent: newMessage.sender,
117
+ to_agent: newMessage.recipient,
111
118
  subject: newMessage.subject || null,
112
- body: newMessage.body || null,
119
+ message: newMessage.body || null,
113
120
  encrypted: newMessage.encrypted,
114
121
  ciphertext: newMessage.ciphertext,
115
122
  nonce: newMessage.nonce,
@@ -120,7 +127,8 @@ export async function sendMessage(
120
127
  ...newMessage,
121
128
  id: (result as unknown as { id: string }).id
122
129
  });
123
- } catch {
130
+ } catch (err) {
131
+ console.error('sendMessage failed, falling back to local:', err);
124
132
  // Fall through to local
125
133
  }
126
134
  }
@@ -138,32 +146,33 @@ export async function getInbox(
138
146
  ): Promise<Message[]> {
139
147
  if (hasSupabase()) {
140
148
  try {
141
- let query = `/agent_messages?recipient=eq.${agentId}&archived=eq.false&order=created_at.desc`;
149
+ let query = `/agent_messages?to_agent=eq.${agentId}&order=created_at.desc`;
142
150
  if (options?.unreadOnly) query += '&read=eq.false';
143
151
  if (options?.limit) query += `&limit=${options.limit}`;
144
152
 
145
153
  const results = await supabaseRequest<Array<{
146
- id: string; sender: string; recipient: string;
147
- subject: string; body: string; encrypted: boolean;
154
+ id: string; from_agent: string; to_agent: string;
155
+ subject: string; message: string; encrypted: boolean;
148
156
  ciphertext: string; nonce: string; sender_public_key: string;
149
- read: boolean; archived: boolean; created_at: string;
157
+ read: boolean; created_at: string;
150
158
  }>>(query);
151
159
 
152
160
  return results.map(r => ({
153
161
  id: r.id,
154
- sender: r.sender,
155
- recipient: r.recipient,
162
+ sender: r.from_agent,
163
+ recipient: r.to_agent,
156
164
  subject: r.subject || '',
157
- body: r.body || '',
165
+ body: r.message || '',
158
166
  encrypted: r.encrypted,
159
167
  ciphertext: r.ciphertext,
160
168
  nonce: r.nonce,
161
169
  senderPublicKey: r.sender_public_key,
162
170
  read: r.read,
163
- archived: r.archived,
171
+ archived: false,
164
172
  createdAt: new Date(r.created_at),
165
173
  }));
166
- } catch {
174
+ } catch (err) {
175
+ console.error('getInbox failed, falling back to local:', err);
167
176
  // Fall through to local
168
177
  }
169
178
  }
@@ -181,30 +190,31 @@ export async function getMessage(id: string): Promise<Message | null> {
181
190
  if (hasSupabase()) {
182
191
  try {
183
192
  const results = await supabaseRequest<Array<{
184
- id: string; sender: string; recipient: string;
185
- subject: string; body: string; encrypted: boolean;
193
+ id: string; from_agent: string; to_agent: string;
194
+ subject: string; message: string; encrypted: boolean;
186
195
  ciphertext: string; nonce: string; sender_public_key: string;
187
- read: boolean; archived: boolean; created_at: string;
196
+ read: boolean; created_at: string;
188
197
  }>>(`/agent_messages?id=eq.${id}`);
189
198
 
190
199
  if (results.length > 0) {
191
200
  const r = results[0];
192
201
  return {
193
202
  id: r.id,
194
- sender: r.sender,
195
- recipient: r.recipient,
203
+ sender: r.from_agent,
204
+ recipient: r.to_agent,
196
205
  subject: r.subject || '',
197
- body: r.body || '',
206
+ body: r.message || '',
198
207
  encrypted: r.encrypted,
199
208
  ciphertext: r.ciphertext,
200
209
  nonce: r.nonce,
201
210
  senderPublicKey: r.sender_public_key,
202
211
  read: r.read,
203
- archived: r.archived,
212
+ archived: false,
204
213
  createdAt: new Date(r.created_at),
205
214
  };
206
215
  }
207
- } catch {
216
+ } catch (err) {
217
+ console.error('getMessage failed, falling back to local:', err);
208
218
  // Fall through
209
219
  }
210
220
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myceliumail",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "End-to-End Encrypted Messaging for AI Agents",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -16,6 +16,7 @@ import { loadConfig } from '../lib/config.js';
16
16
  import { createKeygenCommand } from '../commands/keygen.js';
17
17
  import { createKeysCommand } from '../commands/keys.js';
18
18
  import { createKeyImportCommand } from '../commands/key-import.js';
19
+ import { createKeyAnnounceCommand } from '../commands/key-announce.js';
19
20
  import { createSendCommand } from '../commands/send.js';
20
21
  import { createInboxCommand } from '../commands/inbox.js';
21
22
  import { createReadCommand } from '../commands/read.js';
@@ -41,6 +42,7 @@ Config: ~/.myceliumail/config.json
41
42
  program.addCommand(createKeygenCommand());
42
43
  program.addCommand(createKeysCommand());
43
44
  program.addCommand(createKeyImportCommand());
45
+ program.addCommand(createKeyAnnounceCommand());
44
46
  program.addCommand(createSendCommand());
45
47
  program.addCommand(createInboxCommand());
46
48
  program.addCommand(createReadCommand());
@@ -0,0 +1,70 @@
1
+ /**
2
+ * key-announce command - Publish your public key to Supabase
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import { loadConfig, hasSupabaseConfig } from '../lib/config.js';
7
+ import { hasKeyPair, loadKeyPair, getPublicKeyBase64 } from '../lib/crypto.js';
8
+
9
+ export function createKeyAnnounceCommand(): Command {
10
+ return new Command('key-announce')
11
+ .description('Publish your public key to the network')
12
+ .action(async () => {
13
+ const config = loadConfig();
14
+ const agentId = config.agentId;
15
+
16
+ // Check for keypair
17
+ if (!hasKeyPair(agentId)) {
18
+ console.error(`❌ No keypair found for ${agentId}`);
19
+ console.log(' Run: mycmail keygen');
20
+ process.exit(1);
21
+ }
22
+
23
+ // Check for Supabase
24
+ if (!hasSupabaseConfig(config)) {
25
+ console.error('❌ Supabase not configured');
26
+ console.log(' Set supabase_url and supabase_key in ~/.myceliumail/config.json');
27
+ process.exit(1);
28
+ }
29
+
30
+ const keyPair = loadKeyPair(agentId);
31
+ if (!keyPair) {
32
+ console.error('❌ Failed to load keypair');
33
+ process.exit(1);
34
+ }
35
+
36
+ const publicKey = getPublicKeyBase64(keyPair);
37
+
38
+ try {
39
+ // Make direct Supabase request
40
+ const url = `${config.supabaseUrl}/rest/v1/agent_keys`;
41
+ const response = await fetch(url, {
42
+ method: 'POST',
43
+ headers: {
44
+ 'Content-Type': 'application/json',
45
+ 'apikey': config.supabaseKey!,
46
+ 'Authorization': `Bearer ${config.supabaseKey}`,
47
+ 'Prefer': 'resolution=merge-duplicates',
48
+ },
49
+ body: JSON.stringify({
50
+ agent_id: agentId,
51
+ public_key: publicKey,
52
+ updated_at: new Date().toISOString(),
53
+ }),
54
+ });
55
+
56
+ if (!response.ok) {
57
+ const error = await response.text();
58
+ throw new Error(error);
59
+ }
60
+
61
+ console.log('📢 Public key announced successfully!\n');
62
+ console.log(`Agent ID: ${agentId}`);
63
+ console.log(`Public Key: ${publicKey}`);
64
+ console.log('\n✅ Other agents can now send you encrypted messages');
65
+ } catch (error) {
66
+ console.error('❌ Failed to announce key:', error);
67
+ process.exit(1);
68
+ }
69
+ });
70
+ }