aimeat 1.30.2 → 1.32.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 (192) hide show
  1. package/dist/locales/en.json +246 -1
  2. package/dist/locales/fi.json +246 -1
  3. package/dist/public/components/InboxLink.js +29 -0
  4. package/dist/public/components/offer-card-view.js +87 -0
  5. package/dist/public/css/views/companies.css +65 -0
  6. package/dist/public/css/views/inbox.css +153 -1
  7. package/dist/public/css/views/members.css +33 -0
  8. package/dist/public/css/views/my-company.css +315 -0
  9. package/dist/public/css/views/profile.css +22 -0
  10. package/dist/public/js/services/messages.js +21 -3
  11. package/dist/public/js/services/notebook.js +16 -6
  12. package/dist/public/llms-template.txt +7 -0
  13. package/dist/public/spa.html +14 -0
  14. package/dist/public/views/companies.js +190 -0
  15. package/dist/public/views/members.js +42 -0
  16. package/dist/public/views/my-company.js +664 -0
  17. package/dist/public/views/portfolio-themes.js +77 -0
  18. package/dist/public/views/profile/agents-tab.js +4 -1
  19. package/dist/public/views/profile/discover-tab.js +118 -0
  20. package/dist/public/views/profile/inbox-tab.js +663 -16
  21. package/dist/public/views/profile/landing-page.js +2 -1
  22. package/dist/public/views/profile/notebook-card.js +11 -3
  23. package/dist/public/views/profile/offers-tab.js +5 -24
  24. package/dist/public/views/profile/organisms/home.js +5 -3
  25. package/dist/public/views/profile/organisms/mindmap.js +14 -3
  26. package/dist/public/views/profile/organisms/workspace.js +5 -2
  27. package/dist/public/views/profile/organisms-tab.js +12 -4
  28. package/dist/public/views/profile.js +2 -0
  29. package/dist/src/cli/connect/mcp/tools/core.d.ts.map +1 -1
  30. package/dist/src/cli/connect/mcp/tools/core.js +34 -0
  31. package/dist/src/cli/connect/mcp/tools/core.js.map +1 -1
  32. package/dist/src/cli/connect/mcp/tools/dm-messages.d.ts +1 -0
  33. package/dist/src/cli/connect/mcp/tools/dm-messages.d.ts.map +1 -1
  34. package/dist/src/cli/connect/mcp/tools/dm-messages.js +23 -0
  35. package/dist/src/cli/connect/mcp/tools/dm-messages.js.map +1 -1
  36. package/dist/src/cli/connect/mcp/tools/organisms.d.ts.map +1 -1
  37. package/dist/src/cli/connect/mcp/tools/organisms.js +31 -0
  38. package/dist/src/cli/connect/mcp/tools/organisms.js.map +1 -1
  39. package/dist/src/cli/connect/mcp/tools/workspaces.d.ts.map +1 -1
  40. package/dist/src/cli/connect/mcp/tools/workspaces.js +4 -0
  41. package/dist/src/cli/connect/mcp/tools/workspaces.js.map +1 -1
  42. package/dist/src/generated/api-types.d.ts +249 -0
  43. package/dist/src/generated/api-types.d.ts.map +1 -1
  44. package/dist/src/generated/prisma-postgres/edge.js +6 -3
  45. package/dist/src/generated/prisma-postgres/index-browser.js +3 -0
  46. package/dist/src/generated/prisma-postgres/index.d.ts +112 -1
  47. package/dist/src/generated/prisma-postgres/index.js +6 -3
  48. package/dist/src/generated/prisma-postgres/package.json +1 -1
  49. package/dist/src/generated/prisma-postgres/schema.prisma +4 -0
  50. package/dist/src/generated/prisma-postgres/wasm.js +6 -3
  51. package/dist/src/mcp/annotations.d.ts.map +1 -1
  52. package/dist/src/mcp/annotations.js +2 -0
  53. package/dist/src/mcp/annotations.js.map +1 -1
  54. package/dist/src/mcp/catalog/definitions.d.ts.map +1 -1
  55. package/dist/src/mcp/catalog/definitions.js +33 -1
  56. package/dist/src/mcp/catalog/definitions.js.map +1 -1
  57. package/dist/src/mcp/catalog/scopes.d.ts.map +1 -1
  58. package/dist/src/mcp/catalog/scopes.js +1 -0
  59. package/dist/src/mcp/catalog/scopes.js.map +1 -1
  60. package/dist/src/mcp/catalog/surfaces.d.ts.map +1 -1
  61. package/dist/src/mcp/catalog/surfaces.js +7 -4
  62. package/dist/src/mcp/catalog/surfaces.js.map +1 -1
  63. package/dist/src/mcp/core.d.ts.map +1 -1
  64. package/dist/src/mcp/core.js +49 -0
  65. package/dist/src/mcp/core.js.map +1 -1
  66. package/dist/src/mcp/dm-messages.d.ts +2 -0
  67. package/dist/src/mcp/dm-messages.d.ts.map +1 -1
  68. package/dist/src/mcp/dm-messages.js +45 -1
  69. package/dist/src/mcp/dm-messages.js.map +1 -1
  70. package/dist/src/models/message-schemas.d.ts +140 -2
  71. package/dist/src/models/message-schemas.d.ts.map +1 -1
  72. package/dist/src/models/message-schemas.js +60 -3
  73. package/dist/src/models/message-schemas.js.map +1 -1
  74. package/dist/src/routes/bootstrap.d.ts.map +1 -1
  75. package/dist/src/routes/bootstrap.js +3 -0
  76. package/dist/src/routes/bootstrap.js.map +1 -1
  77. package/dist/src/routes/discover.d.ts +22 -0
  78. package/dist/src/routes/discover.d.ts.map +1 -0
  79. package/dist/src/routes/discover.js +123 -0
  80. package/dist/src/routes/discover.js.map +1 -0
  81. package/dist/src/routes/federation-sync.d.ts.map +1 -1
  82. package/dist/src/routes/federation-sync.js +69 -2
  83. package/dist/src/routes/federation-sync.js.map +1 -1
  84. package/dist/src/routes/messages.d.ts.map +1 -1
  85. package/dist/src/routes/messages.js +102 -3
  86. package/dist/src/routes/messages.js.map +1 -1
  87. package/dist/src/routes/organisms.d.ts +3 -0
  88. package/dist/src/routes/organisms.d.ts.map +1 -1
  89. package/dist/src/routes/organisms.js +11 -32
  90. package/dist/src/routes/organisms.js.map +1 -1
  91. package/dist/src/routes/portal.d.ts.map +1 -1
  92. package/dist/src/routes/portal.js +3 -0
  93. package/dist/src/routes/portal.js.map +1 -1
  94. package/dist/src/routes/portfolio.d.ts.map +1 -1
  95. package/dist/src/routes/portfolio.js +30 -0
  96. package/dist/src/routes/portfolio.js.map +1 -1
  97. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  98. package/dist/src/server-bootstrap/routes-loader.js +2 -0
  99. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  100. package/dist/src/services/connect-tunnel.d.ts +3 -0
  101. package/dist/src/services/connect-tunnel.d.ts.map +1 -1
  102. package/dist/src/services/connect-tunnel.js +6 -0
  103. package/dist/src/services/connect-tunnel.js.map +1 -1
  104. package/dist/src/services/discovery/__tests__/discovery.test.d.ts +2 -0
  105. package/dist/src/services/discovery/__tests__/discovery.test.d.ts.map +1 -0
  106. package/dist/src/services/discovery/__tests__/discovery.test.js +178 -0
  107. package/dist/src/services/discovery/__tests__/discovery.test.js.map +1 -0
  108. package/dist/src/services/discovery/classify.d.ts +36 -0
  109. package/dist/src/services/discovery/classify.d.ts.map +1 -0
  110. package/dist/src/services/discovery/classify.js +61 -0
  111. package/dist/src/services/discovery/classify.js.map +1 -0
  112. package/dist/src/services/discovery/facets.d.ts +21 -0
  113. package/dist/src/services/discovery/facets.d.ts.map +1 -0
  114. package/dist/src/services/discovery/facets.js +0 -0
  115. package/dist/src/services/discovery/facets.js.map +1 -0
  116. package/dist/src/services/discovery/index.d.ts +22 -0
  117. package/dist/src/services/discovery/index.d.ts.map +1 -0
  118. package/dist/src/services/discovery/index.js +22 -0
  119. package/dist/src/services/discovery/index.js.map +1 -0
  120. package/dist/src/services/discovery/normalize.d.ts +25 -0
  121. package/dist/src/services/discovery/normalize.d.ts.map +1 -0
  122. package/dist/src/services/discovery/normalize.js +108 -0
  123. package/dist/src/services/discovery/normalize.js.map +1 -0
  124. package/dist/src/services/discovery/registry.d.ts +30 -0
  125. package/dist/src/services/discovery/registry.d.ts.map +1 -0
  126. package/dist/src/services/discovery/registry.js +31 -0
  127. package/dist/src/services/discovery/registry.js.map +1 -0
  128. package/dist/src/services/discovery/run.d.ts +23 -0
  129. package/dist/src/services/discovery/run.d.ts.map +1 -0
  130. package/dist/src/services/discovery/run.js +20 -0
  131. package/dist/src/services/discovery/run.js.map +1 -0
  132. package/dist/src/services/discovery/setup.d.ts +16 -0
  133. package/dist/src/services/discovery/setup.d.ts.map +1 -0
  134. package/dist/src/services/discovery/setup.js +12 -0
  135. package/dist/src/services/discovery/setup.js.map +1 -0
  136. package/dist/src/services/discovery/sources/memory-source.d.ts +24 -0
  137. package/dist/src/services/discovery/sources/memory-source.d.ts.map +1 -0
  138. package/dist/src/services/discovery/sources/memory-source.js +183 -0
  139. package/dist/src/services/discovery/sources/memory-source.js.map +1 -0
  140. package/dist/src/services/discovery/sources/table-sources.d.ts +27 -0
  141. package/dist/src/services/discovery/sources/table-sources.d.ts.map +1 -0
  142. package/dist/src/services/discovery/sources/table-sources.js +128 -0
  143. package/dist/src/services/discovery/sources/table-sources.js.map +1 -0
  144. package/dist/src/services/discovery/types.d.ts +117 -0
  145. package/dist/src/services/discovery/types.d.ts.map +1 -0
  146. package/dist/src/services/discovery/types.js +19 -0
  147. package/dist/src/services/discovery/types.js.map +1 -0
  148. package/dist/src/services/handbooks/agent.d.ts +1 -1
  149. package/dist/src/services/handbooks/agent.d.ts.map +1 -1
  150. package/dist/src/services/handbooks/agent.js +9 -3
  151. package/dist/src/services/handbooks/agent.js.map +1 -1
  152. package/dist/src/services/message-broadcast.d.ts +49 -0
  153. package/dist/src/services/message-broadcast.d.ts.map +1 -0
  154. package/dist/src/services/message-broadcast.js +116 -0
  155. package/dist/src/services/message-broadcast.js.map +1 -0
  156. package/dist/src/services/message-delivery.d.ts.map +1 -1
  157. package/dist/src/services/message-delivery.js +3 -0
  158. package/dist/src/services/message-delivery.js.map +1 -1
  159. package/dist/src/services/message-send.d.ts +9 -1
  160. package/dist/src/services/message-send.d.ts.map +1 -1
  161. package/dist/src/services/message-send.js +11 -5
  162. package/dist/src/services/message-send.js.map +1 -1
  163. package/dist/src/services/presence.d.ts +8 -0
  164. package/dist/src/services/presence.d.ts.map +1 -1
  165. package/dist/src/services/presence.js +38 -0
  166. package/dist/src/services/presence.js.map +1 -1
  167. package/dist/src/services/workspace-enrichment.d.ts +15 -5
  168. package/dist/src/services/workspace-enrichment.d.ts.map +1 -1
  169. package/dist/src/services/workspace-enrichment.js +52 -14
  170. package/dist/src/services/workspace-enrichment.js.map +1 -1
  171. package/dist/src/storage/interface.d.ts +49 -0
  172. package/dist/src/storage/interface.d.ts.map +1 -1
  173. package/dist/src/storage/providers/mongodb/index.d.ts +1 -0
  174. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  175. package/dist/src/storage/providers/mongodb/index.js +16 -0
  176. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  177. package/dist/src/storage/providers/sqlite/index.d.ts +1 -0
  178. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  179. package/dist/src/storage/providers/sqlite/index.js +3 -0
  180. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  181. package/dist/src/storage/providers/sqlite/repos/direct-message.d.ts +3 -0
  182. package/dist/src/storage/providers/sqlite/repos/direct-message.d.ts.map +1 -1
  183. package/dist/src/storage/providers/sqlite/repos/direct-message.js +15 -2
  184. package/dist/src/storage/providers/sqlite/repos/direct-message.js.map +1 -1
  185. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  186. package/dist/src/storage/providers/sqlite/schema.js +10 -0
  187. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  188. package/dist/src/storage/repositories/direct-message.repository.d.ts +3 -0
  189. package/dist/src/storage/repositories/direct-message.repository.d.ts.map +1 -1
  190. package/package.json +1 -1
  191. package/prisma/schema.postgres.prisma +4 -0
  192. package/prisma/schema.prisma +4 -0
