aimeat 1.4.8 → 1.6.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 (268) hide show
  1. package/dist/.env.example +15 -0
  2. package/dist/bin/aimeat.js +0 -0
  3. package/dist/locales/en.json +139 -6
  4. package/dist/locales/fi.json +139 -6
  5. package/dist/public/css/views/admin.css +97 -0
  6. package/dist/public/css/views/profile.css +37 -0
  7. package/dist/public/js/services/admin.js +12 -1
  8. package/dist/public/js/services/boards.js +6 -3
  9. package/dist/public/js/services/memory.js +10 -0
  10. package/dist/public/views/admin/federation-tab.js +153 -7
  11. package/dist/public/views/admin/ghii-tab.js +2 -0
  12. package/dist/public/views/admin/stats-tab.js +480 -187
  13. package/dist/public/views/admin.js +1 -1
  14. package/dist/public/views/profile/access-tab.js +161 -58
  15. package/dist/public/views/profile/agents-tab.js +13 -1
  16. package/dist/public/views/profile/boards-tab.js +12 -0
  17. package/dist/public/views/profile/data-wallet-tab.js +29 -2
  18. package/dist/public/views/profile/knowledge-tab.js +64 -2
  19. package/dist/public/views/profile/landing-page.js +9 -6
  20. package/dist/public/views/profile/memory-tab.js +92 -4
  21. package/dist/src/auth/jwt.d.ts +6 -0
  22. package/dist/src/auth/jwt.d.ts.map +1 -1
  23. package/dist/src/auth/jwt.js +6 -0
  24. package/dist/src/auth/jwt.js.map +1 -1
  25. package/dist/src/auth/middleware.d.ts +7 -0
  26. package/dist/src/auth/middleware.d.ts.map +1 -1
  27. package/dist/src/auth/middleware.js +20 -0
  28. package/dist/src/auth/middleware.js.map +1 -1
  29. package/dist/src/auth/node-keys.d.ts.map +1 -1
  30. package/dist/src/auth/node-keys.js +3 -0
  31. package/dist/src/auth/node-keys.js.map +1 -1
  32. package/dist/src/config.d.ts +13 -0
  33. package/dist/src/config.d.ts.map +1 -1
  34. package/dist/src/config.js +14 -0
  35. package/dist/src/config.js.map +1 -1
  36. package/dist/src/index.js +41 -2
  37. package/dist/src/index.js.map +1 -1
  38. package/dist/src/mcp/extensions.js +1 -1
  39. package/dist/src/mcp/extensions.js.map +1 -1
  40. package/dist/src/middleware/federation-proxy.d.ts +35 -0
  41. package/dist/src/middleware/federation-proxy.d.ts.map +1 -0
  42. package/dist/src/middleware/federation-proxy.js +92 -0
  43. package/dist/src/middleware/federation-proxy.js.map +1 -0
  44. package/dist/src/middleware/rate-limit.d.ts.map +1 -1
  45. package/dist/src/middleware/rate-limit.js +4 -1
  46. package/dist/src/middleware/rate-limit.js.map +1 -1
  47. package/dist/src/models/schemas.d.ts +45 -0
  48. package/dist/src/models/schemas.d.ts.map +1 -1
  49. package/dist/src/models/schemas.js +37 -1
  50. package/dist/src/models/schemas.js.map +1 -1
  51. package/dist/src/routes/actions.d.ts.map +1 -1
  52. package/dist/src/routes/actions.js +8 -2
  53. package/dist/src/routes/actions.js.map +1 -1
  54. package/dist/src/routes/admin-agents.d.ts.map +1 -1
  55. package/dist/src/routes/admin-agents.js +1 -0
  56. package/dist/src/routes/admin-agents.js.map +1 -1
  57. package/dist/src/routes/admin-features.d.ts.map +1 -1
  58. package/dist/src/routes/admin-features.js +2 -0
  59. package/dist/src/routes/admin-features.js.map +1 -1
  60. package/dist/src/routes/admin-monitoring.d.ts.map +1 -1
  61. package/dist/src/routes/admin-monitoring.js +7 -0
  62. package/dist/src/routes/admin-monitoring.js.map +1 -1
  63. package/dist/src/routes/admin.d.ts.map +1 -1
  64. package/dist/src/routes/admin.js +33 -15
  65. package/dist/src/routes/admin.js.map +1 -1
  66. package/dist/src/routes/agents.d.ts.map +1 -1
  67. package/dist/src/routes/agents.js +38 -5
  68. package/dist/src/routes/agents.js.map +1 -1
  69. package/dist/src/routes/apps.d.ts.map +1 -1
  70. package/dist/src/routes/apps.js +5 -1
  71. package/dist/src/routes/apps.js.map +1 -1
  72. package/dist/src/routes/auth.d.ts.map +1 -1
  73. package/dist/src/routes/auth.js +19 -1
  74. package/dist/src/routes/auth.js.map +1 -1
  75. package/dist/src/routes/boards.d.ts.map +1 -1
  76. package/dist/src/routes/boards.js +14 -7
  77. package/dist/src/routes/boards.js.map +1 -1
  78. package/dist/src/routes/consent.d.ts.map +1 -1
  79. package/dist/src/routes/consent.js +2 -1
  80. package/dist/src/routes/consent.js.map +1 -1
  81. package/dist/src/routes/disputes.js +4 -4
  82. package/dist/src/routes/disputes.js.map +1 -1
  83. package/dist/src/routes/extensions.d.ts.map +1 -1
  84. package/dist/src/routes/extensions.js +72 -21
  85. package/dist/src/routes/extensions.js.map +1 -1
  86. package/dist/src/routes/federation-auth.d.ts +15 -0
  87. package/dist/src/routes/federation-auth.d.ts.map +1 -0
  88. package/dist/src/routes/federation-auth.js +122 -0
  89. package/dist/src/routes/federation-auth.js.map +1 -0
  90. package/dist/src/routes/federation-genesis.d.ts +2 -1
  91. package/dist/src/routes/federation-genesis.d.ts.map +1 -1
  92. package/dist/src/routes/federation-genesis.js +58 -2
  93. package/dist/src/routes/federation-genesis.js.map +1 -1
  94. package/dist/src/routes/federation-peer.d.ts.map +1 -1
  95. package/dist/src/routes/federation-peer.js +91 -2
  96. package/dist/src/routes/federation-peer.js.map +1 -1
  97. package/dist/src/routes/federation-sync.d.ts.map +1 -1
  98. package/dist/src/routes/federation-sync.js +14 -1
  99. package/dist/src/routes/federation-sync.js.map +1 -1
  100. package/dist/src/routes/federation.d.ts +2 -1
  101. package/dist/src/routes/federation.d.ts.map +1 -1
  102. package/dist/src/routes/federation.js +4 -2
  103. package/dist/src/routes/federation.js.map +1 -1
  104. package/dist/src/routes/flags.d.ts.map +1 -1
  105. package/dist/src/routes/flags.js +2 -1
  106. package/dist/src/routes/flags.js.map +1 -1
  107. package/dist/src/routes/foundry.d.ts.map +1 -1
  108. package/dist/src/routes/foundry.js +2 -2
  109. package/dist/src/routes/foundry.js.map +1 -1
  110. package/dist/src/routes/generator.d.ts.map +1 -1
  111. package/dist/src/routes/generator.js +2 -2
  112. package/dist/src/routes/generator.js.map +1 -1
  113. package/dist/src/routes/ghii.d.ts +2 -1
  114. package/dist/src/routes/ghii.d.ts.map +1 -1
  115. package/dist/src/routes/ghii.js +154 -34
  116. package/dist/src/routes/ghii.js.map +1 -1
  117. package/dist/src/routes/libs.js +42 -10
  118. package/dist/src/routes/libs.js.map +1 -1
  119. package/dist/src/routes/memory.d.ts.map +1 -1
  120. package/dist/src/routes/memory.js +164 -0
  121. package/dist/src/routes/memory.js.map +1 -1
  122. package/dist/src/routes/owners.d.ts.map +1 -1
  123. package/dist/src/routes/owners.js +104 -8
  124. package/dist/src/routes/owners.js.map +1 -1
  125. package/dist/src/routes/packages.js +1 -1
  126. package/dist/src/routes/packages.js.map +1 -1
  127. package/dist/src/routes/stats.d.ts +13 -0
  128. package/dist/src/routes/stats.d.ts.map +1 -1
  129. package/dist/src/routes/stats.js +54 -4
  130. package/dist/src/routes/stats.js.map +1 -1
  131. package/dist/src/routes/storage-files.d.ts.map +1 -1
  132. package/dist/src/routes/storage-files.js +5 -1
  133. package/dist/src/routes/storage-files.js.map +1 -1
  134. package/dist/src/routes/upload.js +2 -2
  135. package/dist/src/routes/upload.js.map +1 -1
  136. package/dist/src/routes/wallet.d.ts.map +1 -1
  137. package/dist/src/routes/wallet.js +2 -1
  138. package/dist/src/routes/wallet.js.map +1 -1
  139. package/dist/src/routes/work.d.ts.map +1 -1
  140. package/dist/src/routes/work.js +2 -1
  141. package/dist/src/routes/work.js.map +1 -1
  142. package/dist/src/server-bootstrap/routes-loader.d.ts +3 -1
  143. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  144. package/dist/src/server-bootstrap/routes-loader.js +10 -7
  145. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  146. package/dist/src/server-bootstrap/service-init.d.ts +2 -0
  147. package/dist/src/server-bootstrap/service-init.d.ts.map +1 -1
  148. package/dist/src/server-bootstrap/service-init.js +10 -1
  149. package/dist/src/server-bootstrap/service-init.js.map +1 -1
  150. package/dist/src/server-bootstrap/static-files.d.ts.map +1 -1
  151. package/dist/src/server-bootstrap/static-files.js +10 -7
  152. package/dist/src/server-bootstrap/static-files.js.map +1 -1
  153. package/dist/src/server.d.ts.map +1 -1
  154. package/dist/src/server.js +5 -2
  155. package/dist/src/server.js.map +1 -1
  156. package/dist/src/services/capability-invoke.d.ts.map +1 -1
  157. package/dist/src/services/capability-invoke.js +5 -18
  158. package/dist/src/services/capability-invoke.js.map +1 -1
  159. package/dist/src/services/config-schema.d.ts.map +1 -1
  160. package/dist/src/services/config-schema.js +13 -0
  161. package/dist/src/services/config-schema.js.map +1 -1
  162. package/dist/src/services/consent.d.ts.map +1 -1
  163. package/dist/src/services/consent.js +1 -10
  164. package/dist/src/services/consent.js.map +1 -1
  165. package/dist/src/services/directory.d.ts.map +1 -1
  166. package/dist/src/services/directory.js +10 -4
  167. package/dist/src/services/directory.js.map +1 -1
  168. package/dist/src/services/email.d.ts +17 -4
  169. package/dist/src/services/email.d.ts.map +1 -1
  170. package/dist/src/services/email.js +30 -12
  171. package/dist/src/services/email.js.map +1 -1
  172. package/dist/src/services/extension-runtime.d.ts.map +1 -1
  173. package/dist/src/services/extension-runtime.js +5 -0
  174. package/dist/src/services/extension-runtime.js.map +1 -1
  175. package/dist/src/services/federation.d.ts +8 -6
  176. package/dist/src/services/federation.d.ts.map +1 -1
  177. package/dist/src/services/federation.js +53 -1
  178. package/dist/src/services/federation.js.map +1 -1
  179. package/dist/src/services/mailbox-notification.d.ts +17 -0
  180. package/dist/src/services/mailbox-notification.d.ts.map +1 -1
  181. package/dist/src/services/mailbox-notification.js +25 -0
  182. package/dist/src/services/mailbox-notification.js.map +1 -1
  183. package/dist/src/services/memory-replication.d.ts.map +1 -1
  184. package/dist/src/services/memory-replication.js +3 -0
  185. package/dist/src/services/memory-replication.js.map +1 -1
  186. package/dist/src/services/morsel.d.ts.map +1 -1
  187. package/dist/src/services/morsel.js +10 -9
  188. package/dist/src/services/morsel.js.map +1 -1
  189. package/dist/src/services/password.d.ts +1 -6
  190. package/dist/src/services/password.d.ts.map +1 -1
  191. package/dist/src/services/password.js +19 -10
  192. package/dist/src/services/password.js.map +1 -1
  193. package/dist/src/services/push.d.ts +16 -0
  194. package/dist/src/services/push.d.ts.map +1 -1
  195. package/dist/src/services/push.js +20 -0
  196. package/dist/src/services/push.js.map +1 -1
  197. package/dist/src/services/quota.d.ts.map +1 -1
  198. package/dist/src/services/quota.js +2 -1
  199. package/dist/src/services/quota.js.map +1 -1
  200. package/dist/src/services/scheduler.d.ts.map +1 -1
  201. package/dist/src/services/scheduler.js +17 -1
  202. package/dist/src/services/scheduler.js.map +1 -1
  203. package/dist/src/services/stats.d.ts +42 -2
  204. package/dist/src/services/stats.d.ts.map +1 -1
  205. package/dist/src/services/stats.js +176 -1
  206. package/dist/src/services/stats.js.map +1 -1
  207. package/dist/src/services/totp.js +2 -2
  208. package/dist/src/storage/interface.d.ts +15 -2
  209. package/dist/src/storage/interface.d.ts.map +1 -1
  210. package/dist/src/storage/providers/mongodb/index.d.ts +12 -1
  211. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  212. package/dist/src/storage/providers/mongodb/index.js +110 -10
  213. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  214. package/dist/src/storage/providers/sqlite/index.d.ts +12 -1
  215. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  216. package/dist/src/storage/providers/sqlite/index.js +118 -19
  217. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  218. package/dist/src/storage/providers/sqlite/repos/action.d.ts.map +1 -1
  219. package/dist/src/storage/providers/sqlite/repos/action.js +5 -4
  220. package/dist/src/storage/providers/sqlite/repos/action.js.map +1 -1
  221. package/dist/src/storage/providers/sqlite/repos/agent.d.ts.map +1 -1
  222. package/dist/src/storage/providers/sqlite/repos/agent.js +5 -4
  223. package/dist/src/storage/providers/sqlite/repos/agent.js.map +1 -1
  224. package/dist/src/storage/providers/sqlite/repos/board.d.ts.map +1 -1
  225. package/dist/src/storage/providers/sqlite/repos/board.js +3 -2
  226. package/dist/src/storage/providers/sqlite/repos/board.js.map +1 -1
  227. package/dist/src/storage/providers/sqlite/repos/consent.d.ts +1 -0
  228. package/dist/src/storage/providers/sqlite/repos/consent.d.ts.map +1 -1
  229. package/dist/src/storage/providers/sqlite/repos/consent.js +4 -0
  230. package/dist/src/storage/providers/sqlite/repos/consent.js.map +1 -1
  231. package/dist/src/storage/providers/sqlite/repos/storage-file.d.ts.map +1 -1
  232. package/dist/src/storage/providers/sqlite/repos/storage-file.js +4 -2
  233. package/dist/src/storage/providers/sqlite/repos/storage-file.js.map +1 -1
  234. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  235. package/dist/src/storage/providers/sqlite/schema.js +29 -0
  236. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  237. package/dist/src/storage/repositories/board.repository.d.ts +1 -1
  238. package/dist/src/storage/repositories/board.repository.d.ts.map +1 -1
  239. package/dist/src/storage/repositories/consent.repository.d.ts +1 -0
  240. package/dist/src/storage/repositories/consent.repository.d.ts.map +1 -1
  241. package/dist/src/storage/repositories/device-auth.repository.d.ts +1 -0
  242. package/dist/src/storage/repositories/device-auth.repository.d.ts.map +1 -1
  243. package/dist/src/storage/repositories/extension-instance.repository.d.ts +1 -0
  244. package/dist/src/storage/repositories/extension-instance.repository.d.ts.map +1 -1
  245. package/dist/src/storage/repositories/federation.repository.d.ts +1 -0
  246. package/dist/src/storage/repositories/federation.repository.d.ts.map +1 -1
  247. package/dist/src/storage/repositories/index.d.ts +1 -0
  248. package/dist/src/storage/repositories/index.d.ts.map +1 -1
  249. package/dist/src/storage/repositories/knowledge.repository.d.ts +2 -0
  250. package/dist/src/storage/repositories/knowledge.repository.d.ts.map +1 -1
  251. package/dist/src/storage/repositories/moderation.repository.d.ts +1 -0
  252. package/dist/src/storage/repositories/moderation.repository.d.ts.map +1 -1
  253. package/dist/src/storage/repositories/stats.repository.d.ts +12 -0
  254. package/dist/src/storage/repositories/stats.repository.d.ts.map +1 -0
  255. package/dist/src/storage/repositories/stats.repository.js +7 -0
  256. package/dist/src/storage/repositories/stats.repository.js.map +1 -0
  257. package/dist/src/storage/storage-factory.js +1 -0
  258. package/dist/src/storage/storage-factory.js.map +1 -1
  259. package/dist/src/utils/password-validation.d.ts +8 -0
  260. package/dist/src/utils/password-validation.d.ts.map +1 -0
  261. package/dist/src/utils/password-validation.js +25 -0
  262. package/dist/src/utils/password-validation.js.map +1 -0
  263. package/dist/src/utils/service-summary.d.ts +70 -0
  264. package/dist/src/utils/service-summary.d.ts.map +1 -0
  265. package/dist/src/utils/service-summary.js +105 -0
  266. package/dist/src/utils/service-summary.js.map +1 -0
  267. package/package.json +97 -75
  268. package/prisma/schema.prisma +30 -6
