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.
- package/.understand-anything/.understandignore +25 -0
- package/.understand-anything/fingerprints.json +14267 -0
- package/.understand-anything/knowledge-graph.json +18128 -0
- package/.understand-anything/meta.json +6 -0
- package/CLAUDE.md +164 -0
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/apps/cli/package.json +21 -0
- package/apps/cli/src/auth-file.ts +18 -0
- package/apps/cli/src/client.ts +37 -0
- package/apps/cli/src/columnize.ts +32 -0
- package/apps/cli/src/commands/agent.ts +574 -0
- package/apps/cli/src/commands/auth.ts +110 -0
- package/apps/cli/src/commands/backup.ts +135 -0
- package/apps/cli/src/commands/completion.ts +155 -0
- package/apps/cli/src/commands/config.ts +95 -0
- package/apps/cli/src/commands/doctor.ts +131 -0
- package/apps/cli/src/commands/group.ts +132 -0
- package/apps/cli/src/commands/inbox.ts +82 -0
- package/apps/cli/src/commands/login.ts +73 -0
- package/apps/cli/src/commands/memory.ts +106 -0
- package/apps/cli/src/commands/profile.ts +259 -0
- package/apps/cli/src/commands/provider.ts +170 -0
- package/apps/cli/src/commands/send.ts +24 -0
- package/apps/cli/src/commands/serve.ts +89 -0
- package/apps/cli/src/commands/skill.ts +120 -0
- package/apps/cli/src/commands/token.ts +148 -0
- package/apps/cli/src/commands/trigger.ts +129 -0
- package/apps/cli/src/commands/uninstall.ts +156 -0
- package/apps/cli/src/index.ts +196 -0
- package/apps/cli/src/paths.ts +12 -0
- package/apps/cli/test/agent.test.ts +213 -0
- package/apps/cli/test/backup.test.ts +95 -0
- package/apps/cli/test/chat.test.ts +539 -0
- package/apps/cli/test/columnize.test.ts +29 -0
- package/apps/cli/test/completion.test.ts +45 -0
- package/apps/cli/test/config-page.test.ts +151 -0
- package/apps/cli/test/group.test.ts +74 -0
- package/apps/cli/test/helpers.ts +70 -0
- package/apps/cli/test/inbox-autodeliver.test.ts +143 -0
- package/apps/cli/test/inbox.test.ts +147 -0
- package/apps/cli/test/memory.test.ts +69 -0
- package/apps/cli/test/profile.test.ts +110 -0
- package/apps/cli/test/send.test.ts +30 -0
- package/apps/cli/test/server-fixture.ts +212 -0
- package/apps/cli/test/session-head.test.ts +45 -0
- package/apps/cli/test/skill.test.ts +128 -0
- package/apps/cli/test/token.test.ts +109 -0
- package/apps/cli/test/trigger.test.ts +245 -0
- package/apps/cli/tsconfig.json +4 -0
- package/apps/daemon/package.json +31 -0
- package/apps/daemon/src/app.ts +41 -0
- package/apps/daemon/src/core/agent/archive.ts +8 -0
- package/apps/daemon/src/core/agent/delete.ts +30 -0
- package/apps/daemon/src/core/agent/resolve.ts +31 -0
- package/apps/daemon/src/core/agent/spawn.ts +95 -0
- package/apps/daemon/src/core/agent/unarchive.ts +11 -0
- package/apps/daemon/src/core/availableModels.ts +58 -0
- package/apps/daemon/src/core/db/client.ts +113 -0
- package/apps/daemon/src/core/db/migrate.ts +41 -0
- package/apps/daemon/src/core/db/migrations/0001_init.sql +179 -0
- package/apps/daemon/src/core/group/delete.ts +21 -0
- package/apps/daemon/src/core/group/register.ts +68 -0
- package/apps/daemon/src/core/index.ts +71 -0
- package/apps/daemon/src/core/paths.ts +56 -0
- package/apps/daemon/src/core/profile/create.ts +78 -0
- package/apps/daemon/src/core/profile/delete.ts +26 -0
- package/apps/daemon/src/core/profile/identity.ts +70 -0
- package/apps/daemon/src/core/profile/load.ts +45 -0
- package/apps/daemon/src/core/profile/seed.ts +74 -0
- package/apps/daemon/src/core/profile/templates.ts +60 -0
- package/apps/daemon/src/core/profile/update.ts +57 -0
- package/apps/daemon/src/core/profile/validate.ts +9 -0
- package/apps/daemon/src/core/repos/agents.ts +202 -0
- package/apps/daemon/src/core/repos/config.ts +78 -0
- package/apps/daemon/src/core/repos/groups.ts +55 -0
- package/apps/daemon/src/core/repos/messages.ts +127 -0
- package/apps/daemon/src/core/repos/profiles.ts +83 -0
- package/apps/daemon/src/core/repos/providerModels.ts +58 -0
- package/apps/daemon/src/core/repos/providerState.ts +37 -0
- package/apps/daemon/src/core/repos/secrets.ts +145 -0
- package/apps/daemon/src/core/repos/skillMeta.ts +49 -0
- package/apps/daemon/src/core/repos/triggers.ts +101 -0
- package/apps/daemon/src/core/repos/webTokens.ts +87 -0
- package/apps/daemon/src/core/secrets.ts +65 -0
- package/apps/daemon/src/core/services.ts +264 -0
- package/apps/daemon/src/core/skills/discover.ts +28 -0
- package/apps/daemon/src/core/skills/import.ts +136 -0
- package/apps/daemon/src/core/skills/parse.ts +52 -0
- package/apps/daemon/src/core/skills/resolve.ts +50 -0
- package/apps/daemon/src/index.ts +45 -0
- package/apps/daemon/src/lib/agent-cancel.ts +48 -0
- package/apps/daemon/src/lib/agent-id.ts +13 -0
- package/apps/daemon/src/lib/agent-turn.ts +56 -0
- package/apps/daemon/src/lib/api-key.ts +56 -0
- package/apps/daemon/src/lib/auth.ts +41 -0
- package/apps/daemon/src/lib/cron.ts +93 -0
- package/apps/daemon/src/lib/ctx.ts +80 -0
- package/apps/daemon/src/lib/messaging-host.ts +34 -0
- package/apps/daemon/src/lib/middleware-auth.ts +52 -0
- package/apps/daemon/src/lib/scheduler.ts +294 -0
- package/apps/daemon/src/routes/agents.ts +772 -0
- package/apps/daemon/src/routes/auth-login.ts +193 -0
- package/apps/daemon/src/routes/config.ts +267 -0
- package/apps/daemon/src/routes/groups.ts +133 -0
- package/apps/daemon/src/routes/messages.ts +29 -0
- package/apps/daemon/src/routes/misc.ts +239 -0
- package/apps/daemon/src/routes/profiles.ts +197 -0
- package/apps/daemon/src/routes/skills.ts +123 -0
- package/apps/daemon/src/routes/triggers.ts +29 -0
- package/apps/daemon/src/runtime/auth/openai-codex.ts +121 -0
- package/apps/daemon/src/runtime/auto-reply/heartbeat.ts +31 -0
- package/apps/daemon/src/runtime/index.ts +77 -0
- package/apps/daemon/src/runtime/memory/files.ts +103 -0
- package/apps/daemon/src/runtime/memory/qmd.ts +152 -0
- package/apps/daemon/src/runtime/memory/types.ts +16 -0
- package/apps/daemon/src/runtime/pi/events.ts +173 -0
- package/apps/daemon/src/runtime/pi/session.ts +536 -0
- package/apps/daemon/src/runtime/pi/tools.ts +85 -0
- package/apps/daemon/src/runtime/providers/catalog.ts +145 -0
- package/apps/daemon/src/runtime/providers/pi-adapter.ts +272 -0
- package/apps/daemon/src/runtime/providers/registry.ts +374 -0
- package/apps/daemon/src/runtime/providers/retry.ts +176 -0
- package/apps/daemon/src/runtime/providers/types.ts +33 -0
- package/apps/daemon/src/runtime/session/prompt.ts +83 -0
- package/apps/daemon/src/runtime/tools/bootstrap.ts +22 -0
- package/apps/daemon/src/runtime/tools/home.ts +114 -0
- package/apps/daemon/src/runtime/tools/memory.ts +81 -0
- package/apps/daemon/src/runtime/tools/messaging.ts +127 -0
- package/apps/daemon/src/runtime/tools/registry.ts +29 -0
- package/apps/daemon/src/runtime/tools/types.ts +13 -0
- package/apps/daemon/src/runtime/tools/web-extract.ts +110 -0
- package/apps/daemon/src/runtime/tools/web-ssrf.ts +245 -0
- package/apps/daemon/src/runtime/tools/web.ts +273 -0
- package/apps/daemon/src/runtime/worker/entry.ts +221 -0
- package/apps/daemon/src/runtime/worker/ipc-protocol.ts +75 -0
- package/apps/daemon/src/runtime/worker/spawn.ts +249 -0
- package/apps/daemon/test/core/agents.test.ts +319 -0
- package/apps/daemon/test/core/available-models.test.ts +63 -0
- package/apps/daemon/test/core/config.test.ts +99 -0
- package/apps/daemon/test/core/groups.test.ts +63 -0
- package/apps/daemon/test/core/helpers.ts +50 -0
- package/apps/daemon/test/core/identity.test.ts +85 -0
- package/apps/daemon/test/core/migrations.test.ts +83 -0
- package/apps/daemon/test/core/profiles.test.ts +182 -0
- package/apps/daemon/test/core/provider-models.test.ts +57 -0
- package/apps/daemon/test/core/provider-state.test.ts +36 -0
- package/apps/daemon/test/core/skill-meta.test.ts +45 -0
- package/apps/daemon/test/core/skills.test.ts +271 -0
- package/apps/daemon/test/core/triggers.test.ts +182 -0
- package/apps/daemon/test/core/web-tokens.test.ts +79 -0
- package/apps/daemon/test/cron.test.ts +90 -0
- package/apps/daemon/test/runtime/heartbeat.test.ts +34 -0
- package/apps/daemon/test/runtime/memory-qmd.test.ts +97 -0
- package/apps/daemon/test/runtime/memory.test.ts +78 -0
- package/apps/daemon/test/runtime/messaging.test.ts +213 -0
- package/apps/daemon/test/runtime/mock-server.ts +65 -0
- package/apps/daemon/test/runtime/openai-codex-auth.test.ts +116 -0
- package/apps/daemon/test/runtime/providers.test.ts +306 -0
- package/apps/daemon/test/runtime/retry.test.ts +191 -0
- package/apps/daemon/test/runtime/session-head.test.ts +90 -0
- package/apps/daemon/test/runtime/tools-home.test.ts +102 -0
- package/apps/daemon/test/runtime/tools-web.test.ts +206 -0
- package/apps/daemon/tsconfig.json +4 -0
- package/apps/mobile/README.md +60 -0
- package/apps/mobile/app/_layout.tsx +58 -0
- package/apps/mobile/app/agents/[id]/chat.tsx +486 -0
- package/apps/mobile/app/agents/[id]/index.tsx +166 -0
- package/apps/mobile/app/agents/index.tsx +212 -0
- package/apps/mobile/app/index.tsx +21 -0
- package/apps/mobile/app/pair.tsx +226 -0
- package/apps/mobile/app/settings.tsx +419 -0
- package/apps/mobile/app.json +49 -0
- package/apps/mobile/assets/adaptive-icon.png +0 -0
- package/apps/mobile/assets/favicon.png +0 -0
- package/apps/mobile/assets/icon.png +0 -0
- package/apps/mobile/assets/splash-icon.png +0 -0
- package/apps/mobile/babel.config.js +6 -0
- package/apps/mobile/metro.config.js +28 -0
- package/apps/mobile/package.json +44 -0
- package/apps/mobile/src/auth.ts +66 -0
- package/apps/mobile/src/pair-url.ts +48 -0
- package/apps/mobile/src/theme-context.tsx +88 -0
- package/apps/mobile/src/theme.ts +135 -0
- package/apps/mobile/test/pair-url.test.ts +46 -0
- package/apps/mobile/tsconfig.json +23 -0
- package/apps/web/components.json +25 -0
- package/apps/web/package.json +39 -0
- package/apps/web/public/baziu.svg +8 -0
- package/apps/web/src/components/AgentTabs.tsx +45 -0
- package/apps/web/src/components/BaziuLogo.tsx +21 -0
- package/apps/web/src/components/ChatPane.tsx +1033 -0
- package/apps/web/src/components/ConfigTabs.tsx +29 -0
- package/apps/web/src/components/CopyButton.tsx +68 -0
- package/apps/web/src/components/CreateGroupDialog.tsx +127 -0
- package/apps/web/src/components/FieldRow.tsx +94 -0
- package/apps/web/src/components/Footer.tsx +10 -0
- package/apps/web/src/components/PawIcon.tsx +15 -0
- package/apps/web/src/components/Sidebar.tsx +287 -0
- package/apps/web/src/components/SpawnDialog.tsx +129 -0
- package/apps/web/src/components/ThemeToggle.tsx +75 -0
- package/apps/web/src/components/TopNav.tsx +34 -0
- package/apps/web/src/components/ui/button.tsx +67 -0
- package/apps/web/src/components/ui/card.tsx +103 -0
- package/apps/web/src/components/ui/checkbox.tsx +31 -0
- package/apps/web/src/components/ui/dialog.tsx +168 -0
- package/apps/web/src/components/ui/input.tsx +19 -0
- package/apps/web/src/components/ui/label.tsx +22 -0
- package/apps/web/src/components/ui/radio-group.tsx +44 -0
- package/apps/web/src/components/ui/select.tsx +192 -0
- package/apps/web/src/components/ui/separator.tsx +26 -0
- package/apps/web/src/components/ui/table.tsx +116 -0
- package/apps/web/src/components/ui/tabs.tsx +88 -0
- package/apps/web/src/components/ui/textarea.tsx +18 -0
- package/apps/web/src/lib/auth.ts +50 -0
- package/apps/web/src/lib/daemon-client.ts +34 -0
- package/apps/web/src/lib/md.ts +45 -0
- package/apps/web/src/lib/utils.ts +6 -0
- package/apps/web/src/lib/wire-constants.ts +27 -0
- package/apps/web/src/routeTree.gen.ts +408 -0
- package/apps/web/src/router.tsx +20 -0
- package/apps/web/src/routes/__root.tsx +123 -0
- package/apps/web/src/routes/agents/$id/inbox.tsx +207 -0
- package/apps/web/src/routes/agents/$id/index.tsx +527 -0
- package/apps/web/src/routes/agents/$id/triggers.tsx +239 -0
- package/apps/web/src/routes/agents/index.tsx +265 -0
- package/apps/web/src/routes/api/$.ts +88 -0
- package/apps/web/src/routes/config/index.tsx +315 -0
- package/apps/web/src/routes/config/services.tsx +49 -0
- package/apps/web/src/routes/config/tokens.tsx +192 -0
- package/apps/web/src/routes/groups/$id/index.tsx +153 -0
- package/apps/web/src/routes/groups/$id/memory.tsx +321 -0
- package/apps/web/src/routes/groups/index.tsx +191 -0
- package/apps/web/src/routes/index.tsx +133 -0
- package/apps/web/src/routes/login.tsx +63 -0
- package/apps/web/src/routes/profiles/$id.tsx +549 -0
- package/apps/web/src/routes/profiles/index.tsx +458 -0
- package/apps/web/src/routes/skills/index.tsx +297 -0
- package/apps/web/src/routes/welcome.tsx +61 -0
- package/apps/web/src/styles.css +449 -0
- package/apps/web/tsconfig.json +25 -0
- package/apps/web/vite.config.ts +25 -0
- package/biome.json +23 -0
- package/docs/agent-engine.md +219 -0
- package/docs/architecture.md +627 -0
- package/docs/backlog/README.md +42 -0
- package/docs/backlog/draft/BAZ-001-a2a-federation-spike.md +125 -0
- package/docs/openclaw-reference.md +210 -0
- package/package.json +38 -0
- package/packages/api-types/package.json +11 -0
- package/packages/api-types/src/entities.ts +146 -0
- package/packages/api-types/src/events.ts +55 -0
- package/packages/api-types/src/index.ts +488 -0
- package/packages/api-types/src/memory.ts +15 -0
- package/packages/client/package.json +13 -0
- package/packages/client/src/index.ts +117 -0
- package/pnpm-workspace.yaml +3 -0
- package/tsconfig.base.json +23 -0
- package/tsconfig.json +11 -0
- package/vitest.config.ts +22 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env -S tsx
|
|
2
|
+
import { defineCommand, runCommand, showUsage } from 'citty'
|
|
3
|
+
import { ApiClientError } from './client.ts'
|
|
4
|
+
import { agentCommand } from './commands/agent.ts'
|
|
5
|
+
import { authCommand } from './commands/auth.ts'
|
|
6
|
+
import { backupCommand } from './commands/backup.ts'
|
|
7
|
+
import { completionCommand } from './commands/completion.ts'
|
|
8
|
+
import { configCommand } from './commands/config.ts'
|
|
9
|
+
import { doctorCommand } from './commands/doctor.ts'
|
|
10
|
+
import { groupCommand } from './commands/group.ts'
|
|
11
|
+
import { inboxCommand } from './commands/inbox.ts'
|
|
12
|
+
import { loginCommand } from './commands/login.ts'
|
|
13
|
+
import { memoryCommand } from './commands/memory.ts'
|
|
14
|
+
import { profileCommand } from './commands/profile.ts'
|
|
15
|
+
import { providerCommand } from './commands/provider.ts'
|
|
16
|
+
import { sendCommand } from './commands/send.ts'
|
|
17
|
+
import { serveCommand } from './commands/serve.ts'
|
|
18
|
+
import { skillCommand } from './commands/skill.ts'
|
|
19
|
+
import { tokenCommand } from './commands/token.ts'
|
|
20
|
+
import { triggerCommand } from './commands/trigger.ts'
|
|
21
|
+
import { uninstallCommand } from './commands/uninstall.ts'
|
|
22
|
+
|
|
23
|
+
const main = defineCommand({
|
|
24
|
+
meta: {
|
|
25
|
+
name: 'bazilion',
|
|
26
|
+
version: '0.0.0',
|
|
27
|
+
description: 'Multi-agent runtime',
|
|
28
|
+
},
|
|
29
|
+
subCommands: {
|
|
30
|
+
login: loginCommand,
|
|
31
|
+
profile: profileCommand,
|
|
32
|
+
group: groupCommand,
|
|
33
|
+
agent: agentCommand,
|
|
34
|
+
skill: skillCommand,
|
|
35
|
+
memory: memoryCommand,
|
|
36
|
+
provider: providerCommand,
|
|
37
|
+
send: sendCommand,
|
|
38
|
+
inbox: inboxCommand,
|
|
39
|
+
config: configCommand,
|
|
40
|
+
serve: serveCommand,
|
|
41
|
+
doctor: doctorCommand,
|
|
42
|
+
backup: backupCommand,
|
|
43
|
+
trigger: triggerCommand,
|
|
44
|
+
token: tokenCommand,
|
|
45
|
+
auth: authCommand,
|
|
46
|
+
uninstall: uninstallCommand,
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Mutate the command tree after definition so `completion` can introspect its
|
|
51
|
+
// own siblings without a circular import. Cast is safe — citty keeps the
|
|
52
|
+
// object reference on `main` after defineCommand, and we know its shape.
|
|
53
|
+
;(main.subCommands as Record<string, unknown>).completion = completionCommand(main as never)
|
|
54
|
+
|
|
55
|
+
// Resolve `bazilion <a> <b> --help` to the right subcommand for showUsage.
|
|
56
|
+
// citty's built-in version is private, so we re-implement it for plain
|
|
57
|
+
// subCommands objects (the shape we use throughout the tree).
|
|
58
|
+
type AnyCmd = Parameters<typeof showUsage>[0]
|
|
59
|
+
type CmdWithSubs = AnyCmd & { subCommands?: Record<string, AnyCmd> }
|
|
60
|
+
async function resolveSubCommand(
|
|
61
|
+
cmd: CmdWithSubs,
|
|
62
|
+
rawArgs: string[],
|
|
63
|
+
parent?: AnyCmd,
|
|
64
|
+
): Promise<[AnyCmd, AnyCmd | undefined]> {
|
|
65
|
+
const subs = cmd.subCommands
|
|
66
|
+
if (subs && Object.keys(subs).length > 0) {
|
|
67
|
+
const idx = rawArgs.findIndex((a) => !a.startsWith('-'))
|
|
68
|
+
const name = idx >= 0 ? rawArgs[idx] : undefined
|
|
69
|
+
if (name && subs[name]) {
|
|
70
|
+
return resolveSubCommand(subs[name] as CmdWithSubs, rawArgs.slice(idx + 1), cmd)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return [cmd, parent]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function printError(err: unknown): void {
|
|
77
|
+
if (err instanceof ApiClientError) {
|
|
78
|
+
console.error(`error: ${err.body.error}`)
|
|
79
|
+
if (err.status === 401) {
|
|
80
|
+
console.error(' hint: token mismatch. Check ~/.bazilion/auth.json "token"')
|
|
81
|
+
console.error(' or re-run "bazilion login" for a remote server.')
|
|
82
|
+
} else if (err.status === 403) {
|
|
83
|
+
console.error(' hint: request was rejected by the server. If you hit this from a')
|
|
84
|
+
console.error(' fresh CLI build, check that BAZILION_SERVER matches the')
|
|
85
|
+
console.error(' server origin exactly (scheme + host + port).')
|
|
86
|
+
}
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
90
|
+
console.error(`error: ${msg}`)
|
|
91
|
+
// Friendly hints for the most common connection failures. node's fetch
|
|
92
|
+
// wraps the syscall error in err.cause.code, so we peek there too.
|
|
93
|
+
const cause = err instanceof Error ? (err as { cause?: { code?: string } }).cause : undefined
|
|
94
|
+
const code = cause?.code
|
|
95
|
+
if (code === 'ECONNREFUSED' || /ECONNREFUSED/.test(msg)) {
|
|
96
|
+
console.error(' hint: the bazilion server is not running. Start it with "bazilion serve"')
|
|
97
|
+
console.error(' or point BAZILION_SERVER at a running instance.')
|
|
98
|
+
} else if (code === 'ENOTFOUND' || /ENOTFOUND/.test(msg)) {
|
|
99
|
+
console.error(' hint: host not found. Check BAZILION_SERVER / ~/.bazilion/auth.json.')
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Top-level `bazilion --help` renders a categorized layout instead of citty's
|
|
105
|
+
* default `sub1|sub2|…` pipe-soup USAGE line. Subcommand `--help` still uses
|
|
106
|
+
* citty's built-in renderer.
|
|
107
|
+
*/
|
|
108
|
+
function printTopLevelHelp(): void {
|
|
109
|
+
console.log('Multi-agent runtime (bazilion v0.0.0)')
|
|
110
|
+
console.log('')
|
|
111
|
+
console.log('USAGE bazilion <command> [args]')
|
|
112
|
+
console.log('')
|
|
113
|
+
const groups: { title: string; items: [string, string][] }[] = [
|
|
114
|
+
{
|
|
115
|
+
title: 'setup',
|
|
116
|
+
items: [
|
|
117
|
+
['serve', 'Start the bazilion daemon (auto-bootstraps ~/.bazilion on first run)'],
|
|
118
|
+
['doctor', 'Diagnose your bazilion install'],
|
|
119
|
+
['uninstall', 'Wipe bazilion state from ~/.bazilion (or BAZILION_HOME)'],
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
title: 'catalog',
|
|
124
|
+
items: [
|
|
125
|
+
['profile', 'Manage profiles (templates agents are spawned from)'],
|
|
126
|
+
['group', 'Manage groups (collaboration context — filesystem root + USER.md + roster)'],
|
|
127
|
+
['skill', 'Manage the skill library'],
|
|
128
|
+
['provider', 'Manage and test LLM providers'],
|
|
129
|
+
['config', 'Manage service config (credentials + URLs/IDs)'],
|
|
130
|
+
['auth', 'OAuth provider sign-in (ChatGPT account, …)'],
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
title: 'agents',
|
|
135
|
+
items: [
|
|
136
|
+
['agent', 'Spawn, list, chat, archive agents'],
|
|
137
|
+
['memory', "Read/write a group's shared memory"],
|
|
138
|
+
['send', 'Send a message from one agent to another'],
|
|
139
|
+
['inbox', 'Inspect agent inboxes'],
|
|
140
|
+
['trigger', 'Manage agent heartbeats / cron triggers'],
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
title: 'ops',
|
|
145
|
+
items: [['backup', 'Download a tar.gz backup of ~/.bazilion']],
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
title: 'remote',
|
|
149
|
+
items: [
|
|
150
|
+
['login', 'Save a remote bazilion server + token pair to auth.json'],
|
|
151
|
+
['token', 'Manage web tokens for API/CLI clients'],
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
title: 'shell',
|
|
156
|
+
items: [['completion', 'Print shell completion script (bash | zsh | fish)']],
|
|
157
|
+
},
|
|
158
|
+
]
|
|
159
|
+
const nameWidth = 12
|
|
160
|
+
for (const g of groups) {
|
|
161
|
+
console.log(` ${g.title}`)
|
|
162
|
+
for (const [name, desc] of g.items) {
|
|
163
|
+
console.log(` ${name.padEnd(nameWidth)}${desc}`)
|
|
164
|
+
}
|
|
165
|
+
console.log('')
|
|
166
|
+
}
|
|
167
|
+
console.log('Use `bazilion <command> --help` for more information about a command.')
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function entry(): Promise<void> {
|
|
171
|
+
const rawArgs = process.argv.slice(2)
|
|
172
|
+
try {
|
|
173
|
+
if (rawArgs.includes('--help') || rawArgs.includes('-h')) {
|
|
174
|
+
// For the top-level `bazilion --help`, render our grouped layout.
|
|
175
|
+
// Subcommand help still goes through citty (preserves native formatting).
|
|
176
|
+
const firstPositional = rawArgs.find((a) => !a.startsWith('-'))
|
|
177
|
+
if (!firstPositional) {
|
|
178
|
+
printTopLevelHelp()
|
|
179
|
+
process.exit(0)
|
|
180
|
+
}
|
|
181
|
+
const [target, parent] = await resolveSubCommand(main as CmdWithSubs, rawArgs)
|
|
182
|
+
await showUsage(target, parent)
|
|
183
|
+
process.exit(0)
|
|
184
|
+
}
|
|
185
|
+
if (rawArgs.length === 1 && (rawArgs[0] === '--version' || rawArgs[0] === '-v')) {
|
|
186
|
+
console.log('0.0.0')
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
await runCommand(main, { rawArgs })
|
|
190
|
+
} catch (err) {
|
|
191
|
+
printError(err)
|
|
192
|
+
process.exit(1)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
entry()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { homedir } from 'node:os'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
|
|
4
|
+
export interface CliPaths {
|
|
5
|
+
home: string
|
|
6
|
+
authFile: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function resolveCliPaths(home?: string): CliPaths {
|
|
10
|
+
const root = home ?? process.env.BAZILION_HOME ?? join(homedir(), '.bazilion')
|
|
11
|
+
return { home: root, authFile: join(root, 'auth.json') }
|
|
12
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'
|
|
2
|
+
import { extractAgentId } from './helpers.ts'
|
|
3
|
+
import { startTestServer, type TestServer } from './server-fixture.ts'
|
|
4
|
+
|
|
5
|
+
// Scheduler disabled — this suite exercises agent-CRUD only (spawn / show /
|
|
6
|
+
// skill / move / archive / delete). Running the scheduler alongside adds
|
|
7
|
+
// tick-rate DB reads and potential background worker spawns for any
|
|
8
|
+
// leftover unread-inbox state from prior tests, which can push a busy
|
|
9
|
+
// full-suite run past the 30s per-test timeout.
|
|
10
|
+
let server: TestServer
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
server = await startTestServer({ BAZILION_SCHEDULER: 'off' })
|
|
13
|
+
})
|
|
14
|
+
afterAll(() => server.stop())
|
|
15
|
+
beforeEach(() => server.reset())
|
|
16
|
+
|
|
17
|
+
test('scaffold profile, spawn three agents, inspect', async () => {
|
|
18
|
+
let r = await server.cli([
|
|
19
|
+
'profile',
|
|
20
|
+
'create',
|
|
21
|
+
'p',
|
|
22
|
+
'--model',
|
|
23
|
+
'anthropic:claude-opus-4-6',
|
|
24
|
+
'--skills',
|
|
25
|
+
's1,s2',
|
|
26
|
+
])
|
|
27
|
+
expect(r.exitCode).toBe(0)
|
|
28
|
+
|
|
29
|
+
await server.cli(['group', 'add', 'g1'])
|
|
30
|
+
await server.cli(['group', 'add', 'g2'])
|
|
31
|
+
|
|
32
|
+
// A1: group g1, default skills
|
|
33
|
+
r = await server.cli(['agent', 'spawn', '--profile', 'p', '--name', 'A1', '--group', 'g1'])
|
|
34
|
+
expect(r.exitCode).toBe(0)
|
|
35
|
+
const id1 = extractAgentId(r.stdout)
|
|
36
|
+
|
|
37
|
+
// A2: group g2, attach an extra skill post-spawn
|
|
38
|
+
r = await server.cli(['agent', 'spawn', '--profile', 'p', '--name', 'A2', '--group', 'g2'])
|
|
39
|
+
expect(r.exitCode).toBe(0)
|
|
40
|
+
const id2 = extractAgentId(r.stdout)
|
|
41
|
+
await server.cli(['agent', 'skill', 'add', id2, 'extra'])
|
|
42
|
+
|
|
43
|
+
// A3: default-group fallback, default skills, model override
|
|
44
|
+
r = await server.cli([
|
|
45
|
+
'agent',
|
|
46
|
+
'spawn',
|
|
47
|
+
'--profile',
|
|
48
|
+
'p',
|
|
49
|
+
'--name',
|
|
50
|
+
'A3',
|
|
51
|
+
'--model',
|
|
52
|
+
'openai:gpt-5',
|
|
53
|
+
])
|
|
54
|
+
expect(r.exitCode).toBe(0)
|
|
55
|
+
const id3 = extractAgentId(r.stdout)
|
|
56
|
+
|
|
57
|
+
// List sees all three
|
|
58
|
+
r = await server.cli(['agent', 'list'])
|
|
59
|
+
expect(r.stdout).toContain('A1')
|
|
60
|
+
expect(r.stdout).toContain('A2')
|
|
61
|
+
expect(r.stdout).toContain('A3')
|
|
62
|
+
|
|
63
|
+
// Show A1: inherits default skills s1,s2 and is in g1
|
|
64
|
+
r = await server.cli(['agent', 'show', id1])
|
|
65
|
+
expect(r.stdout).toContain('A1')
|
|
66
|
+
expect(r.stdout).toContain('group:')
|
|
67
|
+
expect(r.stdout).toContain('g1')
|
|
68
|
+
expect(r.stdout).toContain('s1')
|
|
69
|
+
expect(r.stdout).toContain('s2')
|
|
70
|
+
|
|
71
|
+
// Show A2: has 'extra' skill, in g2
|
|
72
|
+
r = await server.cli(['agent', 'show', id2])
|
|
73
|
+
expect(r.stdout).toContain('extra')
|
|
74
|
+
expect(r.stdout).toContain('g2')
|
|
75
|
+
|
|
76
|
+
// Show A3: model override + lands in the seeded default group
|
|
77
|
+
r = await server.cli(['agent', 'show', id3])
|
|
78
|
+
expect(r.stdout).toContain('openai:gpt-5')
|
|
79
|
+
expect(r.stdout).toContain('default')
|
|
80
|
+
|
|
81
|
+
// Archive A3 → list excludes it by default
|
|
82
|
+
r = await server.cli(['agent', 'archive', id3])
|
|
83
|
+
expect(r.exitCode).toBe(0)
|
|
84
|
+
|
|
85
|
+
r = await server.cli(['agent', 'list'])
|
|
86
|
+
expect(r.stdout).toContain('A1')
|
|
87
|
+
expect(r.stdout).toContain('A2')
|
|
88
|
+
expect(r.stdout).not.toContain('A3')
|
|
89
|
+
|
|
90
|
+
// --all reveals it again
|
|
91
|
+
r = await server.cli(['agent', 'list', '--all'])
|
|
92
|
+
expect(r.stdout).toContain('A3')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('agent skill add/rm on the fly', async () => {
|
|
96
|
+
await server.cli(['profile', 'create', 'p', '--model', 'm'])
|
|
97
|
+
let r = await server.cli(['agent', 'spawn', '--profile', 'p'])
|
|
98
|
+
const id = extractAgentId(r.stdout)
|
|
99
|
+
|
|
100
|
+
await server.cli(['agent', 'skill', 'add', id, 'new-skill'])
|
|
101
|
+
r = await server.cli(['agent', 'show', id])
|
|
102
|
+
expect(r.stdout).toContain('new-skill')
|
|
103
|
+
|
|
104
|
+
await server.cli(['agent', 'skill', 'rm', id, 'new-skill'])
|
|
105
|
+
r = await server.cli(['agent', 'show', id])
|
|
106
|
+
expect(r.stdout).not.toContain('new-skill')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('agent lifecycle: spawn -> archive -> unarchive -> delete', async () => {
|
|
110
|
+
await server.cli(['profile', 'create', 'p', '--model', 'm'])
|
|
111
|
+
let r = await server.cli(['agent', 'spawn', '--profile', 'p', '--name', 'lifecycle'])
|
|
112
|
+
const id = extractAgentId(r.stdout)
|
|
113
|
+
|
|
114
|
+
// archive -> hidden from default list, visible under --all
|
|
115
|
+
r = await server.cli(['agent', 'archive', id])
|
|
116
|
+
expect(r.exitCode).toBe(0)
|
|
117
|
+
r = await server.cli(['agent', 'list'])
|
|
118
|
+
expect(r.stdout).not.toContain('lifecycle')
|
|
119
|
+
r = await server.cli(['agent', 'list', '--all'])
|
|
120
|
+
expect(r.stdout).toContain('lifecycle')
|
|
121
|
+
|
|
122
|
+
// unarchive -> back in default list
|
|
123
|
+
r = await server.cli(['agent', 'unarchive', id])
|
|
124
|
+
expect(r.exitCode).toBe(0)
|
|
125
|
+
r = await server.cli(['agent', 'list'])
|
|
126
|
+
expect(r.stdout).toContain('lifecycle')
|
|
127
|
+
|
|
128
|
+
// double unarchive is rejected (already idle)
|
|
129
|
+
r = await server.cli(['agent', 'unarchive', id])
|
|
130
|
+
expect(r.exitCode).not.toBe(0)
|
|
131
|
+
expect(r.stderr + r.stdout).toContain('not archived')
|
|
132
|
+
|
|
133
|
+
// delete -> gone completely
|
|
134
|
+
r = await server.cli(['agent', 'delete', id])
|
|
135
|
+
expect(r.exitCode).toBe(0)
|
|
136
|
+
r = await server.cli(['agent', 'list', '--all'])
|
|
137
|
+
expect(r.stdout).not.toContain('lifecycle')
|
|
138
|
+
|
|
139
|
+
// second delete fails
|
|
140
|
+
r = await server.cli(['agent', 'delete', id])
|
|
141
|
+
expect(r.exitCode).not.toBe(0)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
test('deleting an agent with a mailbox history succeeds', async () => {
|
|
145
|
+
await server.cli(['profile', 'create', 'p', '--model', 'm'])
|
|
146
|
+
let r = await server.cli(['agent', 'spawn', '--profile', 'p', '--name', 'sender'])
|
|
147
|
+
const a = extractAgentId(r.stdout)
|
|
148
|
+
r = await server.cli(['agent', 'spawn', '--profile', 'p', '--name', 'receiver'])
|
|
149
|
+
const b = extractAgentId(r.stdout)
|
|
150
|
+
|
|
151
|
+
await server.cli(['send', a, b, 'hello'])
|
|
152
|
+
await server.cli(['send', b, a, 'hi back'])
|
|
153
|
+
|
|
154
|
+
// Delete the receiver — would fail before the FK-aware delete fix
|
|
155
|
+
r = await server.cli(['agent', 'delete', b])
|
|
156
|
+
expect(r.exitCode).toBe(0)
|
|
157
|
+
|
|
158
|
+
// Sender still exists; their inbox is now empty (message from b was purged)
|
|
159
|
+
r = await server.cli(['inbox', 'list', a])
|
|
160
|
+
expect(r.stdout).toContain('(inbox empty)')
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
test('agent commands accept an unambiguous UUID prefix (8 chars)', async () => {
|
|
164
|
+
await server.cli(['profile', 'create', 'p', '--model', 'm'])
|
|
165
|
+
const r = await server.cli(['agent', 'spawn', '--profile', 'p', '--name', 'prefixed'])
|
|
166
|
+
const fullId = extractAgentId(r.stdout)
|
|
167
|
+
const prefix = fullId.slice(0, 8)
|
|
168
|
+
|
|
169
|
+
// show
|
|
170
|
+
const show = await server.cli(['agent', 'show', prefix])
|
|
171
|
+
expect(show.exitCode).toBe(0)
|
|
172
|
+
expect(show.stdout).toContain('prefixed')
|
|
173
|
+
|
|
174
|
+
// archive/unarchive via prefix
|
|
175
|
+
const archR = await server.cli(['agent', 'archive', prefix])
|
|
176
|
+
expect(archR.exitCode).toBe(0)
|
|
177
|
+
const unarchR = await server.cli(['agent', 'unarchive', prefix])
|
|
178
|
+
expect(unarchR.exitCode).toBe(0)
|
|
179
|
+
|
|
180
|
+
// bogus prefix still 404s cleanly
|
|
181
|
+
const bad = await server.cli(['agent', 'show', 'zzzzzzzz'])
|
|
182
|
+
expect(bad.exitCode).not.toBe(0)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
test('agent list short format shows 8-char prefix, --long shows full id + profile', async () => {
|
|
186
|
+
await server.cli(['profile', 'create', 'myprofile', '--model', 'm'])
|
|
187
|
+
const r = await server.cli(['agent', 'spawn', '--profile', 'myprofile', '--name', 'alpha'])
|
|
188
|
+
const fullId = extractAgentId(r.stdout)
|
|
189
|
+
|
|
190
|
+
const short = await server.cli(['agent', 'list'])
|
|
191
|
+
expect(short.stdout).toContain(fullId.slice(0, 8))
|
|
192
|
+
expect(short.stdout).toContain('alpha')
|
|
193
|
+
// Short form omits the profile column
|
|
194
|
+
expect(short.stdout).not.toContain('myprofile')
|
|
195
|
+
|
|
196
|
+
const long = await server.cli(['agent', 'list', '--long'])
|
|
197
|
+
expect(long.stdout).toContain(fullId)
|
|
198
|
+
expect(long.stdout).toContain('myprofile')
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('agent move changes group membership', async () => {
|
|
202
|
+
await server.cli(['profile', 'create', 'p', '--model', 'm'])
|
|
203
|
+
let r = await server.cli(['agent', 'spawn', '--profile', 'p', '--name', 'wanderer'])
|
|
204
|
+
const id = extractAgentId(r.stdout)
|
|
205
|
+
|
|
206
|
+
await server.cli(['group', 'add', 'elsewhere'])
|
|
207
|
+
|
|
208
|
+
r = await server.cli(['agent', 'move', id, 'elsewhere'])
|
|
209
|
+
expect(r.exitCode).toBe(0)
|
|
210
|
+
|
|
211
|
+
r = await server.cli(['agent', 'show', id])
|
|
212
|
+
expect(r.stdout).toContain('elsewhere')
|
|
213
|
+
})
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'
|
|
5
|
+
import { makeHome, runCli, type TestHome } from './helpers.ts'
|
|
6
|
+
import { startTestServer, type TestServer } from './server-fixture.ts'
|
|
7
|
+
|
|
8
|
+
let server: TestServer
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
server = await startTestServer()
|
|
11
|
+
})
|
|
12
|
+
afterAll(() => server.stop())
|
|
13
|
+
beforeEach(() => server.reset())
|
|
14
|
+
|
|
15
|
+
test('backup create downloads a tar.gz with the home contents', async () => {
|
|
16
|
+
writeFileSync(join(server.home, 'marker.txt'), 'hello')
|
|
17
|
+
const out = join(tmpdir(), `bz-backup-${Date.now()}.tar.gz`)
|
|
18
|
+
|
|
19
|
+
const res = await server.cli(['backup', 'create', out])
|
|
20
|
+
expect(res.exitCode).toBe(0)
|
|
21
|
+
expect(res.stdout).toContain('done')
|
|
22
|
+
expect(existsSync(out)).toBe(true)
|
|
23
|
+
expect(statSync(out).size).toBeGreaterThan(0)
|
|
24
|
+
|
|
25
|
+
rmSync(out, { force: true })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('backup restore extracts the tar.gz into a fresh home', async () => {
|
|
29
|
+
writeFileSync(join(server.home, 'marker.txt'), 'restored-value')
|
|
30
|
+
const archive = join(tmpdir(), `bz-backup-${Date.now()}.tar.gz`)
|
|
31
|
+
await server.cli(['backup', 'create', archive])
|
|
32
|
+
|
|
33
|
+
// Restore into an empty dir. `runCli` doesn't need a server for this — the
|
|
34
|
+
// restore path is pure-offline — but we pass a valid home so the `--home`
|
|
35
|
+
// flag gets exercised.
|
|
36
|
+
const target: TestHome = { home: join(tmpdir(), `bz-restore-${Date.now()}`), cleanup() {} }
|
|
37
|
+
mkdirSync(target.home, { recursive: true })
|
|
38
|
+
const res = await runCli(['backup', 'restore', archive, '--home', target.home], target.home)
|
|
39
|
+
expect(res.exitCode).toBe(0)
|
|
40
|
+
expect(res.stdout).toContain('restored to')
|
|
41
|
+
expect(existsSync(join(target.home, 'marker.txt'))).toBe(true)
|
|
42
|
+
expect(readFileSync(join(target.home, 'marker.txt'), 'utf8')).toBe('restored-value')
|
|
43
|
+
expect(existsSync(join(target.home, 'bazilion.db'))).toBe(true)
|
|
44
|
+
|
|
45
|
+
rmSync(archive, { force: true })
|
|
46
|
+
rmSync(target.home, { recursive: true, force: true })
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('backup restore refuses a non-empty target without --force', async () => {
|
|
50
|
+
const archive = join(tmpdir(), `bz-backup-${Date.now()}.tar.gz`)
|
|
51
|
+
await server.cli(['backup', 'create', archive])
|
|
52
|
+
|
|
53
|
+
const target = makeHome()
|
|
54
|
+
// `makeHome` returns an empty dir; drop a file so restore sees non-empty.
|
|
55
|
+
writeFileSync(join(target.home, 'stray.txt'), 'do not destroy')
|
|
56
|
+
const res = await runCli(['backup', 'restore', archive, '--home', target.home], target.home)
|
|
57
|
+
expect(res.exitCode).not.toBe(0)
|
|
58
|
+
expect(res.stderr + res.stdout).toMatch(/not empty.*--force/)
|
|
59
|
+
// File still there
|
|
60
|
+
expect(existsSync(join(target.home, 'stray.txt'))).toBe(true)
|
|
61
|
+
|
|
62
|
+
rmSync(archive, { force: true })
|
|
63
|
+
target.cleanup()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('backup restore --force overwrites a non-empty target', async () => {
|
|
67
|
+
writeFileSync(join(server.home, 'fresh.txt'), 'from-backup')
|
|
68
|
+
const archive = join(tmpdir(), `bz-backup-${Date.now()}.tar.gz`)
|
|
69
|
+
await server.cli(['backup', 'create', archive])
|
|
70
|
+
|
|
71
|
+
const target = makeHome()
|
|
72
|
+
writeFileSync(join(target.home, 'stray.txt'), 'old data')
|
|
73
|
+
const res = await runCli(
|
|
74
|
+
['backup', 'restore', archive, '--home', target.home, '--force'],
|
|
75
|
+
target.home,
|
|
76
|
+
)
|
|
77
|
+
expect(res.exitCode).toBe(0)
|
|
78
|
+
// The stray file is gone, backup content landed
|
|
79
|
+
expect(existsSync(join(target.home, 'stray.txt'))).toBe(false)
|
|
80
|
+
expect(existsSync(join(target.home, 'fresh.txt'))).toBe(true)
|
|
81
|
+
|
|
82
|
+
rmSync(archive, { force: true })
|
|
83
|
+
target.cleanup()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('backup restore errors clearly on a missing file', async () => {
|
|
87
|
+
const target = makeHome()
|
|
88
|
+
const res = await runCli(
|
|
89
|
+
['backup', 'restore', '/definitely/not/a/real/path.tar.gz', '--home', target.home],
|
|
90
|
+
target.home,
|
|
91
|
+
)
|
|
92
|
+
expect(res.exitCode).not.toBe(0)
|
|
93
|
+
expect(res.stderr + res.stdout).toMatch(/backup file not found/)
|
|
94
|
+
target.cleanup()
|
|
95
|
+
})
|