@@ -9,7 +9,7 @@
9
9
  * @structure
10
10
  * - classifyNote(text) — POST /v1/librarian/classify
11
11
  * - distributeNote(text) — POST /v1/librarian/distribute (split into placed chunks)
12
- * - materializeDocument(plan) — resolve/create org → ws → document space → write doc → drop source
12
+ * - materializeDocument(plan) — resolve/create org → ws → document space → draft→publish doc → drop source
13
13
  * - distributeChunks(chunks, sourceKey, onProgress) — materialize each chunk to its home
14
14
  * - getNotebookSettings/saveNotebookSettings — the per-owner trust toggles (notebook.settings)
15
15
  * @version-history
@@ -18,10 +18,13 @@
18
18
  * v1.2.0 — 2026-06-21 — (reverted) materializeRecord/patchRecord removed — the Tracked Response flow
19
19
  * now writes records through the proper workspace draft→publish path (services/organisms.js), not a
20
20
  * raw memory write, so records land in the right namespace/owner and show up with activity.
21
+ * v1.3.0 — 2026-06-23 — materializeDocument now writes via writeDraft + publishDraft (was a raw
22
+ * `.latest` memory write) so notebook documents get version history and appear in the workspace
23
+ * activity log/heatmap — the document-path counterpart to the v1.2.0 record fix.
21
24
  */
