aimeat 1.2.6 → 1.3.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.
Files changed (128) hide show
  1. package/dist/.env.example +4 -0
  2. package/dist/locales/en.json +74 -2
  3. package/dist/locales/fi.json +74 -2
  4. package/dist/public/css/views/capabilities.css +211 -0
  5. package/dist/public/llms-template.txt +152 -89
  6. package/dist/public/spa.html +1 -0
  7. package/dist/public/views/admin/capabilities-tab.js +114 -0
  8. package/dist/public/views/admin.js +2 -0
  9. package/dist/public/views/aimeat-os.js +194 -34
  10. package/dist/public/views/profile/capabilities-tab.js +252 -0
  11. package/dist/public/views/profile/extensions-tab.js +404 -50
  12. package/dist/public/views/profile/landing-page.js +2 -0
  13. package/dist/public/views/profile.js +2 -0
  14. package/dist/src/config.d.ts +11 -0
  15. package/dist/src/config.d.ts.map +1 -1
  16. package/dist/src/config.js +11 -0
  17. package/dist/src/config.js.map +1 -1
  18. package/dist/src/mcp/apps.d.ts +18 -0
  19. package/dist/src/mcp/apps.d.ts.map +1 -0
  20. package/dist/src/mcp/apps.js +241 -0
  21. package/dist/src/mcp/apps.js.map +1 -0
  22. package/dist/src/mcp/capabilities.d.ts +12 -0
  23. package/dist/src/mcp/capabilities.d.ts.map +1 -0
  24. package/dist/src/mcp/capabilities.js +187 -0
  25. package/dist/src/mcp/capabilities.js.map +1 -0
  26. package/dist/src/mcp/cortex.d.ts +17 -0
  27. package/dist/src/mcp/cortex.d.ts.map +1 -0
  28. package/dist/src/mcp/cortex.js +237 -0
  29. package/dist/src/mcp/cortex.js.map +1 -0
  30. package/dist/src/mcp/extensions.d.ts +4 -3
  31. package/dist/src/mcp/extensions.d.ts.map +1 -1
  32. package/dist/src/mcp/extensions.js +233 -3
  33. package/dist/src/mcp/extensions.js.map +1 -1
  34. package/dist/src/mcp/index.d.ts.map +1 -1
  35. package/dist/src/mcp/index.js +6 -0
  36. package/dist/src/mcp/index.js.map +1 -1
  37. package/dist/src/routes/admin-capabilities.d.ts +11 -0
  38. package/dist/src/routes/admin-capabilities.d.ts.map +1 -0
  39. package/dist/src/routes/admin-capabilities.js +54 -0
  40. package/dist/src/routes/admin-capabilities.js.map +1 -0
  41. package/dist/src/routes/capabilities.d.ts +11 -0
  42. package/dist/src/routes/capabilities.d.ts.map +1 -0
  43. package/dist/src/routes/capabilities.js +289 -0
  44. package/dist/src/routes/capabilities.js.map +1 -0
  45. package/dist/src/routes/cortex.d.ts.map +1 -1
  46. package/dist/src/routes/cortex.js +2 -0
  47. package/dist/src/routes/cortex.js.map +1 -1
  48. package/dist/src/routes/extensions.d.ts.map +1 -1
  49. package/dist/src/routes/extensions.js +9 -0
  50. package/dist/src/routes/extensions.js.map +1 -1
  51. package/dist/src/routes/lib-capabilities.d.ts +9 -0
  52. package/dist/src/routes/lib-capabilities.d.ts.map +1 -0
  53. package/dist/src/routes/lib-capabilities.js +190 -0
  54. package/dist/src/routes/lib-capabilities.js.map +1 -0
  55. package/dist/src/routes/libs.d.ts.map +1 -1
  56. package/dist/src/routes/libs.js +13 -0
  57. package/dist/src/routes/libs.js.map +1 -1
  58. package/dist/src/routes/verification.d.ts +10 -1
  59. package/dist/src/routes/verification.d.ts.map +1 -1
  60. package/dist/src/routes/verification.js +174 -24
  61. package/dist/src/routes/verification.js.map +1 -1
  62. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  63. package/dist/src/server-bootstrap/routes-loader.js +35 -2
  64. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  65. package/dist/src/services/capability-aggregator.d.ts +8 -0
  66. package/dist/src/services/capability-aggregator.d.ts.map +1 -0
  67. package/dist/src/services/capability-aggregator.js +239 -0
  68. package/dist/src/services/capability-aggregator.js.map +1 -0
  69. package/dist/src/services/capability-invoke.d.ts +20 -0
  70. package/dist/src/services/capability-invoke.d.ts.map +1 -0
  71. package/dist/src/services/capability-invoke.js +95 -0
  72. package/dist/src/services/capability-invoke.js.map +1 -0
  73. package/dist/src/services/config-schema.d.ts.map +1 -1
  74. package/dist/src/services/config-schema.js +4 -0
  75. package/dist/src/services/config-schema.js.map +1 -1
  76. package/dist/src/services/core-jobs.d.ts.map +1 -1
  77. package/dist/src/services/core-jobs.js +16 -0
  78. package/dist/src/services/core-jobs.js.map +1 -1
  79. package/dist/src/services/did-document.d.ts +25 -0
  80. package/dist/src/services/did-document.d.ts.map +1 -0
  81. package/dist/src/services/did-document.js +31 -0
  82. package/dist/src/services/did-document.js.map +1 -0
  83. package/dist/src/services/eudiw.d.ts +11 -1
  84. package/dist/src/services/eudiw.d.ts.map +1 -1
  85. package/dist/src/services/eudiw.js +43 -48
  86. package/dist/src/services/eudiw.js.map +1 -1
  87. package/dist/src/services/extension-runtime.d.ts.map +1 -1
  88. package/dist/src/services/extension-runtime.js +8 -5
  89. package/dist/src/services/extension-runtime.js.map +1 -1
  90. package/dist/src/services/job-seeding.d.ts.map +1 -1
  91. package/dist/src/services/job-seeding.js +4 -0
  92. package/dist/src/services/job-seeding.js.map +1 -1
  93. package/dist/src/services/oidc-client.d.ts +32 -0
  94. package/dist/src/services/oidc-client.d.ts.map +1 -0
  95. package/dist/src/services/oidc-client.js +67 -0
  96. package/dist/src/services/oidc-client.js.map +1 -0
  97. package/dist/src/services/sd-jwt.d.ts +21 -0
  98. package/dist/src/services/sd-jwt.d.ts.map +1 -0
  99. package/dist/src/services/sd-jwt.js +87 -0
  100. package/dist/src/services/sd-jwt.js.map +1 -0
  101. package/dist/src/services/vc-issuer.d.ts +20 -1
  102. package/dist/src/services/vc-issuer.d.ts.map +1 -1
  103. package/dist/src/services/vc-issuer.js +75 -15
  104. package/dist/src/services/vc-issuer.js.map +1 -1
  105. package/dist/src/storage/interface.d.ts +118 -1
  106. package/dist/src/storage/interface.d.ts.map +1 -1
  107. package/dist/src/storage/providers/mongodb/index.d.ts +37 -1
  108. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  109. package/dist/src/storage/providers/mongodb/index.js +282 -0
  110. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  111. package/dist/src/storage/providers/sqlite/index.d.ts +37 -1
  112. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  113. package/dist/src/storage/providers/sqlite/index.js +241 -0
  114. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  115. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  116. package/dist/src/storage/providers/sqlite/schema.js +77 -0
  117. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  118. package/dist/src/storage/repositories/capability.repository.d.ts +35 -0
  119. package/dist/src/storage/repositories/capability.repository.d.ts.map +1 -0
  120. package/dist/src/storage/repositories/capability.repository.js +2 -0
  121. package/dist/src/storage/repositories/capability.repository.js.map +1 -0
  122. package/dist/src/storage/repositories/index.d.ts +1 -0
  123. package/dist/src/storage/repositories/index.d.ts.map +1 -1
  124. package/dist/src/storage/repositories/node.repository.d.ts +5 -1
  125. package/dist/src/storage/repositories/node.repository.d.ts.map +1 -1
  126. package/dist/static/app-catalog.html +156 -29
  127. package/package.json +6 -1
  128. package/prisma/schema.prisma +86 -0
