aimeat 1.3.2 → 1.3.5
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/README.md +209 -95
- package/dist/src/auth/jwt.d.ts +4 -0
- package/dist/src/auth/jwt.d.ts.map +1 -1
- package/dist/src/auth/jwt.js +5 -0
- package/dist/src/auth/jwt.js.map +1 -1
- package/dist/src/auth/node-keys.d.ts.map +1 -1
- package/dist/src/auth/node-keys.js +4 -1
- package/dist/src/auth/node-keys.js.map +1 -1
- package/dist/src/mcp/apps.d.ts.map +1 -1
- package/dist/src/mcp/apps.js +35 -12
- package/dist/src/mcp/apps.js.map +1 -1
- package/dist/src/mcp/core.d.ts.map +1 -1
- package/dist/src/mcp/core.js +38 -2
- package/dist/src/mcp/core.js.map +1 -1
- package/dist/src/mcp/cortex.d.ts.map +1 -1
- package/dist/src/mcp/cortex.js +37 -6
- package/dist/src/mcp/cortex.js.map +1 -1
- package/dist/src/mcp/extensions.d.ts.map +1 -1
- package/dist/src/mcp/extensions.js +37 -3
- package/dist/src/mcp/extensions.js.map +1 -1
- package/dist/src/routes/apps.d.ts.map +1 -1
- package/dist/src/routes/apps.js +25 -3
- package/dist/src/routes/apps.js.map +1 -1
- package/dist/src/routes/extensions.d.ts.map +1 -1
- package/dist/src/routes/extensions.js +3 -1
- package/dist/src/routes/extensions.js.map +1 -1
- package/dist/src/routes/storage-files.d.ts.map +1 -1
- package/dist/src/routes/storage-files.js +26 -1
- package/dist/src/routes/storage-files.js.map +1 -1
- package/dist/src/routes/upload.d.ts +22 -0
- package/dist/src/routes/upload.d.ts.map +1 -0
- package/dist/src/routes/upload.js +219 -0
- package/dist/src/routes/upload.js.map +1 -0
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +3 -0
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +11 -3
- package/dist/src/server.js.map +1 -1
- package/dist/src/services/upload-token.d.ts +37 -0
- package/dist/src/services/upload-token.d.ts.map +1 -0
- package/dist/src/services/upload-token.js +98 -0
- package/dist/src/services/upload-token.js.map +1 -0
- package/dist/src/services/upload-zip.d.ts +31 -0
- package/dist/src/services/upload-zip.d.ts.map +1 -0
- package/dist/src/services/upload-zip.js +236 -0
- package/dist/src/services/upload-zip.js.map +1 -0
- package/dist/static/app-catalog.html +170 -16
- package/package.json +2 -2
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file upload-zip.ts
|
|
3
|
+
* @description ZIP parsing for extension and cortex uploads via presigned URL.
|
|
4
|
+
* Validates ZIP structure, extracts manifest + scripts/libs, applies security checks
|
|
5
|
+
* (magic bytes, zip bomb detection, path traversal prevention).
|
|
6
|
+
* @structure
|
|
7
|
+
* - parseExtensionZip() — ZIP buffer -> ExtensionRecord
|
|
8
|
+
* - parseCortexZip() — ZIP buffer -> CortexExtensionRecord + libs
|
|
9
|
+
* - extractZipEntries() — internal secure ZIP extraction
|
|
10
|
+
* @usage
|
|
11
|
+
* import { parseExtensionZip, parseCortexZip } from '../services/upload-zip.js';
|
|
12
|
+
* @version-history
|
|
13
|
+
* v1.0.0 — 2026-05-02 — Initial implementation
|
|
14
|
+
*/
|
|
15
|
+
import yauzl from 'yauzl';
|
|
16
|
+
import YAML from 'yaml';
|
|
17
|
+
import { parseCortexManifest } from './cortex-manifest.js';
|
|
18
|
+
const ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]);
|
|
19
|
+
const MAX_FILES = 50;
|
|
20
|
+
const MAX_DEPTH = 5;
|
|
21
|
+
const DECOMPRESSION_RATIO = 10;
|
|
22
|
+
export async function parseExtensionZip(buffer, config) {
|
|
23
|
+
if (buffer.length < 4 || !buffer.subarray(0, 4).equals(ZIP_MAGIC)) {
|
|
24
|
+
return { ok: false, error: 'Not a valid ZIP file (missing magic bytes)' };
|
|
25
|
+
}
|
|
26
|
+
const maxBytes = config.extensionMaxCodeSizeKb * 1024 * MAX_FILES;
|
|
27
|
+
if (buffer.length > maxBytes) {
|
|
28
|
+
return { ok: false, error: `ZIP exceeds maximum size of ${Math.round(maxBytes / 1024)}KB` };
|
|
29
|
+
}
|
|
30
|
+
let files;
|
|
31
|
+
try {
|
|
32
|
+
files = await extractZipEntries(buffer);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
return { ok: false, error: `ZIP extraction failed: ${err.message}` };
|
|
36
|
+
}
|
|
37
|
+
const manifestBuf = files.get('manifest.yaml');
|
|
38
|
+
if (!manifestBuf) {
|
|
39
|
+
return { ok: false, error: 'ZIP must contain manifest.yaml at root' };
|
|
40
|
+
}
|
|
41
|
+
let manifest;
|
|
42
|
+
try {
|
|
43
|
+
manifest = YAML.parse(manifestBuf.toString('utf-8'));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return { ok: false, error: 'manifest.yaml is not valid YAML' };
|
|
47
|
+
}
|
|
48
|
+
const metadata = manifest.metadata;
|
|
49
|
+
if (!metadata?.name || !metadata?.version || !metadata?.description || !metadata?.author) {
|
|
50
|
+
return { ok: false, error: 'metadata.name, metadata.version, metadata.description, and metadata.author are required' };
|
|
51
|
+
}
|
|
52
|
+
const actions = manifest.actions;
|
|
53
|
+
if (!Array.isArray(actions) || actions.length === 0) {
|
|
54
|
+
return { ok: false, error: 'actions array is required and must not be empty' };
|
|
55
|
+
}
|
|
56
|
+
// Extract scripts from scripts/ directory
|
|
57
|
+
const scripts = {};
|
|
58
|
+
for (const [path, buf] of files) {
|
|
59
|
+
if (path.startsWith('scripts/') && path.length > 'scripts/'.length) {
|
|
60
|
+
const filename = path.slice('scripts/'.length);
|
|
61
|
+
scripts[filename] = buf.toString('utf-8');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const action of actions) {
|
|
65
|
+
if (!action.id || !action.method || !action.path || !action.script) {
|
|
66
|
+
return { ok: false, error: 'Each action must have id, method, path, and script fields' };
|
|
67
|
+
}
|
|
68
|
+
if (!scripts[action.script]) {
|
|
69
|
+
return { ok: false, error: `Script "${action.script}" referenced in action "${action.id}" not found in scripts/ directory` };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const [filename, content] of Object.entries(scripts)) {
|
|
73
|
+
const sizeKb = Buffer.byteLength(content, 'utf-8') / 1024;
|
|
74
|
+
if (sizeKb > config.extensionMaxCodeSizeKb) {
|
|
75
|
+
return { ok: false, error: `Script "${filename}" (${Math.round(sizeKb)}KB) exceeds limit of ${config.extensionMaxCodeSizeKb}KB` };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const name = metadata.name;
|
|
79
|
+
const manifestConfig = manifest.config;
|
|
80
|
+
const manifestLimits = manifest.limits;
|
|
81
|
+
const manifestFederation = manifest.federation;
|
|
82
|
+
const manifestSchedules = manifest.schedules;
|
|
83
|
+
const manifestInstances = manifest.instances;
|
|
84
|
+
const record = {
|
|
85
|
+
name,
|
|
86
|
+
version: metadata.version,
|
|
87
|
+
description: metadata.description,
|
|
88
|
+
author: metadata.author,
|
|
89
|
+
status: 'inactive',
|
|
90
|
+
requiredApis: manifest.required_apis ?? [],
|
|
91
|
+
actions: actions.map(a => ({
|
|
92
|
+
id: a.id,
|
|
93
|
+
method: a.method.toUpperCase(),
|
|
94
|
+
path: a.path,
|
|
95
|
+
inputSchema: a.input ?? {},
|
|
96
|
+
outputSchema: a.output ?? {},
|
|
97
|
+
scriptContent: scripts[a.script],
|
|
98
|
+
})),
|
|
99
|
+
config: {
|
|
100
|
+
...(manifestConfig
|
|
101
|
+
? Object.fromEntries(Object.entries(manifestConfig).map(([k, v]) => {
|
|
102
|
+
if (v && typeof v === 'object' && 'default' in v) {
|
|
103
|
+
return [k, v.default];
|
|
104
|
+
}
|
|
105
|
+
return [k, v];
|
|
106
|
+
}))
|
|
107
|
+
: {}),
|
|
108
|
+
...(manifestSchedules ? { __schedules: manifestSchedules } : {}),
|
|
109
|
+
},
|
|
110
|
+
limits: {
|
|
111
|
+
memoryMb: Math.min(manifestLimits?.memory_mb ?? config.extensionMaxMemoryMb, config.extensionMaxMemoryMb),
|
|
112
|
+
timeoutMs: Math.min(manifestLimits?.timeout_ms ?? config.extensionTimeoutMs, config.extensionTimeoutMs),
|
|
113
|
+
maxApiCalls: Math.min(manifestLimits?.max_api_calls ?? config.extensionMaxApiCalls, config.extensionMaxApiCalls),
|
|
114
|
+
},
|
|
115
|
+
federation: {
|
|
116
|
+
advertise: manifestFederation?.advertise ?? false,
|
|
117
|
+
capabilities: manifestFederation?.capabilities ?? [],
|
|
118
|
+
},
|
|
119
|
+
...(manifestInstances?.supported ? {
|
|
120
|
+
instances: {
|
|
121
|
+
supported: true,
|
|
122
|
+
configSchema: manifestInstances.config_per_instance ?? undefined,
|
|
123
|
+
},
|
|
124
|
+
} : {}),
|
|
125
|
+
installedBy: 'upload',
|
|
126
|
+
installedAt: new Date().toISOString(),
|
|
127
|
+
};
|
|
128
|
+
return { ok: true, record };
|
|
129
|
+
}
|
|
130
|
+
export async function parseCortexZip(buffer, config, ownerName) {
|
|
131
|
+
if (buffer.length < 4 || !buffer.subarray(0, 4).equals(ZIP_MAGIC)) {
|
|
132
|
+
return { ok: false, error: 'Not a valid ZIP file (missing magic bytes)' };
|
|
133
|
+
}
|
|
134
|
+
const maxBytes = config.cortexMaxLibSizeKb * 1024 * MAX_FILES;
|
|
135
|
+
if (buffer.length > maxBytes) {
|
|
136
|
+
return { ok: false, error: 'ZIP exceeds maximum size' };
|
|
137
|
+
}
|
|
138
|
+
let files;
|
|
139
|
+
try {
|
|
140
|
+
files = await extractZipEntries(buffer);
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
return { ok: false, error: `ZIP extraction failed: ${err.message}` };
|
|
144
|
+
}
|
|
145
|
+
const manifestBuf = files.get('manifest.yaml');
|
|
146
|
+
if (!manifestBuf) {
|
|
147
|
+
return { ok: false, error: 'ZIP must contain manifest.yaml at root' };
|
|
148
|
+
}
|
|
149
|
+
const manifestStr = manifestBuf.toString('utf-8');
|
|
150
|
+
const manifestSizeKb = Buffer.byteLength(manifestStr, 'utf-8') / 1024;
|
|
151
|
+
if (manifestSizeKb > config.cortexMaxLibSizeKb) {
|
|
152
|
+
return { ok: false, error: `Manifest size ${Math.round(manifestSizeKb)}KB exceeds limit of ${config.cortexMaxLibSizeKb}KB` };
|
|
153
|
+
}
|
|
154
|
+
// Extract libs from libs/ directory
|
|
155
|
+
const libs = {};
|
|
156
|
+
for (const [path, buf] of files) {
|
|
157
|
+
if (path.startsWith('libs/') && path.length > 'libs/'.length) {
|
|
158
|
+
const filename = path.slice('libs/'.length);
|
|
159
|
+
const content = buf.toString('utf-8');
|
|
160
|
+
const sizeKb = Buffer.byteLength(content, 'utf-8') / 1024;
|
|
161
|
+
if (sizeKb > config.cortexMaxLibSizeKb) {
|
|
162
|
+
return { ok: false, error: `Lib "${filename}" is ${sizeKb.toFixed(1)}KB, max is ${config.cortexMaxLibSizeKb}KB` };
|
|
163
|
+
}
|
|
164
|
+
libs[filename] = content;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const result = parseCortexManifest(manifestStr, ownerName, Object.keys(libs).length > 0 ? libs : undefined);
|
|
168
|
+
if (!result.ok || !result.extension) {
|
|
169
|
+
return { ok: false, error: result.errors?.join('; ') ?? 'Manifest validation failed' };
|
|
170
|
+
}
|
|
171
|
+
return { ok: true, extension: result.extension, libs: Object.keys(libs).length > 0 ? libs : undefined };
|
|
172
|
+
}
|
|
173
|
+
// ── Internal ZIP extraction ──
|
|
174
|
+
function extractZipEntries(buffer) {
|
|
175
|
+
return new Promise((resolve, reject) => {
|
|
176
|
+
yauzl.fromBuffer(buffer, { lazyEntries: true }, (err, zipfile) => {
|
|
177
|
+
if (err)
|
|
178
|
+
return reject(err);
|
|
179
|
+
const files = new Map();
|
|
180
|
+
let totalDecompressed = 0;
|
|
181
|
+
const maxDecompressed = buffer.length * DECOMPRESSION_RATIO;
|
|
182
|
+
let fileCount = 0;
|
|
183
|
+
zipfile.on('error', reject);
|
|
184
|
+
zipfile.on('entry', (entry) => {
|
|
185
|
+
fileCount++;
|
|
186
|
+
if (fileCount > MAX_FILES) {
|
|
187
|
+
zipfile.close();
|
|
188
|
+
return reject(new Error(`ZIP contains more than ${MAX_FILES} files`));
|
|
189
|
+
}
|
|
190
|
+
const fileName = entry.fileName;
|
|
191
|
+
if (fileName.includes('../') || fileName.startsWith('/')) {
|
|
192
|
+
zipfile.close();
|
|
193
|
+
return reject(new Error(`Dangerous path detected: ${fileName}`));
|
|
194
|
+
}
|
|
195
|
+
// Symlink check (Unix external attributes)
|
|
196
|
+
const unixMode = (entry.externalFileAttributes >> 16) & 0xffff;
|
|
197
|
+
if ((unixMode & 0o170000) === 0o120000) {
|
|
198
|
+
zipfile.close();
|
|
199
|
+
return reject(new Error(`Symlink detected: ${fileName}`));
|
|
200
|
+
}
|
|
201
|
+
const depth = fileName.split('/').length - 1;
|
|
202
|
+
if (depth > MAX_DEPTH) {
|
|
203
|
+
zipfile.close();
|
|
204
|
+
return reject(new Error(`Directory depth exceeds ${MAX_DEPTH}: ${fileName}`));
|
|
205
|
+
}
|
|
206
|
+
if (fileName.endsWith('/')) {
|
|
207
|
+
zipfile.readEntry();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
zipfile.openReadStream(entry, (readErr, stream) => {
|
|
211
|
+
if (readErr) {
|
|
212
|
+
zipfile.close();
|
|
213
|
+
return reject(readErr);
|
|
214
|
+
}
|
|
215
|
+
const chunks = [];
|
|
216
|
+
stream.on('data', (chunk) => {
|
|
217
|
+
totalDecompressed += chunk.length;
|
|
218
|
+
if (totalDecompressed > maxDecompressed) {
|
|
219
|
+
zipfile.close();
|
|
220
|
+
return reject(new Error('Decompression ratio exceeded (possible zip bomb)'));
|
|
221
|
+
}
|
|
222
|
+
chunks.push(chunk);
|
|
223
|
+
});
|
|
224
|
+
stream.on('end', () => {
|
|
225
|
+
files.set(fileName, Buffer.concat(chunks));
|
|
226
|
+
zipfile.readEntry();
|
|
227
|
+
});
|
|
228
|
+
stream.on('error', (e) => { zipfile.close(); reject(e); });
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
zipfile.on('end', () => resolve(files));
|
|
232
|
+
zipfile.readEntry();
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=upload-zip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-zip.js","sourceRoot":"","sources":["../../../src/services/upload-zip.ts"],"names":[],"mappings":"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"}
|
|
@@ -96,6 +96,38 @@
|
|
|
96
96
|
box-shadow: 0 0 0 3px rgba(232, 86, 74, .12);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
.mode-toggle {
|
|
100
|
+
display: flex;
|
|
101
|
+
gap: 2px;
|
|
102
|
+
flex-shrink: 0;
|
|
103
|
+
background: var(--surface-glass);
|
|
104
|
+
border: 1px solid var(--border-subtle);
|
|
105
|
+
border-radius: var(--radius-sm);
|
|
106
|
+
padding: 2px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.mode-btn {
|
|
110
|
+
padding: 5px 10px;
|
|
111
|
+
border: none;
|
|
112
|
+
border-radius: 6px;
|
|
113
|
+
background: transparent;
|
|
114
|
+
color: var(--text-muted);
|
|
115
|
+
font-size: .78rem;
|
|
116
|
+
cursor: pointer;
|
|
117
|
+
transition: all var(--transition);
|
|
118
|
+
white-space: nowrap;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.mode-btn.active {
|
|
122
|
+
background: var(--accent);
|
|
123
|
+
color: #fff;
|
|
124
|
+
font-weight: 600;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.mode-btn:hover:not(.active) {
|
|
128
|
+
background: rgba(0,0,0,.05);
|
|
129
|
+
}
|
|
130
|
+
|
|
99
131
|
.header-actions {
|
|
100
132
|
display: flex;
|
|
101
133
|
gap: 8px;
|
|
@@ -1002,7 +1034,8 @@
|
|
|
1002
1034
|
|
|
1003
1035
|
/* ── Published Apps Section ──────────────────────── */
|
|
1004
1036
|
|
|
1005
|
-
#published-section
|
|
1037
|
+
#published-section,
|
|
1038
|
+
#community-section {
|
|
1006
1039
|
margin: 0 24px 24px;
|
|
1007
1040
|
position: relative;
|
|
1008
1041
|
z-index: 1;
|
|
@@ -1201,6 +1234,10 @@
|
|
|
1201
1234
|
|
|
1202
1235
|
<header>
|
|
1203
1236
|
<h1>My Apps</h1>
|
|
1237
|
+
<div class="mode-toggle">
|
|
1238
|
+
<button id="mode-global" class="mode-btn active" onclick="window._launcher.switchDbMode('global')" title="Shared across all accounts on this browser">🌐 Global</button>
|
|
1239
|
+
<button id="mode-personal" class="mode-btn" onclick="window._launcher.switchDbMode('personal')" title="Private to your account">👤 Personal</button>
|
|
1240
|
+
</div>
|
|
1204
1241
|
<input type="text" id="search-input" placeholder="Search apps..." autocomplete="off" spellcheck="false"/>
|
|
1205
1242
|
<div class="header-actions">
|
|
1206
1243
|
<button class="icon-btn" id="add-btn" title="Add app">+</button>
|
|
@@ -1253,7 +1290,7 @@
|
|
|
1253
1290
|
</div>
|
|
1254
1291
|
</div>
|
|
1255
1292
|
|
|
1256
|
-
<!-- Published Apps Section -->
|
|
1293
|
+
<!-- Published Apps Section (own) -->
|
|
1257
1294
|
<div id="published-section" style="display:none">
|
|
1258
1295
|
<div class="published-header" onclick="window._launcher.togglePublished()">
|
|
1259
1296
|
<h2>🚀 Published Apps <span id="published-count"></span></h2>
|
|
@@ -1262,12 +1299,24 @@
|
|
|
1262
1299
|
<div id="published-grid" class="published-grid"></div>
|
|
1263
1300
|
</div>
|
|
1264
1301
|
|
|
1302
|
+
<!-- Community Apps Section (other users) -->
|
|
1303
|
+
<div id="community-section" style="display:none">
|
|
1304
|
+
<div class="published-header" onclick="window._launcher.toggleCommunity()">
|
|
1305
|
+
<h2>🌍 Community Apps <span id="community-count"></span></h2>
|
|
1306
|
+
<span class="arrow open" id="community-arrow">▼</span>
|
|
1307
|
+
</div>
|
|
1308
|
+
<div id="community-grid" class="published-grid"></div>
|
|
1309
|
+
</div>
|
|
1310
|
+
|
|
1265
1311
|
<!-- Recently Opened Strip -->
|
|
1266
1312
|
<div id="recent-section" style="display:none;margin:0 24px 1rem">
|
|
1267
1313
|
<h3 style="margin:0 0 .5rem;font-size:.9rem;color:#6B7280">🕒 Recently Opened</h3>
|
|
1268
1314
|
<div id="recent-strip" style="display:flex;gap:.75rem;overflow-x:auto;padding-bottom:.5rem"></div>
|
|
1269
1315
|
</div>
|
|
1270
1316
|
|
|
1317
|
+
<div id="local-apps-header" style="display:none;margin:0 24px 8px">
|
|
1318
|
+
<h2 style="font-size:1rem;font-weight:700;color:#6366f1;display:flex;align-items:center;gap:8px">💾 Local Apps <span id="local-apps-count" style="opacity:.6;font-weight:400"></span></h2>
|
|
1319
|
+
</div>
|
|
1271
1320
|
<main id="app-grid"></main>
|
|
1272
1321
|
|
|
1273
1322
|
<footer>
|
|
@@ -1509,16 +1558,56 @@
|
|
|
1509
1558
|
|
|
1510
1559
|
// ── IndexedDB wrapper ──────────────────────────────
|
|
1511
1560
|
|
|
1512
|
-
const
|
|
1561
|
+
const DB_NAME_GLOBAL = 'AppLauncherDB';
|
|
1513
1562
|
const DB_VERSION = 2;
|
|
1514
1563
|
const STORE_NAME = 'apps';
|
|
1515
1564
|
|
|
1516
1565
|
let dbInstance = null;
|
|
1566
|
+
let currentDbMode = localStorage.getItem('appCatalogMode') || 'global';
|
|
1567
|
+
|
|
1568
|
+
function getDbName() {
|
|
1569
|
+
if (currentDbMode === 'personal') {
|
|
1570
|
+
var owner = null;
|
|
1571
|
+
try {
|
|
1572
|
+
if (window.AIMEAT && window.AIMEAT.auth && window.AIMEAT.auth.getSession()) {
|
|
1573
|
+
owner = window.AIMEAT.auth.getSession().owner || null;
|
|
1574
|
+
}
|
|
1575
|
+
if (!owner) {
|
|
1576
|
+
var stored = localStorage.getItem('aimeat_session');
|
|
1577
|
+
if (stored) owner = JSON.parse(stored).owner || null;
|
|
1578
|
+
}
|
|
1579
|
+
} catch(e) {}
|
|
1580
|
+
if (owner) return DB_NAME_GLOBAL + '_' + owner;
|
|
1581
|
+
}
|
|
1582
|
+
return DB_NAME_GLOBAL;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
function switchDbMode(mode) {
|
|
1586
|
+
if (mode === currentDbMode) return;
|
|
1587
|
+
currentDbMode = mode;
|
|
1588
|
+
localStorage.setItem('appCatalogMode', mode);
|
|
1589
|
+
if (dbInstance) {
|
|
1590
|
+
dbInstance.close();
|
|
1591
|
+
dbInstance = null;
|
|
1592
|
+
}
|
|
1593
|
+
updateModeToggle();
|
|
1594
|
+
refreshAll();
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
function updateModeToggle() {
|
|
1598
|
+
var globalBtn = document.getElementById('mode-global');
|
|
1599
|
+
var personalBtn = document.getElementById('mode-personal');
|
|
1600
|
+
if (!globalBtn || !personalBtn) return;
|
|
1601
|
+
globalBtn.classList.toggle('active', currentDbMode === 'global');
|
|
1602
|
+
personalBtn.classList.toggle('active', currentDbMode === 'personal');
|
|
1603
|
+
}
|
|
1517
1604
|
|
|
1518
1605
|
function openDB() {
|
|
1519
|
-
|
|
1606
|
+
var name = getDbName();
|
|
1607
|
+
if (dbInstance && dbInstance.name === name) return Promise.resolve(dbInstance);
|
|
1608
|
+
if (dbInstance) { dbInstance.close(); dbInstance = null; }
|
|
1520
1609
|
return new Promise(function (resolve, reject) {
|
|
1521
|
-
const request = indexedDB.open(
|
|
1610
|
+
const request = indexedDB.open(name, DB_VERSION);
|
|
1522
1611
|
|
|
1523
1612
|
request.onupgradeneeded = function (event) {
|
|
1524
1613
|
const db = event.target.result;
|
|
@@ -2291,6 +2380,12 @@
|
|
|
2291
2380
|
}
|
|
2292
2381
|
|
|
2293
2382
|
var grid = document.getElementById('app-grid');
|
|
2383
|
+
var localHeader = document.getElementById('local-apps-header');
|
|
2384
|
+
var localCount = document.getElementById('local-apps-count');
|
|
2385
|
+
if (localHeader) {
|
|
2386
|
+
localHeader.style.display = allApps.length > 0 ? '' : 'none';
|
|
2387
|
+
if (localCount) localCount.textContent = '(' + allApps.length + ')';
|
|
2388
|
+
}
|
|
2294
2389
|
|
|
2295
2390
|
if (filtered.length === 0) {
|
|
2296
2391
|
if (!activeTag && !searchQuery && allApps.length === 0) {
|
|
@@ -3111,14 +3206,25 @@
|
|
|
3111
3206
|
arrow.classList.toggle('open', publishedVisible);
|
|
3112
3207
|
}
|
|
3113
3208
|
|
|
3209
|
+
var communityVisible = true;
|
|
3210
|
+
function toggleCommunity() {
|
|
3211
|
+
communityVisible = !communityVisible;
|
|
3212
|
+
var grid = document.getElementById('community-grid');
|
|
3213
|
+
var arrow = document.getElementById('community-arrow');
|
|
3214
|
+
grid.style.display = communityVisible ? '' : 'none';
|
|
3215
|
+
arrow.classList.toggle('open', communityVisible);
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3114
3218
|
function loadPublishedApps() {
|
|
3115
3219
|
var config = loadConfig();
|
|
3116
3220
|
var aimeatUrl = config.aimeatUrl ? config.aimeatUrl.replace(/\/+$/, '') : '';
|
|
3117
3221
|
var section = document.getElementById('published-section');
|
|
3118
3222
|
var grid = document.getElementById('published-grid');
|
|
3119
3223
|
var countEl = document.getElementById('published-count');
|
|
3224
|
+
var communitySection = document.getElementById('community-section');
|
|
3225
|
+
var communityGrid = document.getElementById('community-grid');
|
|
3226
|
+
var communityCountEl = document.getElementById('community-count');
|
|
3120
3227
|
|
|
3121
|
-
// Get current owner name from session (if logged in)
|
|
3122
3228
|
var currentOwner = null;
|
|
3123
3229
|
try {
|
|
3124
3230
|
if (window.AIMEAT && window.AIMEAT.auth && window.AIMEAT.auth.getSession()) {
|
|
@@ -3132,11 +3238,9 @@
|
|
|
3132
3238
|
}
|
|
3133
3239
|
} catch(e) {}
|
|
3134
3240
|
|
|
3135
|
-
// Always load local published apps
|
|
3136
3241
|
getAllApps().then(function(apps) {
|
|
3137
3242
|
var localPublished = apps.filter(function(a) { return a.published; });
|
|
3138
3243
|
|
|
3139
|
-
// Build a lookup of local published apps by filename
|
|
3140
3244
|
var localByFilename = {};
|
|
3141
3245
|
for (var i = 0; i < localPublished.length; i++) {
|
|
3142
3246
|
var lp = localPublished[i];
|
|
@@ -3145,13 +3249,12 @@
|
|
|
3145
3249
|
}
|
|
3146
3250
|
}
|
|
3147
3251
|
|
|
3148
|
-
// If no aimeatUrl, fall back to local-only display
|
|
3149
3252
|
if (!aimeatUrl) {
|
|
3150
3253
|
renderPublishedApps(localPublished, [], localByFilename, aimeatUrl, section, grid, countEl);
|
|
3254
|
+
if (communitySection) communitySection.style.display = 'none';
|
|
3151
3255
|
return;
|
|
3152
3256
|
}
|
|
3153
3257
|
|
|
3154
|
-
// Fetch server apps
|
|
3155
3258
|
fetch(aimeatUrl + '/v1/apps')
|
|
3156
3259
|
.then(function(resp) {
|
|
3157
3260
|
if (!resp.ok) throw new Error('Server returned ' + resp.status);
|
|
@@ -3160,20 +3263,61 @@
|
|
|
3160
3263
|
.then(function(json) {
|
|
3161
3264
|
var serverApps = json.data && json.data.apps ? json.data.apps : [];
|
|
3162
3265
|
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3266
|
+
var ownApps = currentOwner
|
|
3267
|
+
? serverApps.filter(function(a) { return a.owner === currentOwner; })
|
|
3268
|
+
: [];
|
|
3269
|
+
var communityApps = currentOwner
|
|
3270
|
+
? serverApps.filter(function(a) { return a.owner !== currentOwner; })
|
|
3271
|
+
: serverApps;
|
|
3167
3272
|
|
|
3168
|
-
renderPublishedApps(localPublished,
|
|
3273
|
+
renderPublishedApps(localPublished, ownApps, localByFilename, aimeatUrl, section, grid, countEl);
|
|
3274
|
+
renderCommunityApps(communityApps, aimeatUrl, communitySection, communityGrid, communityCountEl, currentOwner);
|
|
3169
3275
|
})
|
|
3170
3276
|
.catch(function() {
|
|
3171
|
-
// Server fetch failed - fall back to local only
|
|
3172
3277
|
renderPublishedApps(localPublished, [], localByFilename, aimeatUrl, section, grid, countEl);
|
|
3278
|
+
if (communitySection) communitySection.style.display = 'none';
|
|
3173
3279
|
});
|
|
3174
3280
|
});
|
|
3175
3281
|
}
|
|
3176
3282
|
|
|
3283
|
+
function renderCommunityApps(serverApps, aimeatUrl, section, grid, countEl, currentOwner) {
|
|
3284
|
+
if (!section || !grid || !countEl) return;
|
|
3285
|
+
if (serverApps.length === 0) {
|
|
3286
|
+
section.style.display = 'none';
|
|
3287
|
+
return;
|
|
3288
|
+
}
|
|
3289
|
+
|
|
3290
|
+
section.style.display = '';
|
|
3291
|
+
countEl.textContent = '(' + serverApps.length + ')';
|
|
3292
|
+
|
|
3293
|
+
var html = '';
|
|
3294
|
+
for (var i = 0; i < serverApps.length; i++) {
|
|
3295
|
+
var sa = serverApps[i];
|
|
3296
|
+
var name = (sa.manifest && sa.manifest.name) ? sa.manifest.name : (sa.filename || '');
|
|
3297
|
+
var version = sa.version_number ? 'v' + sa.version_number : '';
|
|
3298
|
+
var date = sa.created_at ? new Date(sa.created_at).toLocaleDateString() : '';
|
|
3299
|
+
var author = (sa.manifest && sa.manifest.authorDisplay) ? sa.manifest.authorDisplay : (sa.owner || '');
|
|
3300
|
+
var viewUrl = aimeatUrl + '/v1/apps/' + encodeURIComponent(sa.owner || '') + '/' + encodeURIComponent(sa.filename || '');
|
|
3301
|
+
html +=
|
|
3302
|
+
'<div class="published-card">' +
|
|
3303
|
+
'<div class="published-card-name">' + escapeHtml(name) +
|
|
3304
|
+
(version ? ' <span style="opacity:.5;font-size:.85em">' + escapeHtml(version) + '</span>' : '') +
|
|
3305
|
+
'</div>' +
|
|
3306
|
+
'<div class="published-card-meta">' +
|
|
3307
|
+
'<span>' + escapeHtml(sa.filename || '') + '</span>' +
|
|
3308
|
+
'<span>' + date + '</span>' +
|
|
3309
|
+
'</div>' +
|
|
3310
|
+
'<div class="published-card-meta" style="margin-top:2px">' +
|
|
3311
|
+
'<span style="color:var(--accent)">👤 ' + escapeHtml(author) + '</span>' +
|
|
3312
|
+
'</div>' +
|
|
3313
|
+
'<div class="published-card-actions">' +
|
|
3314
|
+
'<button onclick="window.open(\'' + escapeHtml(viewUrl) + '?mode=inline\', \'_blank\')">👁 View</button>' +
|
|
3315
|
+
'</div>' +
|
|
3316
|
+
'</div>';
|
|
3317
|
+
}
|
|
3318
|
+
grid.innerHTML = html;
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3177
3321
|
function renderPublishedApps(localPublished, serverApps, localByFilename, aimeatUrl, section, grid, countEl) {
|
|
3178
3322
|
// Merge: build a combined list. Server apps take priority for display data,
|
|
3179
3323
|
// but we note which source(s) each app comes from.
|
|
@@ -4040,6 +4184,8 @@
|
|
|
4040
4184
|
submitPublish: submitPublish,
|
|
4041
4185
|
loadPublishedApps: loadPublishedApps,
|
|
4042
4186
|
togglePublished: togglePublished,
|
|
4187
|
+
toggleCommunity: toggleCommunity,
|
|
4188
|
+
switchDbMode: switchDbMode,
|
|
4043
4189
|
unpublishApp: unpublishApp,
|
|
4044
4190
|
deleteServerApp: deleteServerApp,
|
|
4045
4191
|
renderRecentlyOpened: renderRecentlyOpened,
|
|
@@ -4065,11 +4211,19 @@
|
|
|
4065
4211
|
window.showCortexPopup = showCortexPopup;
|
|
4066
4212
|
window.cortexCopy = cortexCopy;
|
|
4067
4213
|
|
|
4214
|
+
function refreshAll() {
|
|
4215
|
+
allApps = [];
|
|
4216
|
+
renderApps();
|
|
4217
|
+
loadPublishedApps();
|
|
4218
|
+
renderRecentlyOpened();
|
|
4219
|
+
}
|
|
4220
|
+
|
|
4068
4221
|
// ── Bootstrap ─────────────────────────────────────
|
|
4069
4222
|
|
|
4070
4223
|
document.addEventListener('DOMContentLoaded', function () {
|
|
4071
4224
|
// ── Apply theme on load ─────────────────────────
|
|
4072
4225
|
applyTheme(loadConfig().theme);
|
|
4226
|
+
updateModeToggle();
|
|
4073
4227
|
|
|
4074
4228
|
renderApps();
|
|
4075
4229
|
loadPublishedApps();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aimeat",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "AIME AT
|
|
3
|
+
"version": "1.3.5",
|
|
4
|
+
"description": "AIME AT - AI Memory Exchange and Action Transfer - reference implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
7
7
|
"bin": {
|