22
25
  import { api, apiPost } from '/js/api.js';
23
26
  import { createMemory, getMemory, deleteMemory } from '/js/services/memory.js';
24
- import { createOrganism, saveManifest, listWorkspaces, saveWorkspaceRegistry, wsRoot } from '/js/services/organisms.js';
27
+ import { createOrganism, saveManifest, listWorkspaces, saveWorkspaceRegistry, wsRoot, writeDraft, publishDraft } from '/js/services/organisms.js';
25
28
 
26
29
  const DOC_SPACE = 'pages';
27
30
 
@@ -162,11 +165,18 @@ export async function materializeDocument(plan) {
162
165
  }
163
166
  }
164
167
 
165
- // 4. Write the document.
168
+ // 4. Write the document through the proper workspace draft→publish path (NOT a raw `.latest` write).
169
+ // A raw write to `…{space}.{docId}.latest` lands the content but produces no version history and
170
+ // no activity event (the feed is derived from `.draft`/`.version.N` keys), so notebook documents
171
+ // used to be invisible in the workspace activity log / heatmap. Going through writeDraft +
172
+ // publishDraft mints `.version.1` + `.latest` and shows up like every other published object —
173
+ // matching the Tracked-Response record path migrated in v1.2.0.
166
174
  const docId = rid('doc-');
