aimeat 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/dist/.env.example +4 -1
  2. package/dist/locales/en.json +573 -10
  3. package/dist/locales/fi.json +572 -9
  4. package/dist/public/css/views/admin-agent-integration.css +304 -0
  5. package/dist/public/css/views/agents-detail.css +1697 -120
  6. package/dist/public/css/views/profile.css +22 -2
  7. package/dist/public/css/views/public-knowledge-viewer.css +479 -0
  8. package/dist/public/js/i18n.js +9 -3
  9. package/dist/public/js/services/admin-agent-integration.js +52 -0
  10. package/dist/public/js/services/agent-integration.js +80 -0
  11. package/dist/public/js/services/agent-messages.js +3 -1
  12. package/dist/public/js/services/generator-spec-validate.js +23 -0
  13. package/dist/public/js/services/memory.js +39 -0
  14. package/dist/public/lib/live-updates.js +9 -4
  15. package/dist/public/llms-template.txt +106 -0
  16. package/dist/public/spa.html +7 -2
  17. package/dist/public/views/admin/agent-integration-tab.js +390 -0
  18. package/dist/public/views/admin/federation-tab.js +5 -5
  19. package/dist/public/views/admin/prompts-tab.js +9 -10
  20. package/dist/public/views/admin.js +2 -0
  21. package/dist/public/views/profile/access-tab.js +18 -0
  22. package/dist/public/views/profile/agents/agent-card.js +396 -0
  23. package/dist/public/views/profile/agents/shared-board.js +97 -0
  24. package/dist/public/views/profile/agents/state-detector.js +62 -0
  25. package/dist/public/views/profile/agents/tab-activity.js +232 -0
  26. package/dist/public/views/profile/agents/tab-agent-config.js +199 -0
  27. package/dist/public/views/profile/agents/tab-data-access.js +304 -0
  28. package/dist/public/views/profile/agents/tab-directives.js +178 -0
  29. package/dist/public/views/profile/agents/tab-integration.js +505 -0
  30. package/dist/public/views/profile/agents/tab-messages.js +278 -0
  31. package/dist/public/views/profile/agents/tab-services.js +17 -0
  32. package/dist/public/views/profile/agents/tab-tasks.js +17 -0
  33. package/dist/public/views/profile/agents-messages-subtab.js +12 -10
  34. package/dist/public/views/profile/agents-services-subtab.js +17 -11
  35. package/dist/public/views/profile/agents-tab.js +159 -255
  36. package/dist/public/views/profile/agents-task-builder.js +403 -0
  37. package/dist/public/views/profile/agents-tasks-subtab.js +186 -140
  38. package/dist/public/views/profile/generator-detail.js +8 -1
  39. package/dist/public/views/profile/memory-tab.js +371 -7
  40. package/dist/public/views/profile/wallet-tab.js +14 -0
  41. package/dist/public/views/public-knowledge-viewer.js +650 -0
  42. package/dist/src/auth/middleware.d.ts.map +1 -1
  43. package/dist/src/auth/middleware.js +17 -0
  44. package/dist/src/auth/middleware.js.map +1 -1
  45. package/dist/src/config.d.ts +1 -0
  46. package/dist/src/config.d.ts.map +1 -1
  47. package/dist/src/config.js +1 -0
  48. package/dist/src/config.js.map +1 -1
  49. package/dist/src/middleware/readiness-gate.d.ts +18 -0
  50. package/dist/src/middleware/readiness-gate.d.ts.map +1 -0
  51. package/dist/src/middleware/readiness-gate.js +50 -0
  52. package/dist/src/middleware/readiness-gate.js.map +1 -0
  53. package/dist/src/models/agent-capabilities-schemas.d.ts +3 -0
  54. package/dist/src/models/agent-capabilities-schemas.d.ts.map +1 -1
  55. package/dist/src/models/agent-capabilities-schemas.js +3 -0
  56. package/dist/src/models/agent-capabilities-schemas.js.map +1 -1
  57. package/dist/src/models/agent-directives-schemas.d.ts +12 -0
  58. package/dist/src/models/agent-directives-schemas.d.ts.map +1 -1
  59. package/dist/src/models/agent-directives-schemas.js +7 -0
  60. package/dist/src/models/agent-directives-schemas.js.map +1 -1
  61. package/dist/src/models/agent-onboarding-schemas.d.ts +45 -0
  62. package/dist/src/models/agent-onboarding-schemas.d.ts.map +1 -0
  63. package/dist/src/models/agent-onboarding-schemas.js +91 -0
  64. package/dist/src/models/agent-onboarding-schemas.js.map +1 -0
  65. package/dist/src/models/webhook-schemas.d.ts +266 -0
  66. package/dist/src/models/webhook-schemas.d.ts.map +1 -0
  67. package/dist/src/models/webhook-schemas.js +137 -0
  68. package/dist/src/models/webhook-schemas.js.map +1 -0
  69. package/dist/src/routes/admin-agent-integration.d.ts +24 -0
  70. package/dist/src/routes/admin-agent-integration.d.ts.map +1 -0
  71. package/dist/src/routes/admin-agent-integration.js +186 -0
  72. package/dist/src/routes/admin-agent-integration.js.map +1 -0
  73. package/dist/src/routes/admin-monitoring.js +12 -7
  74. package/dist/src/routes/admin-monitoring.js.map +1 -1
  75. package/dist/src/routes/agent-activity.d.ts.map +1 -1
  76. package/dist/src/routes/agent-activity.js +17 -0
  77. package/dist/src/routes/agent-activity.js.map +1 -1
  78. package/dist/src/routes/agent-capabilities.d.ts +1 -0
  79. package/dist/src/routes/agent-capabilities.d.ts.map +1 -1
  80. package/dist/src/routes/agent-capabilities.js +9 -0
  81. package/dist/src/routes/agent-capabilities.js.map +1 -1
  82. package/dist/src/routes/agent-directives.d.ts +5 -1
  83. package/dist/src/routes/agent-directives.d.ts.map +1 -1
  84. package/dist/src/routes/agent-directives.js +74 -20
  85. package/dist/src/routes/agent-directives.js.map +1 -1
  86. package/dist/src/routes/agent-integration.d.ts +1 -0
  87. package/dist/src/routes/agent-integration.d.ts.map +1 -1
  88. package/dist/src/routes/agent-integration.js +128 -11
  89. package/dist/src/routes/agent-integration.js.map +1 -1
  90. package/dist/src/routes/agent-messages.d.ts +5 -1
  91. package/dist/src/routes/agent-messages.d.ts.map +1 -1
  92. package/dist/src/routes/agent-messages.js +20 -1
  93. package/dist/src/routes/agent-messages.js.map +1 -1
  94. package/dist/src/routes/agent-onboarding.d.ts +23 -0
  95. package/dist/src/routes/agent-onboarding.d.ts.map +1 -0
  96. package/dist/src/routes/agent-onboarding.js +374 -0
  97. package/dist/src/routes/agent-onboarding.js.map +1 -0
  98. package/dist/src/routes/agent-skill-bundle.d.ts +16 -0
  99. package/dist/src/routes/agent-skill-bundle.d.ts.map +1 -0
  100. package/dist/src/routes/agent-skill-bundle.js +133 -0
  101. package/dist/src/routes/agent-skill-bundle.js.map +1 -0
  102. package/dist/src/routes/agent-tasks.d.ts +8 -1
  103. package/dist/src/routes/agent-tasks.d.ts.map +1 -1
  104. package/dist/src/routes/agent-tasks.js +192 -14
  105. package/dist/src/routes/agent-tasks.js.map +1 -1
  106. package/dist/src/routes/agent-telemetry.d.ts +15 -0
  107. package/dist/src/routes/agent-telemetry.d.ts.map +1 -0
  108. package/dist/src/routes/agent-telemetry.js +99 -0
  109. package/dist/src/routes/agent-telemetry.js.map +1 -0
  110. package/dist/src/routes/agent-webhook.d.ts +18 -0
  111. package/dist/src/routes/agent-webhook.d.ts.map +1 -0
  112. package/dist/src/routes/agent-webhook.js +243 -0
  113. package/dist/src/routes/agent-webhook.js.map +1 -0
  114. package/dist/src/routes/agents.d.ts.map +1 -1
  115. package/dist/src/routes/agents.js +203 -4
  116. package/dist/src/routes/agents.js.map +1 -1
  117. package/dist/src/routes/auth.d.ts.map +1 -1
  118. package/dist/src/routes/auth.js +45 -2
  119. package/dist/src/routes/auth.js.map +1 -1
  120. package/dist/src/routes/federation-auth.d.ts.map +1 -1
  121. package/dist/src/routes/federation-auth.js +3 -1
  122. package/dist/src/routes/federation-auth.js.map +1 -1
  123. package/dist/src/routes/federation-peer.d.ts.map +1 -1
  124. package/dist/src/routes/federation-peer.js +11 -2
  125. package/dist/src/routes/federation-peer.js.map +1 -1
  126. package/dist/src/routes/federation-sync.d.ts +1 -0
  127. package/dist/src/routes/federation-sync.d.ts.map +1 -1
  128. package/dist/src/routes/federation-sync.js +29 -0
  129. package/dist/src/routes/federation-sync.js.map +1 -1
  130. package/dist/src/routes/ghii.d.ts.map +1 -1
  131. package/dist/src/routes/ghii.js +42 -5
  132. package/dist/src/routes/ghii.js.map +1 -1
  133. package/dist/src/routes/knowledge.d.ts.map +1 -1
  134. package/dist/src/routes/knowledge.js +19 -7
  135. package/dist/src/routes/knowledge.js.map +1 -1
  136. package/dist/src/routes/memory.d.ts +9 -0
  137. package/dist/src/routes/memory.d.ts.map +1 -1
  138. package/dist/src/routes/memory.js +307 -12
  139. package/dist/src/routes/memory.js.map +1 -1
  140. package/dist/src/routes/portal.d.ts.map +1 -1
  141. package/dist/src/routes/portal.js +1 -0
  142. package/dist/src/routes/portal.js.map +1 -1
  143. package/dist/src/routes/prompts.d.ts +1 -0
  144. package/dist/src/routes/prompts.d.ts.map +1 -1
  145. package/dist/src/routes/prompts.js +40 -1
  146. package/dist/src/routes/prompts.js.map +1 -1
  147. package/dist/src/routes/sharing-groups.d.ts.map +1 -1
  148. package/dist/src/routes/sharing-groups.js +8 -2
  149. package/dist/src/routes/sharing-groups.js.map +1 -1
  150. package/dist/src/routes/wallet.d.ts.map +1 -1
  151. package/dist/src/routes/wallet.js +2 -0
  152. package/dist/src/routes/wallet.js.map +1 -1
  153. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  154. package/dist/src/server-bootstrap/routes-loader.js +47 -3
  155. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  156. package/dist/src/services/config-schema.d.ts.map +1 -1
  157. package/dist/src/services/config-schema.js +2 -1
  158. package/dist/src/services/config-schema.js.map +1 -1
  159. package/dist/src/services/core-jobs.d.ts.map +1 -1
  160. package/dist/src/services/core-jobs.js +3 -2
  161. package/dist/src/services/core-jobs.js.map +1 -1
  162. package/dist/src/services/federation-helpers.d.ts +1 -0
  163. package/dist/src/services/federation-helpers.d.ts.map +1 -1
  164. package/dist/src/services/federation-helpers.js +1 -1
  165. package/dist/src/services/federation-helpers.js.map +1 -1
  166. package/dist/src/services/generator-autopilot.d.ts.map +1 -1
  167. package/dist/src/services/generator-autopilot.js +125 -26
  168. package/dist/src/services/generator-autopilot.js.map +1 -1
  169. package/dist/src/services/generator-prompts/index.d.ts +1 -1
  170. package/dist/src/services/generator-prompts/index.d.ts.map +1 -1
  171. package/dist/src/services/generator-prompts/index.js +1 -1
  172. package/dist/src/services/generator-prompts/index.js.map +1 -1
  173. package/dist/src/services/generator-prompts/spec-validate.d.ts +1 -0
  174. package/dist/src/services/generator-prompts/spec-validate.d.ts.map +1 -1
  175. package/dist/src/services/generator-prompts/spec-validate.js +20 -0
  176. package/dist/src/services/generator-prompts/spec-validate.js.map +1 -1
  177. package/dist/src/services/generator-prompts/validate.d.ts +7 -1
  178. package/dist/src/services/generator-prompts/validate.d.ts.map +1 -1
  179. package/dist/src/services/generator-prompts/validate.js +70 -0
  180. package/dist/src/services/generator-prompts/validate.js.map +1 -1
  181. package/dist/src/services/onboarding-validator.d.ts +22 -0
  182. package/dist/src/services/onboarding-validator.d.ts.map +1 -0
  183. package/dist/src/services/onboarding-validator.js +202 -0
  184. package/dist/src/services/onboarding-validator.js.map +1 -0
  185. package/dist/src/services/platform-detector.d.ts +27 -0
  186. package/dist/src/services/platform-detector.d.ts.map +1 -0
  187. package/dist/src/services/platform-detector.js +73 -0
  188. package/dist/src/services/platform-detector.js.map +1 -0
  189. package/dist/src/services/prompt-defaults.d.ts +1 -0
  190. package/dist/src/services/prompt-defaults.d.ts.map +1 -1
  191. package/dist/src/services/prompt-defaults.js +875 -47
  192. package/dist/src/services/prompt-defaults.js.map +1 -1
  193. package/dist/src/services/prompt-seeder.js +1 -1
  194. package/dist/src/services/prompt-seeder.js.map +1 -1
  195. package/dist/src/services/readiness-scorer.d.ts +34 -0
  196. package/dist/src/services/readiness-scorer.d.ts.map +1 -0
  197. package/dist/src/services/readiness-scorer.js +87 -0
  198. package/dist/src/services/readiness-scorer.js.map +1 -0
  199. package/dist/src/services/skill-bundle/generator.d.ts +17 -0
  200. package/dist/src/services/skill-bundle/generator.d.ts.map +1 -0
  201. package/dist/src/services/skill-bundle/generator.js +387 -0
  202. package/dist/src/services/skill-bundle/generator.js.map +1 -0
  203. package/dist/src/services/skill-bundle/generic-adapter.d.ts +16 -0
  204. package/dist/src/services/skill-bundle/generic-adapter.d.ts.map +1 -0
  205. package/dist/src/services/skill-bundle/generic-adapter.js +96 -0
  206. package/dist/src/services/skill-bundle/generic-adapter.js.map +1 -0
  207. package/dist/src/services/skill-bundle/hermes-adapter.d.ts +25 -0
  208. package/dist/src/services/skill-bundle/hermes-adapter.d.ts.map +1 -0
  209. package/dist/src/services/skill-bundle/hermes-adapter.js +302 -0
  210. package/dist/src/services/skill-bundle/hermes-adapter.js.map +1 -0
  211. package/dist/src/services/skill-bundle/types.d.ts +54 -0
  212. package/dist/src/services/skill-bundle/types.d.ts.map +1 -0
  213. package/dist/src/services/skill-bundle/types.js +8 -0
  214. package/dist/src/services/skill-bundle/types.js.map +1 -0
  215. package/dist/src/services/task-stall-detector.d.ts +10 -2
  216. package/dist/src/services/task-stall-detector.d.ts.map +1 -1
  217. package/dist/src/services/task-stall-detector.js +34 -5
  218. package/dist/src/services/task-stall-detector.js.map +1 -1
  219. package/dist/src/services/webhook-dispatcher.d.ts +71 -0
  220. package/dist/src/services/webhook-dispatcher.d.ts.map +1 -0
  221. package/dist/src/services/webhook-dispatcher.js +259 -0
  222. package/dist/src/services/webhook-dispatcher.js.map +1 -0
  223. package/dist/src/storage/interface.d.ts +82 -2
  224. package/dist/src/storage/interface.d.ts.map +1 -1
  225. package/dist/src/storage/providers/mongodb/index.d.ts +20 -1
  226. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  227. package/dist/src/storage/providers/mongodb/index.js +254 -3
  228. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  229. package/dist/src/storage/providers/sqlite/index.d.ts +18 -1
  230. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  231. package/dist/src/storage/providers/sqlite/index.js +173 -4
  232. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  233. package/dist/src/storage/providers/sqlite/repos/agent-directives.d.ts.map +1 -1
  234. package/dist/src/storage/providers/sqlite/repos/agent-directives.js +8 -4
  235. package/dist/src/storage/providers/sqlite/repos/agent-directives.js.map +1 -1
  236. package/dist/src/storage/providers/sqlite/repos/agent.d.ts.map +1 -1
  237. package/dist/src/storage/providers/sqlite/repos/agent.js +25 -4
  238. package/dist/src/storage/providers/sqlite/repos/agent.js.map +1 -1
  239. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  240. package/dist/src/storage/providers/sqlite/schema.js +73 -1
  241. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  242. package/dist/src/storage/repositories/agent-onboarding.repository.d.ts +16 -0
  243. package/dist/src/storage/repositories/agent-onboarding.repository.d.ts.map +1 -0
  244. package/dist/src/storage/repositories/agent-onboarding.repository.js +8 -0
  245. package/dist/src/storage/repositories/agent-onboarding.repository.js.map +1 -0
  246. package/dist/src/storage/repositories/agent-webhook.repository.d.ts +21 -0
  247. package/dist/src/storage/repositories/agent-webhook.repository.d.ts.map +1 -0
  248. package/dist/src/storage/repositories/agent-webhook.repository.js +2 -0
  249. package/dist/src/storage/repositories/agent-webhook.repository.js.map +1 -0
  250. package/dist/src/storage/repositories/agent.repository.d.ts +1 -0
  251. package/dist/src/storage/repositories/agent.repository.d.ts.map +1 -1
  252. package/dist/src/storage/repositories/index.d.ts +2 -0
  253. package/dist/src/storage/repositories/index.d.ts.map +1 -1
  254. package/dist/src/utils/env-config.d.ts.map +1 -1
  255. package/dist/src/utils/env-config.js +7 -1
  256. package/dist/src/utils/env-config.js.map +1 -1
  257. package/dist/src/utils/url-validator.d.ts.map +1 -1
  258. package/dist/src/utils/url-validator.js +3 -0
  259. package/dist/src/utils/url-validator.js.map +1 -1
  260. package/package.json +1 -1
  261. package/prisma/schema.prisma +67 -7
