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,549 +0,0 @@
1
- import { ApiClientError } from '@bazilion/client'
2
- import type { LoadedProfile, SkillInfo } 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 ModelGroup {
9
- provider: string
10
- models: string[]
11
- }
12
- interface AvailableModelsResponse {
13
- groups: ModelGroup[]
14
- }
15
-
16
- interface ProfileDetailView {
17
- loaded: LoadedProfile
18
- modelGroups: ModelGroup[]
19
- skills: SkillInfo[]
20
- }
21
-
22
- const fetchProfile = createServerFn({ method: 'POST' })
23
- .inputValidator((d: { id: string }) => d)
24
- .handler(async ({ data }): Promise<ProfileDetailView | null> => {
25
- const c = daemonClient()
26
- let loaded: LoadedProfile
27
- try {
28
- loaded = await c.get<LoadedProfile>(`/api/profiles/${encodeURIComponent(data.id)}`)
29
- } catch (err) {
30
- if (err instanceof ApiClientError && err.status === 404) return null
31
- throw err
32
- }
33
- const [models, skills] = await Promise.all([
34
- c.get<AvailableModelsResponse>('/api/config/available-models'),
35
- c.get<SkillInfo[]>('/api/skills'),
36
- ])
37
- return { loaded, modelGroups: models.groups, skills }
38
- })
39
-
40
- export const Route = createFileRoute('/profiles/$id')({
41
- loader: async ({ params }) => {
42
- const data = await fetchProfile({ data: { id: params.id } })
43
- if (!data) throw redirect({ to: '/profiles' })
44
- return data
45
- },
46
- component: ProfileDetailPage,
47
- })
48
-
49
- function ProfileDetailPage() {
50
- const { loaded, modelGroups, skills } = Route.useLoaderData()
51
- const [tab, setTab] = useState<'basics' | 'skills'>('basics')
52
-
53
- return (
54
- <div>
55
- <header className="mb-1">
56
- <a
57
- href="/profiles"
58
- className="mb-1 inline-block text-[0.85em] text-mocha-light hover:text-sapphire"
59
- >
60
- ← profiles
61
- </a>
62
- <h1>{loaded.profile.name}</h1>
63
- <div className="mt-1 flex flex-wrap items-center gap-2 text-[0.9em] text-mocha-light">
64
- <code>{loaded.profile.id}</code>
65
- <span>·</span>
66
- <span>
67
- model: <code>{loaded.profile.defaultModel}</code>
68
- </span>
69
- </div>
70
- </header>
71
-
72
- <p className="muted my-6">
73
- These settings and files seed every agent spawned from this profile going forward.{' '}
74
- <strong>Existing agents keep their own copies</strong> — edits here don't propagate to
75
- already-spawned agents.
76
- </p>
77
-
78
- <Tabs
79
- items={[
80
- { id: 'basics', label: '1. basics' },
81
- { id: 'skills', label: '2. skills' },
82
- ]}
83
- active={tab}
84
- onChange={(t) => setTab(t as 'basics' | 'skills')}
85
- />
86
-
87
- {tab === 'basics' && (
88
- <div>
89
- <SettingsCard
90
- profileId={loaded.profile.id}
91
- initialName={loaded.profile.name}
92
- currentModel={loaded.profile.defaultModel}
93
- modelGroups={modelGroups}
94
- />
95
- <FileCard profileId={loaded.profile.id} file="SOUL.md" initialContent={loaded.files.soul} rows={14} />
96
- <FileCard
97
- profileId={loaded.profile.id}
98
- file="IDENTITY.md"
99
- initialContent={loaded.files.identity}
100
- rows={10}
101
- />
102
- {loaded.files.bootstrap === null ? (
103
- <section className="file-card">
104
- <div className="file-head">
105
- <h3>BOOTSTRAP.md</h3>
106
- </div>
107
- <p className="muted">
108
- This profile was created without a bootstrap prompt. Spawned agents won't see a
109
- first-run intro message.
110
- </p>
111
- </section>
112
- ) : (
113
- <FileCard
114
- profileId={loaded.profile.id}
115
- file="BOOTSTRAP.md"
116
- initialContent={loaded.files.bootstrap}
117
- rows={10}
118
- />
119
- )}
120
- <OptionalFileCard
121
- profileId={loaded.profile.id}
122
- file="AGENTS.md"
123
- initialContent={loaded.files.agents}
124
- hint="Peers and routing notes — who this agent can hand off to."
125
- />
126
- <OptionalFileCard
127
- profileId={loaded.profile.id}
128
- file="TOOLS.md"
129
- initialContent={loaded.files.tools}
130
- hint="Tool playbook. Agent-specific usage notes that go beyond the generic tool descriptions."
131
- />
132
- <OptionalFileCard
133
- profileId={loaded.profile.id}
134
- file="HEARTBEAT.md"
135
- initialContent={loaded.files.heartbeat}
136
- hint="Tasks the agent should check on scheduled wake-ups. Pair with a trigger whose message is the canned heartbeat prompt."
137
- />
138
- </div>
139
- )}
140
-
141
- {tab === 'skills' && (
142
- <SkillsCard
143
- profileId={loaded.profile.id}
144
- initialMode={loaded.profile.skillsMode}
145
- initialPicked={new Set(loaded.defaultSkills)}
146
- skills={skills}
147
- />
148
- )}
149
- </div>
150
- )
151
- }
152
-
153
- interface TabsProps<T extends string> {
154
- items: { id: T; label: string }[]
155
- active: T
156
- onChange: (id: T) => void
157
- }
158
- function Tabs<T extends string>({ items, active, onChange }: TabsProps<T>) {
159
- return (
160
- <nav
161
- role="tablist"
162
- className="-mb-px mb-5 flex gap-1 border-b border-frost"
163
- >
164
- {items.map((it) => (
165
- <button
166
- key={it.id}
167
- type="button"
168
- role="tab"
169
- aria-selected={active === it.id}
170
- onClick={() => onChange(it.id)}
171
- className={`unstyled cursor-pointer border-b-2 bg-transparent px-4 py-2 text-[0.9em] font-medium transition-colors ${
172
- active === it.id
173
- ? 'border-sapphire text-sapphire'
174
- : 'border-transparent text-mocha hover:text-sapphire'
175
- }`}
176
- >
177
- {it.label}
178
- </button>
179
- ))}
180
- </nav>
181
- )
182
- }
183
-
184
- type StatusKind = 'idle' | 'dirty' | 'saving' | 'saved' | 'error'
185
- interface StatusState {
186
- kind: StatusKind
187
- message: string
188
- }
189
- function statusColor(kind: StatusKind) {
190
- if (kind === 'saved') return 'text-sapphire'
191
- if (kind === 'error') return 'text-[#9B3D3D]'
192
- if (kind === 'dirty') return 'text-mocha'
193
- return 'text-mocha-light'
194
- }
195
-
196
- function FileCardShell({
197
- title,
198
- status,
199
- children,
200
- }: {
201
- title: string
202
- status?: StatusState
203
- children: React.ReactNode
204
- }) {
205
- return (
206
- <section className="file-card mb-5 rounded-[16px] border border-frost bg-snow p-5 shadow-baziu-sm">
207
- <div className="mb-2 flex items-baseline justify-between">
208
- <h3 className="m-0 font-mono text-[0.95em] font-semibold normal-case tracking-normal text-chocolate">
209
- {title}
210
- </h3>
211
- {status && (
212
- <span className={`text-[0.82em] ${statusColor(status.kind)}`}>{status.message}</span>
213
- )}
214
- </div>
215
- {children}
216
- </section>
217
- )
218
- }
219
-
220
- function FileCard({
221
- profileId,
222
- file,
223
- initialContent,
224
- rows = 10,
225
- }: {
226
- profileId: string
227
- file: string
228
- initialContent: string
229
- rows?: number
230
- }) {
231
- const [original, setOriginal] = useState(initialContent)
232
- const [content, setContent] = useState(initialContent)
233
- const [status, setStatus] = useState<StatusState>({ kind: 'idle', message: '' })
234
- const dirty = content !== original
235
- const display: StatusState = dirty && status.kind !== 'saving' && status.kind !== 'error'
236
- ? { kind: 'dirty', message: 'unsaved changes' }
237
- : status
238
-
239
- async function save() {
240
- setStatus({ kind: 'saving', message: 'saving…' })
241
- try {
242
- const res = await fetch(
243
- `/api/profiles/${encodeURIComponent(profileId)}/files/${encodeURIComponent(file)}`,
244
- {
245
- method: 'PUT',
246
- headers: { 'content-type': 'application/json' },
247
- body: JSON.stringify({ content }),
248
- },
249
- )
250
- if (!res.ok && res.status !== 204) {
251
- const e = (await res.json().catch(() => null)) as { error?: string } | null
252
- throw new Error(e?.error ?? res.statusText)
253
- }
254
- setOriginal(content)
255
- setStatus({ kind: 'saved', message: 'saved' })
256
- } catch (err) {
257
- setStatus({ kind: 'error', message: `error: ${(err as Error).message}` })
258
- }
259
- }
260
- function revert() {
261
- setContent(original)
262
- setStatus({ kind: 'idle', message: '' })
263
- }
264
-
265
- return (
266
- <FileCardShell title={file} status={display}>
267
- <textarea
268
- rows={rows}
269
- value={content}
270
- onChange={(e) => setContent(e.target.value)}
271
- className="font-mono text-[0.88em] leading-[1.55]"
272
- />
273
- <div className="mt-3 flex gap-2">
274
- <button
275
- type="button"
276
- className="btn-primary"
277
- onClick={save}
278
- disabled={status.kind === 'saving'}
279
- >
280
- save
281
- </button>
282
- <button type="button" className="ghost-btn" onClick={revert}>
283
- revert
284
- </button>
285
- </div>
286
- </FileCardShell>
287
- )
288
- }
289
-
290
- function OptionalFileCard({
291
- profileId,
292
- file,
293
- initialContent,
294
- hint,
295
- }: {
296
- profileId: string
297
- file: string
298
- initialContent: string | null
299
- hint: string
300
- }) {
301
- const router = useRouter()
302
- const [creating, setCreating] = useState(false)
303
- const [status, setStatus] = useState<StatusState>({ kind: 'idle', message: '' })
304
-
305
- async function createEmpty() {
306
- setCreating(true)
307
- setStatus({ kind: 'saving', message: 'creating…' })
308
- try {
309
- const res = await fetch(
310
- `/api/profiles/${encodeURIComponent(profileId)}/files/${encodeURIComponent(file)}`,
311
- {
312
- method: 'PUT',
313
- headers: { 'content-type': 'application/json' },
314
- body: JSON.stringify({ content: '' }),
315
- },
316
- )
317
- if (!res.ok && res.status !== 204) {
318
- const e = (await res.json().catch(() => null)) as { error?: string } | null
319
- throw new Error(e?.error ?? res.statusText)
320
- }
321
- await router.invalidate()
322
- } catch (err) {
323
- setStatus({ kind: 'error', message: `error: ${(err as Error).message}` })
324
- setCreating(false)
325
- }
326
- }
327
-
328
- if (initialContent === null) {
329
- return (
330
- <FileCardShell title={file} status={status}>
331
- <p className="muted my-1">{hint}</p>
332
- <div className="mt-3 flex gap-2">
333
- <button
334
- type="button"
335
- className="btn-primary"
336
- onClick={createEmpty}
337
- disabled={creating}
338
- >
339
- {creating ? 'creating…' : 'create empty'}
340
- </button>
341
- </div>
342
- </FileCardShell>
343
- )
344
- }
345
- return (
346
- <>
347
- <p className="muted -mb-3 ml-1 mt-2 text-[0.85em]">{hint}</p>
348
- <FileCard profileId={profileId} file={file} initialContent={initialContent} rows={10} />
349
- </>
350
- )
351
- }
352
-
353
- function SettingsCard({
354
- profileId,
355
- initialName,
356
- currentModel,
357
- modelGroups,
358
- }: {
359
- profileId: string
360
- initialName: string
361
- currentModel: string
362
- modelGroups: ModelGroup[]
363
- }) {
364
- const router = useRouter()
365
- const [name, setName] = useState(initialName)
366
- const [defaultModel, setDefaultModel] = useState(currentModel)
367
- const [status, setStatus] = useState<StatusState>({ kind: 'idle', message: '' })
368
-
369
- const modelValues = modelGroups.flatMap((g) => g.models.map((m) => `${g.provider}:${m}`))
370
- const modelMissing = !modelValues.includes(currentModel)
371
-
372
- async function save(e: React.FormEvent<HTMLFormElement>) {
373
- e.preventDefault()
374
- setStatus({ kind: 'saving', message: 'saving…' })
375
- try {
376
- const res = await fetch(`/api/profiles/${encodeURIComponent(profileId)}`, {
377
- method: 'PATCH',
378
- headers: { 'content-type': 'application/json' },
379
- body: JSON.stringify({ name: name.trim(), defaultModel: defaultModel.trim() }),
380
- })
381
- if (!res.ok) {
382
- const e2 = (await res.json().catch(() => null)) as { error?: string } | null
383
- throw new Error(e2?.error ?? res.statusText)
384
- }
385
- await router.invalidate()
386
- setStatus({ kind: 'saved', message: 'saved' })
387
- } catch (err) {
388
- setStatus({ kind: 'error', message: `error: ${(err as Error).message}` })
389
- }
390
- }
391
-
392
- return (
393
- <FileCardShell title="settings" status={status}>
394
- <form onSubmit={save}>
395
- <div className="flex gap-4">
396
- <label className="flex-1">
397
- name
398
- <input value={name} onChange={(e) => setName(e.target.value)} />
399
- </label>
400
- <label className="flex-1">
401
- default model
402
- {modelGroups.length === 0 ? (
403
- <select disabled>
404
- <option>(no models available — enable on /config)</option>
405
- </select>
406
- ) : (
407
- <select
408
- value={defaultModel}
409
- onChange={(e) => setDefaultModel(e.target.value)}
410
- >
411
- {modelMissing && (
412
- <option value={currentModel}>{currentModel} (not in enabled list)</option>
413
- )}
414
- {modelGroups.map((g) => (
415
- <optgroup key={g.provider} label={g.provider}>
416
- {g.models.map((m) => (
417
- <option key={m} value={`${g.provider}:${m}`}>
418
- {`${g.provider}:${m}`}
419
- </option>
420
- ))}
421
- </optgroup>
422
- ))}
423
- </select>
424
- )}
425
- </label>
426
- </div>
427
- <div className="mt-3 flex gap-2">
428
- <button type="submit" disabled={status.kind === 'saving'}>
429
- save settings
430
- </button>
431
- </div>
432
- </form>
433
- </FileCardShell>
434
- )
435
- }
436
-
437
- function SkillsCard({
438
- profileId,
439
- initialMode,
440
- initialPicked,
441
- skills,
442
- }: {
443
- profileId: string
444
- initialMode: 'all' | 'selected'
445
- initialPicked: Set<string>
446
- skills: SkillInfo[]
447
- }) {
448
- const router = useRouter()
449
- const [mode, setMode] = useState<'all' | 'selected'>(initialMode)
450
- const [picked, setPicked] = useState<Set<string>>(initialPicked)
451
- const [status, setStatus] = useState<StatusState>({ kind: 'idle', message: '' })
452
-
453
- function toggle(name: string) {
454
- setPicked((prev) => {
455
- const next = new Set(prev)
456
- if (next.has(name)) next.delete(name)
457
- else next.add(name)
458
- return next
459
- })
460
- }
461
-
462
- async function save(e: React.FormEvent<HTMLFormElement>) {
463
- e.preventDefault()
464
- setStatus({ kind: 'saving', message: 'saving…' })
465
- try {
466
- const res = await fetch(`/api/profiles/${encodeURIComponent(profileId)}`, {
467
- method: 'PATCH',
468
- headers: { 'content-type': 'application/json' },
469
- body: JSON.stringify({
470
- skillsMode: mode,
471
- defaultSkills: mode === 'selected' ? Array.from(picked) : [],
472
- }),
473
- })
474
- if (!res.ok) {
475
- const e2 = (await res.json().catch(() => null)) as { error?: string } | null
476
- throw new Error(e2?.error ?? res.statusText)
477
- }
478
- // Refetch the loader so the parent's `loaded.defaultSkills` reflects
479
- // what we just saved. Without this, switching tabs unmounts SkillsCard
480
- // and remounts it with the stale `initialPicked` snapshot — the save
481
- // looks like it was lost even though the backend persisted it.
482
- await router.invalidate()
483
- setStatus({ kind: 'saved', message: 'saved' })
484
- } catch (err) {
485
- setStatus({ kind: 'error', message: `error: ${(err as Error).message}` })
486
- }
487
- }
488
-
489
- return (
490
- <FileCardShell title="skills" status={status}>
491
- <p className="muted my-2">
492
- Choose what agents spawned from this profile inherit. Existing agents aren't affected
493
- — they keep whatever skills they were spawned with.
494
- </p>
495
- <form onSubmit={save}>
496
- <label className="my-2 flex cursor-pointer items-start gap-2">
497
- <input
498
- type="radio"
499
- checked={mode === 'all'}
500
- onChange={() => setMode('all')}
501
- />
502
- <span>
503
- <strong>allow all skills</strong> — every skill on disk is attached at spawn.
504
- </span>
505
- </label>
506
- <label className="my-2 flex cursor-pointer items-start gap-2">
507
- <input
508
- type="radio"
509
- checked={mode === 'selected'}
510
- onChange={() => setMode('selected')}
511
- />
512
- <span>
513
- <strong>select skills</strong> — attach only the ones ticked below.
514
- </span>
515
- </label>
516
- <div
517
- className={`mt-3 max-h-[22rem] overflow-y-auto rounded-md border border-frost p-3 ${mode === 'all' ? 'pointer-events-none opacity-50' : ''}`}
518
- >
519
- {skills.length === 0 ? (
520
- <p className="muted">
521
- No skills installed yet. Import some on <a href="/skills">/skills</a>.
522
- </p>
523
- ) : (
524
- skills.map((s) => (
525
- <label key={s.name} className="my-1.5 flex cursor-pointer items-start gap-2">
526
- <input
527
- type="checkbox"
528
- checked={picked.has(s.name)}
529
- onChange={() => toggle(s.name)}
530
- />
531
- <span>
532
- <code>{s.name}</code>
533
- {s.description && (
534
- <span className="text-[0.9em] text-mocha-light"> — {s.description}</span>
535
- )}
536
- </span>
537
- </label>
538
- ))
539
- )}
540
- </div>
541
- <div className="mt-3 flex gap-2">
542
- <button type="submit" disabled={status.kind === 'saving'}>
543
- save skills
544
- </button>
545
- </div>
546
- </form>
547
- </FileCardShell>
548
- )
549
- }