167
- const key = `${wsRoot(organismId, workspaceId)}.${space}.${docId}.latest`;
168
- const resp = await createMemory(key, { id: docId, title: plan.title, markdown: plan.markdown }, 'private');
169
- if (resp?.ok === false) throw new Error(resp.error?.message || 'Could not write document');
175
+ const docValue = { id: docId, title: plan.title, markdown: plan.markdown };
176
+ const draftResp = await writeDraft(organismId, workspaceId, space, docId, docValue);
177
+ if (draftResp?.ok === false) throw new Error(draftResp.error?.message || 'Could not write document');
178
+ const pubResp = await publishDraft(organismId, workspaceId, space, docId);
179
+ if (pubResp?.ok === false) throw new Error(pubResp.error?.message || 'Could not publish document');
170
180
 
171
181
  // 5. Drop the source inbox note (it has been filed).
172
182
  if (plan.sourceKey) await deleteMemory(plan.sourceKey).catch(() => {});
@@ -285,6 +285,7 @@ entry: index.html
285
285
 
286
286
  ### Other options (not app building)
287
287
 
288
+ - **Discover everything from one place:** `GET /v1/discover` — the master directory. One faceted query across all domains (capabilities, workflows, knowledge, decisions, companies+offerings, apps, documents, memory). `?mode=map` (or `/v1/discover/facets`) returns counts by type/tag so you can see what exists before pulling content; `scope=own|public|shared`. MCP: `aimeat_discover`.
288
289
  - Browse this node: `GET /v1/catalogue`, `GET /v1/apps`, `GET /v1/stats`
289
290
  - Connect as AI agent: see "Connecting: Device Authorization" section below
290
291
  - Anonymous quick test: `POST /v1/auth/anonymous`
@@ -1668,6 +1669,12 @@ Discover actions, agents, and boards available on this node.
1668
1669
 
1669
1670
  #### Endpoints
1670
1671
 
1672
+ GET {{BASE_URL}}/v1/discover — Master directory: unified cross-domain discovery (start here)
1673
+ Query: ?mode=find&scope=own|public|shared&q=text&type=capability,knowledge&tags=finance&segment=research&page=1&per_page=20
1674
+ → 200: { "ok": true, "data": { "entries": [{ "type": "capability", "segment": "manual", "id": "...", "title": "...", "description": "...", "tags": [...], "visibility": "public", "owner": "alice@node", "updatedAt": "...", "href": "/v1/..." }], "total": 42, "scope": "own", "facets": { "types": [...], "segments": [...], "tags": [...] } } }
1675
+ GET {{BASE_URL}}/v1/discover/facets — Map mode: counts by type/segment/tag only (cheap "what exists?" probe), same filters + scope
1676
+ → 200: { "ok": true, "data": { "scope": "public", "total": 42, "types": [{ "value": "knowledge", "count": 18 }], "segments": [...], "tags": [...] } }
1677
+
1671
1678
  GET {{BASE_URL}}/v1/catalogue — Full catalogue listing
1672
1679
  Query: ?search=translate&category=language&page=1&per_page=20&include_federated=true
1673
1680
  → 200: { "ok": true, "data": { "actions": [{ "id": "...", "display_name": "...", "description": "...", "provider_gaii": "...", "category": "...", "pricing": { "base_morsels": 5 }, "tags": [...] }], "total": 42 } }
@@ -86,10 +86,13 @@
86
86
  <link rel="stylesheet" href="/css/views/aimeat-os.css">
87
87
  <link rel="stylesheet" href="/css/views/openclaw.css">
88
88
  <link rel="stylesheet" href="/css/views/portfolio.css">
