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,24 @@
|
|
|
1
|
+
import type { Message, SendMessageRequest } from '@bazilion/api-types'
|
|
2
|
+
import { defineCommand } from 'citty'
|
|
3
|
+
import { createClient } from '../client.ts'
|
|
4
|
+
|
|
5
|
+
export const sendCommand = defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: 'send',
|
|
8
|
+
description: 'Send a message from one agent to another',
|
|
9
|
+
},
|
|
10
|
+
args: {
|
|
11
|
+
from: { type: 'positional', required: true, description: 'Sender agent id' },
|
|
12
|
+
to: { type: 'positional', required: true, description: 'Recipient agent id' },
|
|
13
|
+
message: { type: 'positional', required: true, description: 'Message text' },
|
|
14
|
+
},
|
|
15
|
+
async run({ args }) {
|
|
16
|
+
const client = createClient()
|
|
17
|
+
const body: SendMessageRequest = {
|
|
18
|
+
from: args.from,
|
|
19
|
+
payload: { text: args.message },
|
|
20
|
+
}
|
|
21
|
+
const msg = await client.post<Message>(`/api/agents/${args.to}/messages`, body)
|
|
22
|
+
console.log(`sent message ${msg.id}`)
|
|
23
|
+
},
|
|
24
|
+
})
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { defineCommand } from 'citty'
|
|
5
|
+
|
|
6
|
+
// apps/cli/src/commands/serve.ts → apps/daemon/src/index.ts
|
|
7
|
+
const daemonEntry = join(import.meta.dirname, '..', '..', '..', 'daemon', 'src', 'index.ts')
|
|
8
|
+
|
|
9
|
+
export const serveCommand = defineCommand({
|
|
10
|
+
meta: {
|
|
11
|
+
name: 'serve',
|
|
12
|
+
description: 'Start the bazilion daemon (HTTP API)',
|
|
13
|
+
},
|
|
14
|
+
args: {
|
|
15
|
+
port: { type: 'string', description: 'Port (default 4321)' },
|
|
16
|
+
host: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Host (default 127.0.0.1). Put a TLS proxy in front before binding 0.0.0.0',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
async run({ args }) {
|
|
22
|
+
if (!existsSync(daemonEntry)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`bazilion daemon not found at ${daemonEntry}. Are you running from a development checkout?`,
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const env: NodeJS.ProcessEnv = { ...process.env }
|
|
29
|
+
if (args.port) env.PORT = args.port
|
|
30
|
+
if (args.host) env.HOST = args.host
|
|
31
|
+
|
|
32
|
+
const host = args.host ?? '127.0.0.1'
|
|
33
|
+
const port = args.port ?? '4321'
|
|
34
|
+
|
|
35
|
+
if (host !== '127.0.0.1' && host !== 'localhost' && host !== '::1') {
|
|
36
|
+
console.error('')
|
|
37
|
+
console.error(`⚠ binding to ${host} — the daemon is now reachable beyond loopback.`)
|
|
38
|
+
console.error(' anyone on this network who has a valid token can reach every API.')
|
|
39
|
+
console.error(' put a TLS proxy in front for untrusted networks.')
|
|
40
|
+
console.error('')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(`starting bazilion daemon at http://${host}:${port}`)
|
|
44
|
+
console.log('(web UI runs separately: cd apps/web && pnpm dev)')
|
|
45
|
+
|
|
46
|
+
// Spawn the daemon under tsx. Stdio inherited so the daemon's startup log
|
|
47
|
+
// and any console.error go to the user's terminal directly. The daemon
|
|
48
|
+
// installs its own SIGINT/SIGTERM handlers and shuts the HTTP server
|
|
49
|
+
// gracefully — we just need to keep this process alive long enough for it.
|
|
50
|
+
//
|
|
51
|
+
// No vite anywhere in this tree → the daemon never flips the TTY into raw
|
|
52
|
+
// mode, so the post-Ctrl+C arrow-key-echo bug is gone by construction.
|
|
53
|
+
const proc = spawn('node', ['--import', 'tsx/esm', daemonEntry], {
|
|
54
|
+
env,
|
|
55
|
+
stdio: 'inherit',
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
let signalled = false
|
|
59
|
+
const onSigterm = (): void => {
|
|
60
|
+
signalled = true
|
|
61
|
+
try {
|
|
62
|
+
proc.kill('SIGTERM')
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
// SIGINT from the kernel (Ctrl+C) goes to the foreground pgroup, so the
|
|
66
|
+
// daemon already gets it directly — we just have to stay alive.
|
|
67
|
+
process.on('SIGINT', () => {
|
|
68
|
+
signalled = true
|
|
69
|
+
})
|
|
70
|
+
process.on('SIGTERM', onSigterm)
|
|
71
|
+
|
|
72
|
+
await new Promise<void>((resolve, reject) => {
|
|
73
|
+
proc.on('error', (err) => reject(err))
|
|
74
|
+
proc.on('close', (code) => {
|
|
75
|
+
// signalled exits get translated to 0 so the outer `pnpm tsx … serve`
|
|
76
|
+
// wrapper doesn't print `ELIFECYCLE Command failed` after a clean Ctrl+C.
|
|
77
|
+
if (signalled) {
|
|
78
|
+
process.exitCode = 0
|
|
79
|
+
resolve()
|
|
80
|
+
} else if (code === 0 || code === null) {
|
|
81
|
+
resolve()
|
|
82
|
+
} else {
|
|
83
|
+
process.exitCode = code
|
|
84
|
+
resolve()
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
},
|
|
89
|
+
})
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from 'node:fs'
|
|
2
|
+
import { basename, resolve } from 'node:path'
|
|
3
|
+
import type {
|
|
4
|
+
ImportSkillsRequest,
|
|
5
|
+
ImportSkillsResponse,
|
|
6
|
+
ResolvedSkillsResponse,
|
|
7
|
+
SkillInfo,
|
|
8
|
+
} from '@bazilion/api-types'
|
|
9
|
+
import { defineCommand } from 'citty'
|
|
10
|
+
import { createClient } from '../client.ts'
|
|
11
|
+
import { columnize } from '../columnize.ts'
|
|
12
|
+
|
|
13
|
+
const listCmd = defineCommand({
|
|
14
|
+
meta: { name: 'list', description: 'List installed skills (or those attached to an agent)' },
|
|
15
|
+
args: {
|
|
16
|
+
agent: { type: 'string', description: 'Filter to skills attached to this agent' },
|
|
17
|
+
},
|
|
18
|
+
async run({ args }) {
|
|
19
|
+
const client = createClient()
|
|
20
|
+
|
|
21
|
+
if (args.agent) {
|
|
22
|
+
const set = await client.get<ResolvedSkillsResponse>(`/api/agents/${args.agent}/skills`)
|
|
23
|
+
if (set.resolved.length === 0 && set.missing.length === 0) {
|
|
24
|
+
console.log('(no skills attached to this agent)')
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
const rows = set.resolved.map((s) => [s.name, s.description])
|
|
28
|
+
for (const line of columnize(rows)) console.log(line)
|
|
29
|
+
for (const m of set.missing) {
|
|
30
|
+
console.log(`(missing: ${m.name} — ${m.reason})`)
|
|
31
|
+
}
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const skills = await client.get<SkillInfo[]>('/api/skills')
|
|
36
|
+
if (skills.length === 0) {
|
|
37
|
+
console.log('(no skills installed; use "bazilion skill import" to add some)')
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
const rows = skills.map((s) => [
|
|
41
|
+
s.name,
|
|
42
|
+
s.parseError ? `(parse error: ${s.parseError})` : s.description,
|
|
43
|
+
])
|
|
44
|
+
for (const line of columnize(rows)) console.log(line)
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const importCmd = defineCommand({
|
|
49
|
+
meta: {
|
|
50
|
+
name: 'import',
|
|
51
|
+
description: 'Import skills from openclaw, a directory, or a local .zip archive',
|
|
52
|
+
},
|
|
53
|
+
args: {
|
|
54
|
+
from: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
required: true,
|
|
57
|
+
description:
|
|
58
|
+
'Source: "openclaw", a filesystem path on the server, or a local .zip file to upload',
|
|
59
|
+
},
|
|
60
|
+
force: { type: 'boolean', description: 'Overwrite existing skills' },
|
|
61
|
+
},
|
|
62
|
+
async run({ args }) {
|
|
63
|
+
const client = createClient()
|
|
64
|
+
|
|
65
|
+
// Auto-detect local .zip uploads: if `--from` points at an existing local
|
|
66
|
+
// file ending in .zip, stream it to the server as multipart. Otherwise
|
|
67
|
+
// fall through to the legacy JSON path (server reads the source directly).
|
|
68
|
+
const absFrom = resolve(args.from)
|
|
69
|
+
const isLocalZip =
|
|
70
|
+
args.from.toLowerCase().endsWith('.zip') && existsSync(absFrom) && statSync(absFrom).isFile()
|
|
71
|
+
|
|
72
|
+
let result: ImportSkillsResponse
|
|
73
|
+
if (isLocalZip) {
|
|
74
|
+
const bytes = readFileSync(absFrom)
|
|
75
|
+
const file = new File([bytes], basename(absFrom), { type: 'application/zip' })
|
|
76
|
+
const fd = new FormData()
|
|
77
|
+
fd.set('file', file)
|
|
78
|
+
if (args.force) fd.set('force', 'true')
|
|
79
|
+
result = await client.postMultipart<ImportSkillsResponse>('/api/skills/import', fd)
|
|
80
|
+
} else {
|
|
81
|
+
const body: ImportSkillsRequest = {
|
|
82
|
+
source: args.from,
|
|
83
|
+
force: args.force,
|
|
84
|
+
}
|
|
85
|
+
result = await client.post<ImportSkillsResponse>('/api/skills/import', body)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (result.imported.length === 0 && result.skipped.length === 0) {
|
|
89
|
+
console.log('(nothing to import)')
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
for (const name of result.imported) {
|
|
93
|
+
console.log(`imported ${name}`)
|
|
94
|
+
}
|
|
95
|
+
for (const s of result.skipped) {
|
|
96
|
+
console.log(`skipped ${s.name}: ${s.reason}`)
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const rmCmd = defineCommand({
|
|
102
|
+
meta: { name: 'rm', description: 'Remove an installed skill' },
|
|
103
|
+
args: {
|
|
104
|
+
name: { type: 'positional', required: true },
|
|
105
|
+
},
|
|
106
|
+
async run({ args }) {
|
|
107
|
+
const client = createClient()
|
|
108
|
+
await client.del(`/api/skills/${encodeURIComponent(args.name)}`)
|
|
109
|
+
console.log(`removed skill ${args.name}`)
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
export const skillCommand = defineCommand({
|
|
114
|
+
meta: { name: 'skill', description: 'Manage the skill library' },
|
|
115
|
+
subCommands: {
|
|
116
|
+
list: listCmd,
|
|
117
|
+
import: importCmd,
|
|
118
|
+
rm: rmCmd,
|
|
119
|
+
},
|
|
120
|
+
})
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { networkInterfaces } from 'node:os'
|
|
2
|
+
import type {
|
|
3
|
+
CreateTokenRequest,
|
|
4
|
+
CreateTokenResponse,
|
|
5
|
+
ListTokensResponse,
|
|
6
|
+
WebToken,
|
|
7
|
+
} from '@bazilion/api-types'
|
|
8
|
+
import { defineCommand } from 'citty'
|
|
9
|
+
import qrcode from 'qrcode-terminal'
|
|
10
|
+
import { readAuthFile } from '../auth-file.ts'
|
|
11
|
+
import { createClient, loadClientConfig } from '../client.ts'
|
|
12
|
+
import { columnize } from '../columnize.ts'
|
|
13
|
+
import { resolveCliPaths } from '../paths.ts'
|
|
14
|
+
|
|
15
|
+
function tokenRow(t: WebToken): string[] {
|
|
16
|
+
const last = t.lastUsedAt ? new Date(t.lastUsedAt).toISOString() : '(never)'
|
|
17
|
+
const state = t.revokedAt ? 'revoked' : 'active'
|
|
18
|
+
return [t.id, state, t.label, `last: ${last}`]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Best-effort LAN host detection for QR pairing. A phone can't reach
|
|
23
|
+
* `127.0.0.1`, so when the server URL is loopback we swap in a routable
|
|
24
|
+
* interface address. Multi-NIC machines get a warning listing every
|
|
25
|
+
* candidate — the user can override with --server.
|
|
26
|
+
*/
|
|
27
|
+
function detectLanOrigin(port: string): { origin: string; warning?: string } | null {
|
|
28
|
+
const candidates: string[] = []
|
|
29
|
+
for (const ifs of Object.values(networkInterfaces())) {
|
|
30
|
+
for (const i of ifs ?? []) {
|
|
31
|
+
if (i.family === 'IPv4' && !i.internal) candidates.push(i.address)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const [first, ...rest] = candidates
|
|
35
|
+
if (!first) return null
|
|
36
|
+
const origin = `http://${first}:${port}`
|
|
37
|
+
return rest.length > 0
|
|
38
|
+
? { origin, warning: `multiple LAN IPs found (${candidates.join(', ')}) — using ${first}` }
|
|
39
|
+
: { origin }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function resolveQrServer(override: string | undefined): string {
|
|
43
|
+
if (override) return override.replace(/\/$/, '')
|
|
44
|
+
|
|
45
|
+
const cfg = loadClientConfig()
|
|
46
|
+
const current = new URL(cfg.serverUrl)
|
|
47
|
+
const isLoopback =
|
|
48
|
+
current.hostname === '127.0.0.1' ||
|
|
49
|
+
current.hostname === 'localhost' ||
|
|
50
|
+
current.hostname === '::1'
|
|
51
|
+
|
|
52
|
+
if (!isLoopback) return cfg.serverUrl.replace(/\/$/, '')
|
|
53
|
+
|
|
54
|
+
const detected = detectLanOrigin(current.port || '4321')
|
|
55
|
+
if (!detected) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
'server URL is loopback-only and no LAN interface was found. ' +
|
|
58
|
+
'Pass --server http://<host>:<port> so the mobile client knows where to connect.',
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
if (detected.warning) console.warn(`⚠ ${detected.warning}`)
|
|
62
|
+
return detected.origin
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const createCmd = defineCommand({
|
|
66
|
+
meta: { name: 'create', description: 'Mint a new web token (shown once)' },
|
|
67
|
+
args: {
|
|
68
|
+
label: { type: 'positional', required: true, description: 'Human-readable label' },
|
|
69
|
+
qr: {
|
|
70
|
+
type: 'boolean',
|
|
71
|
+
description: 'Also render a pairing QR code for mobile clients (bazilion://pair?...)',
|
|
72
|
+
},
|
|
73
|
+
server: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description:
|
|
76
|
+
'Server URL to embed in the pairing QR (default: detect LAN IP; ignored without --qr)',
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
async run({ args }) {
|
|
80
|
+
const client = createClient()
|
|
81
|
+
const body: CreateTokenRequest = { label: args.label }
|
|
82
|
+
const res = await client.post<CreateTokenResponse>('/api/tokens', body)
|
|
83
|
+
console.log(`id: ${res.meta.id}`)
|
|
84
|
+
console.log(`label: ${res.meta.label}`)
|
|
85
|
+
console.log(`token: ${res.token}`)
|
|
86
|
+
console.log('')
|
|
87
|
+
console.log('store the token now — it is not recoverable later.')
|
|
88
|
+
|
|
89
|
+
if (!args.qr) return
|
|
90
|
+
|
|
91
|
+
const serverUrl = resolveQrServer(args.server)
|
|
92
|
+
const pairUrl = `bazilion://pair?server=${encodeURIComponent(serverUrl)}&token=${encodeURIComponent(res.token)}`
|
|
93
|
+
console.log('')
|
|
94
|
+
console.log(`pairing URL: ${pairUrl}`)
|
|
95
|
+
console.log('')
|
|
96
|
+
qrcode.generate(pairUrl, { small: true }, (qr) => console.log(qr))
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const listCmd = defineCommand({
|
|
101
|
+
meta: { name: 'list', description: 'List web tokens' },
|
|
102
|
+
args: {
|
|
103
|
+
all: { type: 'boolean', description: 'Include revoked tokens' },
|
|
104
|
+
},
|
|
105
|
+
async run({ args }) {
|
|
106
|
+
const client = createClient()
|
|
107
|
+
const qs = args.all ? '?includeRevoked=1' : ''
|
|
108
|
+
const { tokens } = await client.get<ListTokensResponse>(`/api/tokens${qs}`)
|
|
109
|
+
if (tokens.length === 0) {
|
|
110
|
+
console.log('(no tokens)')
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
for (const line of columnize(tokens.map(tokenRow))) console.log(line)
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const showLocalCmd = defineCommand({
|
|
118
|
+
meta: {
|
|
119
|
+
name: 'show-local',
|
|
120
|
+
description: 'Print the bootstrap web token stored in ~/.bazilion/auth.json',
|
|
121
|
+
},
|
|
122
|
+
run() {
|
|
123
|
+
const paths = resolveCliPaths()
|
|
124
|
+
console.log(readAuthFile(paths.authFile).token)
|
|
125
|
+
},
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const revokeCmd = defineCommand({
|
|
129
|
+
meta: { name: 'revoke', description: 'Revoke a web token' },
|
|
130
|
+
args: {
|
|
131
|
+
id: { type: 'positional', required: true },
|
|
132
|
+
},
|
|
133
|
+
async run({ args }) {
|
|
134
|
+
const client = createClient()
|
|
135
|
+
await client.del(`/api/tokens/${args.id}`)
|
|
136
|
+
console.log(`revoked token ${args.id}`)
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
export const tokenCommand = defineCommand({
|
|
141
|
+
meta: { name: 'token', description: 'Manage web tokens for API/CLI clients' },
|
|
142
|
+
subCommands: {
|
|
143
|
+
create: createCmd,
|
|
144
|
+
list: listCmd,
|
|
145
|
+
revoke: revokeCmd,
|
|
146
|
+
'show-local': showLocalCmd,
|
|
147
|
+
},
|
|
148
|
+
})
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentTrigger,
|
|
3
|
+
CreateTriggerRequest,
|
|
4
|
+
CreateTriggerResponse,
|
|
5
|
+
ListTriggersResponse,
|
|
6
|
+
UpdateTriggerRequest,
|
|
7
|
+
} from '@bazilion/api-types'
|
|
8
|
+
import { defineCommand } from 'citty'
|
|
9
|
+
import { createClient } from '../client.ts'
|
|
10
|
+
import { columnize } from '../columnize.ts'
|
|
11
|
+
|
|
12
|
+
const addCmd = defineCommand({
|
|
13
|
+
meta: { name: 'add', description: 'Add a heartbeat / cron trigger to an agent' },
|
|
14
|
+
args: {
|
|
15
|
+
agent: { type: 'positional', required: true },
|
|
16
|
+
every: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Interval in seconds (e.g. --every 300 = every 5 minutes)',
|
|
19
|
+
},
|
|
20
|
+
cron: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Cron expression (5 fields: minute hour dom month dow)',
|
|
23
|
+
},
|
|
24
|
+
message: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
required: true,
|
|
27
|
+
description: 'Message to inject when the trigger fires',
|
|
28
|
+
},
|
|
29
|
+
disabled: { type: 'boolean', description: 'Create in disabled state' },
|
|
30
|
+
},
|
|
31
|
+
async run({ args }) {
|
|
32
|
+
if (!args.every === !args.cron) {
|
|
33
|
+
throw new Error('specify exactly one of --every <seconds> or --cron "<expr>"')
|
|
34
|
+
}
|
|
35
|
+
const client = createClient()
|
|
36
|
+
const body: CreateTriggerRequest = args.every
|
|
37
|
+
? {
|
|
38
|
+
kind: 'interval',
|
|
39
|
+
intervalSec: Number(args.every),
|
|
40
|
+
message: args.message,
|
|
41
|
+
enabled: !args.disabled,
|
|
42
|
+
}
|
|
43
|
+
: {
|
|
44
|
+
kind: 'cron',
|
|
45
|
+
cronExpr: args.cron as string,
|
|
46
|
+
message: args.message,
|
|
47
|
+
enabled: !args.disabled,
|
|
48
|
+
}
|
|
49
|
+
const res = await client.post<CreateTriggerResponse>(`/api/agents/${args.agent}/triggers`, body)
|
|
50
|
+
const t = res.trigger
|
|
51
|
+
const spec = t.kind === 'interval' ? `every ${t.intervalSec}s` : `cron "${t.cronExpr}"`
|
|
52
|
+
console.log(`${t.id}\t${spec}\t${t.enabled ? 'enabled' : 'disabled'}`)
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
function triggerRow(t: AgentTrigger): string[] {
|
|
57
|
+
const spec = t.kind === 'interval' ? `every ${t.intervalSec}s` : `cron "${t.cronExpr}"`
|
|
58
|
+
const last = t.lastFiredAt ? new Date(t.lastFiredAt).toISOString() : '(never)'
|
|
59
|
+
const state = t.enabled ? 'enabled' : 'disabled'
|
|
60
|
+
const msgPreview = t.message.length > 60 ? `${t.message.slice(0, 60)}…` : t.message
|
|
61
|
+
return [t.id, state, spec, `last: ${last}`, `"${msgPreview}"`]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const listCmd = defineCommand({
|
|
65
|
+
meta: { name: 'list', description: 'List triggers for an agent' },
|
|
66
|
+
args: {
|
|
67
|
+
agent: { type: 'positional', required: true },
|
|
68
|
+
},
|
|
69
|
+
async run({ args }) {
|
|
70
|
+
const client = createClient()
|
|
71
|
+
const { triggers } = await client.get<ListTriggersResponse>(
|
|
72
|
+
`/api/agents/${args.agent}/triggers`,
|
|
73
|
+
)
|
|
74
|
+
if (triggers.length === 0) {
|
|
75
|
+
console.log('(no triggers)')
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
for (const line of columnize(triggers.map(triggerRow))) console.log(line)
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const rmCmd = defineCommand({
|
|
83
|
+
meta: { name: 'rm', description: 'Delete a trigger' },
|
|
84
|
+
args: {
|
|
85
|
+
id: { type: 'positional', required: true },
|
|
86
|
+
},
|
|
87
|
+
async run({ args }) {
|
|
88
|
+
const client = createClient()
|
|
89
|
+
await client.del(`/api/triggers/${args.id}`)
|
|
90
|
+
console.log(`removed trigger ${args.id}`)
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const enableCmd = defineCommand({
|
|
95
|
+
meta: { name: 'enable', description: 'Enable a trigger' },
|
|
96
|
+
args: {
|
|
97
|
+
id: { type: 'positional', required: true },
|
|
98
|
+
},
|
|
99
|
+
async run({ args }) {
|
|
100
|
+
const client = createClient()
|
|
101
|
+
const body: UpdateTriggerRequest = { enabled: true }
|
|
102
|
+
await client.patch(`/api/triggers/${args.id}`, body)
|
|
103
|
+
console.log(`enabled trigger ${args.id}`)
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const disableCmd = defineCommand({
|
|
108
|
+
meta: { name: 'disable', description: 'Disable a trigger' },
|
|
109
|
+
args: {
|
|
110
|
+
id: { type: 'positional', required: true },
|
|
111
|
+
},
|
|
112
|
+
async run({ args }) {
|
|
113
|
+
const client = createClient()
|
|
114
|
+
const body: UpdateTriggerRequest = { enabled: false }
|
|
115
|
+
await client.patch(`/api/triggers/${args.id}`, body)
|
|
116
|
+
console.log(`disabled trigger ${args.id}`)
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
export const triggerCommand = defineCommand({
|
|
121
|
+
meta: { name: 'trigger', description: 'Manage agent heartbeats / cron triggers' },
|
|
122
|
+
subCommands: {
|
|
123
|
+
add: addCmd,
|
|
124
|
+
list: listCmd,
|
|
125
|
+
rm: rmCmd,
|
|
126
|
+
enable: enableCmd,
|
|
127
|
+
disable: disableCmd,
|
|
128
|
+
},
|
|
129
|
+
})
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { existsSync, readdirSync, rmSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { defineCommand } from 'citty'
|
|
4
|
+
import { resolveCliPaths } from '../paths.ts'
|
|
5
|
+
|
|
6
|
+
// Line-buffered stdin reader. `node:readline/promises` drops data after EOF
|
|
7
|
+
// on piped input (a known Node quirk with back-to-back `question()` calls),
|
|
8
|
+
// so we read chunks ourselves and emit one line per call.
|
|
9
|
+
function makeLineReader(): () => Promise<string> {
|
|
10
|
+
let buffer = ''
|
|
11
|
+
let ended = false
|
|
12
|
+
const queue: ((line: string) => void)[] = []
|
|
13
|
+
|
|
14
|
+
const tryDeliver = (): void => {
|
|
15
|
+
while (queue.length > 0) {
|
|
16
|
+
const nlIdx = buffer.indexOf('\n')
|
|
17
|
+
if (nlIdx === -1) {
|
|
18
|
+
if (ended) {
|
|
19
|
+
const resolve = queue.shift()
|
|
20
|
+
resolve?.(buffer)
|
|
21
|
+
buffer = ''
|
|
22
|
+
} else {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
const line = buffer.slice(0, nlIdx)
|
|
27
|
+
buffer = buffer.slice(nlIdx + 1)
|
|
28
|
+
const resolve = queue.shift()
|
|
29
|
+
resolve?.(line)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
process.stdin.setEncoding('utf8')
|
|
35
|
+
process.stdin.on('data', (chunk) => {
|
|
36
|
+
buffer += chunk
|
|
37
|
+
tryDeliver()
|
|
38
|
+
})
|
|
39
|
+
process.stdin.on('end', () => {
|
|
40
|
+
ended = true
|
|
41
|
+
tryDeliver()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return () =>
|
|
45
|
+
new Promise<string>((resolve) => {
|
|
46
|
+
queue.push(resolve)
|
|
47
|
+
tryDeliver()
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function askYes(readLine: () => Promise<string>, prompt: string): Promise<boolean> {
|
|
52
|
+
process.stdout.write(`${prompt} [y/N] `)
|
|
53
|
+
const answer = (await readLine()).trim().toLowerCase()
|
|
54
|
+
return answer === 'y' || answer === 'yes'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function removePath(p: string): boolean {
|
|
58
|
+
if (!existsSync(p)) return false
|
|
59
|
+
rmSync(p, { recursive: true, force: true })
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const uninstallCommand = defineCommand({
|
|
64
|
+
meta: {
|
|
65
|
+
name: 'uninstall',
|
|
66
|
+
description: 'Wipe bazilion state from ~/.bazilion (or BAZILION_HOME)',
|
|
67
|
+
},
|
|
68
|
+
args: {
|
|
69
|
+
home: { type: 'string', description: 'Override BAZILION_HOME' },
|
|
70
|
+
yes: {
|
|
71
|
+
type: 'boolean',
|
|
72
|
+
description: 'Skip confirmations (data tier only unless --all is set)',
|
|
73
|
+
},
|
|
74
|
+
all: {
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
description: 'Also remove configs, logs, and skills (full wipe)',
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
async run({ args }) {
|
|
80
|
+
const paths = resolveCliPaths(args.home)
|
|
81
|
+
const dbFile = join(paths.home, 'bazilion.db')
|
|
82
|
+
const profilesDir = join(paths.home, 'profiles')
|
|
83
|
+
const agentsDir = join(paths.home, 'agents')
|
|
84
|
+
const groupsDir = join(paths.home, 'groups')
|
|
85
|
+
const skillsDir = join(paths.home, 'skills')
|
|
86
|
+
const logsDir = join(paths.home, 'logs')
|
|
87
|
+
|
|
88
|
+
if (!existsSync(paths.home)) {
|
|
89
|
+
console.log(`nothing to remove: ${paths.home} does not exist`)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(`about to uninstall bazilion at ${paths.home}`)
|
|
94
|
+
console.log('')
|
|
95
|
+
|
|
96
|
+
const dataTargets = [
|
|
97
|
+
dbFile,
|
|
98
|
+
`${dbFile}-wal`,
|
|
99
|
+
`${dbFile}-shm`,
|
|
100
|
+
profilesDir,
|
|
101
|
+
agentsDir,
|
|
102
|
+
groupsDir,
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
const needsPrompt = !args.yes
|
|
106
|
+
const readLine = needsPrompt ? makeLineReader() : null
|
|
107
|
+
const wipeData =
|
|
108
|
+
args.yes ||
|
|
109
|
+
(await askYes(
|
|
110
|
+
readLine as () => Promise<string>,
|
|
111
|
+
'remove DB + agent / profile / workspace data?',
|
|
112
|
+
))
|
|
113
|
+
if (!wipeData) {
|
|
114
|
+
console.log('aborted')
|
|
115
|
+
process.stdin.pause()
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Tier 2: ask only after data wipe is confirmed — configs/logs/skills are
|
|
120
|
+
// reusable across reinstalls, so the two-tier split lets an operator
|
|
121
|
+
// factory-reset agents while keeping credentials + imported skills.
|
|
122
|
+
const wipeAll =
|
|
123
|
+
args.all ||
|
|
124
|
+
(args.yes
|
|
125
|
+
? false
|
|
126
|
+
: await askYes(
|
|
127
|
+
readLine as () => Promise<string>,
|
|
128
|
+
'also remove configs, logs, and skills? (full wipe of ~/.bazilion)',
|
|
129
|
+
))
|
|
130
|
+
|
|
131
|
+
process.stdin.pause()
|
|
132
|
+
|
|
133
|
+
for (const p of dataTargets) {
|
|
134
|
+
if (removePath(p)) console.log(`removed ${p}`)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (wipeAll) {
|
|
138
|
+
const configTargets = [paths.authFile, logsDir, skillsDir]
|
|
139
|
+
for (const p of configTargets) {
|
|
140
|
+
if (removePath(p)) console.log(`removed ${p}`)
|
|
141
|
+
}
|
|
142
|
+
// If nothing else is left in $BAZILION_HOME, drop the empty dir too so
|
|
143
|
+
// a subsequent `bazilion serve` re-bootstraps into a truly fresh home.
|
|
144
|
+
if (readdirSync(paths.home).length === 0) {
|
|
145
|
+
rmSync(paths.home, { recursive: true, force: true })
|
|
146
|
+
console.log(`removed ${paths.home}`)
|
|
147
|
+
} else {
|
|
148
|
+
console.log(`${paths.home} still has unmanaged files; left in place`)
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
console.log('')
|
|
152
|
+
console.log('kept: auth.json, logs/, skills/')
|
|
153
|
+
console.log(`re-run 'bazilion serve' to recreate the DB when you're ready.`)
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
})
|