bazilion 0.0.0 → 0.1.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 (265) hide show
  1. package/README.md +53 -152
  2. package/dist/cli.js +2730 -0
  3. package/dist/cli.js.map +1 -0
  4. package/dist/daemon.js +5888 -0
  5. package/dist/daemon.js.map +1 -0
  6. package/dist/worker.js +1830 -0
  7. package/dist/worker.js.map +1 -0
  8. package/package.json +57 -28
  9. package/.understand-anything/.understandignore +0 -25
  10. package/.understand-anything/fingerprints.json +0 -14267
  11. package/.understand-anything/knowledge-graph.json +0 -18128
  12. package/.understand-anything/meta.json +0 -6
  13. package/CLAUDE.md +0 -164
  14. package/apps/cli/package.json +0 -21
  15. package/apps/cli/src/auth-file.ts +0 -18
  16. package/apps/cli/src/client.ts +0 -37
  17. package/apps/cli/src/columnize.ts +0 -32
  18. package/apps/cli/src/commands/agent.ts +0 -574
  19. package/apps/cli/src/commands/auth.ts +0 -110
  20. package/apps/cli/src/commands/backup.ts +0 -135
  21. package/apps/cli/src/commands/completion.ts +0 -155
  22. package/apps/cli/src/commands/config.ts +0 -95
  23. package/apps/cli/src/commands/doctor.ts +0 -131
  24. package/apps/cli/src/commands/group.ts +0 -132
  25. package/apps/cli/src/commands/inbox.ts +0 -82
  26. package/apps/cli/src/commands/login.ts +0 -73
  27. package/apps/cli/src/commands/memory.ts +0 -106
  28. package/apps/cli/src/commands/profile.ts +0 -259
  29. package/apps/cli/src/commands/provider.ts +0 -170
  30. package/apps/cli/src/commands/send.ts +0 -24
  31. package/apps/cli/src/commands/serve.ts +0 -89
  32. package/apps/cli/src/commands/skill.ts +0 -120
  33. package/apps/cli/src/commands/token.ts +0 -148
  34. package/apps/cli/src/commands/trigger.ts +0 -129
  35. package/apps/cli/src/commands/uninstall.ts +0 -156
  36. package/apps/cli/src/index.ts +0 -196
  37. package/apps/cli/src/paths.ts +0 -12
  38. package/apps/cli/test/agent.test.ts +0 -213
  39. package/apps/cli/test/backup.test.ts +0 -95
  40. package/apps/cli/test/chat.test.ts +0 -539
  41. package/apps/cli/test/columnize.test.ts +0 -29
  42. package/apps/cli/test/completion.test.ts +0 -45
  43. package/apps/cli/test/config-page.test.ts +0 -151
  44. package/apps/cli/test/group.test.ts +0 -74
  45. package/apps/cli/test/helpers.ts +0 -70
  46. package/apps/cli/test/inbox-autodeliver.test.ts +0 -143
  47. package/apps/cli/test/inbox.test.ts +0 -147
  48. package/apps/cli/test/memory.test.ts +0 -69
  49. package/apps/cli/test/profile.test.ts +0 -110
  50. package/apps/cli/test/send.test.ts +0 -30
  51. package/apps/cli/test/server-fixture.ts +0 -212
  52. package/apps/cli/test/session-head.test.ts +0 -45
  53. package/apps/cli/test/skill.test.ts +0 -128
  54. package/apps/cli/test/token.test.ts +0 -109
  55. package/apps/cli/test/trigger.test.ts +0 -245
  56. package/apps/cli/tsconfig.json +0 -4
  57. package/apps/daemon/package.json +0 -31
  58. package/apps/daemon/src/app.ts +0 -41
  59. package/apps/daemon/src/core/agent/archive.ts +0 -8
  60. package/apps/daemon/src/core/agent/delete.ts +0 -30
  61. package/apps/daemon/src/core/agent/resolve.ts +0 -31
  62. package/apps/daemon/src/core/agent/spawn.ts +0 -95
  63. package/apps/daemon/src/core/agent/unarchive.ts +0 -11
  64. package/apps/daemon/src/core/availableModels.ts +0 -58
  65. package/apps/daemon/src/core/db/client.ts +0 -113
  66. package/apps/daemon/src/core/db/migrate.ts +0 -41
  67. package/apps/daemon/src/core/db/migrations/0001_init.sql +0 -179
  68. package/apps/daemon/src/core/group/delete.ts +0 -21
  69. package/apps/daemon/src/core/group/register.ts +0 -68
  70. package/apps/daemon/src/core/index.ts +0 -71
  71. package/apps/daemon/src/core/paths.ts +0 -56
  72. package/apps/daemon/src/core/profile/create.ts +0 -78
  73. package/apps/daemon/src/core/profile/delete.ts +0 -26
  74. package/apps/daemon/src/core/profile/identity.ts +0 -70
  75. package/apps/daemon/src/core/profile/load.ts +0 -45
  76. package/apps/daemon/src/core/profile/seed.ts +0 -74
  77. package/apps/daemon/src/core/profile/templates.ts +0 -60
  78. package/apps/daemon/src/core/profile/update.ts +0 -57
  79. package/apps/daemon/src/core/profile/validate.ts +0 -9
  80. package/apps/daemon/src/core/repos/agents.ts +0 -202
  81. package/apps/daemon/src/core/repos/config.ts +0 -78
  82. package/apps/daemon/src/core/repos/groups.ts +0 -55
  83. package/apps/daemon/src/core/repos/messages.ts +0 -127
  84. package/apps/daemon/src/core/repos/profiles.ts +0 -83
  85. package/apps/daemon/src/core/repos/providerModels.ts +0 -58
  86. package/apps/daemon/src/core/repos/providerState.ts +0 -37
  87. package/apps/daemon/src/core/repos/secrets.ts +0 -145
  88. package/apps/daemon/src/core/repos/skillMeta.ts +0 -49
  89. package/apps/daemon/src/core/repos/triggers.ts +0 -101
  90. package/apps/daemon/src/core/repos/webTokens.ts +0 -87
  91. package/apps/daemon/src/core/secrets.ts +0 -65
  92. package/apps/daemon/src/core/services.ts +0 -264
  93. package/apps/daemon/src/core/skills/discover.ts +0 -28
  94. package/apps/daemon/src/core/skills/import.ts +0 -136
  95. package/apps/daemon/src/core/skills/parse.ts +0 -52
  96. package/apps/daemon/src/core/skills/resolve.ts +0 -50
  97. package/apps/daemon/src/index.ts +0 -45
  98. package/apps/daemon/src/lib/agent-cancel.ts +0 -48
  99. package/apps/daemon/src/lib/agent-id.ts +0 -13
  100. package/apps/daemon/src/lib/agent-turn.ts +0 -56
  101. package/apps/daemon/src/lib/api-key.ts +0 -56
  102. package/apps/daemon/src/lib/auth.ts +0 -41
  103. package/apps/daemon/src/lib/cron.ts +0 -93
  104. package/apps/daemon/src/lib/ctx.ts +0 -80
  105. package/apps/daemon/src/lib/messaging-host.ts +0 -34
  106. package/apps/daemon/src/lib/middleware-auth.ts +0 -52
  107. package/apps/daemon/src/lib/scheduler.ts +0 -294
  108. package/apps/daemon/src/routes/agents.ts +0 -772
  109. package/apps/daemon/src/routes/auth-login.ts +0 -193
  110. package/apps/daemon/src/routes/config.ts +0 -267
  111. package/apps/daemon/src/routes/groups.ts +0 -133
  112. package/apps/daemon/src/routes/messages.ts +0 -29
  113. package/apps/daemon/src/routes/misc.ts +0 -239
  114. package/apps/daemon/src/routes/profiles.ts +0 -197
  115. package/apps/daemon/src/routes/skills.ts +0 -123
  116. package/apps/daemon/src/routes/triggers.ts +0 -29
  117. package/apps/daemon/src/runtime/auth/openai-codex.ts +0 -121
  118. package/apps/daemon/src/runtime/auto-reply/heartbeat.ts +0 -31
  119. package/apps/daemon/src/runtime/index.ts +0 -77
  120. package/apps/daemon/src/runtime/memory/files.ts +0 -103
  121. package/apps/daemon/src/runtime/memory/qmd.ts +0 -152
  122. package/apps/daemon/src/runtime/memory/types.ts +0 -16
  123. package/apps/daemon/src/runtime/pi/events.ts +0 -173
  124. package/apps/daemon/src/runtime/pi/session.ts +0 -536
  125. package/apps/daemon/src/runtime/pi/tools.ts +0 -85
  126. package/apps/daemon/src/runtime/providers/catalog.ts +0 -145
  127. package/apps/daemon/src/runtime/providers/pi-adapter.ts +0 -272
  128. package/apps/daemon/src/runtime/providers/registry.ts +0 -374
  129. package/apps/daemon/src/runtime/providers/retry.ts +0 -176
  130. package/apps/daemon/src/runtime/providers/types.ts +0 -33
  131. package/apps/daemon/src/runtime/session/prompt.ts +0 -83
  132. package/apps/daemon/src/runtime/tools/bootstrap.ts +0 -22
  133. package/apps/daemon/src/runtime/tools/home.ts +0 -114
  134. package/apps/daemon/src/runtime/tools/memory.ts +0 -81
  135. package/apps/daemon/src/runtime/tools/messaging.ts +0 -127
  136. package/apps/daemon/src/runtime/tools/registry.ts +0 -29
  137. package/apps/daemon/src/runtime/tools/types.ts +0 -13
  138. package/apps/daemon/src/runtime/tools/web-extract.ts +0 -110
  139. package/apps/daemon/src/runtime/tools/web-ssrf.ts +0 -245
  140. package/apps/daemon/src/runtime/tools/web.ts +0 -273
  141. package/apps/daemon/src/runtime/worker/entry.ts +0 -221
  142. package/apps/daemon/src/runtime/worker/ipc-protocol.ts +0 -75
  143. package/apps/daemon/src/runtime/worker/spawn.ts +0 -249
  144. package/apps/daemon/test/core/agents.test.ts +0 -319
  145. package/apps/daemon/test/core/available-models.test.ts +0 -63
  146. package/apps/daemon/test/core/config.test.ts +0 -99
  147. package/apps/daemon/test/core/groups.test.ts +0 -63
  148. package/apps/daemon/test/core/helpers.ts +0 -50
  149. package/apps/daemon/test/core/identity.test.ts +0 -85
  150. package/apps/daemon/test/core/migrations.test.ts +0 -83
  151. package/apps/daemon/test/core/profiles.test.ts +0 -182
  152. package/apps/daemon/test/core/provider-models.test.ts +0 -57
  153. package/apps/daemon/test/core/provider-state.test.ts +0 -36
  154. package/apps/daemon/test/core/skill-meta.test.ts +0 -45
  155. package/apps/daemon/test/core/skills.test.ts +0 -271
  156. package/apps/daemon/test/core/triggers.test.ts +0 -182
  157. package/apps/daemon/test/core/web-tokens.test.ts +0 -79
  158. package/apps/daemon/test/cron.test.ts +0 -90
  159. package/apps/daemon/test/runtime/heartbeat.test.ts +0 -34
  160. package/apps/daemon/test/runtime/memory-qmd.test.ts +0 -97
  161. package/apps/daemon/test/runtime/memory.test.ts +0 -78
  162. package/apps/daemon/test/runtime/messaging.test.ts +0 -213
  163. package/apps/daemon/test/runtime/mock-server.ts +0 -65
  164. package/apps/daemon/test/runtime/openai-codex-auth.test.ts +0 -116
  165. package/apps/daemon/test/runtime/providers.test.ts +0 -306
  166. package/apps/daemon/test/runtime/retry.test.ts +0 -191
  167. package/apps/daemon/test/runtime/session-head.test.ts +0 -90
  168. package/apps/daemon/test/runtime/tools-home.test.ts +0 -102
  169. package/apps/daemon/test/runtime/tools-web.test.ts +0 -206
  170. package/apps/daemon/tsconfig.json +0 -4
  171. package/apps/mobile/README.md +0 -60
  172. package/apps/mobile/app/_layout.tsx +0 -58
  173. package/apps/mobile/app/agents/[id]/chat.tsx +0 -486
  174. package/apps/mobile/app/agents/[id]/index.tsx +0 -166
  175. package/apps/mobile/app/agents/index.tsx +0 -212
  176. package/apps/mobile/app/index.tsx +0 -21
  177. package/apps/mobile/app/pair.tsx +0 -226
  178. package/apps/mobile/app/settings.tsx +0 -419
  179. package/apps/mobile/app.json +0 -49
  180. package/apps/mobile/assets/adaptive-icon.png +0 -0
  181. package/apps/mobile/assets/favicon.png +0 -0
  182. package/apps/mobile/assets/icon.png +0 -0
  183. package/apps/mobile/assets/splash-icon.png +0 -0
  184. package/apps/mobile/babel.config.js +0 -6
  185. package/apps/mobile/metro.config.js +0 -28
  186. package/apps/mobile/package.json +0 -44
  187. package/apps/mobile/src/auth.ts +0 -66
  188. package/apps/mobile/src/pair-url.ts +0 -48
  189. package/apps/mobile/src/theme-context.tsx +0 -88
  190. package/apps/mobile/src/theme.ts +0 -135
  191. package/apps/mobile/test/pair-url.test.ts +0 -46
  192. package/apps/mobile/tsconfig.json +0 -23
  193. package/apps/web/components.json +0 -25
  194. package/apps/web/package.json +0 -39
  195. package/apps/web/public/baziu.svg +0 -8
  196. package/apps/web/src/components/AgentTabs.tsx +0 -45
  197. package/apps/web/src/components/BaziuLogo.tsx +0 -21
  198. package/apps/web/src/components/ChatPane.tsx +0 -1033
  199. package/apps/web/src/components/ConfigTabs.tsx +0 -29
  200. package/apps/web/src/components/CopyButton.tsx +0 -68
  201. package/apps/web/src/components/CreateGroupDialog.tsx +0 -127
  202. package/apps/web/src/components/FieldRow.tsx +0 -94
  203. package/apps/web/src/components/Footer.tsx +0 -10
  204. package/apps/web/src/components/PawIcon.tsx +0 -15
  205. package/apps/web/src/components/Sidebar.tsx +0 -287
  206. package/apps/web/src/components/SpawnDialog.tsx +0 -129
  207. package/apps/web/src/components/ThemeToggle.tsx +0 -75
  208. package/apps/web/src/components/TopNav.tsx +0 -34
  209. package/apps/web/src/components/ui/button.tsx +0 -67
  210. package/apps/web/src/components/ui/card.tsx +0 -103
  211. package/apps/web/src/components/ui/checkbox.tsx +0 -31
  212. package/apps/web/src/components/ui/dialog.tsx +0 -168
  213. package/apps/web/src/components/ui/input.tsx +0 -19
  214. package/apps/web/src/components/ui/label.tsx +0 -22
  215. package/apps/web/src/components/ui/radio-group.tsx +0 -44
  216. package/apps/web/src/components/ui/select.tsx +0 -192
  217. package/apps/web/src/components/ui/separator.tsx +0 -26
  218. package/apps/web/src/components/ui/table.tsx +0 -116
  219. package/apps/web/src/components/ui/tabs.tsx +0 -88
  220. package/apps/web/src/components/ui/textarea.tsx +0 -18
  221. package/apps/web/src/lib/auth.ts +0 -50
  222. package/apps/web/src/lib/daemon-client.ts +0 -34
  223. package/apps/web/src/lib/md.ts +0 -45
  224. package/apps/web/src/lib/utils.ts +0 -6
  225. package/apps/web/src/lib/wire-constants.ts +0 -27
  226. package/apps/web/src/routeTree.gen.ts +0 -408
  227. package/apps/web/src/router.tsx +0 -20
  228. package/apps/web/src/routes/__root.tsx +0 -123
  229. package/apps/web/src/routes/agents/$id/inbox.tsx +0 -207
  230. package/apps/web/src/routes/agents/$id/index.tsx +0 -527
  231. package/apps/web/src/routes/agents/$id/triggers.tsx +0 -239
  232. package/apps/web/src/routes/agents/index.tsx +0 -265
  233. package/apps/web/src/routes/api/$.ts +0 -88
  234. package/apps/web/src/routes/config/index.tsx +0 -315
  235. package/apps/web/src/routes/config/services.tsx +0 -49
  236. package/apps/web/src/routes/config/tokens.tsx +0 -192
  237. package/apps/web/src/routes/groups/$id/index.tsx +0 -153
  238. package/apps/web/src/routes/groups/$id/memory.tsx +0 -321
  239. package/apps/web/src/routes/groups/index.tsx +0 -191
  240. package/apps/web/src/routes/index.tsx +0 -133
  241. package/apps/web/src/routes/login.tsx +0 -63
  242. package/apps/web/src/routes/profiles/$id.tsx +0 -549
  243. package/apps/web/src/routes/profiles/index.tsx +0 -458
  244. package/apps/web/src/routes/skills/index.tsx +0 -297
  245. package/apps/web/src/routes/welcome.tsx +0 -61
  246. package/apps/web/src/styles.css +0 -449
  247. package/apps/web/tsconfig.json +0 -25
  248. package/apps/web/vite.config.ts +0 -25
  249. package/biome.json +0 -23
  250. package/docs/agent-engine.md +0 -219
  251. package/docs/architecture.md +0 -627
  252. package/docs/backlog/README.md +0 -42
  253. package/docs/backlog/draft/BAZ-001-a2a-federation-spike.md +0 -125
  254. package/docs/openclaw-reference.md +0 -210
  255. package/packages/api-types/package.json +0 -11
  256. package/packages/api-types/src/entities.ts +0 -146
  257. package/packages/api-types/src/events.ts +0 -55
  258. package/packages/api-types/src/index.ts +0 -488
  259. package/packages/api-types/src/memory.ts +0 -15
  260. package/packages/client/package.json +0 -13
  261. package/packages/client/src/index.ts +0 -117
  262. package/pnpm-workspace.yaml +0 -3
  263. package/tsconfig.base.json +0 -23
  264. package/tsconfig.json +0 -11
  265. package/vitest.config.ts +0 -22