89
+ <link rel="stylesheet" href="/css/views/members.css">
89
90
  <link rel="stylesheet" href="/css/views/inbox.css">
90
91
  <link rel="stylesheet" href="/css/components/tags.css">
91
92
  <link rel="stylesheet" href="/css/components/image-deliverable.css">
92
93
  <link rel="stylesheet" href="/css/views/help.css">
94
+ <link rel="stylesheet" href="/css/views/my-company.css">
95
+ <link rel="stylesheet" href="/css/views/companies.css">
93
96
  <link rel="stylesheet" href="/css/views/capabilities.css">
94
97
  <link rel="stylesheet" href="/css/views/agents-detail.css">
95
98
  <link rel="stylesheet" href="/css/views/scheduler.css">
@@ -187,8 +190,10 @@
187
190
  "/components/Modal.js": "/components/Modal.js",
188
191
  "/components/useViewCSS.js": "/components/useViewCSS.js",
189
192
  "/components/Markdown.js": "/components/Markdown.js",
193
+ "/components/InboxLink.js": "/components/InboxLink.js",
190
194
  "/components/JsonView.js": "/components/JsonView.js",
191
195
  "/components/ImageDeliverable.js": "/components/ImageDeliverable.js",
196
+ "/components/offer-card-view.js": "/components/offer-card-view.js",
192
197
  "/components/Mermaid.js": "/components/Mermaid.js",
193
198
  "/components/NotificationBell.js": "/components/NotificationBell.js",
194
199
  "/components/CopyButton.js": "/components/CopyButton.js",
@@ -224,6 +229,7 @@
224
229
  "/js/services/admin-agent-integration.js": "/js/services/admin-agent-integration.js",
225
230
  "/js/services/schedules.js": "/js/services/schedules.js",
226
231
  "/js/services/workflows.js": "/js/services/workflows.js",
232
+ "/views/portfolio-themes.js": "/views/portfolio-themes.js",
227
233
  "/views/profile/shared.js": "/views/profile/shared.js",
228
234
  "/views/profile/generator-settings.js": "/views/profile/generator-settings.js",
229
235
  "/views/profile/organisms/helpers.js": "/views/profile/organisms/helpers.js",
@@ -338,6 +344,8 @@
338
344
  '/v1/how-it-works': () => import('/views/how-it-works.js' + B),
339
345
  '/v1/business': () => import('/views/business.js' + B),
340
346
  '/v1/profile': () => import('/views/profile.js' + B),
347
+ '/v1/my-company': () => import('/views/my-company.js' + B),
348
+ '/v1/companies': () => import('/views/companies.js' + B),
341
349
  '/v1/app-grant': () => import('/views/app-grant.js' + B),
342
350
  '/v1/guides': () => import('/views/guides.js' + B),
343
351
  '/v1/hobbies': () => import('/views/hobbies.js' + B),
@@ -346,6 +354,7 @@
346
354
  '/v1/openclaw': () => import('/views/openclaw.js' + B),
347
355
  '/v1/classic': () => import('/views/portal-classic.js' + B),
348
356
  '/v1/portfolio': () => import('/views/portfolio.js' + B),
357
+ '/v1/members': () => import('/views/members.js' + B),
349
358
  '/v1/admin': () => import('/views/admin.js' + B),
350
359
  '/v1/help': () => import('/views/help.js' + B),
351
360
  '/v1/publicknowledgeviewer': () => import('/views/public-knowledge-viewer.js' + B),
@@ -508,6 +517,8 @@
508
517
  { id: 'try', path: '/v1/portal', key: 'nav.try' },
509
518
  { id: 'howItWorks', path: '/v1/how-it-works', key: 'nav.howItWorks', fallback: 'How it works' },
510
519
  { id: 'business', path: '/v1/business', key: 'nav.business', fallback: 'For your business' },
520
+ { id: 'companies', path: '/v1/companies', key: 'nav.companies', fallback: 'Companies' },
521
+ { id: 'members', path: '/v1/members', key: 'nav.members', fallback: 'Members' },
511
522
  { id: 'devView', path: '/v1/portal?view=dev', key: 'nav.devView', matchActive: false },
512
523
  { id: 'help', path: '/v1/help', key: 'nav.help' },
513
524
  ];
@@ -610,6 +621,9 @@
610
621
  <a href="/v1/profile" class="profile-link visible ${currentPath === '/v1/profile' ? 'active' : ''}" onClick=${(e) => { handleNav(e, '/v1/profile'); setMenuOpen(false); }}>
611
622
  ${t('nav.profile')}
612
623
  </a>
