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.
- package/dist/.env.example +4 -0
- package/dist/locales/en.json +74 -2
- package/dist/locales/fi.json +74 -2
- package/dist/public/css/views/capabilities.css +211 -0
- package/dist/public/llms-template.txt +152 -89
- package/dist/public/spa.html +1 -0
- package/dist/public/views/admin/capabilities-tab.js +114 -0
- package/dist/public/views/admin.js +2 -0
- package/dist/public/views/aimeat-os.js +194 -34
- package/dist/public/views/profile/capabilities-tab.js +252 -0
- package/dist/public/views/profile/extensions-tab.js +404 -50
- package/dist/public/views/profile/landing-page.js +2 -0
- package/dist/public/views/profile.js +2 -0
- package/dist/src/config.d.ts +11 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +11 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/mcp/apps.d.ts +18 -0
- package/dist/src/mcp/apps.d.ts.map +1 -0
- package/dist/src/mcp/apps.js +241 -0
- package/dist/src/mcp/apps.js.map +1 -0
- package/dist/src/mcp/capabilities.d.ts +12 -0
- package/dist/src/mcp/capabilities.d.ts.map +1 -0
- package/dist/src/mcp/capabilities.js +187 -0
- package/dist/src/mcp/capabilities.js.map +1 -0
- package/dist/src/mcp/cortex.d.ts +17 -0
- package/dist/src/mcp/cortex.d.ts.map +1 -0
- package/dist/src/mcp/cortex.js +237 -0
- package/dist/src/mcp/cortex.js.map +1 -0
- package/dist/src/mcp/extensions.d.ts +4 -3
- package/dist/src/mcp/extensions.d.ts.map +1 -1
- package/dist/src/mcp/extensions.js +233 -3
- package/dist/src/mcp/extensions.js.map +1 -1
- package/dist/src/mcp/index.d.ts.map +1 -1
- package/dist/src/mcp/index.js +6 -0
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/routes/admin-capabilities.d.ts +11 -0
- package/dist/src/routes/admin-capabilities.d.ts.map +1 -0
- package/dist/src/routes/admin-capabilities.js +54 -0
- package/dist/src/routes/admin-capabilities.js.map +1 -0
- package/dist/src/routes/capabilities.d.ts +11 -0
- package/dist/src/routes/capabilities.d.ts.map +1 -0
- package/dist/src/routes/capabilities.js +289 -0
- package/dist/src/routes/capabilities.js.map +1 -0
- package/dist/src/routes/cortex.d.ts.map +1 -1
- package/dist/src/routes/cortex.js +2 -0
- package/dist/src/routes/cortex.js.map +1 -1
- package/dist/src/routes/extensions.d.ts.map +1 -1
- package/dist/src/routes/extensions.js +9 -0
- package/dist/src/routes/extensions.js.map +1 -1
- package/dist/src/routes/lib-capabilities.d.ts +9 -0
- package/dist/src/routes/lib-capabilities.d.ts.map +1 -0
- package/dist/src/routes/lib-capabilities.js +190 -0
- package/dist/src/routes/lib-capabilities.js.map +1 -0
- package/dist/src/routes/libs.d.ts.map +1 -1
- package/dist/src/routes/libs.js +13 -0
- package/dist/src/routes/libs.js.map +1 -1
- package/dist/src/routes/verification.d.ts +10 -1
- package/dist/src/routes/verification.d.ts.map +1 -1
- package/dist/src/routes/verification.js +174 -24
- package/dist/src/routes/verification.js.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +35 -2
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/services/capability-aggregator.d.ts +8 -0
- package/dist/src/services/capability-aggregator.d.ts.map +1 -0
- package/dist/src/services/capability-aggregator.js +239 -0
- package/dist/src/services/capability-aggregator.js.map +1 -0
- package/dist/src/services/capability-invoke.d.ts +20 -0
- package/dist/src/services/capability-invoke.d.ts.map +1 -0
- package/dist/src/services/capability-invoke.js +95 -0
- package/dist/src/services/capability-invoke.js.map +1 -0
- package/dist/src/services/config-schema.d.ts.map +1 -1
- package/dist/src/services/config-schema.js +4 -0
- package/dist/src/services/config-schema.js.map +1 -1
- package/dist/src/services/core-jobs.d.ts.map +1 -1
- package/dist/src/services/core-jobs.js +16 -0
- package/dist/src/services/core-jobs.js.map +1 -1
- package/dist/src/services/did-document.d.ts +25 -0
- package/dist/src/services/did-document.d.ts.map +1 -0
- package/dist/src/services/did-document.js +31 -0
- package/dist/src/services/did-document.js.map +1 -0
- package/dist/src/services/eudiw.d.ts +11 -1
- package/dist/src/services/eudiw.d.ts.map +1 -1
- package/dist/src/services/eudiw.js +43 -48
- package/dist/src/services/eudiw.js.map +1 -1
- package/dist/src/services/extension-runtime.d.ts.map +1 -1
- package/dist/src/services/extension-runtime.js +8 -5
- package/dist/src/services/extension-runtime.js.map +1 -1
- package/dist/src/services/job-seeding.d.ts.map +1 -1
- package/dist/src/services/job-seeding.js +4 -0
- package/dist/src/services/job-seeding.js.map +1 -1
- package/dist/src/services/oidc-client.d.ts +32 -0
- package/dist/src/services/oidc-client.d.ts.map +1 -0
- package/dist/src/services/oidc-client.js +67 -0
- package/dist/src/services/oidc-client.js.map +1 -0
- package/dist/src/services/sd-jwt.d.ts +21 -0
- package/dist/src/services/sd-jwt.d.ts.map +1 -0
- package/dist/src/services/sd-jwt.js +87 -0
- package/dist/src/services/sd-jwt.js.map +1 -0
- package/dist/src/services/vc-issuer.d.ts +20 -1
- package/dist/src/services/vc-issuer.d.ts.map +1 -1
- package/dist/src/services/vc-issuer.js +75 -15
- package/dist/src/services/vc-issuer.js.map +1 -1
- package/dist/src/storage/interface.d.ts +118 -1
- package/dist/src/storage/interface.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.d.ts +37 -1
- package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.js +282 -0
- package/dist/src/storage/providers/mongodb/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/index.d.ts +37 -1
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/index.js +241 -0
- package/dist/src/storage/providers/sqlite/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +77 -0
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/src/storage/repositories/capability.repository.d.ts +35 -0
- package/dist/src/storage/repositories/capability.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/capability.repository.js +2 -0
- package/dist/src/storage/repositories/capability.repository.js.map +1 -0
- package/dist/src/storage/repositories/index.d.ts +1 -0
- package/dist/src/storage/repositories/index.d.ts.map +1 -1
- package/dist/src/storage/repositories/node.repository.d.ts +5 -1
- package/dist/src/storage/repositories/node.repository.d.ts.map +1 -1
- package/dist/static/app-catalog.html +156 -29
- package/package.json +6 -1
- package/prisma/schema.prisma +86 -0
package/dist/public/spa.html
CHANGED
|
@@ -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
|
-
|
|
121
|
-
|
|
122
|
-
md
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
md +=
|
|
126
|
-
md += `
|
|
127
|
-
md += `
|
|
128
|
-
md +=
|
|
129
|
-
md += `- **Node URL:**
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
md +=
|
|
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
|
-
|
|
147
|
-
md +=
|
|
148
|
-
|
|
149
|
-
md +=
|
|
150
|
-
md +=
|
|
151
|
-
md +=
|
|
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 +=
|
|
154
|
-
md +=
|
|
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 +=
|
|
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 += `-
|
|
169
|
-
md += `-
|
|
170
|
-
md += `-
|
|
171
|
-
md += `- Additional libraries as needed\n`;
|
|
172
|
-
md += `-
|
|
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
|
}
|