@@ -1,153 +0,0 @@
1
- import { ApiClientError } from '@bazilion/client'
2
- import type { Agent, Group } from '@bazilion/api-types'
3
- import { createFileRoute, redirect, useRouter } from '@tanstack/react-router'
4
- import { createServerFn } from '@tanstack/react-start'
5
- import { useState } from 'react'
6
- import { daemonClient } from '../../../lib/daemon-client'
7
-
8
- interface GroupDetail {
9
- group: Group
10
- members: Agent[]
11
- }
12
-
13
- const fetchGroup = createServerFn({ method: 'POST' })
14
- .inputValidator((d: { id: string }) => d)
15
- .handler(async ({ data }): Promise<GroupDetail | null> => {
16
- const c = daemonClient()
17
- let group: Group
18
- try {
19
- group = await c.get<Group>(`/api/groups/${encodeURIComponent(data.id)}`)
20
- } catch (err) {
21
- if (err instanceof ApiClientError && err.status === 404) return null
22
- throw err
23
- }
24
- const all = await c.get<Agent[]>('/api/agents?includeArchived=true')
25
- const members = all.filter((a) => a.groupId === group.id)
26
- return { group, members }
27
- })
28
-
29
- export const Route = createFileRoute('/groups/$id/')({
30
- loader: async ({ params }) => {
31
- const data = await fetchGroup({ data: { id: params.id } })
32
- if (!data) throw redirect({ to: '/groups' })
33
- return data
34
- },
35
- component: GroupDetailPage,
36
- })
37
-
38
- function GroupDetailPage() {
39
- const { group, members } = Route.useLoaderData()
40
- const router = useRouter()
41
- const [userMd, setUserMd] = useState(group.userMd)
42
- const [busy, setBusy] = useState(false)
43
- const [err, setErr] = useState<string | null>(null)
44
- const [savedAt, setSavedAt] = useState<number | null>(null)
45
-
46
- async function save() {
47
- setBusy(true)
48
- setErr(null)
49
- try {
50
- const res = await fetch(`/api/groups/${encodeURIComponent(group.id)}/user-md`, {
51
- method: 'PUT',
52
- headers: { 'content-type': 'application/json' },
53
- body: JSON.stringify({ userMd }),
54
- })
55
- if (!res.ok) {
56
- const e = (await res.json().catch(() => ({}))) as { error?: string }
57
- throw new Error(e.error ?? `${res.status} ${res.statusText}`)
58
- }
59
- setSavedAt(Date.now())
60
- await router.invalidate()
61
- } catch (e) {
62
- setErr((e as Error).message)
63
- } finally {
64
- setBusy(false)
65
- }
66
- }
67
-
68
- return (
69
- <main className="mx-auto max-w-3xl px-6 py-8">
70
- <h1 className="font-serif text-3xl text-foreground">
71
- {group.name} <span className="text-muted-foreground text-base">({group.id})</span>
72
- </h1>
73
- <p className="text-muted-foreground text-sm mb-6 mt-1">
74
- <code className="font-mono">{group.path}</code> · {members.length}{' '}
75
- member{members.length === 1 ? '' : 's'} ·{' '}
76
- <a
77
- href={`/groups/${encodeURIComponent(group.id)}/memory`}
78
- className="text-primary underline"
79
- >
80
- shared memory →
81
- </a>
82
- </p>
83
-
84
- <section className="rounded-lg border bg-card p-5 mb-6">
85
- <h3 className="font-serif text-xl mb-1">USER.md</h3>
86
- <p className="text-muted-foreground text-sm mb-3">
87
- Read-only context about the human, injected into every member agent's system prompt.
88
- 12 KB cap. Agents cannot edit this file — only you can.
89
- </p>
90
- <textarea
91
- value={userMd}
92
- onChange={(e) => setUserMd(e.target.value)}
93
- rows={16}
94
- className="w-full rounded-md border bg-background px-3 py-2 font-mono text-sm outline-none focus:ring-2 focus:ring-ring/30"
95
- placeholder="What the agent should know about you in this group context…"
96
- />
97
- <div className="flex items-center gap-3 mt-3">
98
- <button
99
- type="button"
100
- onClick={save}
101
- disabled={busy || userMd === group.userMd}
102
- className="rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground disabled:opacity-50"
103
- >
104
- {busy ? 'saving…' : 'save'}
105
- </button>
106
- <span className="text-xs text-muted-foreground">
107
- {userMd.length} / 12000 chars
108
- </span>
109
- {savedAt && <span className="text-xs text-emerald-700">✓ saved</span>}
110
- {err && <span className="text-xs text-rose-700">{err}</span>}
111
- </div>
112
- </section>
113
-
114
- <section className="rounded-lg border bg-card p-5">
115
- <h3 className="font-serif text-xl mb-3">members</h3>
116
- {members.length === 0 ? (
117
- <p className="text-muted-foreground text-sm">No agents in this group yet.</p>
118
- ) : (
119
- <table className="w-full text-sm">
120
- <thead className="text-left text-muted-foreground border-b">
121
- <tr>
122
- <th className="py-2">name</th>
123
- <th>status</th>
124
- <th>profile</th>
125
- <th />
126
- </tr>
127
- </thead>
128
- <tbody>
129
- {members.map((m) => (
130
- <tr key={m.id} className="border-b last:border-0">
131
- <td className="py-2">
132
- <a href={`/agents/${m.id}`} className="text-primary underline">
133
- {m.name}
134
- </a>
135
- </td>
136
- <td>{m.status}</td>
137
- <td>
138
- <code className="font-mono text-xs">{m.profileId}</code>
139
- </td>
140
- <td>
141
- <code className="font-mono text-xs text-muted-foreground">
142
- {m.id.slice(0, 8)}…
143
- </code>
144
- </td>
145
- </tr>
146
- ))}
147
- </tbody>
148
- </table>
149
- )}
150
- </section>
151
- </main>
152
- )
153
- }
@@ -1,321 +0,0 @@
1
- // Per-group shared memory: BM25-indexed markdown notes that every member
2
- // agent reads from and writes to. Browse the list, search, edit/create
3
- // entries. The qmd index lives at <group.path>/memory/ on disk.
4
-
5
- import { ApiClientError } from '@bazilion/client'
6
- import type { Agent, Group, MemoryEntry, MemoryHit } from '@bazilion/api-types'
7
- import { createFileRoute, redirect } from '@tanstack/react-router'
8
- import { createServerFn } from '@tanstack/react-start'
9
- import { useEffect, useRef, useState } from 'react'
10
- import { daemonClient } from '../../../lib/daemon-client'
11
-
12
- interface MemoryView {
13
- group: Group
14
- memberCount: number
15
- entries: MemoryEntry[]
16
- }
17
-
18
- const fetchMemory = createServerFn({ method: 'POST' })
19
- .inputValidator((d: { id: string }) => d)
20
- .handler(async ({ data }): Promise<MemoryView | null> => {
21
- const c = daemonClient()
22
- let group: Group
23
- try {
24
- group = await c.get<Group>(`/api/groups/${encodeURIComponent(data.id)}`)
25
- } catch (err) {
26
- if (err instanceof ApiClientError && err.status === 404) return null
27
- throw err
28
- }
29
- const [entries, agents] = await Promise.all([
30
- c.get<MemoryEntry[]>(`/api/groups/${encodeURIComponent(group.id)}/memory`),
31
- c.get<Agent[]>('/api/agents?includeArchived=true'),
32
- ])
33
- const memberCount = agents.filter((a) => a.groupId === group.id).length
34
- return { group, memberCount, entries }
35
- })
36
-
37
- export const Route = createFileRoute('/groups/$id/memory')({
38
- loader: async ({ params }) => {
39
- const data = await fetchMemory({ data: { id: params.id } })
40
- if (!data) throw redirect({ to: '/groups' })
41
- return data
42
- },
43
- component: MemoryPage,
44
- })
45
-
46
- function encodeKey(key: string): string {
47
- return key.split('/').map(encodeURIComponent).join('/')
48
- }
49
-
50
- interface ListRow {
51
- key: string
52
- preview: string
53
- score?: number
54
- }
55
-
56
- type Mode = 'none' | 'edit' | 'new'
57
-
58
- function MemoryPage() {
59
- const { group, memberCount, entries: initialEntries } = Route.useLoaderData()
60
- const groupId = group.id
61
-
62
- const [rows, setRows] = useState<ListRow[]>(() =>
63
- initialEntries.map((e) => ({ key: e.key, preview: e.content.slice(0, 80) })),
64
- )
65
- const [isSearch, setIsSearch] = useState(false)
66
- const [query, setQuery] = useState('')
67
- const [selectedKey, setSelectedKey] = useState<string | null>(null)
68
- const [mode, setMode] = useState<Mode>('none')
69
- const [keyInput, setKeyInput] = useState('')
70
- const [content, setContent] = useState('')
71
- const [status, setStatus] = useState<{ msg: string; kind: 'info' | 'error' } | null>(null)
72
- const searchTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
73
-
74
- async function loadList(q: string) {
75
- try {
76
- const url =
77
- q.trim().length > 0
78
- ? `/api/groups/${encodeURIComponent(groupId)}/memory/search?q=${encodeURIComponent(q)}&limit=50`
79
- : `/api/groups/${encodeURIComponent(groupId)}/memory`
80
- const res = await fetch(url)
81
- if (!res.ok) {
82
- const body = (await res.json().catch(() => null)) as { error?: string } | null
83
- throw new Error(body?.error ?? res.statusText)
84
- }
85
- const data = (await res.json()) as MemoryEntry[] | MemoryHit[]
86
- if (q.trim().length > 0) {
87
- setRows(
88
- (data as MemoryHit[]).map((h) => ({
89
- key: h.key,
90
- preview: h.snippet,
91
- score: h.score,
92
- })),
93
- )
94
- setIsSearch(true)
95
- } else {
96
- setRows(
97
- (data as MemoryEntry[]).map((e) => ({
98
- key: e.key,
99
- preview: e.content.slice(0, 80),
100
- })),
101
- )
102
- setIsSearch(false)
103
- }
104
- } catch (err) {
105
- setStatus({ msg: `error: ${(err as Error).message}`, kind: 'error' })
106
- }
107
- }
108
-
109
- useEffect(() => {
110
- if (searchTimer.current) clearTimeout(searchTimer.current)
111
- searchTimer.current = setTimeout(() => {
112
- void loadList(query)
113
- }, 200)
114
- return () => {
115
- if (searchTimer.current) clearTimeout(searchTimer.current)
116
- }
117
- // eslint-disable-next-line react-hooks/exhaustive-deps
118
- }, [query])
119
-
120
- async function openEntry(key: string) {
121
- setSelectedKey(key)
122
- setMode('edit')
123
- setStatus({ msg: 'loading…', kind: 'info' })
124
- try {
125
- const res = await fetch(
126
- `/api/groups/${encodeURIComponent(groupId)}/memory/${encodeKey(key)}`,
127
- )
128
- if (!res.ok) {
129
- const body = (await res.json().catch(() => null)) as { error?: string } | null
130
- throw new Error(body?.error ?? res.statusText)
131
- }
132
- const entry = (await res.json()) as MemoryEntry
133
- setKeyInput(entry.key)
134
- setContent(entry.content ?? '')
135
- setStatus({ msg: `loaded ${entry.key}`, kind: 'info' })
136
- } catch (err) {
137
- setStatus({ msg: `error: ${(err as Error).message}`, kind: 'error' })
138
- }
139
- }
140
-
141
- function newEntry() {
142
- setSelectedKey(null)
143
- setMode('new')
144
- setKeyInput('')
145
- setContent('')
146
- setStatus({ msg: 'new entry — type a key and content, then save', kind: 'info' })
147
- }
148
-
149
- async function save() {
150
- const key = keyInput.trim()
151
- if (!key) {
152
- setStatus({ msg: 'key is required', kind: 'error' })
153
- return
154
- }
155
- try {
156
- const res = await fetch(
157
- `/api/groups/${encodeURIComponent(groupId)}/memory/${encodeKey(key)}`,
158
- {
159
- method: 'PUT',
160
- headers: { 'content-type': 'application/json' },
161
- body: JSON.stringify({ content }),
162
- },
163
- )
164
- if (!res.ok) {
165
- const body = (await res.json().catch(() => null)) as { error?: string } | null
166
- throw new Error(body?.error ?? res.statusText)
167
- }
168
- setSelectedKey(key)
169
- setMode('edit')
170
- setStatus({ msg: 'saved', kind: 'info' })
171
- await loadList(query)
172
- } catch (err) {
173
- setStatus({ msg: `error: ${(err as Error).message}`, kind: 'error' })
174
- }
175
- }
176
-
177
- async function del() {
178
- if (!selectedKey) return
179
- if (!confirm(`delete ${selectedKey}?`)) return
180
- try {
181
- const res = await fetch(
182
- `/api/groups/${encodeURIComponent(groupId)}/memory/${encodeKey(selectedKey)}`,
183
- { method: 'DELETE' },
184
- )
185
- if (!res.ok && res.status !== 204) {
186
- const body = (await res.json().catch(() => null)) as { error?: string } | null
187
- throw new Error(body?.error ?? res.statusText)
188
- }
189
- setSelectedKey(null)
190
- setMode('none')
191
- setKeyInput('')
192
- setContent('')
193
- setStatus({ msg: 'deleted', kind: 'info' })
194
- await loadList(query)
195
- } catch (err) {
196
- setStatus({ msg: `error: ${(err as Error).message}`, kind: 'error' })
197
- }
198
- }
199
-
200
- const canSave = mode !== 'none' && keyInput.trim().length > 0
201
- const canDelete = mode === 'edit' && selectedKey !== null
202
-
203
- return (
204
- <main className="mx-auto max-w-5xl px-6 py-8">
205
- <header className="mb-6">
206
- <h1 className="font-serif text-3xl text-foreground">
207
- {group.name}{' '}
208
- <span className="text-muted-foreground text-base">/ shared memory</span>
209
- </h1>
210
- <p className="text-muted-foreground text-sm mt-1">
211
- BM25-indexed markdown notes shared by every agent in{' '}
212
- <a
213
- href={`/groups/${encodeURIComponent(group.id)}`}
214
- className="font-mono underline"
215
- >
216
- {group.id}
217
- </a>{' '}
218
- ({memberCount} member{memberCount === 1 ? '' : 's'}). Anything written here is
219
- visible to every member; per-agent notes belong in the agent's own{' '}
220
- <code className="font-mono">IDENTITY.md</code> via <code>home_write</code>.
221
- </p>
222
- </header>
223
-
224
- <div className="grid grid-cols-1 gap-6 lg:grid-cols-[320px_1fr]">
225
- <div>
226
- <div className="overflow-hidden rounded-[16px] border border-frost bg-snow">
227
- <div className="border-b border-frost p-2">
228
- <input
229
- type="text"
230
- placeholder="search (BM25)..."
231
- autoComplete="off"
232
- value={query}
233
- onChange={(e) => setQuery(e.target.value)}
234
- />
235
- </div>
236
- <div className="max-h-[520px] overflow-y-auto">
237
- {rows.length === 0 ? (
238
- <div className="p-8 text-center text-mocha-light">no entries</div>
239
- ) : (
240
- rows.map((r) => {
241
- const active = r.key === selectedKey
242
- return (
243
- <button
244
- key={r.key}
245
- type="button"
246
- onClick={() => openEntry(r.key)}
247
- className={`unstyled block w-full cursor-pointer border-b border-frost/50 px-3.5 py-2 text-left font-mono text-[0.84em] transition-colors last:border-b-0 hover:bg-sapphire-glow ${
248
- active ? 'bg-sapphire-glow font-medium text-sapphire-deep' : ''
249
- }`}
250
- >
251
- {isSearch && r.score !== undefined && (
252
- <span className="float-right text-[0.75em] text-sapphire">
253
- {r.score.toFixed(2)}
254
- </span>
255
- )}
256
- {r.key}
257
- {r.preview && (
258
- <span className="mt-1 block text-[0.9em] font-normal text-mocha-light">
259
- {r.preview}
260
- </span>
261
- )}
262
- </button>
263
- )
264
- })
265
- )}
266
- </div>
267
- </div>
268
- <button type="button" className="ghost-btn mt-3 w-full" onClick={newEntry}>
269
- + new entry
270
- </button>
271
- </div>
272
-
273
- <div>
274
- <div className="mb-3 flex items-center gap-2">
275
- <input
276
- type="text"
277
- placeholder="key (e.g. prefs/hiking.md)"
278
- value={keyInput}
279
- onChange={(e) => setKeyInput(e.target.value)}
280
- disabled={mode !== 'new'}
281
- className="flex-1 font-mono"
282
- />
283
- </div>
284
- <textarea
285
- placeholder="select an entry on the left, or create a new one."
286
- value={content}
287
- onChange={(e) => setContent(e.target.value)}
288
- className="min-h-[360px] w-full font-mono text-[0.9em] leading-[1.55]"
289
- />
290
- <div className="mt-3 flex gap-2">
291
- <button
292
- type="button"
293
- className="btn-primary"
294
- onClick={save}
295
- disabled={!canSave}
296
- >
297
- save
298
- </button>
299
- <button
300
- type="button"
301
- className="ghost-btn"
302
- onClick={del}
303
- disabled={!canDelete}
304
- >
305
- delete
306
- </button>
307
- </div>
308
- {status && (
309
- <p
310
- className={`mt-2 text-[0.9em] ${
311
- status.kind === 'error' ? 'text-[#9B3D3D]' : 'text-mocha-light'
312
- }`}
313
- >
314
- {status.msg}
315
- </p>
316
- )}
317
- </div>
318
- </div>
319
- </main>
320
- )
321
- }
@@ -1,191 +0,0 @@
1
- import type { Agent, Group } from '@bazilion/api-types'
2
- import { createFileRoute, useRouter } from '@tanstack/react-router'
3
- import { createServerFn } from '@tanstack/react-start'
4
- import { useState } from 'react'
5
- import { daemonClient } from '../../lib/daemon-client'
6
-
7
- interface GroupsData {
8
- groups: Group[]
9
- memberCounts: Record<string, number>
10
- }
11
-
12
- const fetchGroupsData = createServerFn({ method: 'GET' }).handler(
13
- async (): Promise<GroupsData> => {
14
- const c = daemonClient()
15
- const [groups, agents] = await Promise.all([
16
- c.get<Group[]>('/api/groups'),
17
- c.get<Agent[]>('/api/agents?includeArchived=true'),
18
- ])
19
- const memberCounts: Record<string, number> = {}
20
- for (const a of agents) memberCounts[a.groupId] = (memberCounts[a.groupId] ?? 0) + 1
21
- return { groups, memberCounts }
22
- },
23
- )
24
-
25
- export const Route = createFileRoute('/groups/')({
26
- loader: () => fetchGroupsData(),
27
- component: GroupsPage,
28
- })
29
-
30
- function GroupsPage() {
31
- const { groups, memberCounts } = Route.useLoaderData()
32
- const router = useRouter()
33
-
34
- async function remove(id: string) {
35
- if (!confirm('remove this group registration?')) return
36
- const res = await fetch(`/api/groups/${id}`, { method: 'DELETE' })
37
- if (!res.ok && res.status !== 204) {
38
- alert(res.statusText)
39
- return
40
- }
41
- await router.invalidate()
42
- }
43
-
44
- return (
45
- <div>
46
- <h1>groups</h1>
47
- <p className="muted">
48
- A group is a collaboration context: one filesystem root, one USER.md, one roster. Every
49
- agent belongs to exactly one group.
50
- </p>
51
-
52
- <RegisterGroupForm onRegistered={() => router.invalidate()} />
53
-
54
- <table>
55
- <thead>
56
- <tr>
57
- <th>id</th>
58
- <th>name</th>
59
- <th>path</th>
60
- <th>members</th>
61
- <th>USER.md</th>
62
- <th />
63
- </tr>
64
- </thead>
65
- <tbody>
66
- {groups.length === 0 && (
67
- <tr>
68
- <td colSpan={6} className="muted">
69
- no groups registered
70
- </td>
71
- </tr>
72
- )}
73
- {groups.map((g) => {
74
- const count = memberCounts[g.id] ?? 0
75
- const userMdBytes = g.userMd.length
76
- return (
77
- <tr key={g.id}>
78
- <td>
79
- <code>{g.id}</code>
80
- </td>
81
- <td>{g.name}</td>
82
- <td>
83
- <code>{g.path}</code>
84
- </td>
85
- <td>{count}</td>
86
- <td>
87
- <a href={`/groups/${g.id}`}>
88
- {userMdBytes > 0 ? `${userMdBytes} chars` : 'empty'}
89
- </a>
90
- </td>
91
- <td>
92
- {count === 0 ? (
93
- <button type="button" className="ghost-btn" onClick={() => remove(g.id)}>
94
- remove
95
- </button>
96
- ) : (
97
- <span
98
- className="muted"
99
- title={`${count} agent(s) belong to this group`}
100
- >
101
- in use
102
- </span>
103
- )}
104
- </td>
105
- </tr>
106
- )
107
- })}
108
- </tbody>
109
- </table>
110
- </div>
111
- )
112
- }
113
-
114
- function RegisterGroupForm({ onRegistered }: { onRegistered: () => void }) {
115
- const [id, setId] = useState('')
116
- const [name, setName] = useState('')
117
- const [link, setLink] = useState('')
118
- const [err, setErr] = useState<string | null>(null)
119
- const [submitting, setSubmitting] = useState(false)
120
-
121
- async function submit(e: React.FormEvent<HTMLFormElement>) {
122
- e.preventDefault()
123
- setErr(null)
124
- if (!id.trim()) {
125
- setErr('id is required')
126
- return
127
- }
128
- setSubmitting(true)
129
- try {
130
- const res = await fetch('/api/groups', {
131
- method: 'POST',
132
- headers: { 'content-type': 'application/json' },
133
- body: JSON.stringify({
134
- id: id.trim(),
135
- name: name.trim() || undefined,
136
- link: link.trim() || undefined,
137
- }),
138
- })
139
- if (!res.ok) {
140
- const e2 = (await res.json().catch(() => null)) as { error?: string } | null
141
- throw new Error(e2?.error ?? res.statusText)
142
- }
143
- setId('')
144
- setName('')
145
- setLink('')
146
- onRegistered()
147
- } catch (e2) {
148
- setErr((e2 as Error).message)
149
- } finally {
150
- setSubmitting(false)
151
- }
152
- }
153
-
154
- return (
155
- <form className="card" onSubmit={submit}>
156
- <h3>register group</h3>
157
- {err && <div className="err">{err}</div>}
158
- <p className="muted">
159
- Groups always live under <code>~/.bazilion/groups/&lt;slug&gt;/</code>. Leave the link
160
- target blank to create a fresh directory; supply an absolute path to materialize the slot
161
- as a symlink to your existing project tree instead.
162
- </p>
163
- <div className="flex gap-4">
164
- <label className="flex-1">
165
- id (slug)
166
- <input
167
- value={id}
168
- onChange={(e) => setId(e.target.value)}
169
- required
170
- placeholder="myproject"
171
- />
172
- </label>
173
- <label className="flex-1">
174
- name
175
- <input value={name} onChange={(e) => setName(e.target.value)} placeholder="My Project" />
176
- </label>
177
- </div>
178
- <label>
179
- link target (optional, absolute path)
180
- <input
181
- value={link}
182
- onChange={(e) => setLink(e.target.value)}
183
- placeholder="/home/user/projects/myproject"
184
- />
185
- </label>
186
- <button type="submit" disabled={submitting}>
187
- {submitting ? 'registering…' : 'register'}
188
- </button>
189
- </form>
190
- )
191
- }