@@ -51,11 +51,13 @@ export const getFederationPeers = () => apiGet('/v1/federation/peers');
51
51
  export const getFederationDirectory = () => apiGet('/v1/federation/directory');
52
52
  export const approvePeeringRequest = (id) => apiPut(`/v1/admin/peering/requests/${encodeURIComponent(id)}`, { decision: 'approve' });
53
53
  export const rejectPeeringRequest = (id) => apiPut(`/v1/admin/peering/requests/${encodeURIComponent(id)}`, { decision: 'reject' });
54
+ export const deletePeeringRequest = (id) => apiDelete(`/v1/admin/peering/requests/${encodeURIComponent(id)}`);
54
55
  export const activatePeer = (nodeId) => apiPost('/v1/federation/peer/activate', { peer_node_id: nodeId });
55
56
  export const addPeerDirect = (nodeId, url, publicKey) => apiPost('/v1/federation/peers', { node_id: nodeId, url, public_key: publicKey || undefined });
56
57
  export const removePeer = (nodeId) => apiDelete(`/v1/federation/peers/${encodeURIComponent(nodeId)}`);
57
58
  export const removePeerEmergency = (nodeId) => apiDelete(`/v1/federation/peers/${encodeURIComponent(nodeId)}?emergency=true`);
58
59
  export const testFederationNode = (targetUrl) => apiPost('/v1/federation/test', { target_url: targetUrl });