@@ -0,0 +1,650 @@
1
+ /**
2
+ * @file public-knowledge-viewer.js
3
+ * @description Public knowledge package viewer -- no login required.
4
+ * Browse, search, filter, and read public knowledge packages.
5
+ * @version-history v1.0.0 — 2026-05-26 — Initial implementation
6
+ * v1.1.0 — 2026-05-26 — Human-readable entry rendering, Copy as Markdown
7
+ */
8
+ import { h } from 'preact';
9
+ import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
10
+ import htm from 'htm';
11
+ const html = htm.bind(h);
12
+ import { t } from '/js/i18n.js';
13
+
14
+ const NODE_URL = typeof window !== 'undefined' ? window.location.origin : '';
15
+
16
+ const CONTENT_TYPES = [
17
+ 'idea', 'research', 'plan', 'dataset', 'document',
18
+ 'tutorial', 'collection', 'article', 'story', 'fiction', 'guide'
19
+ ];
20
+
21
+ const SORT_OPTIONS = ['newest', 'name'];
22
+
23
+ /* ── API helpers (no auth required for public endpoints) ── */
24
+
25
+ async function fetchPublic(path) {
26
+ const resp = await fetch(path);
27
+ if (!resp.ok) {
28
+ const err = new Error(`HTTP ${resp.status}`);
29
+ err.status = resp.status;
30
+ throw err;
31
+ }
32
+ return resp.json();
33
+ }
34
+
35
+ async function discoverPackages(opts = {}) {
36
+ const params = new URLSearchParams();
37
+ if (opts.content_type) params.set('content_type', opts.content_type);
38
+ if (opts.tags) params.set('tags', opts.tags);
39
+ if (opts.language) params.set('language', opts.language);
40
+ if (opts.sort) params.set('sort', opts.sort);
41
+ if (opts.page) params.set('page', String(opts.page));
42
+ if (opts.limit) params.set('limit', String(opts.limit));
43
+ const qs = params.toString();
44
+ return fetchPublic(`/v1/catalogue/knowledge${qs ? '?' + qs : ''}`);
45
+ }
46
+
47
+ async function getPackageManifest(id) {
48
+ return fetchPublic(`/v1/knowledge/${encodeURIComponent(id)}`);
49
+ }
50
+
51
+ async function getPackageReputation(id) {
52
+ return fetchPublic(`/v1/knowledge/${encodeURIComponent(id)}/reputation`);
53
+ }
54
+
55
+ async function getPackageLinks(id) {
56
+ return fetchPublic(`/v1/knowledge/${encodeURIComponent(id)}/links`);
57
+ }
58
+
59
+ async function exportPackage(id) {
60
+ return fetchPublic(`/v1/knowledge/${encodeURIComponent(id)}/export`);
61
+ }
62
+
63
+ /* ── Human-readable JSON rendering ── */
64
+
65
+ function formatKey(key) {
66
+ return key.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
67
+ }
68
+
69
+ function RenderValue({ value, depth = 0 }) {
70
+ if (value == null) return null;
71
+ if (typeof value === 'string') return html`<span>${value}</span>`;
72
+ if (typeof value === 'number' || typeof value === 'boolean') return html`<span>${String(value)}</span>`;
73
+ if (Array.isArray(value)) {
74
+ if (value.length === 0) return null;
75
+ if (value.every(v => typeof v === 'string' || typeof v === 'number')) {
76
+ return html`<span>${value.join(', ')}</span>`;
77
+ }
78
+ return html`<div class="pkv-val-list">
79
+ ${value.map((item, i) => html`
80
+ <div class="pkv-val-list-item">
81
+ <${RenderValue} value=${item} depth=${depth + 1} />
82
+ </div>
83
+ `)}
84
+ </div>`;
85
+ }
86
+ if (typeof value === 'object') {
87
+ const entries = Object.entries(value);
88
+ if (entries.length === 0) return null;
89
+ return html`<div class=${depth > 0 ? 'pkv-val-nested' : 'pkv-val-obj'}>
90
+ ${entries.map(([k, v]) => html`
91
+ <div class="pkv-val-row">
92
+ <span class="pkv-val-key">${formatKey(k)}</span>
93
+ <div class="pkv-val-value"><${RenderValue} value=${v} depth=${depth + 1} /></div>
94
+ </div>
95
+ `)}
96
+ </div>`;
97
+ }
98
+ return html`<span>${String(value)}</span>`;
99
+ }
100
+
101
+ function RenderEntryContent({ content }) {
102
+ if (typeof content === 'string') {
103
+ return html`<div class="pkv-entry-text">${content}</div>`;
104
+ }
105
+ if (content && typeof content === 'object') {
106
+ const title = content.title;
107
+ const summary = content.summary;
108
+ const rest = Object.fromEntries(
109
+ Object.entries(content).filter(([k]) => k !== 'title' && k !== 'summary')
110
+ );
111
+ return html`
112
+ <div class="pkv-entry-readable">
113
+ ${title && html`<h4 class="pkv-entry-h">${title}</h4>`}
114
+ ${summary && html`<p class="pkv-entry-summary">${summary}</p>`}
115
+ ${Object.keys(rest).length > 0 && html`<${RenderValue} value=${rest} />`}
116
+ </div>
117
+ `;
118
+ }
119
+ return html`<div class="pkv-entry-text">${String(content)}</div>`;
120
+ }
121
+
122
+ /* ── Markdown export ── */
123
+
124
+ function valueToMarkdown(value, depth = 0) {
125
+ if (value == null) return '';
126
+ if (typeof value === 'string') return value;
127
+ if (typeof value === 'number' || typeof value === 'boolean') return String(value);
128
+ if (Array.isArray(value)) {
129
+ if (value.every(v => typeof v === 'string' || typeof v === 'number')) {
130
+ return value.join(', ');
131
+ }
132
+ return value.map(item => {
133
+ if (typeof item === 'object' && item !== null) {
134
+ const parts = Object.entries(item)
135
+ .map(([k, v]) => `**${formatKey(k)}:** ${valueToMarkdown(v, depth + 1)}`)
136
+ .join(' | ');
137
+ return `- ${parts}`;
138
+ }
139
+ return `- ${valueToMarkdown(item, depth + 1)}`;
140
+ }).join('\n');
141
+ }
142
+ if (typeof value === 'object') {
143
+ return Object.entries(value).map(([k, v]) => {
144
+ const rendered = valueToMarkdown(v, depth + 1);
145
+ if (rendered.includes('\n')) {
146
+ return `**${formatKey(k)}:**\n${rendered}`;
147
+ }
148
+ return `**${formatKey(k)}:** ${rendered}`;
149
+ }).join('\n\n');
150
+ }
151
+ return String(value);
152
+ }
153
+
154
+ function entryToMarkdown(content) {
155
+ if (typeof content === 'string') return content;
156
+ if (!content || typeof content !== 'object') return String(content || '');
157
+
158
+ const lines = [];
159
+ if (content.summary) lines.push(`*${content.summary}*\n`);
160
+ const rest = Object.entries(content).filter(([k]) => k !== 'title' && k !== 'summary');
161
+ for (const [key, val] of rest) {
162
+ lines.push(`### ${formatKey(key)}\n`);
163
+ lines.push(valueToMarkdown(val, 0));
164
+ lines.push('');
165
+ }
166
+ return lines.join('\n');
167
+ }
168
+
169
+ function buildFullMarkdown(manifest, publicEntries, entryData) {
170
+ const lines = [];
171
+ lines.push(`# ${manifest.name}\n`);
172
+ lines.push(`**Author:** ${manifest.author || '-'} `);
173
+ lines.push(`**Version:** ${manifest.version || '1.0.0'} `);
174
+ lines.push(`**Content type:** ${manifest.content_type || '-'} `);
175
+ lines.push(`**Language:** ${manifest.language || '-'} `);
176
+ lines.push(`**Maturity:** ${manifest.maturity || '-'} `);
177
+ if (manifest.created) lines.push(`**Created:** ${new Date(manifest.created).toLocaleDateString()} `);
178
+ if (manifest.updated) lines.push(`**Updated:** ${new Date(manifest.updated).toLocaleDateString()} `);
179
+ if (manifest.synthesis) {
180
+ lines.push(`**Synthesis:** ${manifest.synthesis.level}${manifest.synthesis.model ? ' (' + manifest.synthesis.model + ')' : ''} `);
181
+ if (manifest.synthesis.description) lines.push(`> ${manifest.synthesis.description}`);
182
+ }
183
+ if (manifest.tags?.length) lines.push(`\n**Tags:** ${manifest.tags.join(', ')}`);
184
+ if (manifest.sharing?.license) lines.push(`**License:** ${manifest.sharing.license}`);
185
+ lines.push('\n---\n');
186
+
187
+ for (const entry of publicEntries) {
188
+ const entryName = entry.key.split('/').pop() || entry.key;
189
+ const content = entryData[entryName];
190
+ lines.push(`## ${entry.title || entryName}\n`);
191
+ if (content) {
192
+ lines.push(entryToMarkdown(content));
193
+ } else {
194
+ lines.push('*Content unavailable*');
195
+ }
196
+ lines.push('\n---\n');
197
+ }
198
+
199
+ if (manifest.references?.length) {
200
+ lines.push('## References\n');
201
+ for (const ref of manifest.references) {
202
+ const title = ref.title || ref.url || 'Untitled';
203
+ if (ref.url) {
204
+ lines.push(`- [${title}](${ref.url})${ref.accessed ? ' -- accessed ' + new Date(ref.accessed).toLocaleDateString() : ''}`);
205
+ } else {
206
+ lines.push(`- ${title}`);
207
+ }
208
+ }
209
+ }
210
+
211
+ return lines.join('\n');
212
+ }
213
+
214
+ /* ── Main Component ── */
215
+
216
+ export default function PublicKnowledgeViewer() {
217
+ const [view, setView] = useState('browse'); // 'browse' | 'detail'
218
+ const [packageId, setPackageId] = useState(null);
219
+
220
+ useEffect(() => {
221
+ const params = new URLSearchParams(window.location.search);
222
+ const id = params.get('id');
223
+ if (id) {
224
+ setPackageId(id);
225
+ setView('detail');
226
+ }
227
+ }, []);
228
+
229
+ const openPackage = useCallback((id) => {
230
+ setPackageId(id);
231
+ setView('detail');
232
+ const url = new URL(window.location);
233
+ url.searchParams.set('id', id);
234
+ history.pushState(null, '', url.toString());
235
+ }, []);
236
+
237
+ const goBack = useCallback(() => {
238
+ setView('browse');
239
+ setPackageId(null);
240
+ const url = new URL(window.location);
241
+ url.searchParams.delete('id');
242
+ history.pushState(null, '', url.toString());
243
+ }, []);
244
+
245
+ useEffect(() => {
246
+ const handler = () => {
247
+ const params = new URLSearchParams(window.location.search);
248
+ const id = params.get('id');
249
+ if (id) {
250
+ setPackageId(id);
251
+ setView('detail');
252
+ } else {
253
+ setView('browse');
254
+ setPackageId(null);
255
+ }
256
+ };
257
+ window.addEventListener('popstate', handler);
258
+ return () => window.removeEventListener('popstate', handler);
259
+ }, []);
260
+
261
+ return html`
262
+ <div class="pkv-container">
263
+ ${view === 'browse'
264
+ ? html`<${BrowseView} onSelect=${openPackage} />`
265
+ : html`<${DetailView} packageId=${packageId} onBack=${goBack} />`
266
+ }
267
+ </div>
268
+ `;
269
+ }
270
+
271
+ /* ── Browse View ── */
272
+
273
+ function BrowseView({ onSelect }) {
274
+ const [packages, setPackages] = useState([]);
275
+ const [loading, setLoading] = useState(true);
276
+ const [error, setError] = useState(null);
277
+ const [search, setSearch] = useState('');
278
+ const [contentType, setContentType] = useState('');
279
+ const [language, setLanguage] = useState('');
280
+ const [sort, setSort] = useState('newest');
281
+ const [page, setPage] = useState(1);
282
+ const [hasMore, setHasMore] = useState(false);
283
+ const debounceRef = useRef(null);
284
+
285
+ const loadPackages = useCallback(async (opts = {}) => {
286
+ setLoading(true);
287
+ setError(null);
288
+ try {
289
+ const result = await discoverPackages({
290
+ content_type: opts.contentType || contentType,
291
+ tags: opts.search || search,
292
+ language: opts.language || language,
293
+ sort: opts.sort || sort,
294
+ page: opts.page || page,
295
+ limit: 20,
296
+ });
297
+ const items = result?.data?.packages || result?.data?.items || [];
298
+ setPackages(opts.page > 1 ? prev => [...prev, ...items] : items);
299
+ const total = result?.data?.total || 0;
300
+ const perPage = result?.data?.per_page || 20;
301
+ setHasMore((opts.page || page) * perPage < total);
302
+ } catch (err) {
303
+ setError(err.message);
304
+ } finally {
305
+ setLoading(false);
306
+ }
307
+ }, [contentType, language, sort, search, page]);
308
+
309
+ useEffect(() => {
310
+ loadPackages({ page: 1 });
311
+ }, [contentType, language, sort]);
312
+
313
+ const handleSearch = useCallback((e) => {
314
+ const val = e.target.value;
315
+ setSearch(val);
316
+ setPage(1);
317
+ if (debounceRef.current) clearTimeout(debounceRef.current);
318
+ debounceRef.current = setTimeout(() => {
319
+ loadPackages({ search: val, page: 1 });
320
+ }, 400);
321
+ }, [loadPackages]);
322
+
323
+ const loadMore = useCallback(() => {
324
+ const next = page + 1;
325
+ setPage(next);
326
+ loadPackages({ page: next });
327
+ }, [page, loadPackages]);
328
+
329
+ return html`
330
+ <div class="pkv-header">
331
+ <h1>${t('pkv.title')}</h1>
332
+ <p>${t('pkv.subtitle')}</p>
333
+ </div>
334
+
335
+ <div class="pkv-controls">
336
+ <input
337
+ class="pkv-search"
338
+ type="text"
339
+ placeholder=${t('pkv.searchPlaceholder')}
340
+ value=${search}
341
+ onInput=${handleSearch}
342
+ />
343
+ <select class="pkv-filter-select" value=${contentType} onChange=${e => { setContentType(e.target.value); setPage(1); }}>
344
+ <option value="">${t('pkv.allTypes')}</option>
345
+ ${CONTENT_TYPES.map(ct => html`<option value=${ct}>${ct}</option>`)}
346
+ </select>
347
+ <select class="pkv-filter-select" value=${sort} onChange=${e => { setSort(e.target.value); setPage(1); }}>
348
+ ${SORT_OPTIONS.map(s => html`<option value=${s}>${t('pkv.sort.' + s)}</option>`)}
349
+ </select>
350
+ </div>
351
+
352
+ ${error && html`<div class="pkv-error">${error}</div>`}
353
+
354
+ ${!loading && packages.length === 0 && !error && html`
355
+ <div class="pkv-empty">
356
+ <h3>${t('pkv.emptyTitle')}</h3>
357
+ <p>${t('pkv.emptyDesc')}</p>
358
+ </div>
359
+ `}
360
+
361
+ <div class="pkv-grid">
362
+ ${packages.map(pkg => html`<${PackageCard} pkg=${pkg} onSelect=${onSelect} />`)}
363
+ </div>
364
+
365
+ ${loading && html`<div class="pkv-loading">${t('pkv.loading')}</div>`}
366
+
367
+ ${!loading && hasMore && html`
368
+ <div style="text-align:center; margin-top:1.5rem;">
369
+ <button class="btn-outline" onClick=${loadMore}>${t('pkv.loadMore')}</button>
370
+ </div>
371
+ `}
372
+ `;
373
+ }
374
+
375
+ /* ── Package Card ── */
376
+
377
+ function PackageCard({ pkg, onSelect }) {
378
+ const manifest = pkg.manifest || pkg;
379
+ const id = pkg.package_id || pkg.id || pkg.packageId || manifest.name;
380
+ const tags = manifest.tags || [];
381
+
382
+ return html`
383
+ <div class="pkv-card" onClick=${() => onSelect(id)}>
384
+ <div class="pkv-card-title">${manifest.name || id}</div>
385
+ <div class="pkv-card-meta">
386
+ ${manifest.content_type && html`<span class="pkv-badge pkv-badge-type">${manifest.content_type}</span>`}
387
+ ${manifest.maturity && html`<span class="pkv-badge pkv-badge-maturity">${manifest.maturity}</span>`}
388
+ ${manifest.language && html`<span class="pkv-badge pkv-badge-lang">${manifest.language}</span>`}
389
+ ${manifest.entries && html`<span>${manifest.entries.length} ${t('pkv.entries')}</span>`}
390
+ </div>
391
+ ${tags.length > 0 && html`
392
+ <div class="pkv-card-tags">
393
+ ${tags.slice(0, 5).map(tag => html`<span class="pkv-tag">${tag}</span>`)}
394
+ ${tags.length > 5 && html`<span class="pkv-tag">+${tags.length - 5}</span>`}
395
+ </div>
396
+ `}
397
+ ${manifest.author && html`
398
+ <div class="pkv-card-meta" style="margin-top:0.5rem;">
399
+ <span>${manifest.author}</span>
400
+ </div>
401
+ `}
402
+ </div>
403
+ `;
404
+ }
405
+
406
+ /* ── Detail View ── */
407
+
408
+ function DetailView({ packageId, onBack }) {
409
+ const [manifest, setManifest] = useState(null);
410
+ const [reputation, setReputation] = useState(null);
411
+ const [links, setLinks] = useState([]);
412
+ const [entryData, setEntryData] = useState({});
413
+ const [expandedEntries, setExpandedEntries] = useState({});
414
+ const [loading, setLoading] = useState(true);
415
+ const [error, setError] = useState(null);
416
+
417
+ useEffect(() => {
418
+ let cancelled = false;
419
+ async function load() {
420
+ setLoading(true);
421
+ setError(null);
422
+ try {
423
+ const [pkgResult, repResult, linksResult, exportResult] = await Promise.allSettled([
424
+ getPackageManifest(packageId),
425
+ getPackageReputation(packageId),
426
+ getPackageLinks(packageId),
427
+ exportPackage(packageId),
428
+ ]);
429
+ if (cancelled) return;
430
+ if (pkgResult.status === 'fulfilled') {
431
+ setManifest(pkgResult.value?.data?.manifest || pkgResult.value?.data || null);
432
+ } else {
433
+ setError(pkgResult.reason?.message || 'Failed to load package');
434
+ }
435
+ if (repResult.status === 'fulfilled') {
436
+ setReputation(repResult.value?.data || null);
437
+ }
438
+ if (linksResult.status === 'fulfilled') {
439
+ const l = linksResult.value?.data?.links || linksResult.value?.data || [];
440
+ setLinks(Array.isArray(l) ? l : []);
441
+ }
442
+ if (exportResult.status === 'fulfilled') {
443
+ const ed = exportResult.value?.entry_data || exportResult.value?.data?.entry_data || {};
444
+ setEntryData(ed);
445
+ }
446
+ } catch (err) {
447
+ if (!cancelled) setError(err.message);
448
+ } finally {
449
+ if (!cancelled) setLoading(false);
450
+ }
451
+ }
452
+ load();
453
+ return () => { cancelled = true; };
454
+ }, [packageId]);
455
+
456
+ const [copied, setCopied] = useState(false);
457
+
458
+ const toggleEntry = useCallback((entryKey) => {
459
+ setExpandedEntries(prev => ({ ...prev, [entryKey]: !prev[entryKey] }));
460
+ }, []);
461
+
462
+ const handleCopyMarkdown = useCallback(() => {
463
+ if (!manifest) return;
464
+ const publicE = (manifest.entries || []).filter(e => e.visibility === 'public');
465
+ const md = buildFullMarkdown(manifest, publicE, entryData);
466
+ navigator.clipboard.writeText(md).then(() => {
467
+ setCopied(true);
468
+ setTimeout(() => setCopied(false), 2000);
469
+ });
470
+ }, [manifest, entryData]);
471
+
472
+ const handleExport = useCallback(async () => {
473
+ try {
474
+ const result = await exportPackage(packageId);
475
+ const blob = new Blob([JSON.stringify(result?.data || result, null, 2)], { type: 'application/json' });
476
+ const url = URL.createObjectURL(blob);
477
+ const a = document.createElement('a');
478
+ a.href = url;
479
+ a.download = `${manifest?.name || packageId}.json`;
480
+ a.click();
481
+ URL.revokeObjectURL(url);
482
+ } catch (err) {
483
+ alert(err.message);
484
+ }
485
+ }, [packageId, manifest]);
486
+
487
+ if (loading) return html`<div class="pkv-loading">${t('pkv.loading')}</div>`;
488
+ if (error) return html`
489
+ <div>
490
+ <button class="pkv-back" onClick=${onBack}>← ${t('pkv.backToLibrary')}</button>
491
+ <div class="pkv-error">${error}</div>
492
+ </div>
493
+ `;
494
+ if (!manifest) return html`
495
+ <div>
496
+ <button class="pkv-back" onClick=${onBack}>← ${t('pkv.backToLibrary')}</button>
497
+ <div class="pkv-empty"><h3>${t('pkv.notFound')}</h3></div>
498
+ </div>
499
+ `;
500
+
501
+ const publicEntries = (manifest.entries || []).filter(e => e.visibility === 'public');
502
+
503
+ return html`
504
+ <div class="pkv-detail">
505
+ <button class="pkv-back" onClick=${onBack}>← ${t('pkv.backToLibrary')}</button>
506
+
507
+ <h2 class="pkv-detail-title">${manifest.name}</h2>
508
+ <div class="pkv-detail-version">v${manifest.version || '1.0.0'}</div>
509
+
510
+ <div class="pkv-detail-meta">
511
+ <div class="pkv-meta-item">
512
+ <span class="pkv-meta-label">${t('pkv.author')}</span>
513
+ <span class="pkv-meta-value">${manifest.author || '-'}</span>
514
+ </div>
515
+ <div class="pkv-meta-item">
516
+ <span class="pkv-meta-label">${t('pkv.contentType')}</span>
517
+ <span class="pkv-meta-value">${manifest.content_type || '-'}</span>
518
+ </div>
519
+ <div class="pkv-meta-item">
520
+ <span class="pkv-meta-label">${t('pkv.language')}</span>
521
+ <span class="pkv-meta-value">${manifest.language || '-'}</span>
522
+ </div>
523
+ <div class="pkv-meta-item">
524
+ <span class="pkv-meta-label">${t('pkv.maturity')}</span>
525
+ <span class="pkv-meta-value">${manifest.maturity || '-'}</span>
526
+ </div>
527
+ <div class="pkv-meta-item">
528
+ <span class="pkv-meta-label">${t('pkv.created')}</span>
529
+ <span class="pkv-meta-value">${manifest.created ? new Date(manifest.created).toLocaleDateString() : '-'}</span>
530
+ </div>
531
+ <div class="pkv-meta-item">
532
+ <span class="pkv-meta-label">${t('pkv.updated')}</span>
533
+ <span class="pkv-meta-value">${manifest.updated ? new Date(manifest.updated).toLocaleDateString() : '-'}</span>
534
+ </div>
535
+ </div>
536
+
537
+ ${manifest.synthesis && html`
538
+ <div class="pkv-meta-item" style="margin-bottom:0.75rem;">
539
+ <span class="pkv-meta-label">${t('pkv.synthesis')}</span>
540
+ <span class="pkv-meta-value">${manifest.synthesis.level}${manifest.synthesis.model ? ' (' + manifest.synthesis.model + ')' : ''}</span>
541
+ </div>
542
+ `}
543
+
544
+ ${manifest.tags && manifest.tags.length > 0 && html`
545
+ <div class="pkv-card-tags" style="margin-bottom:1rem;">
546
+ ${manifest.tags.map(tag => html`<span class="pkv-tag">${tag}</span>`)}
547
+ </div>
548
+ `}
549
+
550
+ ${manifest.sharing && html`
551
+ <div class="pkv-meta-item" style="margin-bottom:0.5rem;">
552
+ <span class="pkv-meta-label">${t('pkv.license')}</span>
553
+ <span class="pkv-meta-value">${manifest.sharing.license || t('pkv.noLicense')}</span>
554
+ </div>
555
+ `}
556
+
557
+ ${reputation && html`
558
+ <div class="pkv-detail-section">
559
+ <h3>${t('pkv.reputation')}</h3>
560
+ <div class="pkv-reputation">
561
+ ${reputation.review_count != null && html`
562
+ <div class="pkv-rep-item">
563
+ <span class="pkv-rep-value">${reputation.review_count}</span>
564
+ <span class="pkv-rep-label">${t('pkv.reviews')}</span>
565
+ </div>
566
+ `}
567
+ ${reputation.average_score != null && html`
568
+ <div class="pkv-rep-item">
569
+ <span class="pkv-rep-value">${reputation.average_score.toFixed(1)}</span>
570
+ <span class="pkv-rep-label">${t('pkv.avgScore')}</span>
571
+ </div>
572
+ `}
573
+ ${reputation.clone_count != null && html`
574
+ <div class="pkv-rep-item">
575
+ <span class="pkv-rep-value">${reputation.clone_count}</span>
576
+ <span class="pkv-rep-label">${t('pkv.clones')}</span>
577
+ </div>
578
+ `}
579
+ </div>
580
+ </div>
581
+ `}
582
+
583
+ ${publicEntries.length > 0 && html`
584
+ <div class="pkv-detail-section">
585
+ <h3>${t('pkv.publicEntries')} (${publicEntries.length})</h3>
586
+ <div class="pkv-entries-list">
587
+ ${publicEntries.map(entry => {
588
+ const entryName = entry.key.split('/').pop() || entry.key;
589
+ const content = entryData[entryName];
590
+ return html`
591
+ <div class="pkv-entry">
592
+ <div class="pkv-entry-header" onClick=${() => toggleEntry(entry.key)}>
593
+ <span class="pkv-entry-arrow ${expandedEntries[entry.key] ? 'expanded' : ''}">▶</span>
594
+ <span class="pkv-entry-title">${entry.title || entry.key}</span>
595
+ </div>
596
+ ${expandedEntries[entry.key] && html`
597
+ <div class="pkv-entry-content">
598
+ ${content
599
+ ? html`<${RenderEntryContent} content=${content} />`
600
+ : html`<span class="pkv-entry-unavailable">${t('pkv.entryUnavailable')}</span>`
601
+ }
602
+ </div>
603
+ `}
604
+ </div>
605
+ `;
606
+ })}
607
+ </div>
608
+ </div>
609
+ `}
610
+
611
+ ${manifest.references && manifest.references.length > 0 && html`
612
+ <div class="pkv-detail-section">
613
+ <h3>${t('pkv.references')}</h3>
614
+ <ul class="pkv-references">
615
+ ${manifest.references.map(ref => html`
616
+ <li>
617
+ ${ref.url
618
+ ? html`<a href=${ref.url} target="_blank" rel="noopener">${ref.title || ref.url}</a>`
619
+ : html`<span>${ref.title}</span>`
620
+ }
621
+ ${ref.accessed && html` — ${new Date(ref.accessed).toLocaleDateString()}`}
622
+ </li>
623
+ `)}
624
+ </ul>
625
+ </div>
626
+ `}
627
+
628
+ ${links.length > 0 && html`
629
+ <div class="pkv-detail-section">
630
+ <h3>${t('pkv.relatedPackages')}</h3>
631
+ <div class="pkv-links-list">
632
+ ${links.map(link => html`
633
+ <div class="pkv-link-item">
634
+ <span class="pkv-link-relation">${link.relation}</span>
635
+ <span>${link.target_name || link.target}</span>
636
+ </div>
637
+ `)}
638
+ </div>
639
+ </div>
640
+ `}
641
+
642
+ <div class="pkv-actions">
643
+ <button class="btn-primary" onClick=${handleCopyMarkdown}>
644
+ ${copied ? t('pkv.copied') : t('pkv.copyMarkdown')}
645
+ </button>
646
+ <button class="btn-outline" onClick=${handleExport}>${t('pkv.exportJson')}</button>
647
+ </div>
648
+ </div>
649
+ `;
650
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/auth/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAGpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAMvD,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEtD;AAOD,gGAAgG;AAChG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIrE;AAED,yCAAyC;AACzC,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,2FAA2F;AAC3F,wBAAgB,uBAAuB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAEzE;AAGD,OAAO,CAAC,MAAM,CAAC;IAEb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,IAAI,CAAC,EAAE,aAAa,CAAC;SACtB;KACF;CACF;AAED;;;GAGG;AACH,wBAAgB,YAAY,KACZ,KAAK,OAAO,EAAE,MAAM,QAAQ,EAAE,MAAM,YAAY,mBA0B/D;AAED;;;GAGG;AACH,wBAAgB,WAAW,KACX,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,mBA6D9D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,KAC5B,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAQxD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,IAC9B,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UA6BxD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,KACzB,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAOxD;AAOD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,cAAc,EAAE,MAAM,EAAE,IAC9C,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAmCxD"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/auth/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAGpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAMvD,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEtD;AAqBD,gGAAgG;AAChG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIrE;AAED,yCAAyC;AACzC,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,2FAA2F;AAC3F,wBAAgB,uBAAuB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAEzE;AAGD,OAAO,CAAC,MAAM,CAAC;IAEb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,IAAI,CAAC,EAAE,aAAa,CAAC;SACtB;KACF;CACF;AAED;;;GAGG;AACH,wBAAgB,YAAY,KACZ,KAAK,OAAO,EAAE,MAAM,QAAQ,EAAE,MAAM,YAAY,mBA0B/D;AAED;;;GAGG;AACH,wBAAgB,WAAW,KACX,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,mBA+D9D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,KAC5B,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAQxD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,IAC9B,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UA6BxD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,KACzB,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAOxD;AAOD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,cAAc,EAAE,MAAM,EAAE,IAC9C,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAmCxD"}
@@ -8,6 +8,21 @@ let _sessionStorage = null;
8
8
  export function initSessionAuth(storage) {
9
9
  _sessionStorage = storage;
10
10
  }
