@rubytech/create-maxy 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +428 -0
- package/package.json +31 -0
- package/payload/maxy/.env.example +12 -0
- package/payload/maxy/app/admin/components/ActivityTimeline.tsx +348 -0
- package/payload/maxy/app/admin/components/MarkdownMessage.tsx +40 -0
- package/payload/maxy/app/api/admin/chat/route.ts +72 -0
- package/payload/maxy/app/api/admin/logs/route.ts +40 -0
- package/payload/maxy/app/api/admin/session/route.ts +74 -0
- package/payload/maxy/app/api/chat/route.ts +72 -0
- package/payload/maxy/app/api/health/route.ts +26 -0
- package/payload/maxy/app/api/onboarding/claude-auth/route.ts +216 -0
- package/payload/maxy/app/api/onboarding/set-pin/route.ts +44 -0
- package/payload/maxy/app/api/session/route.ts +51 -0
- package/payload/maxy/app/api/telegram/webhook/route.ts +107 -0
- package/payload/maxy/app/apple-icon.png +0 -0
- package/payload/maxy/app/bot/page.tsx +373 -0
- package/payload/maxy/app/favicon.ico +0 -0
- package/payload/maxy/app/globals.css +1681 -0
- package/payload/maxy/app/layout.tsx +58 -0
- package/payload/maxy/app/lib/claude-agent.ts +503 -0
- package/payload/maxy/app/og/layout.tsx +15 -0
- package/payload/maxy/app/og/page.tsx +252 -0
- package/payload/maxy/app/page.tsx +594 -0
- package/payload/maxy/app/privacy/page.tsx +72 -0
- package/payload/maxy/app/public/page.tsx +266 -0
- package/payload/maxy/next.config.mjs +26 -0
- package/payload/maxy/package-lock.json +2198 -0
- package/payload/maxy/package.json +25 -0
- package/payload/maxy/proxy.ts +41 -0
- package/payload/maxy/public/brand/claude.png +0 -0
- package/payload/maxy/public/brand/maxy-black.png +0 -0
- package/payload/maxy/public/brand/maxy.png +0 -0
- package/payload/maxy/public/favicon.ico +0 -0
- package/payload/maxy/public/og-landscape.png +0 -0
- package/payload/maxy/public/og-portrait.png +0 -0
- package/payload/maxy/public/og-square.png +0 -0
- package/payload/maxy/public/pi-5.jpg +0 -0
- package/payload/maxy/public/robots.txt +5 -0
- package/payload/maxy/tsconfig.json +41 -0
- package/payload/maxy/tsconfig.tsbuildinfo +1 -0
- package/payload/maxy/ui.md +28 -0
- package/payload/platform/config/cloudflared.yml +17 -0
- package/payload/platform/knowledge/maxy.md +161 -0
- package/payload/platform/neo4j/schema.cypher +108 -0
- package/payload/platform/package-lock.json +1835 -0
- package/payload/platform/package.json +17 -0
- package/payload/platform/plugins/admin/PLUGIN.md +24 -0
- package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +56 -0
- package/payload/platform/plugins/admin/hooks/session-start.sh +20 -0
- package/payload/platform/plugins/admin/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +149 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/package.json +18 -0
- package/payload/platform/plugins/anthropic/PLUGIN.md +30 -0
- package/payload/platform/plugins/anthropic/references/setup-guide.md +146 -0
- package/payload/platform/plugins/business-assistant/PLUGIN.md +46 -0
- package/payload/platform/plugins/business-assistant/references/crm.md +112 -0
- package/payload/platform/plugins/business-assistant/references/document-management.md +96 -0
- package/payload/platform/plugins/business-assistant/references/escalation.md +126 -0
- package/payload/platform/plugins/business-assistant/references/invoicing.md +163 -0
- package/payload/platform/plugins/business-assistant/references/quoting.md +56 -0
- package/payload/platform/plugins/business-assistant/references/scheduling.md +127 -0
- package/payload/platform/plugins/cloudflare/PLUGIN.md +31 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js +174 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +45 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +256 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -0
- package/payload/platform/plugins/cloudflare/mcp/package.json +18 -0
- package/payload/platform/plugins/cloudflare/references/setup-guide.md +110 -0
- package/payload/platform/plugins/contacts/PLUGIN.md +18 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.js +182 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.js +34 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +19 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +68 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.d.ts +22 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.js +46 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +20 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +56 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.d.ts +13 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.js +54 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/package.json +19 -0
- package/payload/platform/plugins/documents/PLUGIN.md +12 -0
- package/payload/platform/plugins/documents/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/documents/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/documents/mcp/dist/index.js +82 -0
- package/payload/platform/plugins/documents/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/documents/mcp/package.json +20 -0
- package/payload/platform/plugins/memory/PLUGIN.md +17 -0
- package/payload/platform/plugins/memory/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js +164 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js +29 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js +34 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts +8 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js +71 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts +24 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +125 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +18 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +56 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/package.json +19 -0
- package/payload/platform/plugins/sales/PLUGIN.md +65 -0
- package/payload/platform/plugins/sales/references/close-tracking.md +76 -0
- package/payload/platform/plugins/sales/references/closing-framework.md +108 -0
- package/payload/platform/plugins/sales/references/comparisons.md +99 -0
- package/payload/platform/plugins/sales/references/competitive-positioning.md +51 -0
- package/payload/platform/plugins/sales/references/faq.md +62 -0
- package/payload/platform/plugins/sales/references/objection-handling.md +157 -0
- package/payload/platform/plugins/sales/references/pricing.md +71 -0
- package/payload/platform/plugins/sales/references/waitlist.md +23 -0
- package/payload/platform/plugins/scheduling/PLUGIN.md +12 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +13 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/package.json +18 -0
- package/payload/platform/plugins/telegram/PLUGIN.md +31 -0
- package/payload/platform/plugins/telegram/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/index.js +101 -0
- package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts +27 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js +41 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.d.ts +16 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js +62 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts +20 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.js +34 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/package.json +19 -0
- package/payload/platform/plugins/telegram/references/setup-guide.md +50 -0
- package/payload/platform/plugins/web/PLUGIN.md +12 -0
- package/payload/platform/plugins/web/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/web/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/web/mcp/dist/index.js +12 -0
- package/payload/platform/plugins/web/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/web/mcp/package.json +18 -0
- package/payload/platform/scripts/seed-neo4j.sh +73 -0
- package/payload/platform/scripts/setup.sh +177 -0
- package/payload/platform/scripts/start.sh +62 -0
- package/payload/platform/templates/account.json +4 -0
- package/payload/platform/templates/agents/admin/IDENTITY.md +28 -0
- package/payload/platform/templates/agents/admin/SOUL.md +1 -0
- package/payload/platform/templates/agents/public/IDENTITY.md +21 -0
- package/payload/platform/templates/agents/public/SOUL.md +1 -0
- package/payload/platform/tsconfig.base.json +18 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Metadata } from 'next'
|
|
2
|
+
import { Cormorant, DM_Sans } from 'next/font/google'
|
|
3
|
+
import './globals.css'
|
|
4
|
+
|
|
5
|
+
const cormorant = Cormorant({
|
|
6
|
+
subsets: ['latin'],
|
|
7
|
+
weight: ['300', '400', '500'],
|
|
8
|
+
variable: '--font-display',
|
|
9
|
+
display: 'swap',
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const dmSans = DM_Sans({
|
|
13
|
+
subsets: ['latin'],
|
|
14
|
+
weight: ['400', '500'],
|
|
15
|
+
variable: '--font-body',
|
|
16
|
+
display: 'swap',
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export const metadata: Metadata = {
|
|
20
|
+
title: 'Maxy — AI for Productive People.',
|
|
21
|
+
description: 'A personal AI assistant that lives in your home. Your data never leaves.',
|
|
22
|
+
icons: {
|
|
23
|
+
icon: '/favicon.ico',
|
|
24
|
+
apple: '/apple-icon.png',
|
|
25
|
+
},
|
|
26
|
+
openGraph: {
|
|
27
|
+
title: 'Maxy — AI for Productive People.',
|
|
28
|
+
description: 'A personal AI assistant that lives in your home. Your data never leaves.',
|
|
29
|
+
siteName: 'Maxy',
|
|
30
|
+
type: 'website',
|
|
31
|
+
images: [
|
|
32
|
+
{
|
|
33
|
+
url: '/og-landscape.png',
|
|
34
|
+
width: 1200,
|
|
35
|
+
height: 628,
|
|
36
|
+
alt: 'Maxy — AI for Productive People.',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
twitter: {
|
|
41
|
+
card: 'summary_large_image',
|
|
42
|
+
title: 'Maxy — AI for Productive People.',
|
|
43
|
+
description: 'A personal AI assistant that lives in your home. Your data never leaves.',
|
|
44
|
+
images: ['/og-landscape.png'],
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default function RootLayout({
|
|
49
|
+
children,
|
|
50
|
+
}: {
|
|
51
|
+
children: React.ReactNode
|
|
52
|
+
}) {
|
|
53
|
+
return (
|
|
54
|
+
<html lang="en" className={`${cormorant.variable} ${dmSans.variable}`}>
|
|
55
|
+
<body>{children}</body>
|
|
56
|
+
</html>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { readFileSync, readdirSync, existsSync, mkdirSync, createWriteStream } from "node:fs";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
|
|
7
|
+
const LOG_DIR = resolve(homedir(), ".maxy/logs");
|
|
8
|
+
function agentLogStream(name: string) {
|
|
9
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
10
|
+
return createWriteStream(resolve(LOG_DIR, `${name}.log`), { flags: "a" });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Load ANTHROPIC_API_KEY from persistent storage if not already in env.
|
|
14
|
+
// Written by the admin agent during onboarding.
|
|
15
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
16
|
+
const keyFile = resolve(homedir(), ".maxy/.anthropic-api-key");
|
|
17
|
+
if (existsSync(keyFile)) {
|
|
18
|
+
try {
|
|
19
|
+
process.env.ANTHROPIC_API_KEY = readFileSync(keyFile, "utf-8").trim();
|
|
20
|
+
} catch { /* ignore */ }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PLATFORM_ROOT = resolve(process.cwd(), "../platform");
|
|
25
|
+
const ACCOUNTS_DIR = resolve(PLATFORM_ROOT, "config/accounts");
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Account resolution
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
interface AccountConfig {
|
|
32
|
+
accountId: string;
|
|
33
|
+
tier: string;
|
|
34
|
+
name: string;
|
|
35
|
+
description: string;
|
|
36
|
+
domains: { public: string; admin: string };
|
|
37
|
+
brand: Record<string, string>;
|
|
38
|
+
telegram: { publicBotToken: string };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Find the first account on this device.
|
|
43
|
+
* Phase 0: single account. Multi-account resolution is a Phase 1 concern.
|
|
44
|
+
*/
|
|
45
|
+
function resolveAccount(): { accountId: string; accountDir: string; config: AccountConfig } | null {
|
|
46
|
+
if (!existsSync(ACCOUNTS_DIR)) return null;
|
|
47
|
+
|
|
48
|
+
const entries = readdirSync(ACCOUNTS_DIR, { withFileTypes: true });
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
if (!entry.isDirectory()) continue;
|
|
51
|
+
const configPath = resolve(ACCOUNTS_DIR, entry.name, "account.json");
|
|
52
|
+
if (!existsSync(configPath)) continue;
|
|
53
|
+
|
|
54
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
55
|
+
const config = JSON.parse(raw) as AccountConfig;
|
|
56
|
+
return {
|
|
57
|
+
accountId: config.accountId,
|
|
58
|
+
accountDir: resolve(ACCOUNTS_DIR, entry.name),
|
|
59
|
+
config,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Read the IDENTITY.md for a specific agent within an account.
|
|
68
|
+
*/
|
|
69
|
+
function readIdentity(accountDir: string, agentName: string): string | null {
|
|
70
|
+
const identityPath = resolve(accountDir, "agents", agentName, "IDENTITY.md");
|
|
71
|
+
if (!existsSync(identityPath)) return null;
|
|
72
|
+
return readFileSync(identityPath, "utf-8");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Session store
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
const sessionStore = new Map<
|
|
80
|
+
string,
|
|
81
|
+
{ agentSessionId?: string; createdAt: number; agentType: "public" | "admin"; accountId: string }
|
|
82
|
+
>();
|
|
83
|
+
|
|
84
|
+
export function registerSession(
|
|
85
|
+
sessionKey: string,
|
|
86
|
+
agentType: "public" | "admin",
|
|
87
|
+
accountId: string
|
|
88
|
+
): void {
|
|
89
|
+
sessionStore.set(sessionKey, { createdAt: Date.now(), agentType, accountId });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function validateSession(
|
|
93
|
+
sessionKey: string,
|
|
94
|
+
agentType: "public" | "admin"
|
|
95
|
+
): boolean {
|
|
96
|
+
const session = sessionStore.get(sessionKey);
|
|
97
|
+
if (!session) return false;
|
|
98
|
+
if (session.agentType !== agentType) return false;
|
|
99
|
+
if (Date.now() - session.createdAt > 24 * 60 * 60 * 1000) {
|
|
100
|
+
sessionStore.delete(sessionKey);
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function storeAgentSessionId(sessionKey: string, agentSessionId: string): void {
|
|
107
|
+
const session = sessionStore.get(sessionKey);
|
|
108
|
+
if (session) {
|
|
109
|
+
session.agentSessionId = agentSessionId;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getAgentSessionId(sessionKey: string): string | undefined {
|
|
114
|
+
return sessionStore.get(sessionKey)?.agentSessionId;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getAccountIdForSession(sessionKey: string): string | undefined {
|
|
118
|
+
return sessionStore.get(sessionKey)?.accountId;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// MCP server configuration (admin agent only)
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
function getMcpServers(accountId: string) {
|
|
126
|
+
return {
|
|
127
|
+
"memory": {
|
|
128
|
+
command: "node",
|
|
129
|
+
args: [resolve(PLATFORM_ROOT, "plugins/memory/mcp/dist/index.js")],
|
|
130
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT },
|
|
131
|
+
},
|
|
132
|
+
"contacts": {
|
|
133
|
+
command: "node",
|
|
134
|
+
args: [resolve(PLATFORM_ROOT, "plugins/contacts/mcp/dist/index.js")],
|
|
135
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT },
|
|
136
|
+
},
|
|
137
|
+
"telegram": {
|
|
138
|
+
command: "node",
|
|
139
|
+
args: [resolve(PLATFORM_ROOT, "plugins/telegram/mcp/dist/index.js")],
|
|
140
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT },
|
|
141
|
+
},
|
|
142
|
+
"admin": {
|
|
143
|
+
command: "node",
|
|
144
|
+
args: [resolve(PLATFORM_ROOT, "plugins/admin/mcp/dist/index.js")],
|
|
145
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT },
|
|
146
|
+
},
|
|
147
|
+
"cloudflare": {
|
|
148
|
+
command: "node",
|
|
149
|
+
args: [resolve(PLATFORM_ROOT, "plugins/cloudflare/mcp/dist/index.js")],
|
|
150
|
+
env: { PLATFORM_ROOT },
|
|
151
|
+
},
|
|
152
|
+
"scheduling": {
|
|
153
|
+
command: "node",
|
|
154
|
+
args: [resolve(PLATFORM_ROOT, "plugins/scheduling/mcp/dist/index.js")],
|
|
155
|
+
env: { PLATFORM_ROOT },
|
|
156
|
+
},
|
|
157
|
+
"documents": {
|
|
158
|
+
command: "node",
|
|
159
|
+
args: [resolve(PLATFORM_ROOT, "plugins/documents/mcp/dist/index.js")],
|
|
160
|
+
env: { PLATFORM_ROOT },
|
|
161
|
+
},
|
|
162
|
+
"web": {
|
|
163
|
+
command: "node",
|
|
164
|
+
args: [resolve(PLATFORM_ROOT, "plugins/web/mcp/dist/index.js")],
|
|
165
|
+
env: { PLATFORM_ROOT },
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const ADMIN_ALLOWED_TOOLS = [
|
|
171
|
+
"Read", "Write", "Edit", "Bash", "Glob", "Grep", "Agent",
|
|
172
|
+
"mcp__memory__memory-search",
|
|
173
|
+
"mcp__memory__memory-write",
|
|
174
|
+
"mcp__memory__memory-reindex",
|
|
175
|
+
"mcp__contacts__contact-create",
|
|
176
|
+
"mcp__contacts__contact-lookup",
|
|
177
|
+
"mcp__contacts__contact-update",
|
|
178
|
+
"mcp__contacts__contact-list",
|
|
179
|
+
"mcp__telegram__message",
|
|
180
|
+
"mcp__telegram__message-history",
|
|
181
|
+
"mcp__admin__system-status",
|
|
182
|
+
"mcp__admin__brand-settings",
|
|
183
|
+
"mcp__admin__account-manage",
|
|
184
|
+
"mcp__admin__logs-read",
|
|
185
|
+
"mcp__admin__plugin-read",
|
|
186
|
+
"mcp__cloudflare__tunnel-status",
|
|
187
|
+
"mcp__cloudflare__tunnel-install",
|
|
188
|
+
"mcp__cloudflare__tunnel-login",
|
|
189
|
+
"mcp__cloudflare__tunnel-create",
|
|
190
|
+
"mcp__cloudflare__tunnel-enable",
|
|
191
|
+
"mcp__cloudflare__tunnel-disable",
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Memory pre-fetch for public agent
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Call the memory MCP server via JSON-RPC to search for relevant context.
|
|
200
|
+
* Returns a formatted string of results, or null if the server is unavailable.
|
|
201
|
+
*/
|
|
202
|
+
async function fetchMemoryContext(accountId: string, query: string): Promise<string | null> {
|
|
203
|
+
const serverPath = resolve(PLATFORM_ROOT, "plugins/memory/mcp/dist/index.js");
|
|
204
|
+
if (!existsSync(serverPath)) return null;
|
|
205
|
+
|
|
206
|
+
return new Promise((resolve) => {
|
|
207
|
+
const proc = spawn(process.execPath, [serverPath], {
|
|
208
|
+
env: { ...process.env, ACCOUNT_ID: accountId, READ_ONLY: "true" },
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
let buffer = "";
|
|
212
|
+
let settled = false;
|
|
213
|
+
const TOOL_CALL_ID = 2;
|
|
214
|
+
|
|
215
|
+
const settle = (value: string | null) => {
|
|
216
|
+
if (settled) return;
|
|
217
|
+
settled = true;
|
|
218
|
+
proc.kill();
|
|
219
|
+
resolve(value);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
proc.stdout.on("data", (chunk: Buffer) => {
|
|
223
|
+
buffer += chunk.toString();
|
|
224
|
+
const lines = buffer.split("\n");
|
|
225
|
+
buffer = lines.pop() ?? "";
|
|
226
|
+
|
|
227
|
+
for (const line of lines) {
|
|
228
|
+
if (!line.trim()) continue;
|
|
229
|
+
let msg: Record<string, unknown>;
|
|
230
|
+
try { msg = JSON.parse(line); } catch { continue; }
|
|
231
|
+
|
|
232
|
+
if (msg.id === 1) {
|
|
233
|
+
// Server initialised — send initialized notification then tool call
|
|
234
|
+
proc.stdin.write(
|
|
235
|
+
JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) + "\n"
|
|
236
|
+
);
|
|
237
|
+
proc.stdin.write(
|
|
238
|
+
JSON.stringify({
|
|
239
|
+
jsonrpc: "2.0",
|
|
240
|
+
id: TOOL_CALL_ID,
|
|
241
|
+
method: "tools/call",
|
|
242
|
+
params: { name: "memory-search", arguments: { query, account_id: accountId } },
|
|
243
|
+
}) + "\n"
|
|
244
|
+
);
|
|
245
|
+
} else if (msg.id === TOOL_CALL_ID) {
|
|
246
|
+
const result = msg.result as Record<string, unknown> | undefined;
|
|
247
|
+
const content = result?.content as Array<{ type: string; text?: string }> | undefined;
|
|
248
|
+
const text = content?.find((c) => c.type === "text")?.text ?? null;
|
|
249
|
+
settle(text);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
proc.on("error", () => settle(null));
|
|
255
|
+
proc.on("close", () => settle(null));
|
|
256
|
+
|
|
257
|
+
// Send MCP initialize
|
|
258
|
+
proc.stdin.write(
|
|
259
|
+
JSON.stringify({
|
|
260
|
+
jsonrpc: "2.0",
|
|
261
|
+
id: 1,
|
|
262
|
+
method: "initialize",
|
|
263
|
+
params: {
|
|
264
|
+
protocolVersion: "2024-11-05",
|
|
265
|
+
capabilities: {},
|
|
266
|
+
clientInfo: { name: "maxy-site", version: "1.0" },
|
|
267
|
+
},
|
|
268
|
+
}) + "\n"
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// Timeout after 8 seconds
|
|
272
|
+
setTimeout(() => settle(null), 8000);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
// Admin agent: Claude Code CLI subprocess
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
|
|
280
|
+
async function* invokeAdminAgent(
|
|
281
|
+
message: string,
|
|
282
|
+
systemPrompt: string,
|
|
283
|
+
accountDir: string,
|
|
284
|
+
accountId: string,
|
|
285
|
+
sessionKey?: string
|
|
286
|
+
): AsyncGenerator<AgentStreamEvent> {
|
|
287
|
+
const resumeSessionId = sessionKey ? getAgentSessionId(sessionKey) : undefined;
|
|
288
|
+
|
|
289
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId) });
|
|
290
|
+
|
|
291
|
+
const args = [
|
|
292
|
+
"--print",
|
|
293
|
+
"--output-format", "stream-json",
|
|
294
|
+
"--verbose",
|
|
295
|
+
"--system-prompt", systemPrompt,
|
|
296
|
+
"--mcp-config", mcpConfig,
|
|
297
|
+
"--allowedTools", ADMIN_ALLOWED_TOOLS.join(","),
|
|
298
|
+
"--permission-mode", "dontAsk",
|
|
299
|
+
"--model", "claude-opus-4-6",
|
|
300
|
+
"--max-turns", "20",
|
|
301
|
+
];
|
|
302
|
+
|
|
303
|
+
if (resumeSessionId) {
|
|
304
|
+
args.push("--resume", resumeSessionId);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
args.push(message);
|
|
308
|
+
|
|
309
|
+
const proc = spawn("claude", args, {
|
|
310
|
+
cwd: accountDir,
|
|
311
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
312
|
+
});
|
|
313
|
+
// Pipe stderr to a log file. Never discard it — MCP warnings, first-run
|
|
314
|
+
// setup messages, and real errors all arrive here. Without draining it the
|
|
315
|
+
// 64 KB pipe buffer fills, the process deadlocks, and stdout stops flowing.
|
|
316
|
+
const stderrLog = agentLogStream("claude-agent-stderr");
|
|
317
|
+
proc.stderr?.pipe(stderrLog);
|
|
318
|
+
|
|
319
|
+
let buffer = "";
|
|
320
|
+
|
|
321
|
+
const readLines = async function* () {
|
|
322
|
+
for await (const chunk of proc.stdout) {
|
|
323
|
+
buffer += (chunk as Buffer).toString();
|
|
324
|
+
const lines = buffer.split("\n");
|
|
325
|
+
buffer = lines.pop() ?? "";
|
|
326
|
+
for (const line of lines) {
|
|
327
|
+
if (line.trim()) yield line;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (buffer.trim()) {
|
|
331
|
+
yield buffer;
|
|
332
|
+
buffer = "";
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// Map tool call IDs to tool names so tool_result events carry the correct name
|
|
337
|
+
const toolIdToName = new Map<string, string>();
|
|
338
|
+
|
|
339
|
+
for await (const line of readLines()) {
|
|
340
|
+
let msg: Record<string, unknown>;
|
|
341
|
+
try { msg = JSON.parse(line); } catch { continue; }
|
|
342
|
+
|
|
343
|
+
if (msg.type === "system" && msg.subtype === "init") {
|
|
344
|
+
const agentSessionId = msg.session_id as string;
|
|
345
|
+
yield { type: "session_init", sessionId: agentSessionId };
|
|
346
|
+
if (sessionKey) storeAgentSessionId(sessionKey, agentSessionId);
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (msg.type === "assistant") {
|
|
351
|
+
const content = (msg.message as Record<string, unknown> | undefined)?.content as
|
|
352
|
+
Array<{ type: string; text?: string; thinking?: string; name?: string; input?: Record<string, unknown>; id?: string }> | undefined;
|
|
353
|
+
|
|
354
|
+
if (content) {
|
|
355
|
+
for (const block of content) {
|
|
356
|
+
if (block.type === "text" && block.text) {
|
|
357
|
+
yield { type: "text", content: block.text };
|
|
358
|
+
} else if (block.type === "thinking" && block.thinking) {
|
|
359
|
+
yield { type: "thinking", content: block.thinking };
|
|
360
|
+
} else if (block.type === "tool_use" && block.name) {
|
|
361
|
+
if (block.id) toolIdToName.set(block.id, block.name);
|
|
362
|
+
yield { type: "tool_use", name: block.name, input: block.input ?? {} };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (msg.type === "user") {
|
|
370
|
+
const content = (msg as Record<string, unknown>).content as
|
|
371
|
+
Array<{ type: string; tool_use_id?: string; content?: string; is_error?: boolean }> | undefined;
|
|
372
|
+
|
|
373
|
+
if (Array.isArray(content)) {
|
|
374
|
+
for (const block of content) {
|
|
375
|
+
if (block.type === "tool_result") {
|
|
376
|
+
const name = (block.tool_use_id && toolIdToName.get(block.tool_use_id)) || block.tool_use_id || "unknown";
|
|
377
|
+
yield {
|
|
378
|
+
type: "tool_result",
|
|
379
|
+
name,
|
|
380
|
+
output: typeof block.content === "string" ? block.content : JSON.stringify(block.content),
|
|
381
|
+
error: block.is_error,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (msg.type === "system") {
|
|
390
|
+
if (msg.subtype === "task_started") {
|
|
391
|
+
yield { type: "subagent_start", name: "subagent", task: String(msg.description ?? "") };
|
|
392
|
+
} else if (msg.subtype === "task_notification") {
|
|
393
|
+
yield { type: "subagent_end", name: "subagent", result: String(msg.message ?? "") };
|
|
394
|
+
}
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if ("result" in msg) {
|
|
399
|
+
const usage = (msg as { usage?: { input_tokens?: number; output_tokens?: number } }).usage;
|
|
400
|
+
if (usage?.input_tokens !== undefined) {
|
|
401
|
+
yield { type: "usage", input_tokens: usage.input_tokens, output_tokens: usage.output_tokens ?? 0 };
|
|
402
|
+
}
|
|
403
|
+
yield { type: "done" };
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ---------------------------------------------------------------------------
|
|
409
|
+
// Public agent: direct Anthropic API (no Claude Code)
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
411
|
+
|
|
412
|
+
async function* invokePublicAgent(
|
|
413
|
+
message: string,
|
|
414
|
+
systemPrompt: string,
|
|
415
|
+
accountId: string
|
|
416
|
+
): AsyncGenerator<AgentStreamEvent> {
|
|
417
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
418
|
+
if (!apiKey) {
|
|
419
|
+
yield { type: "text", content: "Public agent is not configured. ANTHROPIC_API_KEY is missing." };
|
|
420
|
+
yield { type: "done" };
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const memoryContext = await fetchMemoryContext(accountId, message);
|
|
425
|
+
|
|
426
|
+
const system = memoryContext
|
|
427
|
+
? `${systemPrompt}\n\n<memory>\n${memoryContext}\n</memory>`
|
|
428
|
+
: systemPrompt;
|
|
429
|
+
|
|
430
|
+
const client = new Anthropic({ apiKey });
|
|
431
|
+
|
|
432
|
+
const stream = client.messages.stream({
|
|
433
|
+
model: "claude-haiku-4-5-20251001",
|
|
434
|
+
max_tokens: 1024,
|
|
435
|
+
system,
|
|
436
|
+
messages: [{ role: "user", content: message }],
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
for await (const event of stream) {
|
|
440
|
+
if (
|
|
441
|
+
event.type === "content_block_delta" &&
|
|
442
|
+
event.delta.type === "text_delta"
|
|
443
|
+
) {
|
|
444
|
+
yield { type: "text", content: event.delta.text };
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
yield { type: "done" };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ---------------------------------------------------------------------------
|
|
452
|
+
// Public entry point
|
|
453
|
+
// ---------------------------------------------------------------------------
|
|
454
|
+
|
|
455
|
+
interface AgentInvokeConfig {
|
|
456
|
+
type: "public" | "admin";
|
|
457
|
+
agentName?: string;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export async function* invokeAgent(
|
|
461
|
+
config: AgentInvokeConfig,
|
|
462
|
+
message: string,
|
|
463
|
+
sessionKey?: string
|
|
464
|
+
): AsyncGenerator<AgentStreamEvent> {
|
|
465
|
+
const { type: agentType, agentName } = config;
|
|
466
|
+
|
|
467
|
+
const sessionAccountId = sessionKey ? getAccountIdForSession(sessionKey) : undefined;
|
|
468
|
+
const account = resolveAccount();
|
|
469
|
+
|
|
470
|
+
if (!account) {
|
|
471
|
+
yield { type: "text", content: "No account configured. Please complete onboarding first." };
|
|
472
|
+
yield { type: "done" };
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const accountId = sessionAccountId ?? account.accountId;
|
|
477
|
+
const resolvedAgentName = agentName ?? agentType;
|
|
478
|
+
const identity = readIdentity(account.accountDir, resolvedAgentName);
|
|
479
|
+
|
|
480
|
+
const defaultSystemPrompt = agentType === "public"
|
|
481
|
+
? "You are a public assistant. Answer only from the memory context provided. Never use training data. British English. Be concise."
|
|
482
|
+
: "You are an admin agent. Full access. Professional, concise. British English. Be proactive — tell the user what needs doing and do it.";
|
|
483
|
+
|
|
484
|
+
const systemPrompt = identity ?? defaultSystemPrompt;
|
|
485
|
+
|
|
486
|
+
if (agentType === "public") {
|
|
487
|
+
yield* invokePublicAgent(message, systemPrompt, accountId);
|
|
488
|
+
} else {
|
|
489
|
+
yield* invokeAdminAgent(message, systemPrompt, account.accountDir, accountId, sessionKey);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export type AgentStreamEvent =
|
|
494
|
+
| { type: "session_init"; sessionId: string }
|
|
495
|
+
| { type: "text"; content: string }
|
|
496
|
+
| { type: "thinking"; content: string }
|
|
497
|
+
| { type: "tool_use"; name: string; input: Record<string, unknown> }
|
|
498
|
+
| { type: "tool_result"; name: string; output: string; error?: boolean }
|
|
499
|
+
| { type: "subagent_start"; name: string; task: string }
|
|
500
|
+
| { type: "subagent_end"; name: string; result: string }
|
|
501
|
+
| { type: "status"; message: string }
|
|
502
|
+
| { type: "usage"; input_tokens: number; output_tokens: number }
|
|
503
|
+
| { type: "done" };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function OGLayout({ children }: { children: React.ReactNode }) {
|
|
2
|
+
return (
|
|
3
|
+
<div
|
|
4
|
+
style={{
|
|
5
|
+
margin: 0,
|
|
6
|
+
padding: 0,
|
|
7
|
+
overflow: 'hidden',
|
|
8
|
+
backgroundColor: '#FAFAF8',
|
|
9
|
+
minHeight: '100vh',
|
|
10
|
+
}}
|
|
11
|
+
>
|
|
12
|
+
{children}
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
}
|