aimeat 1.25.0 → 1.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/locales/en.json +14 -0
  2. package/dist/locales/fi.json +14 -0
  3. package/dist/public/js/services/admin.js +8 -5
  4. package/dist/public/spa.html +22 -0
  5. package/dist/public/views/admin/messages-tab.js +115 -0
  6. package/dist/public/views/admin.js +27 -8
  7. package/dist/src/cli/scaffold.d.ts +26 -3
  8. package/dist/src/cli/scaffold.d.ts.map +1 -1
  9. package/dist/src/cli/scaffold.js +33 -3
  10. package/dist/src/cli/scaffold.js.map +1 -1
  11. package/dist/src/generated/api-types.d.ts +79 -4
  12. package/dist/src/generated/api-types.d.ts.map +1 -1
  13. package/dist/src/generated/prisma-postgres/edge.js +17 -4
  14. package/dist/src/generated/prisma-postgres/index-browser.js +14 -1
  15. package/dist/src/generated/prisma-postgres/index.d.ts +1548 -153
  16. package/dist/src/generated/prisma-postgres/index.js +17 -4
  17. package/dist/src/generated/prisma-postgres/package.json +1 -1
  18. package/dist/src/generated/prisma-postgres/schema.prisma +15 -0
  19. package/dist/src/generated/prisma-postgres/wasm.js +17 -4
  20. package/dist/src/index.js +36 -4
  21. package/dist/src/index.js.map +1 -1
  22. package/dist/src/routes/admin-monitoring.d.ts.map +1 -1
  23. package/dist/src/routes/admin-monitoring.js +8 -0
  24. package/dist/src/routes/admin-monitoring.js.map +1 -1
  25. package/dist/src/routes/messages.d.ts.map +1 -1
  26. package/dist/src/routes/messages.js +2 -1
  27. package/dist/src/routes/messages.js.map +1 -1
  28. package/dist/src/routes/site.d.ts +10 -0
  29. package/dist/src/routes/site.d.ts.map +1 -1
  30. package/dist/src/routes/site.js +18 -1
  31. package/dist/src/routes/site.js.map +1 -1
  32. package/dist/src/services/message-delivery.d.ts +3 -1
  33. package/dist/src/services/message-delivery.d.ts.map +1 -1
  34. package/dist/src/services/message-delivery.js +23 -2
  35. package/dist/src/services/message-delivery.js.map +1 -1
  36. package/dist/src/storage/interface.d.ts +30 -0
  37. package/dist/src/storage/interface.d.ts.map +1 -1
  38. package/dist/src/storage/providers/mongodb/index.d.ts +6 -1
  39. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  40. package/dist/src/storage/providers/mongodb/index.js +63 -0
  41. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  42. package/dist/src/storage/providers/sqlite/index.d.ts +5 -1
  43. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  44. package/dist/src/storage/providers/sqlite/index.js +12 -0
  45. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  46. package/dist/src/storage/providers/sqlite/repos/direct-message.d.ts +5 -1
  47. package/dist/src/storage/providers/sqlite/repos/direct-message.d.ts.map +1 -1
  48. package/dist/src/storage/providers/sqlite/repos/direct-message.js +48 -0
  49. package/dist/src/storage/providers/sqlite/repos/direct-message.js.map +1 -1
  50. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  51. package/dist/src/storage/providers/sqlite/schema.js +15 -0
  52. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  53. package/dist/src/storage/repositories/direct-message.repository.d.ts +5 -1
  54. package/dist/src/storage/repositories/direct-message.repository.d.ts.map +1 -1
  55. package/package.json +1 -1
  56. package/prisma/schema.postgres.prisma +15 -0
  57. package/prisma/schema.prisma +15 -0
@@ -18,6 +18,20 @@
18
18
  "inbox.empty": "Nothing here yet.",
19
19
  "inbox.attachmentPending": "attachment pending",
20
20
  "inbox.attachmentExpired": "attachment expired",
