bazilion 0.0.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 (260) hide show
  1. package/.understand-anything/.understandignore +25 -0
  2. package/.understand-anything/fingerprints.json +14267 -0
  3. package/.understand-anything/knowledge-graph.json +18128 -0
  4. package/.understand-anything/meta.json +6 -0
  5. package/CLAUDE.md +164 -0
  6. package/LICENSE +21 -0
  7. package/README.md +195 -0
  8. package/apps/cli/package.json +21 -0
  9. package/apps/cli/src/auth-file.ts +18 -0
  10. package/apps/cli/src/client.ts +37 -0
  11. package/apps/cli/src/columnize.ts +32 -0
  12. package/apps/cli/src/commands/agent.ts +574 -0
  13. package/apps/cli/src/commands/auth.ts +110 -0
  14. package/apps/cli/src/commands/backup.ts +135 -0
  15. package/apps/cli/src/commands/completion.ts +155 -0
  16. package/apps/cli/src/commands/config.ts +95 -0
  17. package/apps/cli/src/commands/doctor.ts +131 -0
  18. package/apps/cli/src/commands/group.ts +132 -0
  19. package/apps/cli/src/commands/inbox.ts +82 -0
  20. package/apps/cli/src/commands/login.ts +73 -0
  21. package/apps/cli/src/commands/memory.ts +106 -0
  22. package/apps/cli/src/commands/profile.ts +259 -0
  23. package/apps/cli/src/commands/provider.ts +170 -0
  24. package/apps/cli/src/commands/send.ts +24 -0
  25. package/apps/cli/src/commands/serve.ts +89 -0
  26. package/apps/cli/src/commands/skill.ts +120 -0
  27. package/apps/cli/src/commands/token.ts +148 -0
  28. package/apps/cli/src/commands/trigger.ts +129 -0
  29. package/apps/cli/src/commands/uninstall.ts +156 -0
  30. package/apps/cli/src/index.ts +196 -0
  31. package/apps/cli/src/paths.ts +12 -0
  32. package/apps/cli/test/agent.test.ts +213 -0
  33. package/apps/cli/test/backup.test.ts +95 -0
  34. package/apps/cli/test/chat.test.ts +539 -0
  35. package/apps/cli/test/columnize.test.ts +29 -0
  36. package/apps/cli/test/completion.test.ts +45 -0
  37. package/apps/cli/test/config-page.test.ts +151 -0
  38. package/apps/cli/test/group.test.ts +74 -0
  39. package/apps/cli/test/helpers.ts +70 -0
  40. package/apps/cli/test/inbox-autodeliver.test.ts +143 -0
  41. package/apps/cli/test/inbox.test.ts +147 -0
  42. package/apps/cli/test/memory.test.ts +69 -0
  43. package/apps/cli/test/profile.test.ts +110 -0
  44. package/apps/cli/test/send.test.ts +30 -0
  45. package/apps/cli/test/server-fixture.ts +212 -0
  46. package/apps/cli/test/session-head.test.ts +45 -0
  47. package/apps/cli/test/skill.test.ts +128 -0
  48. package/apps/cli/test/token.test.ts +109 -0
  49. package/apps/cli/test/trigger.test.ts +245 -0
  50. package/apps/cli/tsconfig.json +4 -0
  51. package/apps/daemon/package.json +31 -0
  52. package/apps/daemon/src/app.ts +41 -0
  53. package/apps/daemon/src/core/agent/archive.ts +8 -0
  54. package/apps/daemon/src/core/agent/delete.ts +30 -0
  55. package/apps/daemon/src/core/agent/resolve.ts +31 -0
  56. package/apps/daemon/src/core/agent/spawn.ts +95 -0
  57. package/apps/daemon/src/core/agent/unarchive.ts +11 -0
  58. package/apps/daemon/src/core/availableModels.ts +58 -0
  59. package/apps/daemon/src/core/db/client.ts +113 -0
  60. package/apps/daemon/src/core/db/migrate.ts +41 -0
  61. package/apps/daemon/src/core/db/migrations/0001_init.sql +179 -0
  62. package/apps/daemon/src/core/group/delete.ts +21 -0
  63. package/apps/daemon/src/core/group/register.ts +68 -0
  64. package/apps/daemon/src/core/index.ts +71 -0
  65. package/apps/daemon/src/core/paths.ts +56 -0
  66. package/apps/daemon/src/core/profile/create.ts +78 -0
  67. package/apps/daemon/src/core/profile/delete.ts +26 -0
  68. package/apps/daemon/src/core/profile/identity.ts +70 -0
  69. package/apps/daemon/src/core/profile/load.ts +45 -0
  70. package/apps/daemon/src/core/profile/seed.ts +74 -0
  71. package/apps/daemon/src/core/profile/templates.ts +60 -0
  72. package/apps/daemon/src/core/profile/update.ts +57 -0
  73. package/apps/daemon/src/core/profile/validate.ts +9 -0
  74. package/apps/daemon/src/core/repos/agents.ts +202 -0
  75. package/apps/daemon/src/core/repos/config.ts +78 -0
  76. package/apps/daemon/src/core/repos/groups.ts +55 -0
  77. package/apps/daemon/src/core/repos/messages.ts +127 -0
  78. package/apps/daemon/src/core/repos/profiles.ts +83 -0
  79. package/apps/daemon/src/core/repos/providerModels.ts +58 -0
  80. package/apps/daemon/src/core/repos/providerState.ts +37 -0
  81. package/apps/daemon/src/core/repos/secrets.ts +145 -0
  82. package/apps/daemon/src/core/repos/skillMeta.ts +49 -0
  83. package/apps/daemon/src/core/repos/triggers.ts +101 -0
  84. package/apps/daemon/src/core/repos/webTokens.ts +87 -0
  85. package/apps/daemon/src/core/secrets.ts +65 -0
  86. package/apps/daemon/src/core/services.ts +264 -0
  87. package/apps/daemon/src/core/skills/discover.ts +28 -0
  88. package/apps/daemon/src/core/skills/import.ts +136 -0
  89. package/apps/daemon/src/core/skills/parse.ts +52 -0
  90. package/apps/daemon/src/core/skills/resolve.ts +50 -0
  91. package/apps/daemon/src/index.ts +45 -0
  92. package/apps/daemon/src/lib/agent-cancel.ts +48 -0
  93. package/apps/daemon/src/lib/agent-id.ts +13 -0
  94. package/apps/daemon/src/lib/agent-turn.ts +56 -0
  95. package/apps/daemon/src/lib/api-key.ts +56 -0
  96. package/apps/daemon/src/lib/auth.ts +41 -0
  97. package/apps/daemon/src/lib/cron.ts +93 -0
  98. package/apps/daemon/src/lib/ctx.ts +80 -0
  99. package/apps/daemon/src/lib/messaging-host.ts +34 -0
  100. package/apps/daemon/src/lib/middleware-auth.ts +52 -0
  101. package/apps/daemon/src/lib/scheduler.ts +294 -0
  102. package/apps/daemon/src/routes/agents.ts +772 -0
  103. package/apps/daemon/src/routes/auth-login.ts +193 -0
  104. package/apps/daemon/src/routes/config.ts +267 -0
  105. package/apps/daemon/src/routes/groups.ts +133 -0
  106. package/apps/daemon/src/routes/messages.ts +29 -0
  107. package/apps/daemon/src/routes/misc.ts +239 -0
  108. package/apps/daemon/src/routes/profiles.ts +197 -0
  109. package/apps/daemon/src/routes/skills.ts +123 -0
  110. package/apps/daemon/src/routes/triggers.ts +29 -0
  111. package/apps/daemon/src/runtime/auth/openai-codex.ts +121 -0
  112. package/apps/daemon/src/runtime/auto-reply/heartbeat.ts +31 -0
  113. package/apps/daemon/src/runtime/index.ts +77 -0
  114. package/apps/daemon/src/runtime/memory/files.ts +103 -0
  115. package/apps/daemon/src/runtime/memory/qmd.ts +152 -0
  116. package/apps/daemon/src/runtime/memory/types.ts +16 -0
  117. package/apps/daemon/src/runtime/pi/events.ts +173 -0
  118. package/apps/daemon/src/runtime/pi/session.ts +536 -0
  119. package/apps/daemon/src/runtime/pi/tools.ts +85 -0
  120. package/apps/daemon/src/runtime/providers/catalog.ts +145 -0
  121. package/apps/daemon/src/runtime/providers/pi-adapter.ts +272 -0
  122. package/apps/daemon/src/runtime/providers/registry.ts +374 -0
  123. package/apps/daemon/src/runtime/providers/retry.ts +176 -0
  124. package/apps/daemon/src/runtime/providers/types.ts +33 -0
  125. package/apps/daemon/src/runtime/session/prompt.ts +83 -0
  126. package/apps/daemon/src/runtime/tools/bootstrap.ts +22 -0
  127. package/apps/daemon/src/runtime/tools/home.ts +114 -0
  128. package/apps/daemon/src/runtime/tools/memory.ts +81 -0
  129. package/apps/daemon/src/runtime/tools/messaging.ts +127 -0
  130. package/apps/daemon/src/runtime/tools/registry.ts +29 -0
  131. package/apps/daemon/src/runtime/tools/types.ts +13 -0
  132. package/apps/daemon/src/runtime/tools/web-extract.ts +110 -0
  133. package/apps/daemon/src/runtime/tools/web-ssrf.ts +245 -0
  134. package/apps/daemon/src/runtime/tools/web.ts +273 -0
  135. package/apps/daemon/src/runtime/worker/entry.ts +221 -0
  136. package/apps/daemon/src/runtime/worker/ipc-protocol.ts +75 -0
  137. package/apps/daemon/src/runtime/worker/spawn.ts +249 -0
  138. package/apps/daemon/test/core/agents.test.ts +319 -0
  139. package/apps/daemon/test/core/available-models.test.ts +63 -0
  140. package/apps/daemon/test/core/config.test.ts +99 -0
  141. package/apps/daemon/test/core/groups.test.ts +63 -0
  142. package/apps/daemon/test/core/helpers.ts +50 -0
  143. package/apps/daemon/test/core/identity.test.ts +85 -0
  144. package/apps/daemon/test/core/migrations.test.ts +83 -0
  145. package/apps/daemon/test/core/profiles.test.ts +182 -0
  146. package/apps/daemon/test/core/provider-models.test.ts +57 -0
  147. package/apps/daemon/test/core/provider-state.test.ts +36 -0
  148. package/apps/daemon/test/core/skill-meta.test.ts +45 -0
  149. package/apps/daemon/test/core/skills.test.ts +271 -0
  150. package/apps/daemon/test/core/triggers.test.ts +182 -0
  151. package/apps/daemon/test/core/web-tokens.test.ts +79 -0
  152. package/apps/daemon/test/cron.test.ts +90 -0
  153. package/apps/daemon/test/runtime/heartbeat.test.ts +34 -0
  154. package/apps/daemon/test/runtime/memory-qmd.test.ts +97 -0
  155. package/apps/daemon/test/runtime/memory.test.ts +78 -0
  156. package/apps/daemon/test/runtime/messaging.test.ts +213 -0
  157. package/apps/daemon/test/runtime/mock-server.ts +65 -0
  158. package/apps/daemon/test/runtime/openai-codex-auth.test.ts +116 -0
  159. package/apps/daemon/test/runtime/providers.test.ts +306 -0
  160. package/apps/daemon/test/runtime/retry.test.ts +191 -0
  161. package/apps/daemon/test/runtime/session-head.test.ts +90 -0
  162. package/apps/daemon/test/runtime/tools-home.test.ts +102 -0
  163. package/apps/daemon/test/runtime/tools-web.test.ts +206 -0
  164. package/apps/daemon/tsconfig.json +4 -0
  165. package/apps/mobile/README.md +60 -0
  166. package/apps/mobile/app/_layout.tsx +58 -0
  167. package/apps/mobile/app/agents/[id]/chat.tsx +486 -0
  168. package/apps/mobile/app/agents/[id]/index.tsx +166 -0
  169. package/apps/mobile/app/agents/index.tsx +212 -0
  170. package/apps/mobile/app/index.tsx +21 -0
  171. package/apps/mobile/app/pair.tsx +226 -0
  172. package/apps/mobile/app/settings.tsx +419 -0
  173. package/apps/mobile/app.json +49 -0
  174. package/apps/mobile/assets/adaptive-icon.png +0 -0
  175. package/apps/mobile/assets/favicon.png +0 -0
  176. package/apps/mobile/assets/icon.png +0 -0
  177. package/apps/mobile/assets/splash-icon.png +0 -0
  178. package/apps/mobile/babel.config.js +6 -0
  179. package/apps/mobile/metro.config.js +28 -0
  180. package/apps/mobile/package.json +44 -0
  181. package/apps/mobile/src/auth.ts +66 -0
  182. package/apps/mobile/src/pair-url.ts +48 -0
  183. package/apps/mobile/src/theme-context.tsx +88 -0
  184. package/apps/mobile/src/theme.ts +135 -0
  185. package/apps/mobile/test/pair-url.test.ts +46 -0
  186. package/apps/mobile/tsconfig.json +23 -0
  187. package/apps/web/components.json +25 -0
  188. package/apps/web/package.json +39 -0
  189. package/apps/web/public/baziu.svg +8 -0
  190. package/apps/web/src/components/AgentTabs.tsx +45 -0
  191. package/apps/web/src/components/BaziuLogo.tsx +21 -0
  192. package/apps/web/src/components/ChatPane.tsx +1033 -0
  193. package/apps/web/src/components/ConfigTabs.tsx +29 -0
  194. package/apps/web/src/components/CopyButton.tsx +68 -0
  195. package/apps/web/src/components/CreateGroupDialog.tsx +127 -0
  196. package/apps/web/src/components/FieldRow.tsx +94 -0
  197. package/apps/web/src/components/Footer.tsx +10 -0
  198. package/apps/web/src/components/PawIcon.tsx +15 -0
  199. package/apps/web/src/components/Sidebar.tsx +287 -0
  200. package/apps/web/src/components/SpawnDialog.tsx +129 -0
  201. package/apps/web/src/components/ThemeToggle.tsx +75 -0
  202. package/apps/web/src/components/TopNav.tsx +34 -0
  203. package/apps/web/src/components/ui/button.tsx +67 -0
  204. package/apps/web/src/components/ui/card.tsx +103 -0
  205. package/apps/web/src/components/ui/checkbox.tsx +31 -0
  206. package/apps/web/src/components/ui/dialog.tsx +168 -0
  207. package/apps/web/src/components/ui/input.tsx +19 -0
  208. package/apps/web/src/components/ui/label.tsx +22 -0
  209. package/apps/web/src/components/ui/radio-group.tsx +44 -0
  210. package/apps/web/src/components/ui/select.tsx +192 -0
  211. package/apps/web/src/components/ui/separator.tsx +26 -0
  212. package/apps/web/src/components/ui/table.tsx +116 -0
  213. package/apps/web/src/components/ui/tabs.tsx +88 -0
  214. package/apps/web/src/components/ui/textarea.tsx +18 -0
  215. package/apps/web/src/lib/auth.ts +50 -0
  216. package/apps/web/src/lib/daemon-client.ts +34 -0
  217. package/apps/web/src/lib/md.ts +45 -0
  218. package/apps/web/src/lib/utils.ts +6 -0
  219. package/apps/web/src/lib/wire-constants.ts +27 -0
  220. package/apps/web/src/routeTree.gen.ts +408 -0
  221. package/apps/web/src/router.tsx +20 -0
  222. package/apps/web/src/routes/__root.tsx +123 -0
  223. package/apps/web/src/routes/agents/$id/inbox.tsx +207 -0
  224. package/apps/web/src/routes/agents/$id/index.tsx +527 -0
  225. package/apps/web/src/routes/agents/$id/triggers.tsx +239 -0
  226. package/apps/web/src/routes/agents/index.tsx +265 -0
  227. package/apps/web/src/routes/api/$.ts +88 -0
  228. package/apps/web/src/routes/config/index.tsx +315 -0
  229. package/apps/web/src/routes/config/services.tsx +49 -0
  230. package/apps/web/src/routes/config/tokens.tsx +192 -0
  231. package/apps/web/src/routes/groups/$id/index.tsx +153 -0
  232. package/apps/web/src/routes/groups/$id/memory.tsx +321 -0
  233. package/apps/web/src/routes/groups/index.tsx +191 -0
  234. package/apps/web/src/routes/index.tsx +133 -0
  235. package/apps/web/src/routes/login.tsx +63 -0
  236. package/apps/web/src/routes/profiles/$id.tsx +549 -0
  237. package/apps/web/src/routes/profiles/index.tsx +458 -0
  238. package/apps/web/src/routes/skills/index.tsx +297 -0
  239. package/apps/web/src/routes/welcome.tsx +61 -0
  240. package/apps/web/src/styles.css +449 -0
  241. package/apps/web/tsconfig.json +25 -0
  242. package/apps/web/vite.config.ts +25 -0
  243. package/biome.json +23 -0
  244. package/docs/agent-engine.md +219 -0
  245. package/docs/architecture.md +627 -0
  246. package/docs/backlog/README.md +42 -0
  247. package/docs/backlog/draft/BAZ-001-a2a-federation-spike.md +125 -0
  248. package/docs/openclaw-reference.md +210 -0
  249. package/package.json +38 -0
  250. package/packages/api-types/package.json +11 -0
  251. package/packages/api-types/src/entities.ts +146 -0
  252. package/packages/api-types/src/events.ts +55 -0
  253. package/packages/api-types/src/index.ts +488 -0
  254. package/packages/api-types/src/memory.ts +15 -0
  255. package/packages/client/package.json +13 -0
  256. package/packages/client/src/index.ts +117 -0
  257. package/pnpm-workspace.yaml +3 -0
  258. package/tsconfig.base.json +23 -0
  259. package/tsconfig.json +11 -0
  260. package/vitest.config.ts +22 -0
