myceliumail 1.0.9 → 1.0.13

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.
Files changed (96) hide show
  1. package/.mappersan/outbox.json +15 -0
  2. package/.spidersan/registry.json +39 -0
  3. package/CHANGELOG.md +29 -0
  4. package/CLAUDE.md +29 -0
  5. package/README.md +23 -2
  6. package/dist/bin/myceliumail.js +17 -1
  7. package/dist/bin/myceliumail.js.map +1 -1
  8. package/dist/commands/close.d.ts +9 -0
  9. package/dist/commands/close.d.ts.map +1 -0
  10. package/dist/commands/close.js +153 -0
  11. package/dist/commands/close.js.map +1 -0
  12. package/dist/commands/collab.d.ts +8 -0
  13. package/dist/commands/collab.d.ts.map +1 -0
  14. package/dist/commands/collab.js +112 -0
  15. package/dist/commands/collab.js.map +1 -0
  16. package/dist/commands/inbox.d.ts.map +1 -1
  17. package/dist/commands/inbox.js +105 -26
  18. package/dist/commands/inbox.js.map +1 -1
  19. package/dist/commands/tags.d.ts +6 -0
  20. package/dist/commands/tags.d.ts.map +1 -0
  21. package/dist/commands/tags.js +90 -0
  22. package/dist/commands/tags.js.map +1 -0
  23. package/dist/commands/wake.d.ts +9 -0
  24. package/dist/commands/wake.d.ts.map +1 -0
  25. package/dist/commands/wake.js +198 -0
  26. package/dist/commands/wake.js.map +1 -0
  27. package/dist/dashboard/public/app.js +117 -0
  28. package/dist/dashboard/public/index.html +63 -5
  29. package/dist/dashboard/routes.d.ts.map +1 -1
  30. package/dist/dashboard/routes.js +31 -2
  31. package/dist/dashboard/routes.js.map +1 -1
  32. package/dist/lib/update-check.d.ts.map +1 -1
  33. package/dist/lib/update-check.js +6 -4
  34. package/dist/lib/update-check.js.map +1 -1
  35. package/dist/lib/watson-digest.d.ts +40 -0
  36. package/dist/lib/watson-digest.d.ts.map +1 -0
  37. package/dist/lib/watson-digest.js +164 -0
  38. package/dist/lib/watson-digest.js.map +1 -0
  39. package/dist/storage/supabase.d.ts +4 -0
  40. package/dist/storage/supabase.d.ts.map +1 -1
  41. package/dist/storage/supabase.js +57 -0
  42. package/dist/storage/supabase.js.map +1 -1
  43. package/docs/COLLAB_mappersan_mycmail_setup.md +115 -0
  44. package/docs/COLLAB_wake_close_commands.md +518 -0
  45. package/docs/CROSS_TOOL_INTEGRATION_PLAN.md +246 -0
  46. package/docs/JSON_SCHEMA_WAKE_CLOSE.md +246 -0
  47. package/docs/MYCMAIL_QUICKSTART.md +103 -0
  48. package/docs/WAKE_AGENTS_SHARED_DOC.md +1215 -0
  49. package/mcp-server/README.md +75 -69
  50. package/mcp-server/package-lock.json +2 -2
  51. package/mcp-server/package.json +5 -1
  52. package/mcp-server/postinstall.js +14 -0
  53. package/mcp-server/src/server.ts +39 -0
  54. package/mobile-app/README.md +36 -0
  55. package/mobile-app/app/compose/page.tsx +140 -0
  56. package/mobile-app/app/favicon.ico +0 -0
  57. package/mobile-app/app/globals.css +26 -0
  58. package/mobile-app/app/layout.tsx +42 -0
  59. package/mobile-app/app/message/[id]/page.tsx +126 -0
  60. package/mobile-app/app/page.tsx +131 -0
  61. package/mobile-app/components/MessageCard.tsx +60 -0
  62. package/mobile-app/eslint.config.mjs +18 -0
  63. package/mobile-app/lib/supabase.ts +87 -0
  64. package/mobile-app/next.config.ts +7 -0
  65. package/mobile-app/package-lock.json +6674 -0
  66. package/mobile-app/package.json +27 -0
  67. package/mobile-app/postcss.config.mjs +7 -0
  68. package/mobile-app/public/file.svg +1 -0
  69. package/mobile-app/public/globe.svg +1 -0
  70. package/mobile-app/public/next.svg +1 -0
  71. package/mobile-app/public/vercel.svg +1 -0
  72. package/mobile-app/public/window.svg +1 -0
  73. package/mobile-app/tsconfig.json +34 -0
  74. package/package.json +2 -1
  75. package/postinstall.js +14 -0
  76. package/src/bin/myceliumail.ts +19 -1
  77. package/src/commands/close.ts +172 -0
  78. package/src/commands/collab.ts +125 -0
  79. package/src/commands/inbox.ts +120 -29
  80. package/src/commands/tags.ts +102 -0
  81. package/src/commands/wake.ts +228 -0
  82. package/src/dashboard/public/app.js +117 -0
  83. package/src/dashboard/public/index.html +63 -5
  84. package/src/dashboard/routes.ts +31 -2
  85. package/src/lib/update-check.ts +7 -4
  86. package/src/lib/watson-digest.ts +217 -0
  87. package/src/storage/supabase.ts +71 -0
  88. package/vscode-extension/README.md +107 -0
  89. package/vscode-extension/package-lock.json +1941 -0
  90. package/vscode-extension/package.json +117 -0
  91. package/vscode-extension/src/chatParticipant.ts +179 -0
  92. package/vscode-extension/src/extension.ts +262 -0
  93. package/vscode-extension/src/handlers.ts +265 -0
  94. package/vscode-extension/src/realtime.ts +302 -0
  95. package/vscode-extension/src/types.ts +41 -0
  96. package/vscode-extension/tsconfig.json +26 -0