21
+ "admin.messages.title": "Direct messages",
22
+ "admin.messages.desc": "Delivery telemetry for user-to-user messages. No message content or participant identities are shown.",
23
+ "admin.messages.total": "Total sent",
24
+ "admin.messages.last24h": "in 24h",
25
+ "admin.messages.targetNodes": "Top target nodes",
26
+ "admin.messages.node": "Node",
27
+ "admin.messages.totalCol": "Total",
28
+ "admin.messages.failedCol": "Failed",
29
+ "admin.messages.recent": "Recent attempts",
30
+ "admin.messages.when": "When",
31
+ "admin.messages.origin": "Origin",
32
+ "admin.messages.statusCol": "Status",
33
+ "admin.messages.detail": "Detail",
34
+ "admin.messages.none": "No deliveries yet.",
21
35
  "inbox.attach": "Attach a file",
22
36
  "inbox.today": "Today",
23
37
  "inbox.yesterday": "Yesterday",
@@ -18,6 +18,20 @@
18
18
  "inbox.empty": "Ei mitään vielä.",
19
19
  "inbox.attachmentPending": "liite odottaa",
20
20
  "inbox.attachmentExpired": "liite vanhentui",
21
+ "admin.messages.title": "Suorat viestit",
22
+ "admin.messages.desc": "Toimitustelemetria käyttäjien välisille viesteille. Viestien sisältöä tai osapuolten henkilöllisyyttä ei näytetä.",
23
+ "admin.messages.total": "Lähetetty yhteensä",
24
+ "admin.messages.last24h": "24 h aikana",
25
+ "admin.messages.targetNodes": "Yleisimmät kohdesolmut",
26
+ "admin.messages.node": "Solmu",
27
+ "admin.messages.totalCol": "Yhteensä",
28
+ "admin.messages.failedCol": "Epäonnistui",
29
+ "admin.messages.recent": "Viimeisimmät yritykset",
30
+ "admin.messages.when": "Milloin",
31
+ "admin.messages.origin": "Alkuperä",
32
+ "admin.messages.statusCol": "Tila",
33
+ "admin.messages.detail": "Lisätieto",
34
+ "admin.messages.none": "Ei toimituksia vielä.",
21
35
  "inbox.attach": "Liitä tiedosto",
22
36
  "inbox.today": "Tänään",
23
37
  "inbox.yesterday": "Eilen",