@@ -0,0 +1,574 @@
1
+ import { stdin, stdout } from 'node:process'
2
+ import { createInterface } from 'node:readline/promises'
3
+ import type {
4
+ Agent,
5
+ AttachSkillRequest,
6
+ ChatCompactRequest,
7
+ ChatCompactResponse,
8
+ ChatContextResponse,
9
+ ChatFrame,
10
+ MoveAgentRequest,
11
+ ResolvedAgent,
12
+ SessionEvent,
13
+ SessionHeadResponse,
14
+ SpawnAgentRequest,
15
+ TruncateChatRequest,
16
+ TruncateChatResponse,
17
+ } from '@bazilion/api-types'
18
+ import { defineCommand } from 'citty'
19
+ import { createClient } from '../client.ts'
20
+ import { columnize } from '../columnize.ts'
21
+
22
+ const spawnCmd = defineCommand({
23
+ meta: { name: 'spawn', description: 'Spawn an agent from a profile into a group' },
24
+ args: {
25
+ profile: { type: 'string', required: true, description: 'Profile id' },
26
+ name: { type: 'string', description: 'Agent name (defaults to profile name)' },
27
+ group: {
28
+ type: 'string',
29
+ description: "Group to join (defaults to 'default')",
30
+ },
31
+ model: { type: 'string', description: 'Override profile default model' },
32
+ reasoning: {
33
+ type: 'string',
34
+ description: 'Reasoning level: off|minimal|low|medium|high|xhigh (default: medium)',
35
+ },
36
+ },
37
+ async run({ args }) {
38
+ const client = createClient()
39
+ const body: SpawnAgentRequest = {
40
+ profileId: args.profile,
41
+ name: args.name,
42
+ model: args.model,
43
+ reasoningLevel: args.reasoning as SpawnAgentRequest['reasoningLevel'],
44
+ groupId: args.group,
45
+ }
46
+ const agent = await client.post<Agent>('/api/agents', body)
47
+ console.log(`spawned agent ${agent.id} (${agent.name})`)
48
+ console.log(`dir: ${agent.dir}`)
49
+ },
50
+ })
51
+
52
+ const editCmd = defineCommand({
53
+ meta: {
54
+ name: 'edit',
55
+ description: 'Edit agent settings (name, model override, reasoning level)',
56
+ },
57
+ args: {
58
+ id: { type: 'positional', required: true, description: 'Agent id or prefix' },
59
+ name: { type: 'string', description: 'Rename the agent' },
60
+ model: { type: 'string', description: 'Set model override (use --model "" to clear)' },
61
+ reasoning: {
62
+ type: 'string',
63
+ description: 'Reasoning level: off|minimal|low|medium|high|xhigh',
64
+ },
65
+ },
66
+ async run({ args }) {
67
+ if (args.name === undefined && args.model === undefined && args.reasoning === undefined) {
68
+ console.error('agent edit: specify at least one of --name, --model, or --reasoning')
69
+ process.exit(2)
70
+ }
71
+ const client = createClient()
72
+ const body: Record<string, unknown> = {}
73
+ if (args.name !== undefined) body.name = args.name
74
+ if (args.model !== undefined) {
75
+ body.modelOverride = args.model === '' ? null : args.model
76
+ }
77
+ if (args.reasoning !== undefined) body.reasoningLevel = args.reasoning
78
+ const agent = await client.patch<Agent>(`/api/agents/${args.id}`, body)
79
+ console.log(`updated agent ${agent.id} (${agent.name})`)
80
+ },
81
+ })
82
+
83
+ const listCmd = defineCommand({
84
+ meta: { name: 'list', description: 'List agents' },
85
+ args: {
86
+ all: { type: 'boolean', description: 'Include archived agents' },
87
+ long: { type: 'boolean', alias: 'l', description: 'Show profile + full UUID columns' },
88
+ },
89
+ async run({ args }) {
90
+ const client = createClient()
91
+ const q = args.all ? '?includeArchived=true' : ''
92
+ const list = await client.get<Agent[]>(`/api/agents${q}`)
93
+ if (list.length === 0) {
94
+ console.log('(no agents)')
95
+ return
96
+ }
97
+ const rows = list.map((a) =>
98
+ args.long
99
+ ? [a.id, a.status, a.name, a.profileId]
100
+ : // Short UUID prefix — all CLI commands accept 4+ char prefixes now.
101
+ [a.id.slice(0, 8), a.status, a.name],
102
+ )
103
+ for (const line of columnize(rows)) console.log(line)
104
+ },
105
+ })
106
+
107
+ const showCmd = defineCommand({
108
+ meta: { name: 'show', description: 'Show agent details' },
109
+ args: {
110
+ id: { type: 'positional', required: true },
111
+ },
112
+ async run({ args }) {
113
+ const client = createClient()
114
+ const r = await client.get<ResolvedAgent>(`/api/agents/${args.id}`)
115
+ console.log(`# ${r.agent.id}`)
116
+ console.log(`name: ${r.agent.name}`)
117
+ console.log(`status: ${r.agent.status}`)
118
+ console.log(`profile: ${r.profile.id}`)
119
+ console.log(`model: ${r.model}`)
120
+ console.log(`dir: ${r.agent.dir}`)
121
+ console.log(`group: ${r.group.id} ${r.group.path}`)
122
+ console.log('skills:')
123
+ if (r.skills.length === 0) {
124
+ console.log(' (none)')
125
+ } else {
126
+ for (const s of r.skills) console.log(` ${s}`)
127
+ }
128
+ },
129
+ })
130
+
131
+ const archiveCmd = defineCommand({
132
+ meta: { name: 'archive', description: 'Archive an agent' },
133
+ args: {
134
+ id: { type: 'positional', required: true },
135
+ },
136
+ async run({ args }) {
137
+ const client = createClient()
138
+ await client.post(`/api/agents/${args.id}/archive`)
139
+ console.log(`archived agent ${args.id}`)
140
+ },
141
+ })
142
+
143
+ const unarchiveCmd = defineCommand({
144
+ meta: { name: 'unarchive', description: 'Restore an archived agent to idle' },
145
+ args: {
146
+ id: { type: 'positional', required: true },
147
+ },
148
+ async run({ args }) {
149
+ const client = createClient()
150
+ await client.post(`/api/agents/${args.id}/unarchive`)
151
+ console.log(`unarchived agent ${args.id}`)
152
+ },
153
+ })
154
+
155
+ const deleteCmd = defineCommand({
156
+ meta: { name: 'delete', description: 'Permanently delete an agent and its data' },
157
+ args: {
158
+ id: { type: 'positional', required: true },
159
+ },
160
+ async run({ args }) {
161
+ const client = createClient()
162
+ await client.del(`/api/agents/${args.id}`)
163
+ console.log(`deleted agent ${args.id}`)
164
+ },
165
+ })
166
+
167
+ // --- chat ---
168
+
169
+ interface PrintState {
170
+ inDeltaStream: boolean
171
+ }
172
+
173
+ function printEvent(e: SessionEvent, state: PrintState): void {
174
+ switch (e.type) {
175
+ case 'assistant_delta':
176
+ process.stdout.write(e.delta)
177
+ state.inDeltaStream = true
178
+ break
179
+ case 'assistant_message':
180
+ if (state.inDeltaStream) {
181
+ // Deltas already rendered the text; just close the line.
182
+ process.stdout.write('\n')
183
+ state.inDeltaStream = false
184
+ } else {
185
+ console.log(e.text)
186
+ }
187
+ break
188
+ case 'tool_call':
189
+ if (state.inDeltaStream) {
190
+ process.stdout.write('\n')
191
+ state.inDeltaStream = false
192
+ }
193
+ console.log(` [tool: ${e.name} ${e.arguments}]`)
194
+ break
195
+ case 'tool_result':
196
+ console.log(` [result: ${e.result.split('\n')[0]?.slice(0, 100)}]`)
197
+ break
198
+ case 'tool_error':
199
+ console.log(` [tool error: ${e.name} — ${e.error}]`)
200
+ break
201
+ case 'error':
202
+ if (state.inDeltaStream) {
203
+ process.stdout.write('\n')
204
+ state.inDeltaStream = false
205
+ }
206
+ console.log(` [error: ${e.error}]`)
207
+ break
208
+ }
209
+ }
210
+
211
+ async function streamTurn(
212
+ client: ReturnType<typeof createClient>,
213
+ agentId: string,
214
+ message: string,
215
+ ): Promise<void> {
216
+ const state: PrintState = { inDeltaStream: false }
217
+ for await (const frame of client.stream<ChatFrame>('POST', `/api/agents/${agentId}/chat`, {
218
+ message,
219
+ })) {
220
+ if (frame.kind === 'event') {
221
+ if (frame.event.type !== 'user_message') printEvent(frame.event, state)
222
+ } else if (frame.kind === 'fatal') {
223
+ if (state.inDeltaStream) process.stdout.write('\n')
224
+ throw new Error(frame.error)
225
+ }
226
+ }
227
+ }
228
+
229
+ const chatCmd = defineCommand({
230
+ meta: { name: 'chat', description: 'Chat with an agent' },
231
+ args: {
232
+ id: { type: 'positional', required: true },
233
+ message: {
234
+ type: 'string',
235
+ description: 'Send a single message and exit (one-shot mode)',
236
+ },
237
+ },
238
+ async run({ args }) {
239
+ const client = createClient()
240
+ const resolved = await client.get<ResolvedAgent>(`/api/agents/${args.id}`)
241
+
242
+ if (args.message) {
243
+ await streamTurn(client, resolved.agent.id, args.message)
244
+ return
245
+ }
246
+
247
+ console.log(`chatting with ${resolved.agent.name} (${resolved.model})`)
248
+ console.log('(type /exit to quit)')
249
+
250
+ const rl = createInterface({ input: stdin, output: stdout })
251
+ rl.setPrompt('> ')
252
+ rl.prompt()
253
+
254
+ for await (const line of rl) {
255
+ const trimmed = line.trim()
256
+ if (trimmed === '/exit' || trimmed === '/quit') break
257
+ if (!trimmed) {
258
+ rl.prompt()
259
+ continue
260
+ }
261
+ try {
262
+ await streamTurn(client, resolved.agent.id, trimmed)
263
+ } catch (err) {
264
+ console.error(`error: ${(err as Error).message}`)
265
+ }
266
+ rl.prompt()
267
+ }
268
+ rl.close()
269
+ },
270
+ })
271
+
272
+ const chatResetCmd = defineCommand({
273
+ meta: { name: 'chat-reset', description: "Reset an agent's chat history to empty" },
274
+ args: {
275
+ id: { type: 'positional', required: true },
276
+ force: { type: 'boolean', description: 'Skip the y/N prompt' },
277
+ },
278
+ async run({ args }) {
279
+ if (!args.force) {
280
+ process.stdout.write(`reset all chat history for ${args.id}? [y/N] `)
281
+ const answer = await new Promise<string>((resolve) => {
282
+ process.stdin.once('data', (d) => resolve(String(d).trim().toLowerCase()))
283
+ })
284
+ if (answer !== 'y' && answer !== 'yes') {
285
+ console.log('aborted')
286
+ return
287
+ }
288
+ }
289
+ const client = createClient()
290
+ await client.post(`/api/agents/${args.id}/chat/reset`)
291
+ console.log(`reset chat history for ${args.id}`)
292
+ },
293
+ })
294
+
295
+ const chatTrimCmd = defineCommand({
296
+ meta: {
297
+ name: 'chat-trim',
298
+ description: "Keep the first N messages of an agent's chat history; drop the rest",
299
+ },
300
+ args: {
301
+ id: { type: 'positional', required: true },
302
+ keep: { type: 'string', required: true, description: 'Number of leading messages to keep' },
303
+ },
304
+ async run({ args }) {
305
+ const n = Number(args.keep)
306
+ if (!Number.isFinite(n) || n < 0 || !Number.isInteger(n)) {
307
+ throw new Error('--keep must be a non-negative integer')
308
+ }
309
+ const client = createClient()
310
+ const body: TruncateChatRequest = { keepCount: n }
311
+ const res = await client.post<TruncateChatResponse>(
312
+ `/api/agents/${args.id}/chat/truncate`,
313
+ body,
314
+ )
315
+ console.log(
316
+ `trimmed: ${res.before} → ${res.after} messages (${res.before - res.after} dropped)`,
317
+ )
318
+ },
319
+ })
320
+
321
+ function formatBytes(n: number): string {
322
+ if (n < 1024) return `${n} B`
323
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KiB`
324
+ return `${(n / (1024 * 1024)).toFixed(2)} MiB`
325
+ }
326
+
327
+ const chatContextCmd = defineCommand({
328
+ meta: {
329
+ name: 'chat-context',
330
+ description: "Break down what consumes the agent's context window",
331
+ },
332
+ args: {
333
+ id: { type: 'positional', required: true },
334
+ detail: {
335
+ type: 'boolean',
336
+ description: 'Include every tool/skill entry instead of the top 30',
337
+ },
338
+ json: { type: 'boolean', description: 'Emit raw JSON instead of a human report' },
339
+ },
340
+ async run({ args }) {
341
+ const client = createClient()
342
+ const query = args.detail || args.json ? '?detail=1' : ''
343
+ const ctx = await client.get<ChatContextResponse>(`/api/agents/${args.id}/chat/context${query}`)
344
+ if (args.json) {
345
+ console.log(JSON.stringify(ctx, null, 2))
346
+ return
347
+ }
348
+
349
+ const fmt = (chars: number, tokens: number): string =>
350
+ `${chars.toLocaleString()} chars (~${tokens.toLocaleString()} tok)`
351
+
352
+ console.log(`🧠 context for ${ctx.agentId}`)
353
+ console.log(`model: ${ctx.model}`)
354
+ console.log('')
355
+ console.log('## system prompt')
356
+ console.log(` total: ${fmt(ctx.systemPrompt.chars, ctx.systemPrompt.tokens)}`)
357
+ if (ctx.systemPrompt.files.length > 0) {
358
+ console.log(' profile files:')
359
+ for (const f of ctx.systemPrompt.files) {
360
+ console.log(` - ${f.name}: ${fmt(f.chars, f.tokens)}`)
361
+ }
362
+ }
363
+ if (ctx.systemPrompt.skillsListChars > 0) {
364
+ console.log(` skills line: ${ctx.systemPrompt.skillsListChars.toLocaleString()} chars`)
365
+ }
366
+ if (ctx.systemPrompt.groupListChars > 0) {
367
+ console.log(` group block: ${ctx.systemPrompt.groupListChars.toLocaleString()} chars`)
368
+ }
369
+ if (ctx.systemPrompt.userMdChars > 0) {
370
+ console.log(` user_md block: ${ctx.systemPrompt.userMdChars.toLocaleString()} chars`)
371
+ }
372
+ console.log(` memory hint: ${ctx.systemPrompt.memoryHintChars.toLocaleString()} chars`)
373
+ console.log('')
374
+ console.log('## tools')
375
+ console.log(` count: ${ctx.tools.count}`)
376
+ console.log(
377
+ ` schemas (JSON): ${ctx.tools.schemaChars.toLocaleString()} chars (~${estimate(ctx.tools.schemaChars).toLocaleString()} tok)`,
378
+ )
379
+ console.log(` list text: ${ctx.tools.listChars.toLocaleString()} chars`)
380
+ if (ctx.tools.entries.length > 0) {
381
+ console.log(' top tools by schema size:')
382
+ for (const t of ctx.tools.entries) {
383
+ const params = t.paramCount != null ? ` (${t.paramCount} params)` : ''
384
+ console.log(` - ${t.name}: ${t.schemaChars.toLocaleString()} chars${params}`)
385
+ }
386
+ }
387
+ console.log('')
388
+ if (ctx.skills.count > 0) {
389
+ console.log('## skills')
390
+ console.log(` count: ${ctx.skills.count}`)
391
+ for (const s of ctx.skills.entries) {
392
+ console.log(` - ${s.name}: ${s.blockChars.toLocaleString()} chars`)
393
+ }
394
+ console.log('')
395
+ }
396
+ console.log('## group')
397
+ console.log(` - ${ctx.group.id} (${ctx.group.name}): ${ctx.group.path}`)
398
+ if (ctx.group.userMdChars > 0) {
399
+ console.log(` user_md: ${ctx.group.userMdChars.toLocaleString()} chars`)
400
+ }
401
+ console.log('')
402
+ console.log('## history')
403
+ console.log(` messages: ${ctx.history.messageEntries}`)
404
+ console.log(` compactions: ${ctx.history.compactionEntries}`)
405
+ console.log(` total: ${fmt(ctx.history.chars, ctx.history.tokensEstimate)}`)
406
+ console.log(` bytes: ${formatBytes(ctx.history.bytes)}`)
407
+ console.log('')
408
+ console.log(`TOTAL: ${fmt(ctx.totals.chars, ctx.totals.tokens)}`)
409
+ },
410
+ })
411
+
412
+ function estimate(chars: number): number {
413
+ return Math.ceil(Math.max(0, chars) / 4)
414
+ }
415
+
416
+ const chatCompactCmd = defineCommand({
417
+ meta: {
418
+ name: 'chat-compact',
419
+ description: "Summarize the head of an agent's chat log, preserving the tail verbatim",
420
+ },
421
+ args: {
422
+ id: { type: 'positional', required: true },
423
+ 'keep-tail': {
424
+ type: 'string',
425
+ description: 'Message entries to keep verbatim after the summary (default 10)',
426
+ },
427
+ instructions: {
428
+ type: 'string',
429
+ description: 'Optional extra guidance prepended to the summarizer prompt',
430
+ },
431
+ force: { type: 'boolean', description: 'Skip the y/N prompt' },
432
+ },
433
+ async run({ args }) {
434
+ if (!args.force) {
435
+ process.stdout.write(
436
+ `compact chat history for ${args.id}? head turns become a summary; tail is preserved. [y/N] `,
437
+ )
438
+ const answer = await new Promise<string>((resolve) => {
439
+ process.stdin.once('data', (d) => resolve(String(d).trim().toLowerCase()))
440
+ })
441
+ if (answer !== 'y' && answer !== 'yes') {
442
+ console.log('aborted')
443
+ return
444
+ }
445
+ }
446
+ const keepTailNum = args['keep-tail'] ? Number(args['keep-tail']) : undefined
447
+ if (keepTailNum !== undefined && (!Number.isFinite(keepTailNum) || keepTailNum < 0)) {
448
+ console.error('--keep-tail must be a non-negative integer')
449
+ process.exitCode = 1
450
+ return
451
+ }
452
+ const body: ChatCompactRequest = {}
453
+ if (keepTailNum !== undefined) body.keepTail = Math.floor(keepTailNum)
454
+ if (args.instructions) body.customInstructions = args.instructions
455
+ const client = createClient()
456
+ const res = await client.post<ChatCompactResponse>(`/api/agents/${args.id}/chat/compact`, body)
457
+ console.log(
458
+ `compacted: ${res.before} → ${res.after} entries (${res.summarized} summarized, ${res.keptTail} tail kept verbatim)`,
459
+ )
460
+ console.log(
461
+ `tokens: ~${res.tokensBefore.toLocaleString()} → ~${res.tokensAfter.toLocaleString()}`,
462
+ )
463
+ console.log('')
464
+ console.log('summary:')
465
+ console.log(res.summary)
466
+ },
467
+ })
468
+
469
+ // --- skill subcommand ---
470
+
471
+ const skillAddCmd = defineCommand({
472
+ meta: { name: 'add', description: 'Attach a skill to an agent' },
473
+ args: {
474
+ agent: { type: 'positional', required: true },
475
+ skill: { type: 'positional', required: true },
476
+ },
477
+ async run({ args }) {
478
+ const client = createClient()
479
+ const body: AttachSkillRequest = { skill: args.skill }
480
+ await client.post(`/api/agents/${args.agent}/skills`, body)
481
+ console.log(`attached skill ${args.skill} to ${args.agent}`)
482
+ },
483
+ })
484
+
485
+ const skillRmCmd = defineCommand({
486
+ meta: { name: 'rm', description: 'Detach a skill from an agent' },
487
+ args: {
488
+ agent: { type: 'positional', required: true },
489
+ skill: { type: 'positional', required: true },
490
+ },
491
+ async run({ args }) {
492
+ const client = createClient()
493
+ await client.del(`/api/agents/${args.agent}/skills/${encodeURIComponent(args.skill)}`)
494
+ console.log(`detached skill ${args.skill} from ${args.agent}`)
495
+ },
496
+ })
497
+
498
+ const skillCmd = defineCommand({
499
+ meta: { name: 'skill', description: 'Attach/detach skills on an agent' },
500
+ subCommands: {
501
+ add: skillAddCmd,
502
+ rm: skillRmCmd,
503
+ },
504
+ })
505
+
506
+ // --- group membership ---
507
+
508
+ const moveCmd = defineCommand({
509
+ meta: { name: 'move', description: 'Move an agent to a different group' },
510
+ args: {
511
+ agent: { type: 'positional', required: true },
512
+ group: { type: 'positional', required: true },
513
+ },
514
+ async run({ args }) {
515
+ const client = createClient()
516
+ const body: MoveAgentRequest = { groupId: args.group }
517
+ await client.patch(`/api/agents/${args.agent}/group`, body)
518
+ console.log(`moved ${args.agent} to group ${args.group}`)
519
+ },
520
+ })
521
+
522
+ const sessionHeadCmd = defineCommand({
523
+ meta: {
524
+ name: 'session-head',
525
+ description: "Print the agent's current session file head (for stale-tab checks)",
526
+ },
527
+ args: {
528
+ id: { type: 'positional', required: true },
529
+ json: { type: 'boolean', description: 'Emit raw JSON instead of a human line' },
530
+ },
531
+ async run({ args }) {
532
+ const client = createClient()
533
+ const head = await client.get<SessionHeadResponse>(`/api/agents/${args.id}/sessions/head`)
534
+ if (args.json) {
535
+ console.log(JSON.stringify(head))
536
+ return
537
+ }
538
+ console.log(head.file ? `${head.file} (${head.size} bytes)` : '(no session yet)')
539
+ },
540
+ })
541
+
542
+ const cancelCmd = defineCommand({
543
+ meta: { name: 'cancel', description: "Abort the agent's currently-running turn" },
544
+ args: {
545
+ id: { type: 'positional', required: true },
546
+ },
547
+ async run({ args }) {
548
+ const client = createClient()
549
+ await client.post(`/api/agents/${args.id}/cancel`)
550
+ console.log(`cancelled active turn for ${args.id}`)
551
+ },
552
+ })
553
+
554
+ export const agentCommand = defineCommand({
555
+ meta: { name: 'agent', description: 'Manage agent instances' },
556
+ subCommands: {
557
+ spawn: spawnCmd,
558
+ list: listCmd,
559
+ show: showCmd,
560
+ edit: editCmd,
561
+ archive: archiveCmd,
562
+ unarchive: unarchiveCmd,
563
+ delete: deleteCmd,
564
+ chat: chatCmd,
565
+ 'chat-reset': chatResetCmd,
566
+ 'chat-trim': chatTrimCmd,
567
+ 'chat-context': chatContextCmd,
568
+ 'chat-compact': chatCompactCmd,
569
+ cancel: cancelCmd,
570
+ skill: skillCmd,
571
+ move: moveCmd,
572
+ 'session-head': sessionHeadCmd,
573
+ },
574
+ })
@@ -0,0 +1,110 @@
1
+ // Import pi-ai directly so the CLI bundle stays slim. `loginOpenAICodex` is
2
+ // exposed at pi-ai's `/oauth` subpath; types come from pi-ai's main export
3
+ // and `OpenAICodexStatus` (the wire shape) from api-types.
4
+ import type { OpenAICodexStatus } from '@bazilion/api-types'
5
+ import type { OAuthAuthInfo, OAuthPrompt } from '@mariozechner/pi-ai'
6
+ import { loginOpenAICodex } from '@mariozechner/pi-ai/oauth'
7
+ import { defineCommand } from 'citty'
8
+ import { createClient } from '../client.ts'
9
+
10
+ function formatExpiry(ms: number | null): string {
11
+ if (!ms) return '(unknown)'
12
+ const d = new Date(ms)
13
+ return d.toISOString().replace(/\.\d+Z$/, 'Z')
14
+ }
15
+
16
+ const openaiLoginCmd = defineCommand({
17
+ meta: {
18
+ name: 'login',
19
+ description: 'Connect your ChatGPT account via OAuth (browser sign-in)',
20
+ },
21
+ async run() {
22
+ const client = createClient()
23
+ // The loopback callback at localhost:1455 must be on the CLI user's
24
+ // machine, so we run pi-ai's flow client-side (not via a server POST).
25
+ // After it completes we ship the credentials to the daemon so they land
26
+ // in the daemon-owned `secrets` table (encrypted with the bootstrap token).
27
+ console.log('opening your browser for OpenAI sign-in...')
28
+ console.log("(if it doesn't open automatically, paste the URL shown below)")
29
+ const creds = await loginOpenAICodex({
30
+ onAuth: ({ url }: OAuthAuthInfo) => {
31
+ console.log('')
32
+ console.log(` → ${url}`)
33
+ console.log('')
34
+ },
35
+ onPrompt: async (prompt: OAuthPrompt): Promise<string> => {
36
+ process.stdout.write(`${prompt.message}: `)
37
+ return new Promise<string>((resolve) => {
38
+ process.stdin.once('data', (d) => resolve(String(d).trim()))
39
+ })
40
+ },
41
+ onProgress: (msg: string) => {
42
+ console.log(` ${msg}`)
43
+ },
44
+ })
45
+ const status = await client.put<OpenAICodexStatus>('/api/auth/openai', {
46
+ refresh: creds.refresh,
47
+ access: creds.access,
48
+ expires: creds.expires,
49
+ })
50
+ console.log('')
51
+ console.log('connected ✓')
52
+ if (status.accountId) console.log(`account: ${status.accountId}`)
53
+ console.log(`access token expires: ${formatExpiry(status.expiresAt)}`)
54
+ console.log('')
55
+ console.log(`enable the 'openai-codex' provider on /config and add at least one model`)
56
+ console.log('(e.g. gpt-5.1-codex-max, gpt-5.2, gpt-5.3-codex) to start using it.')
57
+ },
58
+ })
59
+
60
+ const openaiLogoutCmd = defineCommand({
61
+ meta: {
62
+ name: 'logout',
63
+ description: 'Forget the stored ChatGPT OAuth credentials',
64
+ },
65
+ async run() {
66
+ const client = createClient()
67
+ await client.del('/api/auth/openai')
68
+ console.log('disconnected ChatGPT OAuth credentials')
69
+ },
70
+ })
71
+
72
+ const openaiStatusCmd = defineCommand({
73
+ meta: {
74
+ name: 'status',
75
+ description: 'Show ChatGPT OAuth connection status',
76
+ },
77
+ async run() {
78
+ const client = createClient()
79
+ const status = await client.get<OpenAICodexStatus>('/api/auth/openai')
80
+ if (!status.connected) {
81
+ console.log('not connected — run `bazilion auth openai login`')
82
+ return
83
+ }
84
+ console.log('connected ✓')
85
+ if (status.accountId) console.log(`account: ${status.accountId}`)
86
+ console.log(`access token expires: ${formatExpiry(status.expiresAt)}`)
87
+ },
88
+ })
89
+
90
+ const openaiCmd = defineCommand({
91
+ meta: {
92
+ name: 'openai',
93
+ description: 'Manage ChatGPT (OAuth) authentication',
94
+ },
95
+ subCommands: {
96
+ login: openaiLoginCmd,
97
+ logout: openaiLogoutCmd,
98
+ status: openaiStatusCmd,
99
+ },
100
+ })
101
+
102
+ export const authCommand = defineCommand({
103
+ meta: {
104
+ name: 'auth',
105
+ description: 'OAuth-based provider authentication (ChatGPT account, etc.)',
106
+ },
107
+ subCommands: {
108
+ openai: openaiCmd,
109
+ },
110
+ })