60
+ export const updatePeerPolicy = (nodeId, policy) => apiPut(`/v1/federation/peers/${encodeURIComponent(nodeId)}`, policy);
59
61
  export const joinGenesisNetwork = (genesisUrl, role) => apiPost('/v1/admin/federation/join', { genesis_url: genesisUrl, role: role || 'contributor' });
60
62
 
61
63
  // ── Hooks ──
@@ -129,6 +131,9 @@ export const deleteMsm = (name) => apiDelete(`/v1/admin/msm/${encodeURI
129
131
  export const getMsmTemplates = () => apiGet('/v1/msm/templates');
130
132
  export const getMsmTemplate = (type) => fetch(`/v1/msm/templates/${encodeURIComponent(type)}`, { headers: { 'Accept': 'application/x-yaml' } }).then(r => r.text());
131
133
 
134
+ // ── Network Directory ──
135
+ export const getNetworkDirectory = (keyword) => apiGet(`/v1/federation/cross-catalogue${keyword ? '?keyword=' + encodeURIComponent(keyword) + '&source=network' : '?source=network'}`);
136
+
132
137
  // ── Genesis ──
133
138
  export const getGenesisPeers = () => apiGet('/v1/admin/genesis-peers');
134
139
  export const approveGenesisPeer = (id) => apiPost(`/v1/admin/genesis-peers/${encodeURIComponent(id)}/approve`);
@@ -168,7 +173,13 @@ export const getAdminMemory = (params = {}) => {
168
173
  export const deleteAdminMemory = (owner, key) => apiDelete(`/v1/admin/memory/${encodeURIComponent(owner)}/${encodeURIComponent(key)}`);
169
174
 
170
175
  // ── Stats ──
171
- export const getStats = () => apiGet('/v1/stats');
176
+ export const getStats = (from, to) => {
177
+ const params = new URLSearchParams();
178
+ if (from) params.set('from', from);
179
+ if (to) params.set('to', to);
180
+ const qs = params.toString();
181
+ return apiGet('/v1/stats' + (qs ? '?' + qs : ''));
182
+ };
172
183
 
173
184
  // ── Extensions & Instances ──
174
185
  export const getAvailableExtensions = () => apiGet('/v1/admin/extensions/available');
@@ -29,11 +29,14 @@ export async function deleteBoard(boardId) {
29
29
  return apiDelete(`/v1/boards/${encodeURIComponent(boardId)}`);
30
30
  }
31
31
 
32
- /** Update board visibility. */
33
- export async function updateBoardVisibility(boardId, visibility) {
32
+ /** Update board visibility and/or federate flag. */
33
+ export async function updateBoardVisibility(boardId, visibility, federate) {
34
+ const body = {};
35
+ if (visibility !== undefined) body.visibility = visibility;
36
+ if (federate !== undefined) body.federate = federate;
34
37
  return api(`/v1/boards/${encodeURIComponent(boardId)}/visibility`, {
35
38
  method: 'PATCH',
36
- body: JSON.stringify({ visibility }),
39
+ body: JSON.stringify(body),
37
40
  });
38
41
  }
39
42
 
@@ -110,3 +110,13 @@ export async function updateFileVisibility(key, visibility) {
110
110
  export async function deleteFile(key) {
111
111
  return apiDelete(`/v1/memory/files/${encodeURIComponent(key)}`);
112
112
  }
113
+
114
+ /** Pull (copy) a memory entry from the home node to this node (federated sessions). */
115
+ export async function pullFromHome(key) {
116
+ return apiPost('/v1/memory/pull', { key });
117
+ }
118
+
119
+ /** Push (save) a local memory entry to the home node (federated sessions). */
120
+ export async function pushToHome(key) {
121
+ return apiPost('/v1/memory/push-home', { key });
122
+ }
@@ -1,5 +1,5 @@
1
1
  import { h } from 'preact';
2
- import { useState, useCallback } from 'preact/hooks';
2
+ import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
3
3
  import htm from 'htm';
4
4
  const html = htm.bind(h);
5
5
  import { t } from '/js/i18n.js';
@@ -7,8 +7,9 @@ import { escHtml } from '/js/utils.js';
7
7
  import { dt, num, Badge, EconRow, StatsGrid, Empty, ExpandableHelp, ErrorBox } from './shared.js';
8
8
  import {
9
9
  getFederationPeers, getFederationDirectory, approvePeeringRequest, rejectPeeringRequest,
10
- activatePeer, addPeerDirect, removePeer, removePeerEmergency, testFederationNode,
11
- joinGenesisNetwork,
10
+ deletePeeringRequest, activatePeer, addPeerDirect, removePeer, removePeerEmergency,
11
+ testFederationNode, joinGenesisNetwork, updatePeerPolicy, getNetworkDirectory,
12
+ getConfig, saveConfig,
12
13
  } from '/js/services/admin.js';
13
14
  import { useConfirm } from '/components/Modal.js';
14
15
 
@@ -30,14 +31,76 @@ export default function FederationTab({ data, reload }) {
30
31
  const [joining, setJoining] = useState(false);
31
32
  const [joinResult, setJoinResult] = useState(null);
32
33
 
34
+ // ── Federation Auth Policy state ──
35
+ const [authPolicy, setAuthPolicy] = useState('disabled');
36
+ const [defaultScopes, setDefaultScopes] = useState([]);
37
+ const [authPolicyLoading, setAuthPolicyLoading] = useState(true);
38
+
39
+ useEffect(() => {
40
+ getConfig().then(res => {
41
+ if (res.ok && res.data?.config) {
42
+ const c = res.data.config;
43
+ setAuthPolicy(c.federation?.auth_policy ?? c.federationAuthPolicy ?? 'disabled');
44
+ const scopes = c.federation?.default_scopes ?? c.federationDefaultScopes ?? 'memory:read,catalogue:read';
45
+ setDefaultScopes(typeof scopes === 'string' ? scopes.split(',').filter(Boolean) : (scopes || []));
46
+ }
47
+ setAuthPolicyLoading(false);
48
+ });
49
+ }, []);
50
+
51
+ const doSaveAuthPolicy = async (policy, scopes) => {
52
+ const changes = [
53
+ { key: 'federation.auth_policy', value: policy },
54
+ { key: 'federation.default_scopes', value: Array.isArray(scopes) ? scopes.join(',') : scopes },
55
+ ];
56
+ const res = await saveConfig(changes);
57
+ if (res.ok) { setActionSuccess(t('dashboard.fedPolicyUpdated')); setTimeout(() => setActionSuccess(null), 3000); }
58
+ else setActionError(res.error?.message || 'Failed to save');
59
+ };
60
+
61
+ const toggleScope = (scope) => {
62
+ const next = defaultScopes.includes(scope) ? defaultScopes.filter(s => s !== scope) : [...defaultScopes, scope];
63
+ setDefaultScopes(next);
64
+ doSaveAuthPolicy(authPolicy, next);
65
+ };
66
+
67
+ // ── Network Directory state ──
68
+ const [dirEntries, setDirEntries] = useState([]);
69
+ const [dirSearch, setDirSearch] = useState('');
70
+ const [dirLoading, setDirLoading] = useState(false);
71
+ const dirDebounceRef = useRef(null);
72
+
73
+ const loadDirectory = useCallback(async (keyword) => {
74
+ setDirLoading(true);
75
+ try {
76
+ const r = await getNetworkDirectory(keyword || '');
77
+ setDirEntries(r.data?.entries || []);
78
+ } catch { setDirEntries([]); }
79
+ finally { setDirLoading(false); }
80
+ }, []);
81
+
82
+ useEffect(() => { loadDirectory(''); }, []);
83
+
84
+ useEffect(() => {
85
+ const handler = () => loadDirectory(dirSearch);
86
+ window.addEventListener('aimeat-live-update', handler);
87
+ return () => window.removeEventListener('aimeat-live-update', handler);
88
+ }, [dirSearch]);
89
+
90
+ const onDirSearchInput = useCallback((e) => {
91
+ const val = e.target.value;
92
+ setDirSearch(val);
93
+ if (dirDebounceRef.current) clearTimeout(dirDebounceRef.current);
94
+ dirDebounceRef.current = setTimeout(() => loadDirectory(val), 400);
95
+ }, [loadDirectory]);
96
+
33
97
  // ── Derived stats ──
34
98
  const activePeers = livePeers.filter(p => p.status === 'active');
35
99
  const degradedPeers = livePeers.filter(p => p.status === 'degraded');
36
100
  const offlinePeers = livePeers.filter(p => p.status === 'offline');
37
101
  const depeeringPeers = livePeers.filter(p => p.status === 'depeering');
38
102
  const pendingRequests = peeringRequests.filter(r => r.status === 'pending');
39
- const approvedRequests = peeringRequests.filter(r => r.status === 'approved');
40
- const rejectedRequests = peeringRequests.filter(r => r.status === 'rejected');
103
+ const historyRequests = peeringRequests.filter(r => r.status !== 'pending');
41
104
 
42
105
  const { confirm, ConfirmUI } = useConfirm();
43
106
 
@@ -59,6 +122,13 @@ export default function FederationTab({ data, reload }) {
59
122
  }, { danger: true });
60
123
  }, [reload]);
61
124
 
125
+ const doDeleteRequest = useCallback((id) => {
126
+ confirm(t('dashboard.fedDeleteRequestConfirm') || 'Delete this peering request?', async () => {
127
+ try { await deletePeeringRequest(id); flash(t('dashboard.fedRequestDeleted') || 'Request deleted'); reload(); }
128
+ catch (e) { flashErr(e.message); }
129
+ }, { danger: true });
130
+ }, [reload]);
131
+
62
132
  const doActivate = useCallback((nodeId) => {
63
133
  confirm(t('dashboard.fedActivateConfirm'), async () => {
64
134
  try { await activatePeer(nodeId); flash(t('dashboard.fedPeerActivated')); reload(); }
@@ -80,6 +150,14 @@ export default function FederationTab({ data, reload }) {
80
150
  }, { danger: true });
81
151
  }, [reload]);
82
152
 
153
+ const doUpdatePolicy = useCallback(async (nodeId, field, value) => {
154
+ try {
155
+ await updatePeerPolicy(nodeId, { [field]: value });
156
+ flash(t('dashboard.fedPolicyUpdated'));
157
+ reload();
158
+ } catch (e) { flashErr(e.message); }
159
+ }, [reload]);
160
+
83
161
  const doTest = useCallback(async () => {
84
162
  if (!testUrl) return;
85
163
  setTesting(true); setTestResult(null);
@@ -173,6 +251,61 @@ export default function FederationTab({ data, reload }) {
173
251
  { label: t('dashboard.fedPendingRequests'), value: pendingRequests.length, color: '#06b6d4' },
174
252
  ]} />
175
253
 
254
+ <!-- ═══ Federation Auth Policy ═══ -->
255
+ <div class="adm-card adm-mt-lg">
256
+ <h4 class="adm-mb-sm" style="margin:0">${t('dashboard.fedAuthPolicyTitle')}</h4>
257
+ <p class="adm-text-dim adm-text-base adm-mb-md" style="margin:0">${t('dashboard.fedAuthPolicyDesc')}</p>
258
+ ${authPolicyLoading ? html`<p class="adm-text-dim">...</p>` : html`
259
+ <div style="display:flex;gap:12px;align-items:center;flex-wrap:wrap">
260
+ <select class="adm-input" style="max-width:240px" value=${authPolicy} onChange=${(e) => { setAuthPolicy(e.target.value); doSaveAuthPolicy(e.target.value, defaultScopes); }}>
261
+ <option value="disabled">${t('dashboard.fedAuthDisabled')}</option>
262
+ <option value="all_peers">${t('dashboard.fedAuthAllPeers')}</option>
263
+ <option value="specific_peers">${t('dashboard.fedAuthSpecificPeers')}</option>
264
+ </select>
265
+ </div>
266
+ ${authPolicy !== 'disabled' && html`
267
+ <div class="adm-mt-md">
268
+ <p class="adm-text-sm adm-text-dim adm-mb-sm">${t('dashboard.fedDefaultScopes')}</p>
269
+ <div style="display:flex;gap:8px;flex-wrap:wrap">
270
+ ${['memory:read','memory:write','catalogue:read','social:read','social:write','work:request','boards:read','boards:write'].map(s => html`
271
+ <label class="adm-text-sm"><input type="checkbox" checked=${defaultScopes.includes(s)} onChange=${() => toggleScope(s)} /> ${s}</label>
272
+ `)}
273
+ </div>
274
+ </div>
275
+ `}
276
+ `}
277
+ </div>
278
+
279
+ <!-- ═══ Network Directory ═══ -->
280
+ <div class="adm-card adm-mt-lg">
281
+ <h4 class="adm-mb-sm" style="margin:0">${t('dashboard.fedNetworkDirectoryTitle')}</h4>
282
+ <p class="adm-text-dim adm-text-base adm-mb-md" style="margin:0">${t('dashboard.fedNetworkDirectoryDesc')}</p>
283
+ <div class="adm-mb-md">
284
+ <input class="input-field" placeholder=${t('dashboard.fedNetworkDirSearch')} value=${dirSearch} onInput=${onDirSearchInput} />
285
+ </div>
286
+ ${dirLoading
287
+ ? html`<p class="adm-text-dim">...</p>`
288
+ : !dirEntries.length
289
+ ? html`<${Empty} text=${t('dashboard.fedNetworkDirEmpty')} />`
290
+ : html`<div class="scrollable"><table>
291
+ <thead><tr>
292
+ <th>${t('dashboard.fedNetworkDirType')}</th>
293
+ <th>${t('dashboard.fedNetworkDirName')}</th>
294
+ <th>${t('dashboard.fedNetworkDirNode')}</th>
295
+ <th>${t('dashboard.fedNetworkDirCategory')}</th>
296
+ </tr></thead>
297
+ <tbody>
298
+ ${dirEntries.map(e => html`<tr>
299
+ <td><${Badge} type=${e.type === 'action' ? 'info' : e.type === 'agent' ? 'healthy' : e.type === 'board' ? 'watch' : 'neutral'} label=${e.type} /></td>
300
+ <td>${escHtml(e.name || e.id || '—')}</td>
301
+ <td class="mono adm-text-sm">${escHtml(e.source_node || '—')}</td>
302
+ <td>${escHtml(e.category || e.service_type || '—')}</td>
303
+ </tr>`)}
304
+ </tbody>
305
+ </table></div>`
306
+ }
307
+ </div>
308
+
176
309
  <!-- ═══ Pending Peering Requests ═══ -->
177
310
  ${pendingRequests.length > 0 && html`
178
311
  <div class="adm-card adm-mt-lg" style="border-left:3px solid #f59e0b">
@@ -224,6 +357,7 @@ export default function FederationTab({ data, reload }) {
224
357
  <th>${t('dashboard.fedAddedAt')}</th>
225
358
  <th>${t('dashboard.lastSeen')}</th>
226
359
  <th>${t('dashboard.fedPublicKey')}</th>
360
+ <th>${t('dashboard.fedPolicySectionTitle')}</th>
227
361
  <th>${t('dashboard.actions')}</th>
228
362
  </tr></thead>
229
363
  <tbody>
@@ -234,6 +368,16 @@ export default function FederationTab({ data, reload }) {
234
368
  <td class="adm-text-dim">${dt(p.added_at)}</td>
235
369
  <td class="adm-text-dim">${dt(p.last_seen)}</td>
236
370
  <td class="mono" style="font-size:.7rem;max-width:100px;overflow:hidden;text-overflow:ellipsis" title=${p.public_key || ''}>${p.public_key ? p.public_key.substring(0, 16) + '...' : '\u2014'}</td>
371
+ <td>
372
+ <label class="adm-text-sm"><input type="checkbox" checked=${p.share_catalogue !== false} onChange=${(e) => doUpdatePolicy(p.node_id, 'share_catalogue', e.target.checked)} /> ${t('dashboard.fedShareCatalogue')}</label>
373
+ <label class="adm-text-sm"><input type="checkbox" checked=${p.replicate_memory !== false} onChange=${(e) => doUpdatePolicy(p.node_id, 'replicate_memory', e.target.checked)} /> ${t('dashboard.fedReplicateMemory')}</label>
374
+ <label class="adm-text-sm"><input type="checkbox" checked=${p.allow_routing !== false} onChange=${(e) => doUpdatePolicy(p.node_id, 'allow_routing', e.target.checked)} /> ${t('dashboard.fedAllowRouting')}</label>
375
+ <label class="adm-text-sm"><input type="checkbox" checked=${!!p.allow_federated_auth} onChange=${(e) => doUpdatePolicy(p.node_id, 'allow_federated_auth', e.target.checked)} /> ${t('dashboard.fedAllowAuth')}</label>
376
+ <select class="adm-input" style="font-size:.75rem;padding:2px 4px;margin-top:4px" value=${p.peer_mode || 'federation'} onChange=${(e) => doUpdatePolicy(p.node_id, 'peer_mode', e.target.value)}>
377
+ <option value="federation">${t('dashboard.fedPeerModeFederation')}</option>
378
+ <option value="private">${t('dashboard.fedPeerModePrivate')}</option>
379
+ </select>
380
+ </td>
237
381
  <td style="display:flex;gap:4px;flex-wrap:wrap">
238
382
  ${p.status === 'approved' && html`
239
383
  <button class="adm-btn-sm" onClick=${() => doActivate(p.node_id)}>\u25B6 ${t('dashboard.fedActivate')}</button>
@@ -252,7 +396,7 @@ export default function FederationTab({ data, reload }) {
252
396
  </div>
253
397
 
254
398
  <!-- ═══ Peering Request History ═══ -->
255
- ${(approvedRequests.length > 0 || rejectedRequests.length > 0) && html`
399
+ ${historyRequests.length > 0 && html`
256
400
  <div class="adm-card adm-mt-lg">
257
401
  <h4 class="adm-mb-sm" style="margin:0">\u{1F4DC} ${t('dashboard.fedRequestHistoryTitle')}</h4>
258
402
  <div class="scrollable"><table>
@@ -262,14 +406,16 @@ export default function FederationTab({ data, reload }) {
262
406
  <th>${t('dashboard.endpoint')}</th>
263
407
  <th>${t('dashboard.statusLabel')}</th>
264
408
  <th>${t('dashboard.fedCreatedAt')}</th>
409
+ <th></th>
265
410
  </tr></thead>
266
411
  <tbody>
267
- ${[...approvedRequests, ...rejectedRequests].map(r => html`<tr>
412
+ ${historyRequests.map(r => html`<tr>
268
413
  <td class="mono" style="font-size:.75rem">${escHtml(r.id)}</td>
269
414
  <td class="mono adm-text-sm">${escHtml(r.from_node_id || '\u2014')}</td>
270
415
  <td class="mono adm-text-sm">${escHtml(r.from_node_url || r.target_url || '\u2014')}</td>
271
416
  <td>${statusBadge(r.status)}</td>
272
417
  <td class="adm-text-dim">${dt(r.created_at)}</td>
418
+ <td><button class="adm-btn-sm adm-text-error" onClick=${() => doDeleteRequest(r.id)}>\u2718</button></td>
273
419
  </tr>`)}
274
420
  </tbody>
275
421
  </table></div>
@@ -65,6 +65,7 @@ export default function GhiiTab({ data, reload }) {
65
65
  <th>${t('dashboard.ghiiEmail')}</th>
66
66
  <th>${t('dashboard.verification')}</th>
67
67
  <th>${t('dashboard.totp')}</th>
68
+ <th>${t('dashboard.lastLogin')}</th>
68
69
  <th>${t('dashboard.created')}</th>
69
70
  <th>${t('dashboard.actions')}</th>
70
71
  </tr></thead>
@@ -80,6 +81,7 @@ export default function GhiiTab({ data, reload }) {
80
81
  }</td>
81
82
  <td><${Badge} type=${vBadge} /> L${u.verification_level}</td>
82
83
  <td><${Badge} type=${u.totp_enabled ? 'healthy' : 'critical'} /></td>
84
+ <td>${u.last_login_at ? dt(u.last_login_at) : html`<span style="color:var(--text-dim)">–</span>`}</td>
83
85
  <td>${dt(u.created_at)}</td>
84
86
  <td>
85
87
  <select onChange=${e => setLevel(u.ghii, e.target.value)}>