@@ -38,6 +38,9 @@ export const patchBoardMembers = (id, body) => apiPatch(`/v1/boards/${encodeURIC
38
38
  // ── Work ──
39
39
  export const getAdminWork = () => apiGet('/v1/admin/work');
40
40
 
41
+ // ── Direct-message delivery telemetry ──
42
+ export const getMessageStats = () => apiGet('/v1/admin/messages/stats');
43
+
41
44
  // ── Maintenance ──
42
45
  export const getMaintenance = () => apiGet('/v1/admin/maintenance');
43
46
  export const setMaintenance = (enabled, message) => apiPost('/v1/admin/maintenance', { enabled, message });
@@ -269,9 +272,9 @@ export const getAdminAgentTasks = (params = {}) => {
269
272
 
270
273
  // ── Sharing Groups (admin) ──
271
274
  export const getAdminSharingGroups = () => apiGet('/v1/admin/sharing-groups');
272
-
273
- // ── Subdomain mappings (operator) ──
274
- export const getSubdomainSites = () => apiGet('/v1/admin/subdomains');
275
- export const createSubdomainSite = (body) => apiPost('/v1/admin/subdomains', body);
276
- export const updateSubdomainSite = (sub, updates) => apiPatch(`/v1/admin/subdomains/${encodeURIComponent(sub)}`, updates);
275
+
276
+ // ── Subdomain mappings (operator) ──
277
+ export const getSubdomainSites = () => apiGet('/v1/admin/subdomains');
278
+ export const createSubdomainSite = (body) => apiPost('/v1/admin/subdomains', body);
279
+ export const updateSubdomainSite = (sub, updates) => apiPatch(`/v1/admin/subdomains/${encodeURIComponent(sub)}`, updates);
277
280
  export const deleteSubdomainSite = (sub) => apiDelete(`/v1/admin/subdomains/${encodeURIComponent(sub)}`);
@@ -223,6 +223,28 @@
223
223
  <!-- App root (Preact mounts here) -->
224
224
  <div id="app"></div>
225
225
 
226
+ <!-- SSO handoff: the desktop app already holds an authenticated owner JWT (from its inline
227
+ sign-in). It opens the portal with #handoff=<base64({owner,jwt})> so the browser is logged
228
+ in WITHOUT a second sign-in. Runs synchronously before the app reads the session. -->
229
+ <script>
230
+ (function () {
231
+ try {
232
+ var m = location.hash.match(/[#&]handoff=([^&]+)/);
233
+ if (!m) return;
234
+ var s = JSON.parse(atob(decodeURIComponent(m[1])));
235
+ if (s && s.jwt && s.owner) {
236
+ var node = '';
237
+ try { node = (JSON.parse(atob(s.jwt.split('.')[1].replace(/-/g, '+').replace(/_/g, '/'))) || {}).node || ''; } catch (e) {}
238
+ localStorage.setItem('aimeat_session', JSON.stringify({
239
+ owner: s.owner, jwt: s.jwt, ghii: node ? (s.owner + '@' + node) : null, gaii: null,
240
+ }));
241
+ }
242
+ // Strip the token from the URL so it is not bookmarked or left in history.
243
+ history.replaceState(null, '', location.pathname + location.search);
244
+ } catch (e) { /* ignore a malformed handoff */ }
245
+ })();
246
+ </script>
247
+
226
248
  <script type="module">
227
249
  import { h, render, Component } from 'preact';
228
250
  import { useState, useEffect, useCallback } from 'preact/hooks';
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @file messages-tab.js
3
+ * @description Admin dashboard "Messages" tab — operator visibility into direct-message (user↔user)
4
+ * delivery: total + 24h counts by status (delivered / queued / failed / undeliverable), the busiest
5
+ * target nodes, and a recent-attempts table. Privacy by design: shows NO message content and NO
6
+ * participant identities — only routing/outcome metadata from the delivery-telemetry log.
7
+ * @structure MessagesTab (default) — fetches GET /v1/admin/messages/stats, re-fetches on live updates.
8
+ * @usage Registered in admin.js NAV_GROUPS (Data group).
9
+ * @version-history
10
+ * v1.0.0 -- 2026-06-16 -- Initial: delivery stats + target nodes + recent attempts.
11
+ */
12
+ import { h } from 'preact';
13
+ import { useState, useEffect, useCallback } from 'preact/hooks';
14
+ import htm from 'htm';
15
+ const html = htm.bind(h);
16
+ import { t } from '/js/i18n.js';
17
+ import { escHtml } from '/js/utils.js';
18
+ import { StatsGrid } from './shared.js';
19
+ import * as api from '/js/services/admin.js';
20
+
21
+ const STATUSES = ['delivered', 'queued', 'failed', 'undeliverable'];
22
+ const TONE = { delivered: 'good', queued: 'warn', failed: 'bad', undeliverable: 'bad' };
23
+
24
+ function relTime(iso) {
25
+ try {
26
+ const s = Math.round((Date.now() - new Date(iso).getTime()) / 1000);
27
+ if (s < 60) return `${s}s`;
28
+ if (s < 3600) return `${Math.floor(s / 60)}m`;
29
+ if (s < 86400) return `${Math.floor(s / 3600)}h`;
30
+ return new Date(iso).toLocaleDateString();
31
+ } catch { return ''; }
32
+ }
33
+
34
+ export default function MessagesTab() {
35
+ const [data, setData] = useState(null);
36
+ const [err, setErr] = useState(null);
37
+
38
+ const load = useCallback(async () => {
39
+ try {
40
+ const r = await api.getMessageStats();
41
+ setData(r?.data || null); setErr(null);
42
+ } catch (e) { setErr(e?.message || 'Failed to load'); }
43
+ }, []);
44
+
45
+ useEffect(() => { load(); }, [load]);
46
+ useEffect(() => {
47
+ const hh = () => load();
48
+ window.addEventListener('aimeat-live-update', hh);
49
+ return () => window.removeEventListener('aimeat-live-update', hh);
50
+ }, [load]);
51
+
52
+ if (err) return html`<div class="adm-section"><div class="adm-error">${escHtml(err)}</div></div>`;
53
+ if (!data) return html`<div class="adm-section">${t('common.loading') || 'Loading…'}</div>`;
54
+
55
+ const s = data.stats || { total: 0, total24h: 0, byStatus: {}, byStatus24h: {}, topTargetNodes: [] };
56
+ const recent = data.recent || [];
57
+
58
+ const cards = [
59
+ { label: t('admin.messages.total') || 'Total sent', value: s.total ?? 0, sub: `${s.total24h ?? 0} ${t('admin.messages.last24h') || 'in 24h'}` },
60
+ ...STATUSES.map(st => ({
61
+ label: t(`inbox.status.${st}`) || st,
62
+ value: s.byStatus?.[st] ?? 0,
63
+ sub: `${s.byStatus24h?.[st] ?? 0} ${t('admin.messages.last24h') || 'in 24h'}`,
64
+ tone: TONE[st],
65
+ })),
66
+ ];
67
+
68
+ return html`
69
+ <div class="adm-section">
70
+ <div class="section-title">${t('admin.messages.title') || 'Direct messages'}</div>
71
+ <div class="section-desc">${t('admin.messages.desc') || 'Delivery telemetry for user-to-user messages. No message content or participant identities are shown.'}</div>
72
+
73
+ <${StatsGrid} items=${cards} />
74
+
75
+ <div class="adm-subhead">${t('admin.messages.targetNodes') || 'Top target nodes'}</div>
76
+ ${(s.topTargetNodes || []).length === 0
77
+ ? html`<div class="adm-muted">${t('admin.messages.none') || 'No deliveries yet.'}</div>`
78
+ : html`<table class="data-table">
79
+ <thead><tr>
80
+ <th>${t('admin.messages.node') || 'Node'}</th>
81
+ <th>${t('admin.messages.totalCol') || 'Total'}</th>
82
+ <th>${t('admin.messages.failedCol') || 'Failed'}</th>
83
+ </tr></thead>
84
+ <tbody>
85
+ ${s.topTargetNodes.map(n => html`<tr key=${n.nodeId}>
86
+ <td class="mono">${escHtml(n.nodeId)}</td>
87
+ <td>${n.total}</td>
88
+ <td class=${n.failed > 0 ? 'adm-bad' : ''}>${n.failed}</td>
89
+ </tr>`)}
90
+ </tbody>
91
+ </table>`}
92
+
93
+ <div class="adm-subhead">${t('admin.messages.recent') || 'Recent attempts'}</div>
94
+ ${recent.length === 0
95
+ ? html`<div class="adm-muted">${t('admin.messages.none') || 'No deliveries yet.'}</div>`
96
+ : html`<table class="data-table">
97
+ <thead><tr>
98
+ <th>${t('admin.messages.when') || 'When'}</th>
99
+ <th>${t('admin.messages.origin') || 'Origin'}</th>
100
+ <th>${t('admin.messages.node') || 'Node'}</th>
101
+ <th>${t('admin.messages.statusCol') || 'Status'}</th>
102
+ <th>${t('admin.messages.detail') || 'Detail'}</th>
103
+ </tr></thead>
104
+ <tbody>
105
+ ${recent.map(r => html`<tr key=${r.id}>
106
+ <td>${relTime(r.createdAt)}</td>
107
+ <td>${escHtml(r.origin)}</td>
108
+ <td class="mono">${escHtml(r.targetNodeId)}</td>
109
+ <td class=${(r.status === 'failed' || r.status === 'undeliverable') ? 'adm-bad' : (r.status === 'queued' ? 'adm-warn' : 'adm-good')}>${escHtml(r.status)}</td>
110
+ <td class="mono">${escHtml([r.httpStatus ? `http ${r.httpStatus}` : '', r.errorMessage || '', `${r.latencyMs}ms`].filter(Boolean).join(' · '))}</td>
111
+ </tr>`)}
112
+ </tbody>
113
+ </table>`}
114
+ </div>`;
115
+ }
@@ -1,6 +1,10 @@
1
1
  /**
2
- * Admin Dashboard — SPA View
3
- * Sidebar layout with data preloading and tab components.
2
+ * @file public/views/admin.js
3
+ * @description Admin Dashboard SPA view — sidebar layout with data preloading and tab components.
4
+ * @structure Single `loadAll` fetches all dashboard data; tabs render slices of it. SSE
5
+ * live-updates trigger a debounced, silent background refresh.
6
+ * @version-history
7
+ * v1.1.0 — 2026-06-18 — Debounce + silence SSE-driven refresh so busy nodes don't flicker "Loading…".
4
8
  */
5
9
  import { h } from 'preact';
6
10
  import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
@@ -31,6 +35,7 @@ import BoardsTab from './admin/boards-tab.js';
31
35
  import ChatInstancesTab from './admin/chat-instances-tab.js';
32
36
  import RealtimeTab from './admin/realtime-tab.js';
33
37
  import WorkTab from './admin/work-tab.js';
38
+ import MessagesAdminTab from './admin/messages-tab.js';
34
39
  import EmailTab from './admin/email-tab.js';
35
40
  import PushTab from './admin/push-tab.js';
36
41
  import DirectoryTab from './admin/directory-tab.js';
@@ -81,6 +86,7 @@ const NAV_GROUPS = [
81
86
  { id: 'chatInstances', icon: '\u{1F4AC}', key: 'dashboard.chatInstances', component: ChatInstancesTab, count: 'chatInstances' },
82
87
  { id: 'realtime', icon: '\u{1F4E1}', key: 'dashboard.realtime', component: RealtimeTab, count: 'rooms' },
83
88
  { id: 'work', icon: '\u{1F4E6}', key: 'dashboard.work', component: WorkTab, count: 'work' },
89
+ { id: 'messages', icon: '\u{1F4EC}', key: 'admin.messages.title', component: MessagesAdminTab },
84
90
  { id: 'memory-admin', icon: '\u{1F5C4}', key: 'dashboard.memoryAdmin', component: MemoryAdminTab },
85
91
  { id: 'agent-tasks', icon: '\u{1F4CB}', key: 'dashboard.agentTasksTab', component: AgentTasksAdminTab },
86
92
  { id: 'sharing-groups', icon: '\u{1F465}', key: 'dashboard.sharingGroupsTab', component: SharingGroupsAdminTab },
@@ -130,6 +136,7 @@ export default function Admin({ navigate, locale }) {
130
136
  const [lastUpdate, setLastUpdate] = useState(null);
131
137
  const mountRef = useRef(true);
132
138
  const retriedRef = useRef(false);
139
+ const reloadTimerRef = useRef(null);
133
140
 
134
141
  // Auth listener
135
142
  useEffect(() => {
@@ -156,9 +163,11 @@ export default function Admin({ navigate, locale }) {
156
163
  // Cleanup
157
164
  useEffect(() => { mountRef.current = true; return () => { mountRef.current = false; }; }, []);
158
165
 
159
- // Load all data — server-side auth is the source of truth
160
- const loadAll = useCallback(async () => {
161
- setLoading(true);
166
+ // Load all data — server-side auth is the source of truth.
167
+ // `silent` skips the loading spinner so SSE-driven background refreshes don't
168
+ // flicker the "Loading…" button on busy nodes (only manual refresh shows it).
169
+ const loadAll = useCallback(async (silent = false) => {
170
+ if (!silent) setLoading(true);
162
171
  setError(null);
163
172
  setAccessDenied(false);
164
173
  try {
@@ -307,13 +316,23 @@ export default function Admin({ navigate, locale }) {
307
316
  if (session) loadAll();
308
317
  }, [session, loadAll]);
309
318
 
310
- // SSE live updates — auto-reload on server-side data changes
319
+ // SSE live updates — auto-reload on server-side data changes.
320
+ // Debounced + silent: busy nodes emit many events per second; without this the
321
+ // whole dashboard re-fetches (and flickers "Loading…") on every single event.
311
322
  useEffect(() => {
312
323
  if (!session) return;
313
324
  connect(() => getSession()?.jwt);
314
- onUpdate(loadAll);
325
+ const debouncedReload = () => {
326
+ if (reloadTimerRef.current) clearTimeout(reloadTimerRef.current);
327
+ reloadTimerRef.current = setTimeout(() => {
328
+ reloadTimerRef.current = null;
329
+ if (mountRef.current) loadAll(true);
330
+ }, 1500);
331
+ };
332
+ onUpdate(debouncedReload);
315
333
  return () => {
316
- offUpdate(loadAll);
334
+ offUpdate(debouncedReload);
335
+ if (reloadTimerRef.current) clearTimeout(reloadTimerRef.current);
317
336
  disconnect();
318
337
  };
319
338
  }, [session, loadAll]);
@@ -1,7 +1,14 @@
1
1
  /**
2
- * Scaffold utility for `aimeat init` and `aimeat update`.
3
- * Copies runtime assets (public/, locales/, static/) into a target directory
4
- * with a checksum manifest to avoid overwriting user-modified files.
2
+ * @file scaffold.ts
3
+ * @description Scaffold utility for `aimeat init`, `aimeat update`, and start-time self-heal.
4
+ * Copies runtime assets (public/, locales/, static/) into a target directory with a
5
+ * checksum manifest, so the server's CWD-first asset copies stay in sync with the
6
+ * installed package while never clobbering user-modified files.
7
+ * @structure scaffoldFiles (core copy + manifest), autoHealAssets (version-gated auto
8
+ * refresh for `aimeat start`), findPackageRoot (locate shipped assets), manifest helpers.
9
+ * @usage Imported by src/index.ts (update + start) and src/cli/init-wizard.ts (init).
10
+ * @version-history v1.25.1 — 2026-06-18 — Add autoHealAssets so an upgraded package
11
+ * refreshes pages/translations on next `aimeat start` without a manual `aimeat update`.
5
12
  */
6
13
  export interface ScaffoldManifest {
7
14
  version: string;
@@ -25,6 +32,22 @@ export declare function writeManifest(dir: string, manifest: ScaffoldManifest):
25
32
  * - Re-scaffold: skips user-modified files, updates unmodified, copies new.
26
33
  */
27
34
  export declare function scaffoldFiles(pkgRoot: string, targetDir: string, version: string): ScaffoldResult;
35
+ /**
36
+ * Self-heal runtime assets at startup.
37
+ *
38
+ * The server serves CWD/{public,locales,static} ahead of the package's own copies,
39
+ * so after a package upgrade (e.g. `npm i -g aimeat@latest`) those scaffolded copies
40
+ * would keep serving the OLD pages and translations until someone remembered to run
41
+ * `aimeat update`. This refreshes them automatically on the next `aimeat start`,
42
+ * preserving any operator-modified files (same checksum-manifest logic as scaffoldFiles).
43
+ *
44
+ * Returns the ScaffoldResult when it healed, or null when there was nothing to do:
45
+ * - No manifest in targetDir → this directory holds no scaffolded deployment (a fresh/
46
+ * empty dir, or running from the package itself); the server already falls back to the
47
+ * package's current assets, and we must not create a scaffold the operator never asked for.
48
+ * - Manifest already on pkgVersion → assets are current.
49
+ */
50
+ export declare function autoHealAssets(pkgRoot: string, targetDir: string, pkgVersion: string): ScaffoldResult | null;
28
51
  /**
29
52
  * Find the package root directory containing assets.
30
53
  * Searches from the compiled JS location upward.
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/cli/scaffold.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAgBD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAIjE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAE3E;AAiBD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,cAAc,CAkEhB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiB9D"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/cli/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAgBD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAIjE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAE3E;AAiBD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,cAAc,CAkEhB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,cAAc,GAAG,IAAI,CAKvB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiB9D"}
@@ -1,7 +1,14 @@
1
1
  /**
2
- * Scaffold utility for `aimeat init` and `aimeat update`.
3
- * Copies runtime assets (public/, locales/, static/) into a target directory
4
- * with a checksum manifest to avoid overwriting user-modified files.
2
+ * @file scaffold.ts
3
+ * @description Scaffold utility for `aimeat init`, `aimeat update`, and start-time self-heal.
4
+ * Copies runtime assets (public/, locales/, static/) into a target directory with a
5
+ * checksum manifest, so the server's CWD-first asset copies stay in sync with the
6
+ * installed package while never clobbering user-modified files.
7
+ * @structure scaffoldFiles (core copy + manifest), autoHealAssets (version-gated auto
8
+ * refresh for `aimeat start`), findPackageRoot (locate shipped assets), manifest helpers.
9
+ * @usage Imported by src/index.ts (update + start) and src/cli/init-wizard.ts (init).
10
+ * @version-history v1.25.1 — 2026-06-18 — Add autoHealAssets so an upgraded package
11
+ * refreshes pages/translations on next `aimeat start` without a manual `aimeat update`.
5
12
  */
6
13
  import { createHash } from 'node:crypto';
7
14
  import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, } from 'node:fs';
@@ -118,6 +125,29 @@ export function scaffoldFiles(pkgRoot, targetDir, version) {
118
125
  });
119
126
  return result;
120
127
  }
128
+ /**
129
+ * Self-heal runtime assets at startup.
130
+ *
131
+ * The server serves CWD/{public,locales,static} ahead of the package's own copies,
132
+ * so after a package upgrade (e.g. `npm i -g aimeat@latest`) those scaffolded copies
133
+ * would keep serving the OLD pages and translations until someone remembered to run
134
+ * `aimeat update`. This refreshes them automatically on the next `aimeat start`,
135
+ * preserving any operator-modified files (same checksum-manifest logic as scaffoldFiles).
136
+ *
137
+ * Returns the ScaffoldResult when it healed, or null when there was nothing to do:
138
+ * - No manifest in targetDir → this directory holds no scaffolded deployment (a fresh/
139
+ * empty dir, or running from the package itself); the server already falls back to the
140
+ * package's current assets, and we must not create a scaffold the operator never asked for.
141
+ * - Manifest already on pkgVersion → assets are current.
142
+ */
143
+ export function autoHealAssets(pkgRoot, targetDir, pkgVersion) {
144
+ const manifest = readManifest(targetDir);
145
+ if (!manifest)
146
+ return null;
147
+ if (manifest.version === pkgVersion)
148
+ return null;
149
+ return scaffoldFiles(pkgRoot, targetDir, pkgVersion);
150
+ }
121
151
  /**
122
152
  * Find the package root directory containing assets.
123
153
  * Searches from the compiled JS location upward.
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/cli/scaffold.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/cli/scaffold.ts"],"names":[],"mappings":"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"}
@@ -3364,6 +3364,26 @@ export interface paths {
3364
3364
  patch?: never;
3365
3365
  trace?: never;
3366
3366
  };
3367
+ "/v1/admin/messages/stats": {
3368
+ parameters: {
3369
+ query?: never;
3370
+ header?: never;
3371
+ path?: never;
3372
+ cookie?: never;
3373
+ };
3374
+ /**
3375
+ * Direct-message delivery telemetry (operator)
3376
+ * @description Aggregated delivery stats for human↔human direct messages plus the most recent attempts. Privacy by design — carries NO message content and NO participant identities, only routing/outcome metadata (status, target node, http/error, latency).
3377
+ */
3378
+ get: operations["getMessageDeliveryStats"];
3379
+ put?: never;
3380
+ post?: never;
3381
+ delete?: never;
3382
+ options?: never;
3383
+ head?: never;
3384
+ patch?: never;
3385
+ trace?: never;
3386
+ };
3367
3387
  "/v1/admin/backup": {
3368
3388
  parameters: {
3369
3389
  query?: never;
@@ -5753,6 +5773,8 @@ export interface paths {
5753
5773
  /**
5754
5774
  * Download current template
5755
5775
  * @description Download the current custom HTML template.
5776
+ * When no custom template is set (the built-in default portal is active),
5777
+ * returns `200` with `template: null` — this is a normal state, not an error.
5756
5778
  * Operator-only (Tier 2).
5757
5779
  */
5758
5780
  get: operations["getSiteTemplate"];
@@ -18016,6 +18038,59 @@ export interface operations {
18016
18038
  200: components["responses"]["AimeatSuccess"];
18017
18039
  };
18018
18040
  };
18041
+ getMessageDeliveryStats: {
18042
+ parameters: {
18043
+ query?: never;
18044
+ header?: never;
18045
+ path?: never;
18046
+ cookie?: never;
18047
+ };
18048
+ requestBody?: never;
18049
+ responses: {
18050
+ /** @description Delivery stats + recent attempts */
18051
+ 200: {
18052
+ headers: {
18053
+ [name: string]: unknown;
18054
+ };
18055
+ content: {
18056
+ "application/json": components["schemas"]["AimeatEnvelope"] & {
18057
+ data?: {
18058
+ stats?: {
18059
+ total?: number;
18060
+ total24h?: number;
18061
+ byStatus?: {
18062
+ [key: string]: number;
18063
+ };
18064
+ byStatus24h?: {
18065
+ [key: string]: number;
18066
+ };
18067
+ topTargetNodes?: {
18068
+ nodeId?: string;
18069
+ total?: number;
18070
+ failed?: number;
18071
+ }[];
18072
+ };
18073
+ recent?: {
18074
+ id?: string;
18075
+ messageId?: string;
18076
+ /** @enum {string} */
18077
+ origin?: "local" | "federation";
18078
+ targetNodeId?: string;
18079
+ /** @enum {string} */
18080
+ status?: "delivered" | "queued" | "failed" | "undeliverable";
18081
+ httpStatus?: number;
18082
+ errorMessage?: string;
18083
+ latencyMs?: number;
18084
+ /** Format: date-time */
18085
+ createdAt?: string;
18086
+ }[];
18087
+ };
18088
+ };
18089
+ };
18090
+ };
18091
+ 401: components["responses"]["Unauthorized"];
18092
+ };
18093
+ };
18019
18094
  getBackup: {
18020
18095
  parameters: {
18021
18096
  query?: never;
@@ -22601,7 +22676,7 @@ export interface operations {
22601
22676
  };
22602
22677
  requestBody?: never;
22603
22678
  responses: {
22604
- /** @description Current template content */
22679
+ /** @description Current template content (template is null when the default portal is active) */
22605
22680
  200: {
22606
22681
  headers: {
22607
22682
  [name: string]: unknown;
@@ -22609,16 +22684,16 @@ export interface operations {
22609
22684
  content: {
22610
22685
  "application/json": components["schemas"]["AimeatEnvelope"] & {
22611
22686
  data?: {
22612
- template?: string;
22687
+ /** @description HTML template, or null when no custom template is set */
22688
+ template?: string | null;
22613
22689
  size_bytes?: number;
22614
22690
  /** Format: date-time */
22615
- updated_at?: string;
22691
+ updated_at?: string | null;
22616
22692
  tags_found?: string[];
22617
22693
  };
22618
22694
  };
22619
22695
  };
22620
22696
  };
22621
- 404: components["responses"]["NotFound"];
22622
22697
  };
22623
22698
  };
22624
22699
  uploadSiteTemplate: {