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,82 @@
|
|
|
1
|
+
import type { ListInboxResponse, Message } from '@bazilion/api-types'
|
|
2
|
+
import { defineCommand } from 'citty'
|
|
3
|
+
import { createClient } from '../client.ts'
|
|
4
|
+
import { columnize } from '../columnize.ts'
|
|
5
|
+
|
|
6
|
+
function decodeText(payload: string): string {
|
|
7
|
+
try {
|
|
8
|
+
const obj = JSON.parse(payload) as { text?: unknown }
|
|
9
|
+
if (typeof obj.text === 'string') return obj.text
|
|
10
|
+
} catch {}
|
|
11
|
+
return payload
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function messageRow(m: Message): string[] {
|
|
15
|
+
const when = new Date(m.createdAt).toISOString()
|
|
16
|
+
const state = m.readAt ? 'read' : 'NEW'
|
|
17
|
+
const text = decodeText(m.payload)
|
|
18
|
+
const preview = text.length > 60 ? `${text.slice(0, 60)}…` : text
|
|
19
|
+
return [m.id, state, when, `from:${m.fromAgentId.slice(0, 8)}`, `"${preview}"`]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const listCmd = defineCommand({
|
|
23
|
+
meta: { name: 'list', description: 'List messages in an agent inbox' },
|
|
24
|
+
args: {
|
|
25
|
+
agent: { type: 'positional', required: true, description: 'Recipient agent id' },
|
|
26
|
+
unread: { type: 'boolean', description: 'Only show unread messages' },
|
|
27
|
+
},
|
|
28
|
+
async run({ args }) {
|
|
29
|
+
const client = createClient()
|
|
30
|
+
const qs = args.unread ? '?unread=1' : ''
|
|
31
|
+
const { messages } = await client.get<ListInboxResponse>(
|
|
32
|
+
`/api/agents/${args.agent}/messages${qs}`,
|
|
33
|
+
)
|
|
34
|
+
if (messages.length === 0) {
|
|
35
|
+
console.log(args.unread ? '(no unread messages)' : '(inbox empty)')
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
for (const line of columnize(messages.map(messageRow))) console.log(line)
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const showCmd = defineCommand({
|
|
43
|
+
meta: { name: 'show', description: 'Show a single message in full' },
|
|
44
|
+
args: {
|
|
45
|
+
id: { type: 'positional', required: true, description: 'Message id' },
|
|
46
|
+
},
|
|
47
|
+
async run({ args }) {
|
|
48
|
+
const client = createClient()
|
|
49
|
+
const m = await client.get<Message>(`/api/messages/${args.id}`)
|
|
50
|
+
const when = new Date(m.createdAt).toISOString()
|
|
51
|
+
const read = m.readAt ? new Date(m.readAt).toISOString() : '(unread)'
|
|
52
|
+
console.log(`id: ${m.id}`)
|
|
53
|
+
console.log(`from: ${m.fromAgentId}`)
|
|
54
|
+
console.log(`to: ${m.toAgentId}`)
|
|
55
|
+
console.log(`created: ${when}`)
|
|
56
|
+
console.log(`read: ${read}`)
|
|
57
|
+
if (m.replyTo) console.log(`reply_to: ${m.replyTo}`)
|
|
58
|
+
console.log('')
|
|
59
|
+
console.log(decodeText(m.payload))
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const readCmd = defineCommand({
|
|
64
|
+
meta: { name: 'read', description: 'Mark a message as read' },
|
|
65
|
+
args: {
|
|
66
|
+
id: { type: 'positional', required: true, description: 'Message id' },
|
|
67
|
+
},
|
|
68
|
+
async run({ args }) {
|
|
69
|
+
const client = createClient()
|
|
70
|
+
await client.patch<Message>(`/api/messages/${args.id}`, { read: true })
|
|
71
|
+
console.log(`marked read: ${args.id}`)
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
export const inboxCommand = defineCommand({
|
|
76
|
+
meta: { name: 'inbox', description: 'Inspect agent inboxes' },
|
|
77
|
+
subCommands: {
|
|
78
|
+
list: listCmd,
|
|
79
|
+
show: showCmd,
|
|
80
|
+
read: readCmd,
|
|
81
|
+
},
|
|
82
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { defineCommand } from 'citty'
|
|
3
|
+
import type { AuthFile } from '../auth-file.ts'
|
|
4
|
+
import { resolveCliPaths } from '../paths.ts'
|
|
5
|
+
|
|
6
|
+
function normalizeServer(raw: string): string {
|
|
7
|
+
let url: URL
|
|
8
|
+
try {
|
|
9
|
+
url = new URL(raw)
|
|
10
|
+
} catch {
|
|
11
|
+
throw new Error(`invalid --server URL: ${raw}`)
|
|
12
|
+
}
|
|
13
|
+
if (!/^https?:$/.test(url.protocol)) {
|
|
14
|
+
throw new Error(`--server must be http(s), got ${url.protocol}`)
|
|
15
|
+
}
|
|
16
|
+
// Strip trailing slash so `${serverUrl}${path}` never produces `//`.
|
|
17
|
+
return `${url.origin}${url.pathname.replace(/\/$/, '')}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function verifyToken(server: string, token: string): Promise<void> {
|
|
21
|
+
const res = await fetch(`${server}/api/health`, {
|
|
22
|
+
headers: { authorization: `Bearer ${token}`, origin: server },
|
|
23
|
+
})
|
|
24
|
+
if (res.status === 401) throw new Error('server rejected token (401)')
|
|
25
|
+
if (!res.ok) throw new Error(`server returned ${res.status} from /api/health`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const loginCommand = defineCommand({
|
|
29
|
+
meta: {
|
|
30
|
+
name: 'login',
|
|
31
|
+
description: 'Save a remote bazilion server + token pair to auth.json',
|
|
32
|
+
},
|
|
33
|
+
args: {
|
|
34
|
+
server: { type: 'string', description: 'Remote server URL (http[s]://...)' },
|
|
35
|
+
token: { type: 'string', description: 'Token issued by `bazilion token create`' },
|
|
36
|
+
skipVerify: { type: 'boolean', description: "Don't probe /api/health before saving" },
|
|
37
|
+
clear: { type: 'boolean', description: 'Remove the stored remote instead of setting one' },
|
|
38
|
+
},
|
|
39
|
+
async run({ args }) {
|
|
40
|
+
const paths = resolveCliPaths()
|
|
41
|
+
if (!existsSync(paths.authFile)) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`${paths.authFile} not found. Start the daemon with "bazilion serve" first — login stores the remote there.`,
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
const auth = JSON.parse(readFileSync(paths.authFile, 'utf8')) as AuthFile
|
|
47
|
+
|
|
48
|
+
if (args.clear) {
|
|
49
|
+
if (!auth.remote) {
|
|
50
|
+
console.log('no remote stored — nothing to clear')
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
delete auth.remote
|
|
54
|
+
writeFileSync(paths.authFile, `${JSON.stringify(auth, null, 2)}\n`, { mode: 0o600 })
|
|
55
|
+
console.log('cleared stored remote; CLI will fall back to the local daemon')
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!args.server || !args.token) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
'--server and --token are required (or pass --clear to remove the stored remote)',
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
const server = normalizeServer(args.server)
|
|
65
|
+
if (!args.skipVerify) {
|
|
66
|
+
await verifyToken(server, args.token)
|
|
67
|
+
}
|
|
68
|
+
auth.remote = { server, token: args.token }
|
|
69
|
+
writeFileSync(paths.authFile, `${JSON.stringify(auth, null, 2)}\n`, { mode: 0o600 })
|
|
70
|
+
console.log(`saved remote: ${server}`)
|
|
71
|
+
console.log('CLI will now target this server unless BAZILION_SERVER is set in the env.')
|
|
72
|
+
},
|
|
73
|
+
})
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { MemoryEntry, MemoryHit } from '@bazilion/api-types'
|
|
2
|
+
import { defineCommand } from 'citty'
|
|
3
|
+
import { createClient } from '../client.ts'
|
|
4
|
+
|
|
5
|
+
function encodeKey(key: string): string {
|
|
6
|
+
return key.split('/').map(encodeURIComponent).join('/')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const writeCmd = defineCommand({
|
|
10
|
+
meta: { name: 'write', description: "Write an entry into a group's shared memory" },
|
|
11
|
+
args: {
|
|
12
|
+
group: { type: 'positional', required: true, description: 'Group slug' },
|
|
13
|
+
key: { type: 'positional', required: true, description: 'Memory key (path under memory/)' },
|
|
14
|
+
content: { type: 'positional', required: true, description: 'Content to write' },
|
|
15
|
+
},
|
|
16
|
+
async run({ args }) {
|
|
17
|
+
const client = createClient()
|
|
18
|
+
const entry = await client.put<MemoryEntry>(
|
|
19
|
+
`/api/groups/${args.group}/memory/${encodeKey(args.key)}`,
|
|
20
|
+
{ content: args.content },
|
|
21
|
+
)
|
|
22
|
+
console.log(`wrote ${entry.key} (${entry.content.length} bytes)`)
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const readCmd = defineCommand({
|
|
27
|
+
meta: { name: 'read', description: "Read an entry from a group's shared memory" },
|
|
28
|
+
args: {
|
|
29
|
+
group: { type: 'positional', required: true, description: 'Group slug' },
|
|
30
|
+
key: { type: 'positional', required: true },
|
|
31
|
+
},
|
|
32
|
+
async run({ args }) {
|
|
33
|
+
const client = createClient()
|
|
34
|
+
const entry = await client.get<MemoryEntry>(
|
|
35
|
+
`/api/groups/${args.group}/memory/${encodeKey(args.key)}`,
|
|
36
|
+
)
|
|
37
|
+
console.log(entry.content)
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const searchCmd = defineCommand({
|
|
42
|
+
meta: { name: 'search', description: "Search a group's shared memory" },
|
|
43
|
+
args: {
|
|
44
|
+
group: { type: 'positional', required: true, description: 'Group slug' },
|
|
45
|
+
query: { type: 'positional', required: true },
|
|
46
|
+
limit: { type: 'string', description: 'Max results (default 10)' },
|
|
47
|
+
},
|
|
48
|
+
async run({ args }) {
|
|
49
|
+
const client = createClient()
|
|
50
|
+
const limit = args.limit ? Number.parseInt(args.limit, 10) : 10
|
|
51
|
+
const params = new URLSearchParams({ q: args.query, limit: String(limit) })
|
|
52
|
+
const hits = await client.get<MemoryHit[]>(`/api/groups/${args.group}/memory/search?${params}`)
|
|
53
|
+
if (hits.length === 0) {
|
|
54
|
+
console.log('(no matches)')
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
for (const h of hits) {
|
|
58
|
+
console.log(`${h.key}\t${h.snippet.replaceAll('\n', ' ')}`)
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const listCmd = defineCommand({
|
|
64
|
+
meta: { name: 'list', description: "List all entries in a group's shared memory" },
|
|
65
|
+
args: {
|
|
66
|
+
group: { type: 'positional', required: true, description: 'Group slug' },
|
|
67
|
+
},
|
|
68
|
+
async run({ args }) {
|
|
69
|
+
const client = createClient()
|
|
70
|
+
const all = await client.get<MemoryEntry[]>(`/api/groups/${args.group}/memory`)
|
|
71
|
+
if (all.length === 0) {
|
|
72
|
+
console.log('(no memory entries)')
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
for (const e of all) {
|
|
76
|
+
console.log(`${e.key}\t${e.content.length}b`)
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const rmCmd = defineCommand({
|
|
82
|
+
meta: { name: 'rm', description: "Remove an entry from a group's shared memory" },
|
|
83
|
+
args: {
|
|
84
|
+
group: { type: 'positional', required: true, description: 'Group slug' },
|
|
85
|
+
key: { type: 'positional', required: true },
|
|
86
|
+
},
|
|
87
|
+
async run({ args }) {
|
|
88
|
+
const client = createClient()
|
|
89
|
+
await client.del(`/api/groups/${args.group}/memory/${encodeKey(args.key)}`)
|
|
90
|
+
console.log(`removed ${args.key}`)
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
export const memoryCommand = defineCommand({
|
|
95
|
+
meta: {
|
|
96
|
+
name: 'memory',
|
|
97
|
+
description: "Manage a group's shared memory (BM25-indexed markdown notes)",
|
|
98
|
+
},
|
|
99
|
+
subCommands: {
|
|
100
|
+
write: writeCmd,
|
|
101
|
+
read: readCmd,
|
|
102
|
+
search: searchCmd,
|
|
103
|
+
list: listCmd,
|
|
104
|
+
rm: rmCmd,
|
|
105
|
+
},
|
|
106
|
+
})
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
import { randomBytes } from 'node:crypto'
|
|
3
|
+
import { readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
|
+
import type {
|
|
7
|
+
CreateProfileRequest,
|
|
8
|
+
FileContentResponse,
|
|
9
|
+
LoadedProfile,
|
|
10
|
+
Profile,
|
|
11
|
+
ProfileFileName,
|
|
12
|
+
PutFileRequest,
|
|
13
|
+
SkillsMode,
|
|
14
|
+
UpdateProfileRequest,
|
|
15
|
+
} from '@bazilion/api-types'
|
|
16
|
+
import { PROFILE_FILES } from '@bazilion/api-types'
|
|
17
|
+
import { defineCommand } from 'citty'
|
|
18
|
+
import { createClient } from '../client.ts'
|
|
19
|
+
import { columnize } from '../columnize.ts'
|
|
20
|
+
|
|
21
|
+
function splitCsv(v: string | undefined): string[] | undefined {
|
|
22
|
+
if (!v) return undefined
|
|
23
|
+
const out = v
|
|
24
|
+
.split(',')
|
|
25
|
+
.map((s) => s.trim())
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
return out.length > 0 ? out : undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseSkillsMode(v: string | undefined): SkillsMode | undefined {
|
|
31
|
+
if (v === undefined) return undefined
|
|
32
|
+
if (v === 'all' || v === 'selected') return v
|
|
33
|
+
throw new Error(`--skills-mode must be 'all' or 'selected', got '${v}'`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const createCmd = defineCommand({
|
|
37
|
+
meta: { name: 'create', description: 'Create a new profile' },
|
|
38
|
+
args: {
|
|
39
|
+
id: { type: 'positional', required: true, description: 'Profile slug' },
|
|
40
|
+
name: { type: 'string', description: 'Display name (defaults to slug)' },
|
|
41
|
+
model: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
required: true,
|
|
44
|
+
description: 'Default model, e.g. anthropic:claude-opus-4-6',
|
|
45
|
+
},
|
|
46
|
+
'skills-mode': {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: "'all' (attach every discovered skill) or 'selected' (default)",
|
|
49
|
+
},
|
|
50
|
+
skills: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
description: 'Default skills, comma-separated (skills-mode=selected)',
|
|
53
|
+
},
|
|
54
|
+
'soul-file': {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'Path to custom SOUL.md content (defaults to the built-in template)',
|
|
57
|
+
},
|
|
58
|
+
'identity-file': {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'Path to custom IDENTITY.md content (defaults to the built-in template)',
|
|
61
|
+
},
|
|
62
|
+
'bootstrap-file': {
|
|
63
|
+
type: 'string',
|
|
64
|
+
description: 'Path to custom BOOTSTRAP.md content (defaults to the built-in template)',
|
|
65
|
+
},
|
|
66
|
+
'skip-bootstrap': {
|
|
67
|
+
type: 'boolean',
|
|
68
|
+
description: 'Skip the bootstrap file entirely (no first-run intro)',
|
|
69
|
+
},
|
|
70
|
+
'agents-file': {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'Path to AGENTS.md content (optional; omit to skip)',
|
|
73
|
+
},
|
|
74
|
+
'tools-file': {
|
|
75
|
+
type: 'string',
|
|
76
|
+
description: 'Path to TOOLS.md content (optional; omit to skip)',
|
|
77
|
+
},
|
|
78
|
+
'heartbeat-file': {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'Path to HEARTBEAT.md content (optional; omit to skip)',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
async run({ args }) {
|
|
84
|
+
const skillsMode = parseSkillsMode(args['skills-mode'])
|
|
85
|
+
const readTemplate = (path: string | undefined): string | undefined =>
|
|
86
|
+
path ? readFileSync(path, 'utf8') : undefined
|
|
87
|
+
const body: CreateProfileRequest = {
|
|
88
|
+
id: args.id,
|
|
89
|
+
name: args.name,
|
|
90
|
+
defaultModel: args.model,
|
|
91
|
+
skillsMode,
|
|
92
|
+
defaultSkills: splitCsv(args.skills),
|
|
93
|
+
soul: readTemplate(args['soul-file']),
|
|
94
|
+
identity: readTemplate(args['identity-file']),
|
|
95
|
+
bootstrap: args['skip-bootstrap']
|
|
96
|
+
? null
|
|
97
|
+
: (readTemplate(args['bootstrap-file']) ?? undefined),
|
|
98
|
+
agents: readTemplate(args['agents-file']),
|
|
99
|
+
tools: readTemplate(args['tools-file']),
|
|
100
|
+
heartbeat: readTemplate(args['heartbeat-file']),
|
|
101
|
+
}
|
|
102
|
+
const client = createClient()
|
|
103
|
+
const profile = await client.post<Profile>('/api/profiles', body)
|
|
104
|
+
console.log(`created profile ${profile.id} at ${profile.dir}`)
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const listCmd = defineCommand({
|
|
109
|
+
meta: { name: 'list', description: 'List profiles' },
|
|
110
|
+
async run() {
|
|
111
|
+
const client = createClient()
|
|
112
|
+
const list = await client.get<Profile[]>('/api/profiles')
|
|
113
|
+
if (list.length === 0) {
|
|
114
|
+
console.log('(no profiles)')
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
const rows = list.map((p) => [p.id, p.defaultModel, p.name])
|
|
118
|
+
for (const line of columnize(rows)) console.log(line)
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const showCmd = defineCommand({
|
|
123
|
+
meta: { name: 'show', description: 'Show profile details' },
|
|
124
|
+
args: {
|
|
125
|
+
id: { type: 'positional', required: true },
|
|
126
|
+
},
|
|
127
|
+
async run({ args }) {
|
|
128
|
+
const client = createClient()
|
|
129
|
+
const loaded = await client.get<LoadedProfile>(`/api/profiles/${args.id}`)
|
|
130
|
+
console.log(`# ${loaded.profile.id}`)
|
|
131
|
+
console.log(`name: ${loaded.profile.name}`)
|
|
132
|
+
console.log(`dir: ${loaded.profile.dir}`)
|
|
133
|
+
console.log(`default model: ${loaded.profile.defaultModel}`)
|
|
134
|
+
console.log(`skills mode: ${loaded.profile.skillsMode}`)
|
|
135
|
+
console.log(
|
|
136
|
+
`default skills: ${
|
|
137
|
+
loaded.defaultSkills.length === 0 ? '(none)' : loaded.defaultSkills.join(', ')
|
|
138
|
+
}`,
|
|
139
|
+
)
|
|
140
|
+
console.log(`bootstrap: ${loaded.files.bootstrap !== null ? 'yes' : 'no'}`)
|
|
141
|
+
console.log(`agents: ${loaded.files.agents !== null ? 'yes' : 'no'}`)
|
|
142
|
+
console.log(`tools: ${loaded.files.tools !== null ? 'yes' : 'no'}`)
|
|
143
|
+
console.log(`heartbeat: ${loaded.files.heartbeat !== null ? 'yes' : 'no'}`)
|
|
144
|
+
if (loaded.identity) {
|
|
145
|
+
const fields = Object.entries(loaded.identity)
|
|
146
|
+
.filter(([, v]) => typeof v === 'string' && v.length > 0)
|
|
147
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
148
|
+
.join(', ')
|
|
149
|
+
if (fields) console.log(`identity: ${fields}`)
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const editCmd = defineCommand({
|
|
155
|
+
meta: { name: 'edit', description: 'Open a profile file in $EDITOR' },
|
|
156
|
+
args: {
|
|
157
|
+
id: { type: 'positional', required: true },
|
|
158
|
+
file: {
|
|
159
|
+
type: 'string',
|
|
160
|
+
default: 'profile.json',
|
|
161
|
+
description: `File: ${PROFILE_FILES.join(', ')}`,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
async run({ args }) {
|
|
165
|
+
if (!(PROFILE_FILES as readonly string[]).includes(args.file)) {
|
|
166
|
+
throw new Error(`unsupported file: ${args.file}. Allowed: ${PROFILE_FILES.join(', ')}`)
|
|
167
|
+
}
|
|
168
|
+
const file = args.file as ProfileFileName
|
|
169
|
+
const client = createClient()
|
|
170
|
+
const { content } = await client.get<FileContentResponse>(
|
|
171
|
+
`/api/profiles/${args.id}/files/${file}`,
|
|
172
|
+
)
|
|
173
|
+
const tmpPath = join(tmpdir(), `bazilion-${args.id}-${file}-${randomBytes(4).toString('hex')}`)
|
|
174
|
+
writeFileSync(tmpPath, content)
|
|
175
|
+
try {
|
|
176
|
+
const editor = process.env.EDITOR ?? process.env.VISUAL ?? 'vi'
|
|
177
|
+
const code: number = await new Promise((resolve, reject) => {
|
|
178
|
+
const child = spawn(editor, [tmpPath], { stdio: 'inherit' })
|
|
179
|
+
child.on('error', reject)
|
|
180
|
+
child.on('close', (c) => resolve(c ?? 0))
|
|
181
|
+
})
|
|
182
|
+
if (code !== 0) throw new Error(`editor exited with code ${code}`)
|
|
183
|
+
const updated = readFileSync(tmpPath, 'utf8')
|
|
184
|
+
if (updated === content) {
|
|
185
|
+
console.log('(no changes)')
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
const body: PutFileRequest = { content: updated }
|
|
189
|
+
await client.put(`/api/profiles/${args.id}/files/${file}`, body)
|
|
190
|
+
console.log(`saved ${file}`)
|
|
191
|
+
} finally {
|
|
192
|
+
try {
|
|
193
|
+
rmSync(tmpPath, { force: true })
|
|
194
|
+
} catch {}
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
const updateCmd = defineCommand({
|
|
200
|
+
meta: {
|
|
201
|
+
name: 'update',
|
|
202
|
+
description: 'Update profile settings (name, model, skills-mode, skills)',
|
|
203
|
+
},
|
|
204
|
+
args: {
|
|
205
|
+
id: { type: 'positional', required: true },
|
|
206
|
+
name: { type: 'string', description: 'New display name' },
|
|
207
|
+
model: { type: 'string', description: 'New default model' },
|
|
208
|
+
'skills-mode': {
|
|
209
|
+
type: 'string',
|
|
210
|
+
description: "Switch to 'all' or 'selected'",
|
|
211
|
+
},
|
|
212
|
+
skills: { type: 'string', description: 'Replacement default skills, comma-separated' },
|
|
213
|
+
},
|
|
214
|
+
async run({ args }) {
|
|
215
|
+
const body: UpdateProfileRequest = {}
|
|
216
|
+
if (args.name !== undefined) body.name = args.name
|
|
217
|
+
if (args.model !== undefined) body.defaultModel = args.model
|
|
218
|
+
const mode = parseSkillsMode(args['skills-mode'])
|
|
219
|
+
if (mode !== undefined) body.skillsMode = mode
|
|
220
|
+
if (args.skills !== undefined) {
|
|
221
|
+
body.defaultSkills = args.skills
|
|
222
|
+
.split(',')
|
|
223
|
+
.map((s) => s.trim())
|
|
224
|
+
.filter(Boolean)
|
|
225
|
+
}
|
|
226
|
+
if (Object.keys(body).length === 0) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
'nothing to update — pass at least one of --name/--model/--skills-mode/--skills',
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
const client = createClient()
|
|
232
|
+
const profile = await client.patch<Profile>(`/api/profiles/${args.id}`, body)
|
|
233
|
+
console.log(`updated profile ${profile.id}`)
|
|
234
|
+
},
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
const deleteCmd = defineCommand({
|
|
238
|
+
meta: { name: 'delete', description: 'Permanently delete a profile and its files' },
|
|
239
|
+
args: {
|
|
240
|
+
id: { type: 'positional', required: true },
|
|
241
|
+
},
|
|
242
|
+
async run({ args }) {
|
|
243
|
+
const client = createClient()
|
|
244
|
+
await client.del(`/api/profiles/${args.id}`)
|
|
245
|
+
console.log(`deleted profile ${args.id}`)
|
|
246
|
+
},
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
export const profileCommand = defineCommand({
|
|
250
|
+
meta: { name: 'profile', description: 'Manage profiles' },
|
|
251
|
+
subCommands: {
|
|
252
|
+
create: createCmd,
|
|
253
|
+
list: listCmd,
|
|
254
|
+
show: showCmd,
|
|
255
|
+
edit: editCmd,
|
|
256
|
+
update: updateCmd,
|
|
257
|
+
delete: deleteCmd,
|
|
258
|
+
},
|
|
259
|
+
})
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ProviderConfigResponse,
|
|
3
|
+
ProviderTestRequest,
|
|
4
|
+
ProviderTestResponse,
|
|
5
|
+
SetProviderEnabledResponse,
|
|
6
|
+
SetProviderModelsRequest,
|
|
7
|
+
SetProviderModelsResponse,
|
|
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: {
|
|
15
|
+
name: 'list',
|
|
16
|
+
description: 'List all providers with enabled/disabled state and curated model counts',
|
|
17
|
+
},
|
|
18
|
+
args: {
|
|
19
|
+
enabled: { type: 'boolean', description: 'Show only enabled providers' },
|
|
20
|
+
},
|
|
21
|
+
async run({ args }) {
|
|
22
|
+
const client = createClient()
|
|
23
|
+
const { providers } = await client.get<ProviderConfigResponse>('/api/config/providers')
|
|
24
|
+
const filtered = args.enabled ? providers.filter((p) => p.enabled) : providers
|
|
25
|
+
if (filtered.length === 0) {
|
|
26
|
+
console.log('(no providers to show)')
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
const rows = filtered.map((p) => [
|
|
30
|
+
p.id,
|
|
31
|
+
p.enabled ? 'enabled' : 'disabled',
|
|
32
|
+
`${p.curated.length} curated`,
|
|
33
|
+
`${p.catalog.length} catalog`,
|
|
34
|
+
p.enabled ? '' : `(set ${p.envHint})`,
|
|
35
|
+
])
|
|
36
|
+
for (const line of columnize(rows)) console.log(line)
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const modelsCmd = defineCommand({
|
|
41
|
+
meta: {
|
|
42
|
+
name: 'models',
|
|
43
|
+
description: 'Show curated + catalog + live models for one provider',
|
|
44
|
+
},
|
|
45
|
+
args: {
|
|
46
|
+
name: { type: 'positional', required: true, description: 'Provider name' },
|
|
47
|
+
},
|
|
48
|
+
async run({ args }) {
|
|
49
|
+
const client = createClient()
|
|
50
|
+
const { providers } = await client.get<ProviderConfigResponse>('/api/config/providers')
|
|
51
|
+
const p = providers.find((x) => x.id === args.name)
|
|
52
|
+
if (!p) {
|
|
53
|
+
console.error(`unknown provider: ${args.name}`)
|
|
54
|
+
process.exit(1)
|
|
55
|
+
}
|
|
56
|
+
console.log(`# ${p.id} (${p.enabled ? 'enabled' : 'disabled'})`)
|
|
57
|
+
if (!p.enabled) console.log(` (set ${p.envHint} to enable)`)
|
|
58
|
+
|
|
59
|
+
console.log('\n## curated')
|
|
60
|
+
if (p.curated.length === 0) console.log(' (none — use `provider models-set` to pin some)')
|
|
61
|
+
else for (const m of p.curated) console.log(` ${m}`)
|
|
62
|
+
|
|
63
|
+
console.log('\n## catalog (from pi-ai)')
|
|
64
|
+
if (p.catalog.length === 0) console.log(' (empty)')
|
|
65
|
+
else for (const m of p.catalog) console.log(` ${m}`)
|
|
66
|
+
|
|
67
|
+
if (p.live) {
|
|
68
|
+
console.log('\n## live (from upstream /v1/models)')
|
|
69
|
+
if (p.live.error) console.log(` (error: ${p.live.error})`)
|
|
70
|
+
else if (p.live.models.length === 0) console.log(' (empty)')
|
|
71
|
+
else for (const m of p.live.models) console.log(` ${m}`)
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const modelsSetCmd = defineCommand({
|
|
77
|
+
meta: {
|
|
78
|
+
name: 'models-set',
|
|
79
|
+
description: 'Replace the curated model list for a provider (comma-separated)',
|
|
80
|
+
},
|
|
81
|
+
args: {
|
|
82
|
+
name: { type: 'positional', required: true, description: 'Provider name' },
|
|
83
|
+
models: {
|
|
84
|
+
type: 'positional',
|
|
85
|
+
required: true,
|
|
86
|
+
description: 'Comma-separated model list (empty string clears the list)',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
async run({ args }) {
|
|
90
|
+
const client = createClient()
|
|
91
|
+
const models = args.models
|
|
92
|
+
.split(',')
|
|
93
|
+
.map((s) => s.trim())
|
|
94
|
+
.filter(Boolean)
|
|
95
|
+
const body: SetProviderModelsRequest = { models }
|
|
96
|
+
const saved = await client.put<SetProviderModelsResponse>(
|
|
97
|
+
`/api/config/providers/${encodeURIComponent(args.name)}/models`,
|
|
98
|
+
body,
|
|
99
|
+
)
|
|
100
|
+
console.log(`saved ${saved.models.length} curated model(s) for ${args.name}`)
|
|
101
|
+
for (const m of saved.models) console.log(` ${m}`)
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const enableCmd = defineCommand({
|
|
106
|
+
meta: { name: 'enable', description: 'Toggle a provider on so agents can use it' },
|
|
107
|
+
args: {
|
|
108
|
+
name: { type: 'positional', required: true, description: 'Provider id (e.g. anthropic)' },
|
|
109
|
+
},
|
|
110
|
+
async run({ args }) {
|
|
111
|
+
const client = createClient()
|
|
112
|
+
const res = await client.put<SetProviderEnabledResponse>(
|
|
113
|
+
`/api/config/providers/${encodeURIComponent(args.name)}/enabled`,
|
|
114
|
+
{ enabled: true },
|
|
115
|
+
)
|
|
116
|
+
console.log(`${res.name}: enabled`)
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const disableCmd = defineCommand({
|
|
121
|
+
meta: {
|
|
122
|
+
name: 'disable',
|
|
123
|
+
description: 'Toggle a provider off — agents will refuse its model strings',
|
|
124
|
+
},
|
|
125
|
+
args: {
|
|
126
|
+
name: { type: 'positional', required: true, description: 'Provider id' },
|
|
127
|
+
},
|
|
128
|
+
async run({ args }) {
|
|
129
|
+
const client = createClient()
|
|
130
|
+
const res = await client.put<SetProviderEnabledResponse>(
|
|
131
|
+
`/api/config/providers/${encodeURIComponent(args.name)}/enabled`,
|
|
132
|
+
{ enabled: false },
|
|
133
|
+
)
|
|
134
|
+
console.log(`${res.name}: disabled`)
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const testCmd = defineCommand({
|
|
139
|
+
meta: { name: 'test', description: 'Send a single chat to a model and print the reply' },
|
|
140
|
+
args: {
|
|
141
|
+
model: {
|
|
142
|
+
type: 'positional',
|
|
143
|
+
required: true,
|
|
144
|
+
description: 'Model string, e.g. anthropic:claude-opus-4-6',
|
|
145
|
+
},
|
|
146
|
+
message: { type: 'string', description: 'Message text (default "say hi briefly")' },
|
|
147
|
+
},
|
|
148
|
+
async run({ args }) {
|
|
149
|
+
const client = createClient()
|
|
150
|
+
const body: ProviderTestRequest = { model: args.model, message: args.message }
|
|
151
|
+
console.log(`-> ${args.model}: ${args.message ?? 'say hi briefly'}`)
|
|
152
|
+
const res = await client.post<ProviderTestResponse>('/api/providers/test', body)
|
|
153
|
+
console.log(`<- ${res.content}`)
|
|
154
|
+
if (res.usage) {
|
|
155
|
+
console.log(`(${res.usage.promptTokens} in, ${res.usage.completionTokens} out)`)
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
export const providerCommand = defineCommand({
|
|
161
|
+
meta: { name: 'provider', description: 'Manage and test LLM providers' },
|
|
162
|
+
subCommands: {
|
|
163
|
+
list: listCmd,
|
|
164
|
+
enable: enableCmd,
|
|
165
|
+
disable: disableCmd,
|
|
166
|
+
models: modelsCmd,
|
|
167
|
+
'models-set': modelsSetCmd,
|
|
168
|
+
test: testCmd,
|
|
169
|
+
},
|
|
170
|
+
})
|