@@ -0,0 +1,126 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+ import { useParams, useRouter } from 'next/navigation';
5
+ import { supabase, Message, markAsRead } from '@/lib/supabase';
6
+ import Link from 'next/link';
7
+
8
+ export default function MessageDetailPage() {
9
+ const params = useParams();
10
+ const router = useRouter();
11
+ const messageId = params.id as string;
12
+
13
+ const [message, setMessage] = useState<Message | null>(null);
14
+ const [loading, setLoading] = useState(true);
15
+ const [error, setError] = useState<string | null>(null);
16
+
17
+ useEffect(() => {
18
+ if (!messageId) return;
19
+
20
+ const loadMessage = async () => {
21
+ const { data, error: fetchError } = await supabase
22
+ .from('agent_messages')
23
+ .select('*')
24
+ .eq('id', messageId)
25
+ .single();
26
+
27
+ if (fetchError) {
28
+ setError('Failed to load message');
29
+ setLoading(false);
30
+ return;
31
+ }
32
+
33
+ setMessage(data);
34
+ setLoading(false);
35
+
36
+ // Mark as read
37
+ if (!data.read) {
38
+ markAsRead(messageId);
39
+ }
40
+ };
41
+
42
+ loadMessage();
43
+ }, [messageId]);
44
+
45
+ const handleReply = () => {
46
+ if (!message) return;
47
+ router.push(`/compose?to=${message.from_agent}&subject=Re: ${encodeURIComponent(message.subject || '')}`);
48
+ };
49
+
50
+ if (loading) {
51
+ return (
52
+ <div className="min-h-screen bg-gray-950 text-white flex items-center justify-center">
53
+ <div className="text-center">
54
+ <div className="text-4xl mb-4 animate-pulse">🍄</div>
55
+ <p className="text-gray-500">Loading message...</p>
56
+ </div>
57
+ </div>
58
+ );
59
+ }
60
+
61
+ if (error || !message) {
62
+ return (
63
+ <div className="min-h-screen bg-gray-950 text-white flex items-center justify-center">
64
+ <div className="text-center">
65
+ <div className="text-4xl mb-4">❌</div>
66
+ <p className="text-red-400">{error || 'Message not found'}</p>
67
+ <Link href="/" className="mt-4 inline-block px-4 py-2 bg-gray-800 rounded-lg">
68
+ Back to Inbox
69
+ </Link>
70
+ </div>
71
+ </div>
72
+ );
73
+ }
74
+
75
+ return (
76
+ <div className="min-h-screen bg-gray-950 text-white">
77
+ {/* Header */}
78
+ <header className="sticky top-0 z-10 bg-gray-950/90 backdrop-blur-sm border-b border-gray-800 px-4 py-4">
79
+ <div className="flex items-center justify-between">
80
+ <div className="flex items-center gap-3">
81
+ <Link href="/" className="text-2xl">←</Link>
82
+ <span className="text-xl">{message.encrypted ? '🔐' : '📨'}</span>
83
+ </div>
84
+ <button
85
+ onClick={handleReply}
86
+ className="px-4 py-2 bg-purple-600 text-white text-sm rounded-lg font-medium hover:bg-purple-500 transition-colors"
87
+ >
88
+ ↩️ Reply
89
+ </button>
90
+ </div>
91
+ </header>
92
+
93
+ {/* Message Content */}
94
+ <main className="px-4 py-6">
95
+ {/* Meta info */}
96
+ <div className="mb-6 p-4 bg-gray-900 rounded-xl border border-gray-800">
97
+ <div className="grid grid-cols-2 gap-4 text-sm">
98
+ <div>
99
+ <span className="text-gray-400">From</span>
100
+ <p className="text-blue-400 font-mono text-lg">{message.from_agent}</p>
101
+ </div>
102
+ <div>
103
+ <span className="text-gray-400">To</span>
104
+ <p className="text-green-400 font-mono text-lg">{message.to_agent}</p>
105
+ </div>
106
+ </div>
107
+ <div className="mt-3 text-gray-500 text-sm">
108
+ {new Date(message.created_at).toLocaleString()}
109
+ </div>
110
+ </div>
111
+
112
+ {/* Subject */}
113
+ <h1 className="text-2xl font-bold mb-6">
114
+ {message.subject || '(no subject)'}
115
+ </h1>
116
+
117
+ {/* Body */}
118
+ <div className="p-4 bg-gray-900 rounded-xl border border-gray-800">
119
+ <p className="whitespace-pre-wrap text-gray-300 leading-relaxed">
120
+ {message.message}
121
+ </p>
122
+ </div>
123
+ </main>
124
+ </div>
125
+ );
126
+ }
@@ -0,0 +1,131 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useCallback } from 'react';
4
+ import { supabase, getInbox, getAgentKeys, Message } from '@/lib/supabase';
5
+ import MessageCard from '@/components/MessageCard';
6
+ import Link from 'next/link';
7
+
8
+ // Default agents if we can't fetch from agent_keys
9
+ const DEFAULT_AGENTS = ['treebird', 'wsan', 'ssan', 'mycm', 'antigravity'];
10
+
11
+ export default function InboxPage() {
12
+ const [messages, setMessages] = useState<Message[]>([]);
13
+ const [loading, setLoading] = useState(true);
14
+ const [agents, setAgents] = useState<string[]>(DEFAULT_AGENTS);
15
+ const [error, setError] = useState<string | null>(null);
16
+
17
+ const loadInbox = useCallback(async () => {
18
+ try {
19
+ // Try to get agents from agent_keys table
20
+ const agentKeys = await getAgentKeys();
21
+ const agentIds = agentKeys.length > 0 ? agentKeys : DEFAULT_AGENTS;
22
+ setAgents(agentIds);
23
+
24
+ const inbox = await getInbox(agentIds, 50);
25
+ setMessages(inbox);
26
+ setError(null);
27
+ } catch (err) {
28
+ console.error('Failed to load inbox:', err);
29
+ setError('Failed to load messages');
30
+ } finally {
31
+ setLoading(false);
32
+ }
33
+ }, []);
34
+
35
+ useEffect(() => {
36
+ loadInbox();
37
+
38
+ // Set up real-time subscription
39
+ const channel = supabase
40
+ .channel('mobile-inbox')
41
+ .on(
42
+ 'postgres_changes',
43
+ {
44
+ event: 'INSERT',
45
+ schema: 'public',
46
+ table: 'agent_messages',
47
+ },
48
+ (payload) => {
49
+ const newMsg = payload.new as Message;
50
+ // Check if this message is for one of our agents
51
+ if (agents.includes(newMsg.to_agent)) {
52
+ setMessages(prev => [newMsg, ...prev]);
53
+ }
54
+ }
55
+ )
56
+ .subscribe();
57
+
58
+ return () => {
59
+ supabase.removeChannel(channel);
60
+ };
61
+ }, [loadInbox, agents]);
62
+
63
+ const unreadCount = messages.filter(m => !m.read).length;
64
+
65
+ return (
66
+ <div className="min-h-screen bg-gray-950 text-white">
67
+ {/* Header */}
68
+ <header className="sticky top-0 z-10 bg-gray-950/90 backdrop-blur-sm border-b border-gray-800 px-4 py-4">
69
+ <div className="flex items-center justify-between">
70
+ <div className="flex items-center gap-3">
71
+ <span className="text-3xl">🍄</span>
72
+ <h1 className="text-xl font-bold bg-gradient-to-r from-purple-400 to-pink-500 bg-clip-text text-transparent">
73
+ Myceliumail
74
+ </h1>
75
+ </div>
76
+ <div className="flex items-center gap-2">
77
+ <button
78
+ onClick={() => { setLoading(true); loadInbox(); }}
79
+ className="p-2 rounded-lg hover:bg-gray-800 transition-colors"
80
+ >
81
+ 🔄
82
+ </button>
83
+ <Link
84
+ href="/compose"
85
+ className="px-4 py-2 bg-purple-600 text-white text-sm rounded-lg font-medium hover:bg-purple-500 transition-colors"
86
+ >
87
+ ✉️ Compose
88
+ </Link>
89
+ </div>
90
+ </div>
91
+ <div className="mt-2 text-sm text-gray-400">
92
+ {unreadCount > 0 ? (
93
+ <span className="text-blue-400">{unreadCount} unread</span>
94
+ ) : (
95
+ <span>No unread messages</span>
96
+ )} · {messages.length} total
97
+ </div>
98
+ </header>
99
+
100
+ {/* Message List */}
101
+ <main className="px-4 py-4 space-y-3">
102
+ {loading ? (
103
+ <div className="text-center py-12 text-gray-500">
104
+ <div className="text-4xl mb-4 animate-pulse">🍄</div>
105
+ <p>Loading messages...</p>
106
+ </div>
107
+ ) : error ? (
108
+ <div className="text-center py-12 text-red-400">
109
+ <div className="text-4xl mb-4">❌</div>
110
+ <p>{error}</p>
111
+ <button
112
+ onClick={loadInbox}
113
+ className="mt-4 px-4 py-2 bg-gray-800 rounded-lg"
114
+ >
115
+ Retry
116
+ </button>
117
+ </div>
118
+ ) : messages.length === 0 ? (
119
+ <div className="text-center py-12 text-gray-500">
120
+ <div className="text-4xl mb-4">📭</div>
121
+ <p>No messages yet</p>
122
+ </div>
123
+ ) : (
124
+ messages.map(message => (
125
+ <MessageCard key={message.id} message={message} />
126
+ ))
127
+ )}
128
+ </main>
129
+ </div>
130
+ );
131
+ }
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+
3
+ import { Message } from '@/lib/supabase';
4
+ import Link from 'next/link';
5
+
6
+ interface MessageCardProps {
7
+ message: Message;
8
+ }
9
+
10
+ export default function MessageCard({ message }: MessageCardProps) {
11
+ const isUnread = !message.read;
12
+ const timeAgo = formatTimeAgo(new Date(message.created_at));
13
+
14
+ return (
15
+ <Link href={`/message/${message.id}`}>
16
+ <div className={`
17
+ p-4 rounded-xl border transition-all active:scale-[0.98]
18
+ ${isUnread
19
+ ? 'bg-gray-800/50 border-gray-700'
20
+ : 'bg-gray-900 border-gray-800'
21
+ }
22
+ `}>
23
+ <div className="flex items-center justify-between mb-2">
24
+ <div className="flex items-center gap-2">
25
+ <span className="text-lg">
26
+ {message.encrypted ? '🔐' : '📨'}
27
+ </span>
28
+ <span className={`font-medium ${isUnread ? 'text-white' : 'text-gray-300'}`}>
29
+ {message.from_agent}
30
+ </span>
31
+ <span className="text-gray-500 text-sm">→ {message.to_agent}</span>
32
+ </div>
33
+ {isUnread && (
34
+ <span className="w-2 h-2 rounded-full bg-blue-500" />
35
+ )}
36
+ </div>
37
+ <div className={`text-base truncate mb-1 ${isUnread ? 'text-gray-100 font-medium' : 'text-gray-400'}`}>
38
+ {message.subject || '(no subject)'}
39
+ </div>
40
+ <div className="text-sm text-gray-500">
41
+ {timeAgo}
42
+ </div>
43
+ </div>
44
+ </Link>
45
+ );
46
+ }
47
+
48
+ function formatTimeAgo(date: Date): string {
49
+ const now = new Date();
50
+ const diffMs = now.getTime() - date.getTime();
51
+ const diffMins = Math.floor(diffMs / 60000);
52
+ const diffHours = Math.floor(diffMs / 3600000);
53
+ const diffDays = Math.floor(diffMs / 86400000);
54
+
55
+ if (diffMins < 1) return 'just now';
56
+ if (diffMins < 60) return `${diffMins}m ago`;
57
+ if (diffHours < 24) return `${diffHours}h ago`;
58
+ if (diffDays < 7) return `${diffDays}d ago`;
59
+ return date.toLocaleDateString();
60
+ }
@@ -0,0 +1,18 @@
1
+ import { defineConfig, globalIgnores } from "eslint/config";
2
+ import nextVitals from "eslint-config-next/core-web-vitals";
3
+ import nextTs from "eslint-config-next/typescript";
4
+
5
+ const eslintConfig = defineConfig([
6
+ ...nextVitals,
7
+ ...nextTs,
8
+ // Override default ignores of eslint-config-next.
9
+ globalIgnores([
10
+ // Default ignores of eslint-config-next:
11
+ ".next/**",
12
+ "out/**",
13
+ "build/**",
14
+ "next-env.d.ts",
15
+ ]),
16
+ ]);
17
+
18
+ export default eslintConfig;
@@ -0,0 +1,87 @@
1
+ import { createClient } from '@supabase/supabase-js';
2
+
3
+ // Supabase configuration - using the same credentials as mycmail
4
+ const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://ruvwundetxnzesrbkdzr.supabase.co';
5
+ const SUPABASE_ANON_KEY = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || '';
6
+
7
+ export const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
8
+ realtime: {
9
+ params: { eventsPerSecond: 10 }
10
+ }
11
+ });
12
+
13
+ export interface Message {
14
+ id: string;
15
+ from_agent: string;
16
+ to_agent: string;
17
+ subject: string;
18
+ message: string;
19
+ encrypted: boolean;
20
+ read: boolean;
21
+ created_at: string;
22
+ }
23
+
24
+ export async function getInbox(agentIds: string[], limit = 50): Promise<Message[]> {
25
+ const { data, error } = await supabase
26
+ .from('agent_messages')
27
+ .select('*')
28
+ .in('to_agent', agentIds)
29
+ .order('created_at', { ascending: false })
30
+ .limit(limit);
31
+
32
+ if (error) {
33
+ console.error('Failed to fetch inbox:', error);
34
+ return [];
35
+ }
36
+
37
+ return data || [];
38
+ }
39
+
40
+ export async function sendMessage(
41
+ from: string,
42
+ to: string,
43
+ subject: string,
44
+ body: string
45
+ ): Promise<Message | null> {
46
+ const { data, error } = await supabase
47
+ .from('agent_messages')
48
+ .insert({
49
+ from_agent: from,
50
+ to_agent: to,
51
+ subject,
52
+ message: body,
53
+ encrypted: false,
54
+ read: false,
55
+ })
56
+ .select()
57
+ .single();
58
+
59
+ if (error) {
60
+ console.error('Failed to send message:', error);
61
+ return null;
62
+ }
63
+
64
+ return data;
65
+ }
66
+
67
+ export async function markAsRead(messageId: string): Promise<boolean> {
68
+ const { error } = await supabase
69
+ .from('agent_messages')
70
+ .update({ read: true })
71
+ .eq('id', messageId);
72
+
73
+ return !error;
74
+ }
75
+
76
+ export async function getAgentKeys(): Promise<string[]> {
77
+ const { data, error } = await supabase
78
+ .from('agent_keys')
79
+ .select('agent_id');
80
+
81
+ if (error || !data) {
82
+ console.error('Failed to fetch agent keys:', error);
83
+ return [];
84
+ }
85
+
86
+ return data.map(row => row.agent_id);
87
+ }
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ /* config options here */
5
+ };
6
+
7
+ export default nextConfig;