624
+ <a href="/v1/my-company" class="${currentPath === '/v1/my-company' ? 'active' : ''}" onClick=${(e) => { handleNav(e, '/v1/my-company'); setMenuOpen(false); }}>
625
+ ${t('nav.myCompany')}
626
+ </a>
613
627
  ${session.roles && session.roles.includes('operator') && html`
614
628
  <a href="/v1/admin" class="${currentPath === '/v1/admin' ? 'active' : ''}" onClick=${(e) => { handleNav(e, '/v1/admin'); setMenuOpen(false); }}>
615
629
  ${t('nav.admin')}
@@ -0,0 +1,190 @@
1
+ /**
2
+ * @file companies.js
3
+ * @description Public company directory + public company page (the Enterprise discovery surface).
4
+ * Anyone (even anonymous) can browse every company on the node (GET /v1/orgs/directory), open one
5
+ * to see its public profile (name, Y-tunnus, description) and catalogue (GET
6
+ * /v1/orgs/:owner/:slug/offerings — the SAME offer cards as profile>offers), and order an offering
7
+ * (POST .../use — ordering requires login). Backend stays protocol-only; the EE module provides the
8
+ * org APIs (a Community node returns ENTERPRISE_REQUIRED).
9
+ * @structure CompaniesView · directory list · PublicOfferingCard (order box, reuses OfferCardView)
10
+ * @usage routed at /v1/companies; deep-link a company via ?c=owner/slug.
11
+ * @version-history
12
+ * v0.1.0 — 2026-06-23 — initial: directory + public company page + order
13
+ */
14
+ import { h } from 'preact';
15
+ import { useState, useEffect } from 'preact/hooks';
16
+ import htm from 'htm';
17
+ import { t } from '/js/i18n.js';
18
+ import { apiGet, apiPost } from '/js/api.js';
19
+ import { DeliverableBody } from '/components/ImageDeliverable.js';
20
+ import { OfferCardView } from '/components/offer-card-view.js';
21
+
22
+ const html = htm.bind(h);
23
+
24
+ const isLoggedIn = () => !!(typeof window !== 'undefined' && window.AIMEAT?.auth?.getSession?.());
25
+
26
+ /** One orderable offering on the public company page (same card as profile>offers + an order box). */
27
+ function PublicOfferingCard({ o, owner, slug }) {
28
+ const offer = o.offer;
29
+ const [input, setInput] = useState('');
30
+ const [busy, setBusy] = useState(false);
31
+ const [result, setResult] = useState(null);
32
+ const [err, setErr] = useState('');
33
+ const loggedIn = isLoggedIn();
34
+
35
+ async function order() {
36
+ setBusy(true); setErr(''); setResult(null);
37
+ try {
38
+ const res = await apiPost(`/v1/orgs/${encodeURIComponent(owner)}/${encodeURIComponent(slug)}/use`,
39
+ { agentName: o.agentName, offerId: o.offerId, input: input.trim() || undefined });
40
+ setResult(res.data); setInput('');
41
+ } catch (e) { setErr(e.message || 'Order failed'); }
42
+ finally { setBusy(false); }
43
+ }
44
+
45
+ const actions = html`
46
+ <div class="cm-not-callable">${o.callable ? t('companies.callable') : t('companies.viaInbox')}</div>
47
+ ${loggedIn
48
+ ? html`
49
+ <textarea class="input-field input-sm of-input" rows="2" value=${input}
50
+ placeholder=${t('companies.usePlaceholder')} onInput=${(e) => setInput(e.target.value)}></textarea>
51
+ <div class="flex-row-wrap">
52
+ <button class="btn-primary btn-sm" disabled=${busy} onClick=${order}>
53
+ ${t('companies.order')}${offer.price?.morsels ? ` · ${offer.price.morsels} ${t('companies.morsels')}` : ''}
54
+ </button>
55
+ </div>`
56
+ : html`<a class="btn-outline btn-sm" href="/v1/profile">${t('companies.loginToOrder')}</a>`}
57
+ ${err && html`<p class="cm-error">${err}</p>`}
58
+ ${result && html`
59
+ <div class="cm-result">
60
+ ${result.kind === 'result'
61
+ ? html`<div class="cm-result-msg">${t('companies.orderResult')}</div><${DeliverableBody} value=${result.result} alt=${offer.title} format=${offer.deliverable?.format} />`
62
+ : html`<div class="cm-result-msg">${t('companies.orderTask').replace('{agent}', o.agentName)}</div>`}
63
+ ${result.receipt?.charged > 0 && html`<div class="cm-receipt">${t('companies.charged').replace('{n}', result.receipt.charged)}</div>`}
64
+ </div>`}
65
+ `;
66
+ return html`<li class="cm-cat-item"><${OfferCardView} entry=${{ agent: o.agentName, online: o.online }} offer=${offer} actions=${actions} /></li>`;
67
+ }
68
+
69
+ /** One purchase receipt in the buyer's "My orders" panel. */
70
+ function ReceiptRow({ o }) {
71
+ const statusKey = o.kind === 'result' ? 'stInstant' : ({ queued: 'stQueued', active: 'stActive', done: 'stDone', failed: 'stFailed' }[o.status] || 'stQueued');
72
+ return html`
73
+ <li class="cm-receipt-row">
74
+ <div class="cm-receipt-top">
75
+ <span class="cm-receipt-title">${o.offerTitle}</span>
76
+ <span class="cm-receipt-amt">${o.charged > 0 ? `${o.charged} ${t('companies.morsels')}` : t('companies.free')}</span>
77
+ </div>
78
+ <div class="cm-receipt-meta">
79
+ <span>${t('companies.fromCompany')}: ${o.orgSlug}</span>
80
+ <span>🤖 ${o.agentName}</span>
81
+ <span>${t('companies.' + statusKey)}</span>
82
+ <span class="cm-mini">${new Date(o.createdAt).toLocaleString()}</span>
83
+ </div>
84
+ ${o.trackingCode && html`<div class="cm-mini cm-receipt-code">🧾 ${o.trackingCode}</div>`}
85
+ </li>`;
86
+ }
87
+
88
+ export default function CompaniesView() {
89
+ const [companies, setCompanies] = useState(null);
90
+ const [selected, setSelected] = useState(null); // { owner, slug }
91
+ const [profile, setProfile] = useState(null); // { org, offerings }
92
+ const [myOrders, setMyOrders] = useState(null);
93
+ const [showOrders, setShowOrders] = useState(false);
94
+ const [err, setErr] = useState('');
95
+ const loggedIn = isLoggedIn();
96
+
97
+ async function loadMyOrders() {
98
+ try { const r = await apiGet('/v1/orders'); setMyOrders(r.data?.orders ?? []); }
99
+ catch { setMyOrders([]); }
100
+ }
101
+ function toggleOrders() { const n = !showOrders; setShowOrders(n); if (n) loadMyOrders(); }
102
+
103
+ async function loadDirectory() {
104
+ try { const r = await apiGet('/v1/orgs/directory'); setCompanies(r.data?.companies ?? []); }
105
+ catch (e) {
106
+ if (e.code === 'ENTERPRISE_REQUIRED') setCompanies('enterprise');
107
+ else { setErr(e.message || 'Failed to load'); setCompanies([]); }
108
+ }
109
+ }
110
+ useEffect(() => { loadDirectory(); }, []);
111
+
112
+ async function openCompany(owner, slug) {
113
+ setSelected({ owner, slug }); setProfile(null); setErr('');
114
+ try { history.replaceState(null, '', `/v1/companies?c=${encodeURIComponent(owner)}/${encodeURIComponent(slug)}`); } catch { /* ignore */ }
115
+ try {
116
+ const r = await apiGet(`/v1/orgs/${encodeURIComponent(owner)}/${encodeURIComponent(slug)}/offerings`);
117
+ setProfile(r.data ?? null);
118
+ } catch (e) { setErr(e.message || ''); }
119
+ }
120
+
121
+ // Deep-link: once the directory is loaded, open ?c=owner/slug if present.
122
+ useEffect(() => {
123
+ if (Array.isArray(companies) && !selected) {
124
+ const c = new URLSearchParams(location.search).get('c');
125
+ if (c && c.includes('/')) { const [o, s] = c.split('/'); openCompany(o, s); }
126
+ }
127
+ }, [companies]);
128
+
129
+ if (companies === null) return html`<div class="cm-container"><div class="cm-center"><div class="spinner"></div></div></div>`;
130
+ if (companies === 'enterprise') return html`<div class="cm-container"><div class="cm-card"><h1 class="cm-title">${t('companies.title')}</h1><p class="cm-desc">${t('companies.enterpriseRequired')}</p></div></div>`;
131
+
132
+ return html`
133
+ <div class="cm-container">
134
+ <header class="cm-header">
135
+ <div class="cm-header-row">
136
+ <h1 class="cm-title">${t('companies.title')}</h1>
137
+ ${loggedIn && html`<button class="btn-outline btn-sm" onClick=${toggleOrders}>${showOrders ? t('companies.done') : t('companies.myOrders')}</button>`}
138
+ </div>
139
+ <p class="cm-desc">${t('companies.desc')}</p>
140
+ </header>
141
+ ${err && html`<p class="cm-error">${err}</p>`}
142
+
143
+ ${showOrders && html`
144
+ <div class="cm-col cm-orders-panel">
145
+ <h2 class="cm-col-title">${t('companies.myOrders')}</h2>
146
+ ${myOrders === null ? html`<div class="cm-center"><div class="spinner"></div></div>`
147
+ : (myOrders.length === 0 ? html`<p class="cm-empty">${t('companies.noMyOrders')}</p>`
148
+ : html`<ul class="cm-receipt-list">${myOrders.map(o => html`<${ReceiptRow} key=${o.id} o=${o} />`)}</ul>`)}
149
+ </div>`}
150
+ <div class="cm-grid">
151
+ <aside class="cm-col">
152
+ <h2 class="cm-col-title">${t('companies.directory')} (${companies.length})</h2>
153
+ ${companies.length === 0 && html`<p class="cm-empty">${t('companies.none')}</p>`}
154
+ <ul class="cm-dir-list">
155
+ ${companies.map(c => html`<li key=${c.goii}>
156
+ <button class="cm-dir-item ${selected?.slug === c.slug && selected?.owner === c.creatorOwner ? 'active' : ''}" onClick=${() => openCompany(c.creatorOwner, c.slug)}>
157
+ ${c.logo && html`<img class="cm-dir-logo" src=${c.logo} alt=${c.name} />`}
158
+ <div class="cm-dir-text">
159
+ <span class="cm-dir-name">${c.name}</span>
160
+ <span class="cm-dir-meta">${c.businessId ? html`<span class="cm-tag">Y ${c.businessId}</span>` : ''}${c.offerings} ${t('companies.offeringsShort')}</span>
161
+ </div>
162
+ </button></li>`)}
163
+ </ul>
164
+ </aside>
165
+ <section class="cm-col">
166
+ ${!selected && html`<p class="cm-empty">${t('companies.selectHint')}</p>`}
167
+ ${selected && !profile && html`<div class="cm-center"><div class="spinner"></div></div>`}
168
+ ${profile && html`
169
+ <div class="cm-profile-head">
170
+ <div class="cm-profile-id">
171
+ ${profile.org?.logo && html`<img class="cm-profile-logo" src=${profile.org.logo} alt=${profile.org?.name} />`}
172
+ <div>
173
+ <h2 class="cm-col-title">${profile.org?.name}</h2>
174
+ ${profile.org?.businessId && html`<span class="cm-mini">${t('companies.businessId')}: ${profile.org.businessId}</span>`}
175
+ </div>
176
+ </div>
177
+ ${profile.org?.description && html`<p class="cm-profile-desc">${profile.org.description}</p>`}
178
+ ${profile.org?.portfolio?.enabled && profile.org.portfolio.publicUrl && html`
179
+ <a class="btn-outline btn-sm cm-pf-link" href=${profile.org.portfolio.publicUrl} target="_blank">${t('companies.viewPortfolio')}</a>`}
180
+ </div>
181
+ <h3 class="cm-form-title">${t('companies.catalogue')}</h3>
182
+ ${(profile.offerings ?? []).length === 0 && html`<p class="cm-empty">${t('companies.noOfferings')}</p>`}
183
+ <ul class="cm-cat-list">
184
+ ${(profile.offerings ?? []).map(o => html`<${PublicOfferingCard} key=${o.agentName + '/' + o.offerId} o=${o} owner=${selected.owner} slug=${selected.slug} />`)}
185
+ </ul>
186
+ `}
187
+ </section>
188
+ </div>
189
+ </div>`;
190
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @file members.js
3
+ * @description Node members showcase — registered owners who have PUBLISHED a portfolio. A public grid
4
+ * (avatar + name + bio) that links each member to their portfolio (/v1/portfolio/<username>). Data from
5
+ * GET /v1/portfolio/members. "Discover the people on this node."
6
+ * @structure MembersView (default) — fetch + grid.
7
+ * @usage routed at /v1/members by spa.html.
8
+ * @version-history
9
+ * v1.0.0 — 2026-06-23 — Initial node member showcase (published-portfolio owners).
10
+ */
11
+ import { h } from 'preact';
12
+ import htm from 'htm';
13
+ const html = htm.bind(h);
14
+ import { useState, useEffect } from 'preact/hooks';
15
+ import { t } from '/js/i18n.js';
16
+ import { apiGet } from '/js/api.js';
17
+ import { escHtml } from '/js/utils.js';
18
+ import { useViewCSS } from '/components/useViewCSS.js';
19
+
20
+ export default function MembersView() {
21
+ useViewCSS('/css/views/members.css');
22
+ const [members, setMembers] = useState(null);
23
+ useEffect(() => {
24
+ apiGet('/v1/portfolio/members').then(r => setMembers(r?.data?.members || [])).catch(() => setMembers([]));
25
+ }, []);
26
+
27
+ return html`
28
+ <div class="mbr">
29
+ <h1 class="mbr-title">${t('members.title')}</h1>
30
+ <p class="mbr-desc">${t('members.desc')}</p>
31
+ ${members === null ? html`<div class="mbr-empty">…</div>`
32
+ : members.length === 0 ? html`<div class="mbr-empty">${t('members.empty')}</div>`
33
+ : html`<div class="mbr-grid">
34
+ ${members.map(m => html`
35
+ <a class="mbr-card" key=${m.username} href=${`/v1/portfolio/${encodeURIComponent(m.username)}`}>
36
+ <div class="mbr-avatar">${(m.avatar && String(m.avatar).length <= 4) ? m.avatar : '👤'}</div>
37
+ <div class="mbr-name">${escHtml(m.display_name || m.username)}</div>
38
+ ${m.bio ? html`<div class="mbr-bio">${escHtml(m.bio)}</div>` : null}
39
+ </a>`)}
40
+ </div>`}
41
+ </div>`;
42
+ }