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