aimeat 1.3.5 → 1.4.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/README.md +4 -2
- package/dist/locales/en.json +17 -7
- package/dist/locales/fi.json +17 -7
- package/dist/public/css/views/admin.css +1 -0
- package/dist/public/css/views/profile.css +1 -0
- package/dist/public/views/profile/packages-tab.js +60 -16
- package/dist/src/data/example-packages.d.ts +2 -0
- package/dist/src/data/example-packages.d.ts.map +1 -1
- package/dist/src/data/example-packages.js +85 -38
- package/dist/src/data/example-packages.js.map +1 -1
- package/dist/src/routes/apps.d.ts.map +1 -1
- package/dist/src/routes/apps.js +15 -4
- package/dist/src/routes/apps.js.map +1 -1
- package/dist/src/routes/instances.d.ts.map +1 -1
- package/dist/src/routes/instances.js +18 -6
- package/dist/src/routes/instances.js.map +1 -1
- package/dist/src/routes/packages.js +1 -1
- package/dist/src/routes/packages.js.map +1 -1
- package/dist/src/routes/prompts.d.ts.map +1 -1
- package/dist/src/routes/prompts.js +20 -1
- package/dist/src/routes/prompts.js.map +1 -1
- package/dist/src/services/component-registrar.d.ts +5 -0
- package/dist/src/services/component-registrar.d.ts.map +1 -1
- package/dist/src/services/component-registrar.js +78 -11
- package/dist/src/services/component-registrar.js.map +1 -1
- package/dist/src/services/prompt-defaults.d.ts.map +1 -1
- package/dist/src/services/prompt-defaults.js +303 -0
- package/dist/src/services/prompt-defaults.js.map +1 -1
- package/dist/src/services/prompt-seeder.d.ts.map +1 -1
- package/dist/src/services/prompt-seeder.js +3 -2
- package/dist/src/services/prompt-seeder.js.map +1 -1
- package/dist/src/storage/interface.d.ts +1 -1
- package/dist/src/storage/interface.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.js +1 -1
- package/dist/src/storage/providers/mongodb/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 +3 -1
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/static/app-catalog.html +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +1 -1
package/README.md
CHANGED
|
@@ -260,10 +260,12 @@ Each node runs independently with its own identity and portal. Operators customi
|
|
|
260
260
|
|
|
261
261
|
### Quick start with npx
|
|
262
262
|
|
|
263
|
-
Requires Node.js 24+.
|
|
263
|
+
Requires Node.js 24+. Runs without cloning the repo:
|
|
264
264
|
|
|
265
265
|
```bash
|
|
266
|
-
npx aimeat
|
|
266
|
+
npx aimeat init # interactive setup, generates .env
|
|
267
|
+
npx aimeat start # start the server
|
|
268
|
+
npx aimeat seed # seed example packages (in another terminal, server must be running)
|
|
267
269
|
```
|
|
268
270
|
|
|
269
271
|
### From source
|
package/dist/locales/en.json
CHANGED
|
@@ -4431,6 +4431,8 @@
|
|
|
4431
4431
|
"copiedBtn": "Copied!"
|
|
4432
4432
|
},
|
|
4433
4433
|
"packages": {
|
|
4434
|
+
"title": "Packages",
|
|
4435
|
+
"desc": "Packages bundle schemas, extensions, apps, and seed data into installable units. Install ready-made packages from the gallery, browse what others have published, or create your own in the Generator.",
|
|
4434
4436
|
"created": "Package created",
|
|
4435
4437
|
"updated": "Package updated",
|
|
4436
4438
|
"archived": "Package archived",
|
|
@@ -4445,6 +4447,8 @@
|
|
|
4445
4447
|
"noUpdateAvailable": "No update available",
|
|
4446
4448
|
"updateAvailable": "Update available",
|
|
4447
4449
|
"migrationApplied": "Migration applied successfully",
|
|
4450
|
+
"applyUpdateConfirm": "Apply update? Your data (memory, settings) will be preserved. Apps, extensions, and schemas will be updated to the latest version.",
|
|
4451
|
+
"updateApplied": "Update applied successfully",
|
|
4448
4452
|
"exportFailed": "Export failed",
|
|
4449
4453
|
"importFailed": "Import failed",
|
|
4450
4454
|
"myInstances": "My Instances",
|
|
@@ -4452,12 +4456,25 @@
|
|
|
4452
4456
|
"gallery": "Template Gallery",
|
|
4453
4457
|
"search": "Search...",
|
|
4454
4458
|
"allCategories": "All Categories",
|
|
4459
|
+
"featured": "Featured",
|
|
4460
|
+
"categories": {
|
|
4461
|
+
"signage": "Signage",
|
|
4462
|
+
"marketplace": "Marketplace",
|
|
4463
|
+
"iot": "IoT",
|
|
4464
|
+
"social": "Social",
|
|
4465
|
+
"productivity": "Productivity",
|
|
4466
|
+
"communication": "Communication",
|
|
4467
|
+
"other": "Other"
|
|
4468
|
+
},
|
|
4455
4469
|
"version": "Version",
|
|
4456
4470
|
"components": "components",
|
|
4457
4471
|
"by": "by",
|
|
4458
4472
|
"install": "Install",
|
|
4459
4473
|
"remove": "Remove",
|
|
4460
4474
|
"checkUpdate": "Check Update",
|
|
4475
|
+
"createWithAi": "Create Package with AI",
|
|
4476
|
+
"promptCopied": "Prompt copied!",
|
|
4477
|
+
"createWithAiDesc": "Copy this prompt and paste it into any AI. Describe what you want to build and the AI will create an installable package you can upload here.",
|
|
4461
4478
|
"noInstances": "No packages installed yet.",
|
|
4462
4479
|
"noTemplates": "No templates available yet.",
|
|
4463
4480
|
"labelPrompt": "Instance label (optional):",
|
|
@@ -4508,13 +4525,6 @@
|
|
|
4508
4525
|
"sourceNode": "Source Node",
|
|
4509
4526
|
"installFromRemote": "Install from Remote",
|
|
4510
4527
|
"syncSuccess": "Federation sync complete"
|
|
4511
|
-
},
|
|
4512
|
-
"gallery": {
|
|
4513
|
-
"install": "Install",
|
|
4514
|
-
"downloadZip": "Download ZIP",
|
|
4515
|
-
"components": "Components",
|
|
4516
|
-
"rating": "Rating",
|
|
4517
|
-
"installs": "Installs"
|
|
4518
4528
|
}
|
|
4519
4529
|
},
|
|
4520
4530
|
"templates": {
|
package/dist/locales/fi.json
CHANGED
|
@@ -4431,6 +4431,8 @@
|
|
|
4431
4431
|
"copiedBtn": "Kopioitu!"
|
|
4432
4432
|
},
|
|
4433
4433
|
"packages": {
|
|
4434
|
+
"title": "Paketit",
|
|
4435
|
+
"desc": "Paketit kokoavat skeemoja, laajennuksia, sovelluksia ja alkuarvoja asennettaviksi kokonaisuuksiksi. Asenna valmiita paketteja galleriasta, selaa muiden julkaisuja tai luo omasi Generaattorissa.",
|
|
4434
4436
|
"created": "Paketti luotu",
|
|
4435
4437
|
"updated": "Paketti päivitetty",
|
|
4436
4438
|
"archived": "Paketti arkistoitu",
|
|
@@ -4445,6 +4447,8 @@
|
|
|
4445
4447
|
"noUpdateAvailable": "Ei päivitystä saatavilla",
|
|
4446
4448
|
"updateAvailable": "Päivitys saatavilla",
|
|
4447
4449
|
"migrationApplied": "Migraatio suoritettu onnistuneesti",
|
|
4450
|
+
"applyUpdateConfirm": "Paivitetaanko? Tietosi (muisti, asetukset) sailyvat. Sovellukset, laajennukset ja skeemat paivitetaan uusimpaan versioon.",
|
|
4451
|
+
"updateApplied": "Paivitys asennettu onnistuneesti",
|
|
4448
4452
|
"exportFailed": "Vienti epäonnistui",
|
|
4449
4453
|
"importFailed": "Tuonti epäonnistui",
|
|
4450
4454
|
"myInstances": "Omat instanssit",
|
|
@@ -4452,12 +4456,25 @@
|
|
|
4452
4456
|
"gallery": "Mallipohjagalleria",
|
|
4453
4457
|
"search": "Hae...",
|
|
4454
4458
|
"allCategories": "Kaikki kategoriat",
|
|
4459
|
+
"featured": "Suositeltu",
|
|
4460
|
+
"categories": {
|
|
4461
|
+
"signage": "Opasteet",
|
|
4462
|
+
"marketplace": "Markkinapaikka",
|
|
4463
|
+
"iot": "IoT",
|
|
4464
|
+
"social": "Sosiaalinen",
|
|
4465
|
+
"productivity": "Tuottavuus",
|
|
4466
|
+
"communication": "Viestintä",
|
|
4467
|
+
"other": "Muu"
|
|
4468
|
+
},
|
|
4455
4469
|
"version": "Versio",
|
|
4456
4470
|
"components": "komponenttia",
|
|
4457
4471
|
"by": "tekij\u00e4",
|
|
4458
4472
|
"install": "Asenna",
|
|
4459
4473
|
"remove": "Poista",
|
|
4460
4474
|
"checkUpdate": "Tarkista p\u00e4ivitys",
|
|
4475
|
+
"createWithAi": "Luo paketti tekoalylla",
|
|
4476
|
+
"promptCopied": "Prompt kopioitu!",
|
|
4477
|
+
"createWithAiDesc": "Kopioi tama prompt ja liita se mihin tahansa tekoalyyn. Kuvaile mita haluat rakentaa ja tekoaly luo asennettavan paketin.",
|
|
4461
4478
|
"noInstances": "Ei asennettuja paketteja.",
|
|
4462
4479
|
"noTemplates": "Ei malleja saatavilla.",
|
|
4463
4480
|
"labelPrompt": "Instanssin nimi (valinnainen):",
|
|
@@ -4508,13 +4525,6 @@
|
|
|
4508
4525
|
"sourceNode": "Lähdesolmu",
|
|
4509
4526
|
"installFromRemote": "Asenna etäsolmusta",
|
|
4510
4527
|
"syncSuccess": "Federaatiosynkronointi valmis"
|
|
4511
|
-
},
|
|
4512
|
-
"gallery": {
|
|
4513
|
-
"install": "Asenna",
|
|
4514
|
-
"downloadZip": "Lataa ZIP",
|
|
4515
|
-
"components": "Komponentit",
|
|
4516
|
-
"rating": "Arvosana",
|
|
4517
|
-
"installs": "Asennukset"
|
|
4518
4528
|
}
|
|
4519
4529
|
},
|
|
4520
4530
|
"templates": {
|
|
@@ -555,6 +555,7 @@
|
|
|
555
555
|
.adm-badge-suspended { background: #FEF3C7; color: #B45309; }
|
|
556
556
|
.adm-badge-published { background: #D1FAE5; color: #047857; }
|
|
557
557
|
.adm-badge-active { background: #D1FAE5; color: #047857; }
|
|
558
|
+
.adm-badge-installed { background: #D1FAE5; color: #047857; }
|
|
558
559
|
|
|
559
560
|
/* ── Board Members ── */
|
|
560
561
|
.adm-board-detail { border-top: 1px solid var(--card-border, var(--glass-border)); padding-top: 8px; }
|
|
@@ -371,6 +371,7 @@
|
|
|
371
371
|
.pf .pkg-tag{font-size:.7rem;padding:2px 8px;border-radius:6px;background:var(--bg-dim,#F3F4F6);color:var(--text-dim,#6B7280)}
|
|
372
372
|
.pf .pkg-badge{display:inline-block;padding:2px 8px;border-radius:6px;font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.3px}
|
|
373
373
|
.pf .pkg-badge-active{background:rgba(74,222,128,0.15);color:#4ade80}
|
|
374
|
+
.pf .pkg-badge-installed{background:rgba(74,222,128,0.15);color:#4ade80}
|
|
374
375
|
.pf .pkg-badge-inactive{background:rgba(156,163,175,0.15);color:#9ca3af}
|
|
375
376
|
.pf .pkg-badge-error{background:rgba(239,68,68,0.15);color:#f87171}
|
|
376
377
|
.pf .pkg-badge-featured{background:rgba(251,191,36,0.15);color:#fbbf24}
|
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
* importing packages into generator projects (fork vs edit detection)
|
|
17
17
|
* v1.3.0 — 2026-03-20 — add ZIP upload/download and template proposal buttons
|
|
18
18
|
* v1.4.0 — 2026-03-20 — add remote templates section with federation sync
|
|
19
|
+
* v1.5.0 — 2026-05-05 — add intro section, i18n for categories/featured, fix status badge label
|
|
19
20
|
*/
|
|
20
21
|
import { h } from 'preact';
|
|
21
22
|
import { useState, useEffect, useCallback } from 'preact/hooks';
|
|
22
23
|
import htm from 'htm';
|
|
23
24
|
const html = htm.bind(h);
|
|
24
25
|
import { t } from '/js/i18n.js';
|
|
25
|
-
import { escHtml } from '/js/utils.js';
|
|
26
|
+
import { escHtml, copyToClipboard } from '/js/utils.js';
|
|
26
27
|
import { Spinner } from './shared.js';
|
|
27
28
|
import { useConfirm } from '/components/Modal.js';
|
|
28
29
|
import * as pkgService from '/js/services/packages.js';
|
|
@@ -44,8 +45,8 @@ export default function PackagesTab({ session, showToast, navigate, locale }) {
|
|
|
44
45
|
setLoading(true);
|
|
45
46
|
try {
|
|
46
47
|
const [instRes, pkgRes, tplRes] = await Promise.all([
|
|
47
|
-
pkgService.listInstances({ status: '
|
|
48
|
-
pkgService.listPackages({
|
|
48
|
+
pkgService.listInstances({ status: 'installed' }),
|
|
49
|
+
pkgService.listPackages({ author: session?.owner }),
|
|
49
50
|
pkgService.listTemplates({ sort: 'newest' }),
|
|
50
51
|
]);
|
|
51
52
|
if (instRes.ok) setInstances(instRes.data?.instances ?? []);
|
|
@@ -83,7 +84,7 @@ export default function PackagesTab({ session, showToast, navigate, locale }) {
|
|
|
83
84
|
|
|
84
85
|
const handleRemove = async (id) => {
|
|
85
86
|
confirm(t('packages.confirmRemove') || 'Remove this instance?', async () => {
|
|
86
|
-
const res = await pkgService.removeInstance(id,
|
|
87
|
+
const res = await pkgService.removeInstance(id, true);
|
|
87
88
|
if (res.ok) {
|
|
88
89
|
showToast(t('packages.instanceRemoved'));
|
|
89
90
|
loadData();
|
|
@@ -97,7 +98,27 @@ export default function PackagesTab({ session, showToast, navigate, locale }) {
|
|
|
97
98
|
const res = await pkgService.checkUpdate(id);
|
|
98
99
|
if (res.ok && res.data) {
|
|
99
100
|
if (res.data.updateAvailable) {
|
|
100
|
-
|
|
101
|
+
confirm(
|
|
102
|
+
`${t('packages.updateAvailable')}: ${res.data.latestVersion}\n\n${t('packages.applyUpdateConfirm') || 'Apply update? Your data (memory, settings) will be preserved. Apps, extensions, and schemas will be updated to the latest version.'}`,
|
|
103
|
+
async () => {
|
|
104
|
+
const instRes = await pkgService.getInstance(id);
|
|
105
|
+
const installed = instRes.data?.installedComponents || [];
|
|
106
|
+
const components = installed.map(c => ({
|
|
107
|
+
componentId: c.componentId,
|
|
108
|
+
action: (c.type === 'memory' || c.type === 'translation') ? 'skip' : 'replace',
|
|
109
|
+
}));
|
|
110
|
+
const applyRes = await pkgService.applyMigration(id, {
|
|
111
|
+
targetVersion: res.data.latestVersion,
|
|
112
|
+
components,
|
|
113
|
+
});
|
|
114
|
+
if (applyRes.ok) {
|
|
115
|
+
showToast(t('packages.updateApplied') || 'Update applied successfully');
|
|
116
|
+
loadData();
|
|
117
|
+
} else {
|
|
118
|
+
showToast(applyRes.error?.message || 'Update failed', true);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
);
|
|
101
122
|
} else {
|
|
102
123
|
showToast(t('packages.noUpdateAvailable'));
|
|
103
124
|
}
|
|
@@ -183,6 +204,21 @@ export default function PackagesTab({ session, showToast, navigate, locale }) {
|
|
|
183
204
|
|
|
184
205
|
const isOperator = session?.roles?.includes('operator');
|
|
185
206
|
|
|
207
|
+
const copyPackagePrompt = async () => {
|
|
208
|
+
try {
|
|
209
|
+
const res = await fetch('/v1/prompts/package-builder');
|
|
210
|
+
const json = await res.json();
|
|
211
|
+
if (json.ok && json.data?.content) {
|
|
212
|
+
copyToClipboard(json.data.content);
|
|
213
|
+
showToast(t('packages.promptCopied') || 'Prompt copied! Paste it into AI Chat.');
|
|
214
|
+
} else {
|
|
215
|
+
showToast('Failed to load prompt', true);
|
|
216
|
+
}
|
|
217
|
+
} catch (e) {
|
|
218
|
+
showToast('Failed to load prompt', true);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
186
222
|
if (loading) return html`<${Spinner} />`;
|
|
187
223
|
|
|
188
224
|
const filtered = packages.filter(p => {
|
|
@@ -193,6 +229,14 @@ export default function PackagesTab({ session, showToast, navigate, locale }) {
|
|
|
193
229
|
|
|
194
230
|
return html`
|
|
195
231
|
<div class="pkg-tab">
|
|
232
|
+
<div class="section-title">${t('packages.title')}</div>
|
|
233
|
+
<div class="section-desc">${t('packages.desc')}</div>
|
|
234
|
+
<div style="display:flex;gap:.75rem;flex-wrap:wrap;margin-bottom:1.25rem;align-items:center">
|
|
235
|
+
<button class="btn-primary" onClick=${copyPackagePrompt}>
|
|
236
|
+
${'\u{1F916}'} ${t('packages.createWithAi') || 'Create Package with AI'}
|
|
237
|
+
</button>
|
|
238
|
+
<span style="font-size:.8rem;color:var(--text-dim,#6B7280);max-width:420px">${t('packages.createWithAiDesc') || 'Copy this prompt and paste it into any AI. Describe what you want to build and the AI will create an installable package.'}</span>
|
|
239
|
+
</div>
|
|
196
240
|
<div class="pkg-nav">
|
|
197
241
|
<button class=${view === 'instances' ? 'active' : ''} onClick=${() => setView('instances')}>
|
|
198
242
|
\u{1F4E6} ${t('packages.myInstances') || 'My Instances'} (${instances.length})
|
|
@@ -239,23 +283,23 @@ export default function PackagesTab({ session, showToast, navigate, locale }) {
|
|
|
239
283
|
<div class="pkg-section">
|
|
240
284
|
<div class="pkg-section-header">
|
|
241
285
|
<h3>${t('packages.browse') || 'Available Packages'}</h3>
|
|
242
|
-
<
|
|
286
|
+
<input type="file" id="pkg-zip-file" accept=".zip" style="display:none" onChange=${handleUploadZip} />
|
|
287
|
+
<button class="btn-outline" onClick=${() => document.getElementById('pkg-zip-file')?.click()}>
|
|
243
288
|
${t('packages.uploadZip') || 'Upload ZIP'}
|
|
244
|
-
|
|
245
|
-
</label>
|
|
289
|
+
</button>
|
|
246
290
|
</div>
|
|
247
291
|
<div class="pkg-filters">
|
|
248
292
|
<input type="text" placeholder=${t('packages.search') || 'Search...'}
|
|
249
293
|
value=${search} onInput=${e => setSearch(e.target.value)} />
|
|
250
294
|
<select value=${category} onChange=${e => setCategory(e.target.value)}>
|
|
251
295
|
<option value="">${t('packages.allCategories') || 'All Categories'}</option>
|
|
252
|
-
<option value="signage"
|
|
253
|
-
<option value="marketplace"
|
|
254
|
-
<option value="iot"
|
|
255
|
-
<option value="social"
|
|
256
|
-
<option value="productivity"
|
|
257
|
-
<option value="communication"
|
|
258
|
-
<option value="other"
|
|
296
|
+
<option value="signage">${t('packages.categories.signage') || 'Signage'}</option>
|
|
297
|
+
<option value="marketplace">${t('packages.categories.marketplace') || 'Marketplace'}</option>
|
|
298
|
+
<option value="iot">${t('packages.categories.iot') || 'IoT'}</option>
|
|
299
|
+
<option value="social">${t('packages.categories.social') || 'Social'}</option>
|
|
300
|
+
<option value="productivity">${t('packages.categories.productivity') || 'Productivity'}</option>
|
|
301
|
+
<option value="communication">${t('packages.categories.communication') || 'Communication'}</option>
|
|
302
|
+
<option value="other">${t('packages.categories.other') || 'Other'}</option>
|
|
259
303
|
</select>
|
|
260
304
|
</div>
|
|
261
305
|
${filtered.length === 0 ? html`
|
|
@@ -317,7 +361,7 @@ export default function PackagesTab({ session, showToast, navigate, locale }) {
|
|
|
317
361
|
<div class="pkg-card" key=${tpl.id}>
|
|
318
362
|
<div class="pkg-card-header">
|
|
319
363
|
<span class="pkg-card-name">${escHtml(tpl.title)}</span>
|
|
320
|
-
${tpl.featured && html`<span class="pkg-badge pkg-badge-featured">\u2B50 Featured</span>`}
|
|
364
|
+
${tpl.featured && html`<span class="pkg-badge pkg-badge-featured">\u2B50 ${t('packages.featured') || 'Featured'}</span>`}
|
|
321
365
|
</div>
|
|
322
366
|
<div class="pkg-card-desc">${escHtml(tpl.description || '')}</div>
|
|
323
367
|
<div class="pkg-card-meta">
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* v1.2.0 — 2026-03-17 — rename Ad Slots to Rotated Views; add edit capability;
|
|
15
15
|
* support image/HTML/URL content types; layout modes (fullscreen/header/full);
|
|
16
16
|
* light+dark themes with accent colour; AI prompt helper for HTML views
|
|
17
|
+
* v1.3.0 — 2026-05-05 — rewrite cortex manifest with proper components array,
|
|
18
|
+
* .js lib filenames, tags, exports, and api_surface metadata
|
|
17
19
|
*/
|
|
18
20
|
import type { PackageRecord, TemplateListingRecord } from '../storage/interface.js';
|
|
19
21
|
export interface ExamplePackageDef {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"example-packages.d.ts","sourceRoot":"","sources":["../../../src/data/example-packages.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"example-packages.d.ts","sourceRoot":"","sources":["../../../src/data/example-packages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAoB,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAItG,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,QAAQ,GAAG,SAAS,CAAC;IACjC,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IACnG,eAAe,EAAE;QACf,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC;CACH;AAgBD,2CAA2C;AAC3C,wBAAgB,kBAAkB,IAAI,iBAAiB,EAAE,CAExD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;IACxF,GAAG,EAAE,aAAa,CAAC;IACnB,OAAO,EAAE,qBAAqB,CAAC;CAChC,CAwDA"}
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* v1.2.0 — 2026-03-17 — rename Ad Slots to Rotated Views; add edit capability;
|
|
15
15
|
* support image/HTML/URL content types; layout modes (fullscreen/header/full);
|
|
16
16
|
* light+dark themes with accent colour; AI prompt helper for HTML views
|
|
17
|
+
* v1.3.0 — 2026-05-05 — rewrite cortex manifest with proper components array,
|
|
18
|
+
* .js lib filenames, tags, exports, and api_surface metadata
|
|
17
19
|
*/
|
|
18
20
|
import { createHash, randomUUID } from 'node:crypto';
|
|
19
21
|
// ── Helpers ──────────────────────────────────────────────────────────
|
|
@@ -152,7 +154,8 @@ function memoryInit() {
|
|
|
152
154
|
{
|
|
153
155
|
key: 'signage:config',
|
|
154
156
|
value: {
|
|
155
|
-
|
|
157
|
+
rotationEnabled: true, rotationIntervalSec: 8,
|
|
158
|
+
emergencyOverride: true, theme: 'dark',
|
|
156
159
|
layout: 'full', accentColor: '#1a1a2e',
|
|
157
160
|
locale: 'en', buildingName: 'Sunrise Residences', floors: 12, timezone: 'Europe/Helsinki',
|
|
158
161
|
},
|
|
@@ -181,10 +184,36 @@ function memoryInit() {
|
|
|
181
184
|
}, null, 2);
|
|
182
185
|
}
|
|
183
186
|
const CORTEX_SIGNAGE = JSON.stringify({
|
|
184
|
-
manifest:
|
|
187
|
+
manifest: [
|
|
188
|
+
'name: signage-cortex',
|
|
189
|
+
'version: 1.0.0',
|
|
190
|
+
'description: Content management cortex for digital signage -- schedules content rotation, prunes expired announcements, and provides helper libraries for kiosk apps.',
|
|
191
|
+
'tags: [signage, kiosk, content-management, scheduling]',
|
|
192
|
+
'capabilities: [memory_read, memory_write, consent_check]',
|
|
193
|
+
'triggers:',
|
|
194
|
+
' - { event: schedule, cron: "0 8 * * *", action: rotate_daily_content }',
|
|
195
|
+
' - { event: memory_change, key: "signage:announcements", action: notify_kiosk_refresh }',
|
|
196
|
+
'components:',
|
|
197
|
+
' - type: lib',
|
|
198
|
+
' name: contentRotation',
|
|
199
|
+
' filename: contentRotation.js',
|
|
200
|
+
' exports: [selectActiveAnnouncements]',
|
|
201
|
+
' api_surface: |',
|
|
202
|
+
' selectActiveAnnouncements(announcements, now)',
|
|
203
|
+
' Filters expired announcements and sorts by priority (emergency > urgent > normal).',
|
|
204
|
+
' Returns: filtered, sorted array.',
|
|
205
|
+
' - type: lib',
|
|
206
|
+
' name: scheduling',
|
|
207
|
+
' filename: scheduling.js',
|
|
208
|
+
' exports: [rotateDailyContent]',
|
|
209
|
+
' api_surface: |',
|
|
210
|
+
' rotateDailyContent(api)',
|
|
211
|
+
' Reads signage:announcements, removes expired entries, writes back.',
|
|
212
|
+
' Called by the daily cron trigger.',
|
|
213
|
+
].join('\n'),
|
|
185
214
|
libs: {
|
|
186
|
-
contentRotation: 'function selectActiveAnnouncements(a,now){return a.filter(x=>!x.expiresAt||new Date(x.expiresAt)>now).sort((a,b)=>({emergency:0,urgent:1,normal:2}[a.priority]??2)-({emergency:0,urgent:1,normal:2}[b.priority]??2));}',
|
|
187
|
-
scheduling: 'async function rotateDailyContent(api){const a=await api.get("signage:announcements");const now=new Date();const active=a.filter(x=>!x.expiresAt||new Date(x.expiresAt)>now);if(active.length!==a.length)await api.set("signage:announcements",active);}',
|
|
215
|
+
'contentRotation.js': 'function selectActiveAnnouncements(a,now){return a.filter(x=>!x.expiresAt||new Date(x.expiresAt)>now).sort((a,b)=>({emergency:0,urgent:1,normal:2}[a.priority]??2)-({emergency:0,urgent:1,normal:2}[b.priority]??2));}',
|
|
216
|
+
'scheduling.js': 'async function rotateDailyContent(api){const a=await api.get("signage:announcements");const now=new Date();const active=a.filter(x=>!x.expiresAt||new Date(x.expiresAt)>now);if(active.length!==a.length)await api.set("signage:announcements",active);}',
|
|
188
217
|
},
|
|
189
218
|
}, null, 2);
|
|
190
219
|
// ── Admin Panel App ──────────────────────────────────────────────────
|
|
@@ -286,7 +315,11 @@ button.outline{background:#fff;color:#0066cc;border:1px solid #0066cc;padding:.3
|
|
|
286
315
|
<div id="tab-config" class="panel" style="display:none">
|
|
287
316
|
<h2>Display Settings</h2>
|
|
288
317
|
<div class="form-group"><label>Building Name</label><input id="cfg-building" placeholder="Building name"></div>
|
|
289
|
-
<div class="form-group"
|
|
318
|
+
<div class="form-group">
|
|
319
|
+
<label style="display:flex;align-items:center;gap:.5rem;cursor:pointer"><input id="cfg-rotation" type="checkbox" checked> Rotate views automatically</label>
|
|
320
|
+
<small style="color:#666">When off, only the first active view is shown</small>
|
|
321
|
+
</div>
|
|
322
|
+
<div class="form-group" id="cfg-interval-group"><label>Rotation Speed (seconds)</label><input id="cfg-interval" type="number" value="8" min="2" max="120"><small style="color:#666">How many seconds each view is shown before switching to the next</small></div>
|
|
290
323
|
<div class="form-group">
|
|
291
324
|
<label>Layout</label>
|
|
292
325
|
<div class="layout-options">
|
|
@@ -390,32 +423,32 @@ document.getElementById("ai-prompt-box").textContent=AI_CHAT_PROMPT;
|
|
|
390
423
|
el.innerHTML='<div class="type-fields"><div class="form-group"><label>Webpage URL</label><input id="view-content" placeholder="https://example.com"></div></div>';
|
|
391
424
|
}}
|
|
392
425
|
|
|
393
|
-
var AI_CHAT_PROMPT='You are a Digital Signage View Designer. Your job is to interview the user step by step and then produce a single self-contained HTML file (HTML + CSS + JS all in one file) that will be displayed on a digital signage kiosk screen
|
|
394
|
-
+'## Interview Process
|
|
395
|
-
+'Ask these questions ONE AT A TIME. Wait for the user to answer before asking the next one. Be friendly and enthusiastic
|
|
396
|
-
+'1. **Purpose**: "What do you want to show on the signage screen? For example: weather, clock, company info, event schedule, welcome message, menu, news feed, social media, custom animation, or something else?"
|
|
397
|
-
+'2. **Style**: "What visual style do you prefer? Options
|
|
398
|
-
+'3. **Colors**: "Any specific brand colors? Or should I pick colors that match your chosen style? You can say things like: dark background with blue accents, or use our brand colors #FF5733 and #333333"
|
|
399
|
-
+'4. **Content details**: Based on what they chose in step 1, ask relevant follow-up questions. For example
|
|
400
|
-
+'5. **Animations**: "Do you want any animations or movement? Options
|
|
401
|
-
+'6. **Size & layout**: "This will be displayed on a screen section. Should the content
|
|
402
|
-
+'## Output Rules
|
|
403
|
-
+'After the interview, produce the HTML file following these rules
|
|
404
|
-
+'- Single self-contained HTML file with inline CSS and JS (no external dependencies unless from CDN)
|
|
405
|
-
+'- Use <!DOCTYPE html> and proper structure
|
|
406
|
-
+'- The page should look great at any screen size (use vh/vw units, flexbox/grid)
|
|
407
|
-
+'- Background should fill the viewport (no white edges)
|
|
408
|
-
+'- Use system-ui font stack unless a specific font is requested
|
|
409
|
-
+'- If using animations, use CSS animations or requestAnimationFrame for smooth 60fps
|
|
410
|
-
+'- If showing live data (time, weather), update it automatically
|
|
411
|
-
+'- For weather, use a free API like wttr.in or open-meteo.com
|
|
412
|
-
+'- Make text large and readable from a distance (this is for a wall-mounted screen)
|
|
413
|
-
+'- High contrast between text and background
|
|
414
|
-
+'- No scrollbars, no overflow
|
|
415
|
-
+'## Final delivery
|
|
416
|
-
+'When you output the HTML, say
|
|
417
|
-
+'"Here is your signage view! Copy ALL the code below and paste it into the **HTML Code** field in your Digital Signage Admin panel (Rotated Views tab, select HTML type)."
|
|
418
|
-
+'Then output the complete HTML in a code block
|
|
426
|
+
var AI_CHAT_PROMPT='You are a Digital Signage View Designer. Your job is to interview the user step by step and then produce a single self-contained HTML file (HTML + CSS + JS all in one file) that will be displayed on a digital signage kiosk screen.\\n\\n'
|
|
427
|
+
+'## Interview Process\\n\\n'
|
|
428
|
+
+'Ask these questions ONE AT A TIME. Wait for the user to answer before asking the next one. Be friendly and enthusiastic.\\n\\n'
|
|
429
|
+
+'1. **Purpose**: "What do you want to show on the signage screen? For example: weather, clock, company info, event schedule, welcome message, menu, news feed, social media, custom animation, or something else?"\\n\\n'
|
|
430
|
+
+'2. **Style**: "What visual style do you prefer? Options:\\n - Clean & minimal (lots of whitespace, simple fonts)\\n - Bold & colorful (gradients, vibrant colors, shadows)\\n - Corporate & professional (muted colors, structured layout)\\n - Playful & animated (movement, transitions, fun elements)\\n - Futuristic / tech (dark background, glowing effects, particle animations)\\n - Or describe your own style!"\\n\\n'
|
|
431
|
+
+'3. **Colors**: "Any specific brand colors? Or should I pick colors that match your chosen style? You can say things like: dark background with blue accents, or use our brand colors #FF5733 and #333333"\\n\\n'
|
|
432
|
+
+'4. **Content details**: Based on what they chose in step 1, ask relevant follow-up questions. For example:\\n - Weather: "Which city? Celsius or Fahrenheit?"\\n - Clock: "12h or 24h format? Show date too?"\\n - Welcome message: "What should the text say? Any logo URL?"\\n - Menu/schedule: "List the items you want to display"\\n - Custom: "Describe in detail what should appear on screen"\\n\\n'
|
|
433
|
+
+'5. **Animations**: "Do you want any animations or movement? Options:\\n - None (static page)\\n - Subtle (gentle fades, slow floating elements)\\n - Dynamic (sliding text, rotating elements, particle effects)\\n - Eye-catching (bold transitions, attention-grabbing movement)"\\n\\n'
|
|
434
|
+
+'6. **Size & layout**: "This will be displayed on a screen section. Should the content:\\n - Fill the entire area (full bleed)\\n - Be centered with padding\\n - Use a specific layout (sidebar, header+content, grid)?"\\n\\n'
|
|
435
|
+
+'## Output Rules\\n\\n'
|
|
436
|
+
+'After the interview, produce the HTML file following these rules:\\n\\n'
|
|
437
|
+
+'- Single self-contained HTML file with inline CSS and JS (no external dependencies unless from CDN)\\n'
|
|
438
|
+
+'- Use <!DOCTYPE html> and proper structure\\n'
|
|
439
|
+
+'- The page should look great at any screen size (use vh/vw units, flexbox/grid)\\n'
|
|
440
|
+
+'- Background should fill the viewport (no white edges)\\n'
|
|
441
|
+
+'- Use system-ui font stack unless a specific font is requested\\n'
|
|
442
|
+
+'- If using animations, use CSS animations or requestAnimationFrame for smooth 60fps\\n'
|
|
443
|
+
+'- If showing live data (time, weather), update it automatically\\n'
|
|
444
|
+
+'- For weather, use a free API like wttr.in or open-meteo.com\\n'
|
|
445
|
+
+'- Make text large and readable from a distance (this is for a wall-mounted screen)\\n'
|
|
446
|
+
+'- High contrast between text and background\\n'
|
|
447
|
+
+'- No scrollbars, no overflow\\n\\n'
|
|
448
|
+
+'## Final delivery\\n\\n'
|
|
449
|
+
+'When you output the HTML, say:\\n\\n'
|
|
450
|
+
+'"Here is your signage view! Copy ALL the code below and paste it into the **HTML Code** field in your Digital Signage Admin panel (Rotated Views tab, select HTML type)."\\n\\n'
|
|
451
|
+
+'Then output the complete HTML in a code block.\\n\\n'
|
|
419
452
|
+'After the code, add: "You can preview it in the admin panel and edit it anytime. Want me to make any changes?"';
|
|
420
453
|
|
|
421
454
|
function copyAiPrompt(){
|
|
@@ -510,7 +543,11 @@ function loadConfig(){
|
|
|
510
543
|
memGet('signage:config').then(function(_r){
|
|
511
544
|
var c=_r?_r.value:{};
|
|
512
545
|
document.getElementById('cfg-building').value=c.buildingName||'';
|
|
513
|
-
|
|
546
|
+
var rotEnabled=c.rotationEnabled!==false;
|
|
547
|
+
document.getElementById('cfg-rotation').checked=rotEnabled;
|
|
548
|
+
document.getElementById('cfg-interval-group').style.display=rotEnabled?'':'none';
|
|
549
|
+
var sec=c.rotationIntervalSec||Math.round((c.rotationIntervalMs||8000)/1000);
|
|
550
|
+
document.getElementById('cfg-interval').value=sec;
|
|
514
551
|
document.getElementById('cfg-theme').value=c.theme||'dark';
|
|
515
552
|
document.getElementById('cfg-accent').value=c.accentColor||'#1a1a2e';
|
|
516
553
|
var layout=c.layout||'full';
|
|
@@ -521,11 +558,17 @@ if(o.getAttribute('data-layout')===layout)o.classList.add('selected');
|
|
|
521
558
|
});
|
|
522
559
|
})}
|
|
523
560
|
|
|
561
|
+
document.getElementById('cfg-rotation').addEventListener('change',function(){
|
|
562
|
+
document.getElementById('cfg-interval-group').style.display=this.checked?'':'none';
|
|
563
|
+
});
|
|
564
|
+
|
|
524
565
|
function saveConfig(){
|
|
525
566
|
memGet('signage:config').then(function(_r){
|
|
526
567
|
var c=_r?_r.value:{};
|
|
527
568
|
c.buildingName=document.getElementById('cfg-building').value.trim();
|
|
528
|
-
c.
|
|
569
|
+
c.rotationEnabled=document.getElementById('cfg-rotation').checked;
|
|
570
|
+
c.rotationIntervalSec=parseInt(document.getElementById('cfg-interval').value)||8;
|
|
571
|
+
delete c.rotationIntervalMs;
|
|
529
572
|
c.theme=document.getElementById('cfg-theme').value;
|
|
530
573
|
c.layout=document.getElementById('cfg-layout').value;
|
|
531
574
|
c.accentColor=document.getElementById('cfg-accent').value;
|
|
@@ -653,7 +696,7 @@ body{background:var(--bg);color:var(--text)}
|
|
|
653
696
|
|
|
654
697
|
<script>
|
|
655
698
|
var session=null;
|
|
656
|
-
var cfg={
|
|
699
|
+
var cfg={rotationEnabled:true,rotationIntervalSec:8,buildingName:'Digital Signage',theme:'dark',layout:'full',accentColor:'#1a1a2e'};
|
|
657
700
|
var anns=[],views=[],viewIdx=0,rotTimer=null;
|
|
658
701
|
|
|
659
702
|
function getHeaders(){var h={'Content-Type':'application/json'};if(session&&session.jwt)h['Authorization']='Bearer '+session.jwt;return h}
|
|
@@ -724,9 +767,12 @@ viewIdx++;
|
|
|
724
767
|
}
|
|
725
768
|
|
|
726
769
|
function startRotation(){
|
|
727
|
-
if(rotTimer)clearInterval(rotTimer);
|
|
770
|
+
if(rotTimer){clearInterval(rotTimer);rotTimer=null}
|
|
728
771
|
renderView();
|
|
729
|
-
|
|
772
|
+
if(cfg.rotationEnabled!==false){
|
|
773
|
+
var ms=(cfg.rotationIntervalSec||cfg.rotationIntervalMs/1000||8)*1000;
|
|
774
|
+
rotTimer=setInterval(function(){renderView()},ms);
|
|
775
|
+
}
|
|
730
776
|
}
|
|
731
777
|
|
|
732
778
|
/* ── Data loading ── */
|
|
@@ -735,10 +781,11 @@ Promise.all([memGet('signage:config'),memGet('signage:announcements'),memGet('si
|
|
|
735
781
|
.then(function(res){
|
|
736
782
|
var c=res[0],a=res[1],v=res[2];
|
|
737
783
|
if(c){
|
|
738
|
-
var
|
|
784
|
+
var oldSec=cfg.rotationIntervalSec;
|
|
785
|
+
var oldEnabled=cfg.rotationEnabled;
|
|
739
786
|
cfg=Object.assign({},cfg,c);
|
|
740
787
|
applyTheme();
|
|
741
|
-
if(c.
|
|
788
|
+
if(c.rotationIntervalSec!==oldSec||c.rotationEnabled!==oldEnabled)startRotation();
|
|
742
789
|
}
|
|
743
790
|
anns=Array.isArray(a)?a:[];
|
|
744
791
|
views=Array.isArray(v)?v:[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"example-packages.js","sourceRoot":"","sources":["../../../src/data/example-packages.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"example-packages.js","sourceRoot":"","sources":["../../../src/data/example-packages.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../../src/routes/apps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAe,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAQ1D;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../../src/routes/apps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAe,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAQ1D;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CA8cvG"}
|
package/dist/src/routes/apps.js
CHANGED
|
@@ -358,7 +358,9 @@ export function appsRouter(config, storage, peers) {
|
|
|
358
358
|
router.patch('/v1/apps/:filename', requireAuth(), async (req, res) => {
|
|
359
359
|
const gaii = resolveIdentity(req.auth, config.nodeId);
|
|
360
360
|
const filename = req.params.filename;
|
|
361
|
-
|
|
361
|
+
let app = await storage.getApp(gaii, filename);
|
|
362
|
+
if (!app)
|
|
363
|
+
app = await storage.getApp(req.auth.owner, filename);
|
|
362
364
|
if (!app) {
|
|
363
365
|
res.status(404).json(error(config.nodeId, 'NOT_FOUND', `App "${filename}" not found in your uploads`));
|
|
364
366
|
return;
|
|
@@ -387,15 +389,24 @@ export function appsRouter(config, storage, peers) {
|
|
|
387
389
|
const filename = req.params.filename;
|
|
388
390
|
const versionParam = req.query.version;
|
|
389
391
|
const version = versionParam ? parseInt(versionParam, 10) : undefined;
|
|
390
|
-
|
|
392
|
+
// Try full GHII first, fall back to bare owner name (backward compat for
|
|
393
|
+
// apps created before the ownerGaii fix in the package install flow)
|
|
394
|
+
let app = await storage.getApp(gaii, filename, version);
|
|
395
|
+
let effectiveGaii = gaii;
|
|
396
|
+
if (!app) {
|
|
397
|
+
const bareOwner = req.auth.owner;
|
|
398
|
+
app = await storage.getApp(bareOwner, filename, version);
|
|
399
|
+
if (app)
|
|
400
|
+
effectiveGaii = bareOwner;
|
|
401
|
+
}
|
|
391
402
|
if (!app) {
|
|
392
403
|
res.status(404).json(error(config.nodeId, 'NOT_FOUND', `App "${filename}" not found in your uploads${version ? ` (version ${version})` : ''}`));
|
|
393
404
|
return;
|
|
394
405
|
}
|
|
395
|
-
await storage.deleteApp(
|
|
406
|
+
await storage.deleteApp(effectiveGaii, filename, version);
|
|
396
407
|
if (!version) {
|
|
397
408
|
// Full delete — also remove screenshot
|
|
398
|
-
await storage.deleteStorageFile(
|
|
409
|
+
await storage.deleteStorageFile(effectiveGaii, `apps/screenshots/${filename}`);
|
|
399
410
|
}
|
|
400
411
|
const owner = req.auth.owner;
|
|
401
412
|
await storage.addSiteChangeLog({
|