@@ -63,6 +63,7 @@
63
63
  <link rel="stylesheet" href="/css/views/portfolio.css">
64
64
  <link rel="stylesheet" href="/css/components/tags.css">
65
65
  <link rel="stylesheet" href="/css/views/help.css">
66
+ <link rel="stylesheet" href="/css/views/capabilities.css">
66
67
  <link rel="stylesheet" href="/css/views/admin.css">
67
68
  <script type="importmap">
68
69
  {
@@ -0,0 +1,114 @@
1
+ import { h } from 'preact';
2
+ import { useState, useEffect } from 'preact/hooks';
3
+ import htm from 'htm';
4
+ const html = htm.bind(h);
5
+ import { t } from '/js/i18n.js';
6
+ import { escHtml } from '/js/utils.js';
7
+ import { num, Badge, Empty } from './shared.js';
8
+
9
+ function schemaKeys(schema) {
10
+ if (!schema || typeof schema !== 'object') return '';
11
+ const props = schema.properties || schema;
12
+ return Object.entries(props)
13
+ .filter(([k]) => !['type','properties','items','required','description','nullable','enum'].includes(k))
14
+ .map(([k, v]) => {
15
+ const spec = (v && typeof v === 'object') ? v : {};
16
+ let t = spec.type || '?';
17
+ if (t === 'object' && spec.properties) t = '{...}';
18
+ if (t === 'array' && spec.items) t = '[...]';
19
+ return k + ':' + t;
20
+ }).join(', ');
21
+ }
22
+
23
+ export default function CapabilitiesTab({ data, session }) {
24
+ const [caps, setCaps] = useState([]);
25
+ const [loading, setLoading] = useState(true);
26
+ const [filter, setFilter] = useState('');
27
+ const [expanded, setExpanded] = useState(null);
28
+
29
+ useEffect(() => {
30
+ if (!session) return;
31
+ session.fetch('/v1/admin/capabilities?per_page=200').then(res => {
32
+ setCaps(res.data?.capabilities || []);
33
+ setLoading(false);
34
+ }).catch(() => setLoading(false));
35
+ }, [session]);
36
+
37
+ if (loading) return html`<div class="adm-card">${t('common.loading')}</div>`;
38
+ if (!caps.length) return html`<${Empty} text=${t('capabilities.noCapabilities')} />`;
39
+
40
+ const filtered = filter
41
+ ? caps.filter(c => c.name.toLowerCase().includes(filter.toLowerCase()) || c.id.toLowerCase().includes(filter.toLowerCase()))
42
+ : caps;
43
+
44
+ return html`
45
+ <div class="adm-card">
46
+ <div style="margin-bottom:12px;display:flex;gap:8px;align-items:center">
47
+ <input type="text" placeholder=${t('common.search')} value=${filter}
48
+ onInput=${e => setFilter(e.target.value)}
49
+ style="padding:6px 10px;border:1px solid var(--border);border-radius:6px;font-size:.85rem;flex:1;max-width:300px" />
50
+ <span style="font-size:.8rem;color:var(--text-dim)">${filtered.length} / ${caps.length}</span>
51
+ </div>
52
+ <div class="scrollable">
53
+ <table>
54
+ <thead><tr>
55
+ <th>ID</th>
56
+ <th>${t('capabilities.name')}</th>
57
+ <th>Input</th>
58
+ <th>Output</th>
59
+ <th>${t('capabilities.sourceType')}</th>
60
+ <th>${t('capabilities.callable')}</th>
61
+ <th>${t('capabilities.invocations')}</th>
62
+ <th>${t('capabilities.errors')}</th>
63
+ </tr></thead>
64
+ <tbody>
65
+ ${filtered.map(c => {
66
+ const s = c.stats || {};
67
+ const override = c.operatorOverride;
68
+ const isExp = expanded === c.id;
69
+ const inputKeys = schemaKeys(c.inputSchema);
70
+ const outputKeys = schemaKeys(c.outputSchema);
71
+ return html`
72
+ <tr style=${override?.disabled ? 'opacity:0.5;cursor:pointer' : 'cursor:pointer'}
73
+ onClick=${() => setExpanded(isExp ? null : c.id)}>
74
+ <td class="mono" style="font-size:.72rem;max-width:180px;overflow:hidden;text-overflow:ellipsis">${escHtml(c.id)}</td>
75
+ <td>
76
+ <strong>${escHtml(c.name)}</strong>
77
+ ${c.summary ? html`<div style="font-size:.7rem;color:var(--text-dim);max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escHtml(c.summary)}</div>` : ''}
78
+ </td>
79
+ <td style="font-size:.7rem;font-family:monospace;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${inputKeys || html`<span style="opacity:.4">—</span>`}</td>
80
+ <td style="font-size:.7rem;font-family:monospace;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${outputKeys || html`<span style="opacity:.4">—</span>`}</td>
81
+ <td><${Badge} type=${c.source?.type || 'manual'} /></td>
82
+ <td>${c.callable ? '✅' : '➖'}</td>
83
+ <td>${num(s.totalInvocations || 0)}</td>
84
+ <td style=${s.errorCount > 0 ? 'color:#E8564A' : ''}>${num(s.errorCount || 0)}</td>
85
+ </tr>
86
+ ${isExp ? html`<tr>
87
+ <td colspan="8" style="background:var(--bg-dim,#f5f5f5);padding:12px">
88
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;font-size:.78rem">
89
+ <div>
90
+ <div style="font-weight:600;margin-bottom:4px">Input Schema</div>
91
+ ${c.inputSchema ? html`<pre style="margin:0;white-space:pre-wrap;font-size:.72rem;max-height:200px;overflow:auto">${JSON.stringify(c.inputSchema, null, 2)}</pre>` : html`<span style="opacity:.5">None</span>`}
92
+ </div>
93
+ <div>
94
+ <div style="font-weight:600;margin-bottom:4px">Output Schema</div>
95
+ ${c.outputSchema ? html`<pre style="margin:0;white-space:pre-wrap;font-size:.72rem;max-height:200px;overflow:auto">${JSON.stringify(c.outputSchema, null, 2)}</pre>` : html`<span style="opacity:.5">None</span>`}
96
+ </div>
97
+ </div>
98
+ <div style="margin-top:8px;font-size:.75rem;display:flex;gap:16px;flex-wrap:wrap">
99
+ <span>Owner: <code>${escHtml(c.ownerGhii || '')}</code></span>
100
+ <span>Status: <${Badge} type=${c.status === 'active' ? 'success' : c.status === 'disabled' ? 'danger' : 'warning'} label=${c.status} /></span>
101
+ <span>Auth: ${c.authRequired}</span>
102
+ <span>Vouches: ${c.trust?.vouchCount || 0}</span>
103
+ <span>Avg: ${s.avgResponseMs || 0}ms</span>
104
+ ${c.usage ? html`<div style="margin-top:4px;width:100%"><code style="font-size:.7rem;word-break:break-all">${escHtml(c.usage.slice(0,200))}</code></div>` : null}
105
+ </div>
106
+ </td>
107
+ </tr>` : null}`;
108
+ })}
109
+ </tbody>
110
+ </table>
111
+ </div>
112
+ </div>
113
+ `;
114
+ }
@@ -47,6 +47,7 @@ import PromptsTab from './admin/prompts-tab.js';
47
47
  import PackagesAdminTab from './admin/packages-tab.js';
48
48
  import MemoryAdminTab from './admin/memory-tab.js';
49
49
  import GeneratorDebugTab from './admin/generator-debug-tab.js';
50
+ import CapabilitiesAdminTab from './admin/capabilities-tab.js';
50
51
 
51
52
  // ── Sidebar nav structure ──
52
53
  const NAV_GROUPS = [
@@ -73,6 +74,7 @@ const NAV_GROUPS = [
73
74
  { id: 'realtime', icon: '\u{1F4E1}', key: 'dashboard.realtime', component: RealtimeTab, count: 'rooms' },
74
75
  { id: 'work', icon: '\u{1F4E6}', key: 'dashboard.work', component: WorkTab, count: 'work' },
75
76
  { id: 'memory-admin', icon: '\u{1F5C4}', key: 'dashboard.memoryAdmin', component: MemoryAdminTab },
77
+ { id: 'capabilities', icon: '⚡', key: 'capabilities.adminTitle', component: CapabilitiesAdminTab },
76
78
  ]},
77
79
  { key: 'dashboard.navInfrastructure', items: [
78
80
  { id: 'email', icon: '\u2709', key: 'dashboard.email', component: EmailTab },
@@ -25,6 +25,7 @@ const LIBRARIES = [
25
25
  ['aimeat-auth', '/v1/libs/aimeat-auth.js', '—', 'Identity, registration, login, JWT, session management'],
26
26
  ['aimeat-data', '/v1/libs/aimeat-data.js', 'aimeat-auth', 'Memory key-value storage, search, micro-memory'],
27
27
  ['aimeat-storage', '/v1/libs/aimeat-storage.js', 'aimeat-auth', 'Binary file upload/download, drag & drop'],
28
+ ['AimeatRealtime', '/lib/realtime.js', 'aimeat-auth', 'WebSocket P2P rooms, WebRTC data channels, Yjs CRDT'],
28
29
  ['aimeat-social', '/v1/libs/aimeat-social.js', 'aimeat-auth', 'Boards, posts, reactions, replies'],
29
30
  ['aimeat-wallet', '/v1/libs/aimeat-wallet.js', 'aimeat-auth', 'Balance, transactions, morsel economy'],
30
31
  ['aimeat-work', '/v1/libs/aimeat-work.js', 'aimeat-auth', 'Action catalogue, work requests, deliveries'],
@@ -117,25 +118,171 @@ const EXAMPLE_PROMPTS = [
117
118
  /* ── Markdown generator ──────────────────────── */
118
119
 
119
120
  function generateMarkdown(nodeUrl) {
120
- let md = `# AIMEAT App Builder\n\n`;
121
- md += `You are an AIMEAT app builder. Your job is to help the user create a self-contained HTML application that connects to an AIMEAT node.\n\n`;
122
- md += `## Your Process\n\n`;
123
- md += `1. Ask the user what they want to build (free-form description)\n`;
124
- md += `2. Ask what data/features the app needs (memory, files, boards, wallet, etc.)\n`;
125
- md += `3. Ask about the look & feel (or offer sensible defaults)\n`;
126
- md += `4. Build a single HTML+CSS+JS file that works\n\n`;
127
- md += `Keep the interview light 3-4 questions max. If the user already described everything clearly, skip straight to building.\n\n`;
128
- md += `## Node Details\n\n`;
129
- md += `- **Node URL:** \`${nodeUrl}\`\n- **Protocol Version:** 1.2\n- **Generated:** ${new Date().toISOString().split('T')[0]}\n\n---\n\n`;
130
-
131
- md += `## Available Client Libraries\n\nLoad via \`<script>\` tags — served from the node, zero CORS issues.\n\n`;
132
- md += `| Library | URL | Depends On | Purpose |\n|---------|-----|-----------|--------|\n`;
133
- for (const [name, url, dep, desc] of LIBRARIES) {
134
- md += `| **${name}** | \`${nodeUrl}${url}\` | ${dep} | ${desc} |\n`;
135
- }
136
-
137
- md += `\n## Quick Start Template\n\n\`\`\`html\n<!DOCTYPE html>\n<html lang="en">\n<head>\n <meta charset="UTF-8">\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>My AIMEAT App</title>\n <script src="${nodeUrl}/v1/libs/aimeat-auth.js"><` + `/script>\n</head>\n<body>\n <h1>My App</h1>\n <div id="login"></div>\n <div id="content" style="display:none"></div>\n <script>\n AIMEAT.auth.mountLoginButton('#login', {\n onLogin: function(session) {\n document.getElementById('content').style.display = 'block';\n startApp(session);\n },\n onLogout: function() {\n document.getElementById('content').style.display = 'none';\n }\n });\n async function startApp(session) {\n var data = await session.fetch('/v1/memory');\n console.log('My memories:', data);\n }\n <` + `/script>\n</body>\n</html>\n\`\`\`\n\n`;
138
-
121
+ const S = '`'; // backtick helper
122
+ const SS = '```'; // triple backtick helper
123
+ let md = '';
124
+
125
+ // ── Header ──
126
+ md += `# AIMEAT App Builder\n\n`;
127
+ md += `You are helping a human user build a single-file HTML app on an AIMEAT node.\n`;
128
+ md += `Ask what they want to build, then produce the HTML using the starter template and SDK libraries below.\n`;
129
+ md += `Do not ask about deployment, cortex, extensions, CSM, or architecture.\n\n`;
130
+ md += `- **Node URL:** ${S}${nodeUrl}${S}\n`;
131
+ md += `- **Generated:** ${new Date().toISOString().split('T')[0]}\n\n`;
132
+
133
+ // ── Starter Template ──
134
+ md += `## Starter Template (use for every app)\n\n`;
135
+ md += `${SS}html\n`;
136
+ md += `<!-- AIMEAT App Manifest\nname: my-app\nversion: 1.0.0\ndescription: What this app does\nentry: index.html\n-->\n`;
137
+ md += `<!DOCTYPE html>\n<html lang="en">\n<head>\n`;
138
+ md += ` <meta charset="UTF-8">\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n`;
139
+ md += ` <title>App Name</title>\n`;
140
+ md += ` <link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" />\n`;
141
+ md += ` <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"><` + `/script>\n`;
142
+ md += `</head>\n<body class="bg-base-100 min-h-screen flex flex-col">\n`;
143
+ md += ` <nav class="navbar bg-base-200 shadow-sm px-4">\n`;
144
+ md += ` <div class="flex-1"><span class="text-lg font-bold">App Name</span></div>\n`;
145
+ md += ` <div class="flex-none"><span id="header-auth"></span></div>\n`;
146
+ md += ` </nav>\n <div id="app" class="flex-1 p-4">Loading...</div>\n <script>\n`;
147
+ md += ` function loadScript(src) {\n`;
148
+ md += ` return new Promise((resolve, reject) => {\n`;
149
+ md += ` const s = document.createElement('script');\n`;
150
+ md += ` s.src = src; s.onload = resolve; s.onerror = reject;\n`;
151
+ md += ` document.head.appendChild(s);\n });\n }\n`;
152
+ md += ` async function boot() {\n`;
153
+ md += ` await loadScript('/v1/libs/aimeat-auth.js');\n`;
154
+ md += ` await loadScript('/v1/libs/aimeat-data.js');\n`;
155
+ md += ` // Add more as needed:\n`;
156
+ md += ` // await loadScript('/v1/libs/aimeat-storage.js'); // file uploads\n`;
157
+ md += ` // await loadScript('/lib/realtime.js'); // multiplayer P2P\n\n`;
158
+ md += ` AIMEAT.auth.mountLoginButton('#header-auth', {\n`;
159
+ md += ` onLogin: (session) => startApp(session),\n`;
160
+ md += ` onLogout: () => location.reload(),\n });\n`;
161
+ md += ` const session = await AIMEAT.auth.login();\n`;
162
+ md += ` if (session) startApp(session);\n }\n`;
163
+ md += ` async function startApp(session) {\n`;
164
+ md += ` // YOUR APP CODE HERE\n`;
165
+ md += ` document.getElementById('app').innerHTML = '<h2>Hello!</h2>';\n }\n`;
166
+ md += ` boot();\n <` + `/script>\n</body>\n</html>\n`;
167
+ md += `${SS}\n\n`;
168
+
169
+ // ── SDK Libraries ──
170
+ md += `## SDK Libraries\n\nLoad via ${S}loadScript()${S} in boot(). All paths are relative (start with /).\n\n`;
171
+ md += `| Library | Load with | Use for |\n|---------|-----------|--------|\n`;
172
+ md += `| aimeat-auth | Always loaded | Login bar, session |\n`;
173
+ md += `| aimeat-data | Always loaded | ${S}AIMEAT.data.get/set/search/list/delete${S} |\n`;
174
+ md += `| aimeat-storage | ${S}loadScript('/v1/libs/aimeat-storage.js')${S} | ${S}AIMEAT.storage.upload/download/list${S} |\n`;
175
+ md += `| AimeatRealtime | ${S}loadScript('/lib/realtime.js')${S} | P2P rooms, multiplayer, chat |\n`;
176
+ md += `| aimeat-social | ${S}loadScript('/v1/libs/aimeat-social.js')${S} | Discussion boards |\n`;
177
+ md += `| aimeat-wallet | ${S}loadScript('/v1/libs/aimeat-wallet.js')${S} | Morsel balance display |\n`;
178
+ md += `| aimeat-work | ${S}loadScript('/v1/libs/aimeat-work.js')${S} | Actions, work queue |\n\n`;
179
+
180
+ // ── SDK API Reference ──
181
+ md += `## SDK API Reference\n\n`;
182
+
183
+ md += `### AIMEAT.data (Memory)\n${SS}javascript\n`;
184
+ md += `await AIMEAT.data.set(key, value, { visibility: 'private' }) // write\n`;
185
+ md += `await AIMEAT.data.get(key) // read value (null if not found)\n`;
186
+ md += `await AIMEAT.data.getEntry(key) // read full entry with metadata\n`;
187
+ md += `await AIMEAT.data.update(key, value, version) // optimistic locking update\n`;
188
+ md += `await AIMEAT.data.delete(key) // delete\n`;
189
+ md += `await AIMEAT.data.list() // list all keys\n`;
190
+ md += `await AIMEAT.data.search(query) // full-text search\n`;
191
+ md += `await AIMEAT.data.getPublic(gaii, key) // read another user's public data\n`;
192
+ md += `${SS}\n\n`;
193
+
194
+ md += `### AIMEAT.storage (Files)\n${SS}javascript\n`;
195
+ md += `await AIMEAT.storage.upload(file) // upload File or Blob\n`;
196
+ md += `await AIMEAT.storage.upload(base64, { key, mime_type }) // upload base64\n`;
197
+ md += `await AIMEAT.storage.download(key) // download as Blob\n`;
198
+ md += `await AIMEAT.storage.list() // list all files\n`;
199
+ md += `await AIMEAT.storage.delete(key) // delete file\n`;
200
+ md += `await AIMEAT.storage.meta(key) // HEAD request for metadata\n`;
201
+ md += `await AIMEAT.storage.dropZone(el, { onUpload }) // drag & drop helper\n`;
202
+ md += `${SS}\n\n`;
203
+
204
+ md += `**Storage auth gotcha:** ALL /v1/storage endpoints require authentication.\n`;
205
+ md += `${S}<img src="/v1/storage/key">${S} will return 401. To display images:\n`;
206
+ md += `${SS}javascript\n`;
207
+ md += `async function loadImage(key) {\n`;
208
+ md += ` const res = await fetch('/v1/storage/' + encodeURIComponent(key), {\n`;
209
+ md += ` headers: { 'Authorization': 'Bearer ' + session.jwt },\n });\n`;
210
+ md += ` const blob = await res.blob();\n`;
211
+ md += ` return URL.createObjectURL(blob); // use as img.src\n}\n`;
212
+ md += `${SS}\n\n`;
213
+
214
+ md += `### AimeatRealtime (P2P / Multiplayer)\n${SS}javascript\n`;
215
+ md += `const rt = new AimeatRealtime(location.origin, session.jwt); // positional args\n\n`;
216
+ md += `const room = await rt.createRoom({\n app_type: 'my-app', name: 'Room', is_public: true,\n});\n\n`;
217
+ md += `// Register handlers BEFORE connect()\n`;
218
+ md += `rt.on('joined', (msg) => { /* msg.peerId, msg.peers */ });\n`;
219
+ md += `rt.on('broadcast', (msg) => { /* msg.from, msg.payload */ });\n`;
220
+ md += `rt.on('peer-joined', (msg) => { /* msg.nick, msg.peerId */ });\n`;
221
+ md += `rt.on('peer-left', (msg) => { /* msg.peerId */ });\n`;
222
+ md += `rt.on('close', (msg) => { /* msg.code, msg.reason */ });\n\n`;
223
+ md += `rt.connect(room.id, 'nickname');\n`;
224
+ md += `rt.broadcast({ type: 'chat', text: 'Hello' });\n`;
225
+ md += `${SS}\n\n`;
226
+
227
+ md += `**Realtime throttling (critical):** Do NOT call ${S}rt.broadcast()${S} on every\n`;
228
+ md += `pointermove/mousemove. The WebSocket will be rate-limited and silently closed.\n`;
229
+ md += `Batch events into ~30ms intervals:\n`;
230
+ md += `${SS}javascript\nconst FLUSH_MS = 30;\nlet pending = [], flushTimer = null;\n`;
231
+ md += `function queueBroadcast(data) {\n pending.push(data);\n if (!flushTimer) {\n`;
232
+ md += ` flushTimer = setTimeout(() => {\n if (pending.length) rt.broadcast({ type: 'batch', items: pending });\n`;
233
+ md += ` pending = []; flushTimer = null;\n }, FLUSH_MS);\n }\n}\n${SS}\n\n`;
234
+
235
+ md += `**Auto-reconnect on close:**\n${SS}javascript\n`;
236
+ md += `let reconnectDelay = 500;\nrt.on('close', (msg) => {\n`;
237
+ md += ` if (leftIntentionally) return;\n setTimeout(() => {\n`;
238
+ md += ` rt.connect(room.id, nickname);\n reconnectDelay = Math.min(reconnectDelay * 2, 8000);\n`;
239
+ md += ` }, reconnectDelay);\n});\nrt.on('joined', () => { reconnectDelay = 500; });\n${SS}\n\n`;
240
+
241
+ md += `### AIMEAT.social (Boards)\n${SS}javascript\n`;
242
+ md += `await AIMEAT.social.createBoard({ name, visibility, description })\n`;
243
+ md += `await AIMEAT.social.listBoards()\n`;
244
+ md += `await AIMEAT.social.post(boardId, { content })\n`;
245
+ md += `await AIMEAT.social.listPosts(boardId)\n`;
246
+ md += `await AIMEAT.social.react(boardId, postId, emoji) // endpoint: /react\n`;
247
+ md += `await AIMEAT.social.reply(boardId, postId, { content })\n`;
248
+ md += `await AIMEAT.social.subscribe(boardId)\n`;
249
+ md += `${SS}\n\n`;
250
+
251
+ md += `### AIMEAT.wallet\n${SS}javascript\n`;
252
+ md += `await AIMEAT.wallet.balance() // { balance, in_escrow, available }\n`;
253
+ md += `await AIMEAT.wallet.transactions() // list transactions\n`;
254
+ md += `${SS}\n\n`;
255
+
256
+ md += `### AIMEAT.auth\n${SS}javascript\n`;
257
+ md += `AIMEAT.auth.mountLoginButton('#el', { onLogin, onLogout }) // login bar\n`;
258
+ md += `await AIMEAT.auth.login() // restore session (null if not logged in)\n`;
259
+ md += `await AIMEAT.auth.register(name, pw) // register new account\n`;
260
+ md += `await AIMEAT.auth.loginWithPassword(name, pw)\n`;
261
+ md += `AIMEAT.auth.logout()\n`;
262
+ md += `AIMEAT.auth.getSession() // current session (sync)\n`;
263
+ md += `// session.fetch(path, opts) returns parsed JSON, NOT Response\n`;
264
+ md += `// session.jwt, session.owner, session.ghii\n`;
265
+ md += `${SS}\n\n`;
266
+
267
+ // ── Data Layer Guide ──
268
+ md += `## Choosing the Right Data Layer\n\n`;
269
+ md += `Most apps only need **Memory + Storage**:\n`;
270
+ md += `- **Memory** (${S}AIMEAT.data${S}): Any JSON data. Visibility: private/owner/public.\n`;
271
+ md += `- **Storage** (${S}AIMEAT.storage${S}): Files (images, audio, video). Requires auth even for public files.\n\n`;
272
+ md += `Use **Boards** only for discussion/notification features (threaded posts with replies and reactions).\n`;
273
+ md += `Do NOT use Boards for general data sharing. Memory + Storage is simpler and more flexible.\n\n`;
274
+
275
+ // ── Key Rules ──
276
+ md += `## Key Rules\n\n`;
277
+ md += `- ${S}session.fetch()${S} returns already-parsed JSON. Do NOT call ${S}.json()${S} on it.\n`;
278
+ md += `- All API paths must be relative (start with ${S}/${S}), never absolute URLs.\n`;
279
+ md += `- Do NOT add manual token entry or API URL fields. Auth lib handles everything.\n`;
280
+ md += `- Storage images: fetch with auth, convert to blob, use ${S}URL.createObjectURL()${S} as src.\n`;
281
+ md += `- Realtime: register ${S}rt.on()${S} handlers BEFORE ${S}rt.connect()${S}.\n`;
282
+ md += `- Realtime: throttle high-frequency events to ~30ms batches.\n`;
283
+ md += `- ${S}AimeatRealtime${S} constructor takes positional args: ${S}(baseUrl, token)${S}, NOT an options object.\n\n`;
284
+
285
+ // ── REST API Endpoints ──
139
286
  md += `## REST API Endpoints\n\n`;
140
287
  for (const sec of API_SECTIONS) {
141
288
  md += `### ${sec.title}\n\n| Method | Path | Auth | Description |\n|--------|------|------|-------------|\n`;
@@ -143,35 +290,48 @@ function generateMarkdown(nodeUrl) {
143
290
  md += '\n';
144
291
  }
145
292
 
146
- md += `## Key Concepts\n\n`;
147
- md += `- **Owner** A registered human or organization with a master key pair\n`;
148
- md += `- **Agent** — An AI identity under an owner with its own key pair and memory\n`;
149
- md += `- **GAII** Global Agent Instance Identifier: \`agent-name#owner@node-id\`\n`;
150
- md += `- **Morsels** Simple tokens that flow between agents when they help each other\n`;
151
- md += `- **Visibility** private (only you), shared (your agents), public (everyone)\n\n`;
293
+ // ── Request/Response Examples ──
294
+ md += `## API Request/Response Examples\n\n`;
295
+
296
+ md += `### Write Memory\n${SS}\nPOST /v1/memory\nAuthorization: Bearer <jwt>\nContent-Type: application/json\n\n`;
297
+ md += `{ "key": "my-app.notes", "value": { "items": ["note1"] }, "visibility": "private" }\n${SS}\n`;
298
+ md += `Response 201:\n${SS}json\n{ "ok": true, "data": { "key": "my-app.notes", "visibility": "private", "version": 1 } }\n${SS}\n\n`;
299
+
300
+ md += `### Read Memory\n${SS}\nGET /v1/memory/my-app.notes\nAuthorization: Bearer <jwt>\n${SS}\n`;
301
+ md += `Response 200:\n${SS}json\n{ "ok": true, "data": { "key": "my-app.notes", "value": { "items": ["note1"] }, "visibility": "private", "version": 1 } }\n${SS}\n\n`;
302
+
303
+ md += `### List Memory Keys\n${SS}\nGET /v1/memory\nAuthorization: Bearer <jwt>\n${SS}\n`;
304
+ md += `Response 200:\n${SS}json\n{ "ok": true, "data": { "keys": ["my-app.notes", "my-app.settings"] } }\n${SS}\n\n`;
305
+
306
+ md += `### Upload File\n${SS}\nPOST /v1/storage\nAuthorization: Bearer <jwt>\nContent-Type: application/json\n\n`;
307
+ md += `{ "key": "photo.png", "data": "<base64>", "mime_type": "image/png", "visibility": "private" }\n${SS}\n`;
308
+ md += `Response 201:\n${SS}json\n{ "ok": true, "data": { "key": "photo.png", "size": 1024 } }\n${SS}\n\n`;
152
309
 
153
- md += `## Design Guidelines\n\n`;
154
- md += `\`\`\`css\n:root {\n --bg: #0f0a14;\n --card: rgba(30, 20, 40, 0.85);\n --text: #f0e6f6;\n --muted: #c4a6d0;\n --accent: #E8564A;\n --border: rgba(232, 86, 74, 0.15);\n --success: #22c55e;\n --radius: 12px;\n}\nbody {\n font-family: system-ui, -apple-system, sans-serif;\n background: var(--bg);\n color: var(--text);\n margin: 0;\n min-height: 100vh;\n}\n\`\`\`\n\n`;
310
+ md += `### List Files\n${SS}\nGET /v1/storage\nAuthorization: Bearer <jwt>\n${SS}\n`;
311
+ md += `Response 200:\n${SS}json\n{ "ok": true, "data": { "files": [{ "key": "photo.png", "size": 1024, "mime_type": "image/png" }] } }\n${SS}\n\n`;
155
312
 
156
- md += `## Self-Download Pattern\n\n\`\`\`javascript\nfunction downloadSelf() {\n var html = document.documentElement.outerHTML;\n var blob = new Blob(['<!DOCTYPE html>' + html], { type: 'text/html' });\n var a = document.createElement('a');\n a.href = URL.createObjectURL(blob);\n a.download = 'my-app.html';\n a.click();\n}\n\`\`\`\n\n`;
313
+ md += `### Get Wallet Balance\n${SS}\nGET /v1/wallet\nAuthorization: Bearer <jwt>\n${SS}\n`;
314
+ md += `Response 200:\n${SS}json\n{ "ok": true, "data": { "balance": 100, "in_escrow": 0, "available": 100, "daily_allowance": { "amount": 50, "accumulation_cap": 500 }, "lifetime": { "welcome_bonus": 100 } } }\n${SS}\n\n`;
157
315
 
316
+ // ── Limitations ──
158
317
  md += `## Limitations\n\n| Resource | Limit |\n|----------|-------|\n`;
159
318
  for (const [r, l] of LIMITS) md += `| ${r} | ${l} |\n`;
160
319
 
320
+ // ── Example Prompts ──
161
321
  md += `\n## Example Prompts\n\n`;
162
322
  for (const [title, prompt] of EXAMPLE_PROMPTS) {
163
323
  md += `### ${title}\n> "${prompt}"\n\n`;
164
324
  }
165
325
 
326
+ // ── Output Requirements ──
166
327
  md += `## Output Requirements\n\n`;
167
328
  md += `Produce a SINGLE HTML file containing:\n`;
168
- md += `- All CSS in a \`<style>\` tag\n`;
169
- md += `- All JavaScript in \`<script>\` tags\n`;
170
- md += `- The aimeat-auth library loaded via \`<script src="${nodeUrl}/v1/libs/aimeat-auth.js">\`\n`;
171
- md += `- Additional libraries as needed\n`;
172
- md += `- Login/logout UI via \`AIMEAT.auth.mountLoginButton()\`\n`;
329
+ md += `- DaisyUI + Tailwind for styling (CDN links in template)\n`;
330
+ md += `- The aimeat-auth library loaded via ${S}loadScript('/v1/libs/aimeat-auth.js')${S}\n`;
331
+ md += `- Login bar via ${S}AIMEAT.auth.mountLoginButton('#header-auth', { onLogin, onLogout })${S}\n`;
332
+ md += `- Additional libraries as needed via ${S}loadScript()${S}\n`;
333
+ md += `- All API paths relative (start with /)\n`;
173
334
  md += `- Responsive design (mobile-first)\n`;
174
- md += `- The self-download button pattern\n`;
175
335
 
176
336
  return md;
177
337
  }