11
+ const _lastSeenCache = new Map();
12
+ const LAST_SEEN_THROTTLE_MS = 5 * 60_000;
13
+ function touchAgentLastSeen(auth) {
14
+ if (!_sessionStorage)
15
+ return;
16
+ if (!auth.roles.includes('agent'))
17
+ return;
18
+ const gaii = auth.sub;
19
+ const now = Date.now();
20
+ const last = _lastSeenCache.get(gaii) ?? 0;
21
+ if (now - last < LAST_SEEN_THROTTLE_MS)
22
+ return;
23
+ _lastSeenCache.set(gaii, now);
24
+ _sessionStorage.updateAgent(gaii, { lastSeen: new Date(now).toISOString() }).catch(() => { });
25
+ }
11
26
  // Anonymous mode: when enabled, inject this identity for unauthenticated requests
12
27
  let _anonymousMode = false;
13
28
  let _anonymousGaii = '';
@@ -67,6 +82,7 @@ export function requireAuth() {
67
82
  return async (req, res, next) => {
68
83
  // If auth was already resolved by global optionalAuth() (e.g. anonymous mode)
69
84
  if (req.auth) {
85
+ touchAgentLastSeen(req.auth);
70
86
  // SECURITY: Reject anonymous credentials — requireAuth() requires real authentication
71
87
  if (req.auth.anonymous) {
72
88
  const stats = getStats();
@@ -128,6 +144,7 @@ export function requireAuth() {
128
144
  }
129
145
  }
130
146
  req.auth = verified;
147
+ touchAgentLastSeen(verified);
131
148
  next();
132
149
  };
133
150
  }