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,539 +0,0 @@
|
|
|
1
|
-
import { createServer, type ServerResponse } from 'node:http'
|
|
2
|
-
import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'
|
|
3
|
-
import { openDb, resolveAgent, resolvePaths } from '../../daemon/src/core/index.ts'
|
|
4
|
-
import { countSessionMessagesForTest, seedSessionForTest } from '../../daemon/src/runtime/index.ts'
|
|
5
|
-
import { extractAgentId } from './helpers.ts'
|
|
6
|
-
import { startTestServer, type TestServer } from './server-fixture.ts'
|
|
7
|
-
|
|
8
|
-
type MockHandler = unknown | ((res: ServerResponse) => void)
|
|
9
|
-
|
|
10
|
-
interface MockLlm {
|
|
11
|
-
url: string
|
|
12
|
-
push(responses: MockHandler[]): void
|
|
13
|
-
reset(): void
|
|
14
|
-
callCount(): number
|
|
15
|
-
stop(): Promise<void>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface CannedMessage {
|
|
19
|
-
role?: string
|
|
20
|
-
content?: string | null
|
|
21
|
-
tool_calls?: Array<{
|
|
22
|
-
id: string
|
|
23
|
-
type: string
|
|
24
|
-
function: { name: string; arguments: string }
|
|
25
|
-
}>
|
|
26
|
-
}
|
|
27
|
-
interface CannedChoice {
|
|
28
|
-
message: CannedMessage
|
|
29
|
-
finish_reason?: string
|
|
30
|
-
}
|
|
31
|
-
interface CannedResponse {
|
|
32
|
-
choices: CannedChoice[]
|
|
33
|
-
usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Translate a canned non-streaming OpenAI-shaped response into OpenAI SSE
|
|
38
|
-
* chunks and write them to `res`. Pi-ai's openai-completions client always
|
|
39
|
-
* runs in `stream: true` mode, so LMStudio-style JSON bodies need to be
|
|
40
|
-
* re-chunked into `data: {...}\n\n` lines terminated by `data: [DONE]`.
|
|
41
|
-
*/
|
|
42
|
-
function writeSseFromCanned(res: ServerResponse, canned: CannedResponse): void {
|
|
43
|
-
res.writeHead(200, {
|
|
44
|
-
'content-type': 'text/event-stream',
|
|
45
|
-
'cache-control': 'no-cache',
|
|
46
|
-
connection: 'keep-alive',
|
|
47
|
-
})
|
|
48
|
-
const choice = canned.choices[0]
|
|
49
|
-
const message = choice?.message ?? {}
|
|
50
|
-
const finish = choice?.finish_reason ?? 'stop'
|
|
51
|
-
|
|
52
|
-
// Role delta (always first in OpenAI's SSE protocol).
|
|
53
|
-
const roleChunk = {
|
|
54
|
-
id: 'chatcmpl-mock',
|
|
55
|
-
object: 'chat.completion.chunk',
|
|
56
|
-
choices: [{ index: 0, delta: { role: 'assistant' }, finish_reason: null }],
|
|
57
|
-
}
|
|
58
|
-
res.write(`data: ${JSON.stringify(roleChunk)}\n\n`)
|
|
59
|
-
|
|
60
|
-
if (typeof message.content === 'string' && message.content.length > 0) {
|
|
61
|
-
const textChunk = {
|
|
62
|
-
id: 'chatcmpl-mock',
|
|
63
|
-
object: 'chat.completion.chunk',
|
|
64
|
-
choices: [{ index: 0, delta: { content: message.content }, finish_reason: null }],
|
|
65
|
-
}
|
|
66
|
-
res.write(`data: ${JSON.stringify(textChunk)}\n\n`)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
70
|
-
const toolChunk = {
|
|
71
|
-
id: 'chatcmpl-mock',
|
|
72
|
-
object: 'chat.completion.chunk',
|
|
73
|
-
choices: [
|
|
74
|
-
{
|
|
75
|
-
index: 0,
|
|
76
|
-
delta: {
|
|
77
|
-
tool_calls: message.tool_calls.map((tc, i) => ({
|
|
78
|
-
index: i,
|
|
79
|
-
id: tc.id,
|
|
80
|
-
type: tc.type,
|
|
81
|
-
function: { name: tc.function.name, arguments: tc.function.arguments },
|
|
82
|
-
})),
|
|
83
|
-
},
|
|
84
|
-
finish_reason: null,
|
|
85
|
-
},
|
|
86
|
-
],
|
|
87
|
-
}
|
|
88
|
-
res.write(`data: ${JSON.stringify(toolChunk)}\n\n`)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const finalChunk = {
|
|
92
|
-
id: 'chatcmpl-mock',
|
|
93
|
-
object: 'chat.completion.chunk',
|
|
94
|
-
choices: [{ index: 0, delta: {}, finish_reason: finish }],
|
|
95
|
-
usage: canned.usage,
|
|
96
|
-
}
|
|
97
|
-
res.write(`data: ${JSON.stringify(finalChunk)}\n\n`)
|
|
98
|
-
res.write('data: [DONE]\n\n')
|
|
99
|
-
res.end()
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Start a node:http server that serves LMStudio-style /v1/chat/completions
|
|
104
|
-
* responses from an in-memory queue. Push responses before each `agent chat`
|
|
105
|
-
* turn — the server consumes them in order. Queue items may be objects
|
|
106
|
-
* (OpenAI-shaped non-streaming responses, translated to SSE on the wire) or
|
|
107
|
-
* functions `(res) => void` for custom behavior (e.g. a handler that never
|
|
108
|
-
* responds, to exercise client-side cancellation).
|
|
109
|
-
*/
|
|
110
|
-
function startLmStudioMock(): Promise<MockLlm> {
|
|
111
|
-
const queue: MockHandler[] = []
|
|
112
|
-
let calls = 0
|
|
113
|
-
const server = createServer((_req, res) => {
|
|
114
|
-
calls++
|
|
115
|
-
if (queue.length === 0) {
|
|
116
|
-
res.writeHead(500, { 'content-type': 'text/plain' })
|
|
117
|
-
res.end('mock response queue empty')
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
const item = queue.shift()
|
|
121
|
-
if (typeof item === 'function') {
|
|
122
|
-
;(item as (res: ServerResponse) => void)(res)
|
|
123
|
-
return
|
|
124
|
-
}
|
|
125
|
-
writeSseFromCanned(res, item as CannedResponse)
|
|
126
|
-
})
|
|
127
|
-
return new Promise((resolve) => {
|
|
128
|
-
server.listen(0, () => {
|
|
129
|
-
const addr = server.address()
|
|
130
|
-
if (!addr || typeof addr === 'string') throw new Error('unexpected server address')
|
|
131
|
-
resolve({
|
|
132
|
-
url: `http://localhost:${addr.port}/v1`,
|
|
133
|
-
push(items) {
|
|
134
|
-
queue.push(...items)
|
|
135
|
-
},
|
|
136
|
-
reset() {
|
|
137
|
-
queue.length = 0
|
|
138
|
-
calls = 0
|
|
139
|
-
},
|
|
140
|
-
callCount: () => calls,
|
|
141
|
-
stop: () => new Promise((r) => server.close(() => r())),
|
|
142
|
-
})
|
|
143
|
-
})
|
|
144
|
-
})
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
let mock: MockLlm
|
|
148
|
-
let server: TestServer
|
|
149
|
-
|
|
150
|
-
beforeAll(async () => {
|
|
151
|
-
mock = await startLmStudioMock()
|
|
152
|
-
server = await startTestServer({ LMSTUDIO_URL: mock.url })
|
|
153
|
-
})
|
|
154
|
-
afterAll(async () => {
|
|
155
|
-
await server.stop()
|
|
156
|
-
await mock.stop()
|
|
157
|
-
})
|
|
158
|
-
beforeEach(() => {
|
|
159
|
-
mock.reset()
|
|
160
|
-
server.reset()
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
async function spawnLmStudioAgent(): Promise<string> {
|
|
164
|
-
await server.cli(['profile', 'create', 'p', '--model', 'lmstudio:test-model'])
|
|
165
|
-
const r = await server.cli(['agent', 'spawn', '--profile', 'p'])
|
|
166
|
-
return extractAgentId(r.stdout)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
test('agent chat --message round-trips with the provider', async () => {
|
|
170
|
-
const agentId = await spawnLmStudioAgent()
|
|
171
|
-
mock.push([
|
|
172
|
-
{
|
|
173
|
-
choices: [
|
|
174
|
-
{
|
|
175
|
-
message: { role: 'assistant', content: 'hello from mock' },
|
|
176
|
-
finish_reason: 'stop',
|
|
177
|
-
},
|
|
178
|
-
],
|
|
179
|
-
},
|
|
180
|
-
])
|
|
181
|
-
|
|
182
|
-
const r = await server.cli(['agent', 'chat', agentId, '--message', 'hi'])
|
|
183
|
-
expect(r.exitCode).toBe(0)
|
|
184
|
-
expect(r.stdout).toContain('hello from mock')
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
test('memory persists across chat invocations', async () => {
|
|
188
|
-
const agentId = await spawnLmStudioAgent()
|
|
189
|
-
|
|
190
|
-
// Session 1: agent saves a memory via tool call
|
|
191
|
-
mock.push([
|
|
192
|
-
{
|
|
193
|
-
choices: [
|
|
194
|
-
{
|
|
195
|
-
message: {
|
|
196
|
-
role: 'assistant',
|
|
197
|
-
content: null,
|
|
198
|
-
tool_calls: [
|
|
199
|
-
{
|
|
200
|
-
id: 'call_1',
|
|
201
|
-
type: 'function',
|
|
202
|
-
function: {
|
|
203
|
-
name: 'memory_write',
|
|
204
|
-
arguments: '{"key":"prefs.md","content":"User likes hiking on Sundays."}',
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
],
|
|
208
|
-
},
|
|
209
|
-
finish_reason: 'tool_calls',
|
|
210
|
-
},
|
|
211
|
-
],
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
choices: [
|
|
215
|
-
{
|
|
216
|
-
message: { role: 'assistant', content: 'Got it, saved.' },
|
|
217
|
-
finish_reason: 'stop',
|
|
218
|
-
},
|
|
219
|
-
],
|
|
220
|
-
},
|
|
221
|
-
])
|
|
222
|
-
let r = await server.cli(['agent', 'chat', agentId, '--message', 'I like hiking on Sundays'])
|
|
223
|
-
expect(r.exitCode).toBe(0)
|
|
224
|
-
expect(r.stdout).toContain('memory_write')
|
|
225
|
-
expect(r.stdout).toContain('Got it')
|
|
226
|
-
|
|
227
|
-
// Session 2: new chat invocation, agent searches memory and finds it
|
|
228
|
-
mock.push([
|
|
229
|
-
{
|
|
230
|
-
choices: [
|
|
231
|
-
{
|
|
232
|
-
message: {
|
|
233
|
-
role: 'assistant',
|
|
234
|
-
content: null,
|
|
235
|
-
tool_calls: [
|
|
236
|
-
{
|
|
237
|
-
id: 'call_2',
|
|
238
|
-
type: 'function',
|
|
239
|
-
function: { name: 'memory_search', arguments: '{"query":"hiking"}' },
|
|
240
|
-
},
|
|
241
|
-
],
|
|
242
|
-
},
|
|
243
|
-
finish_reason: 'tool_calls',
|
|
244
|
-
},
|
|
245
|
-
],
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
choices: [
|
|
249
|
-
{
|
|
250
|
-
message: { role: 'assistant', content: 'You like hiking on Sundays.' },
|
|
251
|
-
finish_reason: 'stop',
|
|
252
|
-
},
|
|
253
|
-
],
|
|
254
|
-
},
|
|
255
|
-
])
|
|
256
|
-
r = await server.cli(['agent', 'chat', agentId, '--message', 'what do I like to do on weekends?'])
|
|
257
|
-
expect(r.exitCode).toBe(0)
|
|
258
|
-
expect(r.stdout).toContain('hiking')
|
|
259
|
-
expect(r.stdout).toContain('You like hiking on Sundays')
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
test('BOOTSTRAP.md is removed when bootstrap_done tool is called', async () => {
|
|
263
|
-
const agentId = await spawnLmStudioAgent()
|
|
264
|
-
mock.push([
|
|
265
|
-
{
|
|
266
|
-
choices: [
|
|
267
|
-
{
|
|
268
|
-
message: {
|
|
269
|
-
role: 'assistant',
|
|
270
|
-
content: null,
|
|
271
|
-
tool_calls: [
|
|
272
|
-
{
|
|
273
|
-
id: 'call_1',
|
|
274
|
-
type: 'function',
|
|
275
|
-
function: { name: 'bootstrap_done', arguments: '{}' },
|
|
276
|
-
},
|
|
277
|
-
],
|
|
278
|
-
},
|
|
279
|
-
finish_reason: 'tool_calls',
|
|
280
|
-
},
|
|
281
|
-
],
|
|
282
|
-
},
|
|
283
|
-
{
|
|
284
|
-
choices: [
|
|
285
|
-
{
|
|
286
|
-
message: { role: 'assistant', content: 'Welcome aboard.' },
|
|
287
|
-
finish_reason: 'stop',
|
|
288
|
-
},
|
|
289
|
-
],
|
|
290
|
-
},
|
|
291
|
-
])
|
|
292
|
-
|
|
293
|
-
const r = await server.cli(['agent', 'chat', agentId, '--message', 'I am ready'])
|
|
294
|
-
expect(r.exitCode).toBe(0)
|
|
295
|
-
expect(r.stdout).toContain('bootstrap_done')
|
|
296
|
-
|
|
297
|
-
// Verify BOOTSTRAP.md is gone via agent show (which reads the dir path)
|
|
298
|
-
const show = await server.cli(['agent', 'show', agentId])
|
|
299
|
-
expect(show.exitCode).toBe(0)
|
|
300
|
-
const m = show.stdout.match(/dir:\s+(\S+)/)
|
|
301
|
-
expect(m?.[1]).toBeDefined()
|
|
302
|
-
if (m?.[1]) {
|
|
303
|
-
const { existsSync } = await import('node:fs')
|
|
304
|
-
const { join } = await import('node:path')
|
|
305
|
-
expect(existsSync(join(m[1], 'BOOTSTRAP.md'))).toBe(false)
|
|
306
|
-
}
|
|
307
|
-
})
|
|
308
|
-
|
|
309
|
-
test('failed turn surfaces an error event and leaves the agent ready for a retry', async () => {
|
|
310
|
-
// Pi's SessionManager writes entries as they happen (not at turn end), so
|
|
311
|
-
// a failed turn doesn't "roll back" a user message the way the old
|
|
312
|
-
// pre-pi worker did. The test's concern here is simpler: a non-retryable
|
|
313
|
-
// provider error surfaces as an error event, and a fresh follow-up turn
|
|
314
|
-
// succeeds cleanly.
|
|
315
|
-
const agentId = await spawnLmStudioAgent()
|
|
316
|
-
|
|
317
|
-
mock.push([
|
|
318
|
-
(res: ServerResponse) => {
|
|
319
|
-
res.writeHead(400, { 'content-type': 'application/json' })
|
|
320
|
-
res.end(JSON.stringify({ error: { code: 'invalid_request', message: 'bad model' } }))
|
|
321
|
-
},
|
|
322
|
-
])
|
|
323
|
-
|
|
324
|
-
const failR = await server.cli(['agent', 'chat', agentId, '--message', 'go on'])
|
|
325
|
-
expect(failR.stdout).toMatch(/\[error:/)
|
|
326
|
-
|
|
327
|
-
mock.push([
|
|
328
|
-
{
|
|
329
|
-
choices: [
|
|
330
|
-
{ message: { role: 'assistant', content: 'recovered reply' }, finish_reason: 'stop' },
|
|
331
|
-
],
|
|
332
|
-
},
|
|
333
|
-
])
|
|
334
|
-
const okR = await server.cli(['agent', 'chat', agentId, '--message', 'go on'])
|
|
335
|
-
expect(okR.exitCode).toBe(0)
|
|
336
|
-
expect(okR.stdout).toContain('recovered reply')
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
test('agent chat can be cancelled mid-flight via `agent cancel`', async () => {
|
|
340
|
-
const agentId = await spawnLmStudioAgent()
|
|
341
|
-
|
|
342
|
-
// The mock's sole handler never responds — the provider's fetch will hang
|
|
343
|
-
// until its AbortSignal fires, giving us a window to cancel the turn.
|
|
344
|
-
const callCountBefore = mock.callCount()
|
|
345
|
-
mock.push([
|
|
346
|
-
(_res: ServerResponse) => {
|
|
347
|
-
// intentionally left hanging
|
|
348
|
-
},
|
|
349
|
-
])
|
|
350
|
-
|
|
351
|
-
// Kick off the chat in the background. The CLI subprocess will block
|
|
352
|
-
// inside the NDJSON stream until abort.
|
|
353
|
-
const chatPromise = server.cli(['agent', 'chat', agentId, '--message', 'hi'])
|
|
354
|
-
|
|
355
|
-
// Wait for the worker to actually reach the provider fetch (mock receives a
|
|
356
|
-
// request). Cancelling earlier races with worker startup — SIGTERM during
|
|
357
|
-
// Node bootstrap has no handler installed and exits with the signal,
|
|
358
|
-
// producing "worker killed by SIGTERM" instead of the cancelled-event path.
|
|
359
|
-
let providerHit = false
|
|
360
|
-
for (let i = 0; i < 100; i++) {
|
|
361
|
-
if (mock.callCount() > callCountBefore) {
|
|
362
|
-
providerHit = true
|
|
363
|
-
break
|
|
364
|
-
}
|
|
365
|
-
await new Promise((r) => setTimeout(r, 100))
|
|
366
|
-
}
|
|
367
|
-
if (!providerHit) throw new Error('worker did not reach the provider fetch in time')
|
|
368
|
-
|
|
369
|
-
const cancelR = await server.cli(['agent', 'cancel', agentId])
|
|
370
|
-
expect(cancelR.exitCode).toBe(0)
|
|
371
|
-
|
|
372
|
-
// The chat subprocess should terminate promptly. With the mock still
|
|
373
|
-
// hanging, the fetch must have been aborted — otherwise this would time
|
|
374
|
-
// out. Pi surfaces aborts as a `cancelled` error or provider-side text
|
|
375
|
-
// like "Request was aborted." — accept either.
|
|
376
|
-
const chatR = await chatPromise
|
|
377
|
-
expect(chatR.stdout + chatR.stderr).toMatch(/cancelled|aborted/i)
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
// --- chat-reset + chat-trim ---
|
|
381
|
-
|
|
382
|
-
// Seed a canonical pi session file on disk with `n` synthetic user/assistant
|
|
383
|
-
// messages. The on-disk JSONL is the authoritative source of conversation
|
|
384
|
-
// state after migration 0004 dropped `chat_messages`; hitting tests at this
|
|
385
|
-
// layer keeps them faithful to what a real worker writes. See
|
|
386
|
-
// `seedSessionForTest` in runtime for the underlying pi SessionManager
|
|
387
|
-
// append calls.
|
|
388
|
-
function seedChatHistory(home: string, agentId: string, n: number): void {
|
|
389
|
-
const paths = resolvePaths(home)
|
|
390
|
-
const db = openDb(paths.db)
|
|
391
|
-
try {
|
|
392
|
-
const resolved = resolveAgent(db, paths, agentId)
|
|
393
|
-
const messages = Array.from({ length: n }, (_, i) =>
|
|
394
|
-
i % 2 === 0
|
|
395
|
-
? { role: 'user' as const, text: `msg ${i}` }
|
|
396
|
-
: { role: 'assistant' as const, text: `reply ${i}` },
|
|
397
|
-
)
|
|
398
|
-
seedSessionForTest(resolved, paths, messages)
|
|
399
|
-
} finally {
|
|
400
|
-
db.close()
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function readChatLen(home: string, agentId: string): number {
|
|
405
|
-
const paths = resolvePaths(home)
|
|
406
|
-
const db = openDb(paths.db)
|
|
407
|
-
try {
|
|
408
|
-
const resolved = resolveAgent(db, paths, agentId)
|
|
409
|
-
return countSessionMessagesForTest(resolved, paths)
|
|
410
|
-
} finally {
|
|
411
|
-
db.close()
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
test('agent chat-reset --force wipes history without prompting', async () => {
|
|
416
|
-
const agentId = await spawnLmStudioAgent()
|
|
417
|
-
seedChatHistory(server.home, agentId, 6)
|
|
418
|
-
expect(readChatLen(server.home, agentId)).toBe(6)
|
|
419
|
-
|
|
420
|
-
const r = await server.cli(['agent', 'chat-reset', agentId, '--force'])
|
|
421
|
-
expect(r.exitCode).toBe(0)
|
|
422
|
-
expect(r.stdout).toContain('reset')
|
|
423
|
-
expect(readChatLen(server.home, agentId)).toBe(0)
|
|
424
|
-
})
|
|
425
|
-
|
|
426
|
-
test('agent chat-trim reports the before/after counts from pi branching', async () => {
|
|
427
|
-
const agentId = await spawnLmStudioAgent()
|
|
428
|
-
seedChatHistory(server.home, agentId, 6)
|
|
429
|
-
|
|
430
|
-
const r = await server.cli(['agent', 'chat-trim', agentId, '--keep', '3'])
|
|
431
|
-
expect(r.exitCode).toBe(0)
|
|
432
|
-
// Pi's `branch()` moves the leaf pointer in-memory; the session file
|
|
433
|
-
// keeps the original entries on disk (append-only). The next turn
|
|
434
|
-
// will resume from the branched leaf. CLI output is what we assert.
|
|
435
|
-
expect(r.stdout).toMatch(/6 → 3/)
|
|
436
|
-
})
|
|
437
|
-
|
|
438
|
-
test('agent chat-trim rejects non-integer --keep with a clear error', async () => {
|
|
439
|
-
const agentId = await spawnLmStudioAgent()
|
|
440
|
-
seedChatHistory(server.home, agentId, 4)
|
|
441
|
-
|
|
442
|
-
const r = await server.cli(['agent', 'chat-trim', agentId, '--keep', '2.5'])
|
|
443
|
-
expect(r.exitCode).not.toBe(0)
|
|
444
|
-
expect(r.stderr + r.stdout).toMatch(/non-negative integer/)
|
|
445
|
-
// Session file untouched on invalid input
|
|
446
|
-
expect(readChatLen(server.home, agentId)).toBe(4)
|
|
447
|
-
})
|
|
448
|
-
|
|
449
|
-
// --- chat-context + chat-compact ---
|
|
450
|
-
|
|
451
|
-
test('agent chat-context reports system prompt, tools, skills, history breakdown', async () => {
|
|
452
|
-
const agentId = await spawnLmStudioAgent()
|
|
453
|
-
seedChatHistory(server.home, agentId, 6)
|
|
454
|
-
|
|
455
|
-
const r = await server.cli(['agent', 'chat-context', agentId])
|
|
456
|
-
expect(r.exitCode).toBe(0)
|
|
457
|
-
expect(r.stdout).toMatch(/## system prompt/)
|
|
458
|
-
expect(r.stdout).toMatch(/## tools/)
|
|
459
|
-
expect(r.stdout).toMatch(/count:\s+\d+/)
|
|
460
|
-
// 6 seeded message entries, 0 compactions
|
|
461
|
-
expect(r.stdout).toMatch(/messages:\s+6/)
|
|
462
|
-
expect(r.stdout).toMatch(/compactions:\s+0/)
|
|
463
|
-
expect(r.stdout).toMatch(/TOTAL:/)
|
|
464
|
-
})
|
|
465
|
-
|
|
466
|
-
test('agent chat-context --json emits the structured ChatContextResponse', async () => {
|
|
467
|
-
const agentId = await spawnLmStudioAgent()
|
|
468
|
-
seedChatHistory(server.home, agentId, 4)
|
|
469
|
-
|
|
470
|
-
const r = await server.cli(['agent', 'chat-context', agentId, '--json'])
|
|
471
|
-
expect(r.exitCode).toBe(0)
|
|
472
|
-
const body = JSON.parse(r.stdout)
|
|
473
|
-
expect(body.agentId).toBe(agentId)
|
|
474
|
-
expect(typeof body.model).toBe('string')
|
|
475
|
-
expect(body.history).toMatchObject({ messageEntries: 4, compactionEntries: 0 })
|
|
476
|
-
expect(typeof body.history.chars).toBe('number')
|
|
477
|
-
expect(typeof body.history.bytes).toBe('number')
|
|
478
|
-
expect(typeof body.history.tokensEstimate).toBe('number')
|
|
479
|
-
expect(typeof body.tools.count).toBe('number')
|
|
480
|
-
expect(Array.isArray(body.tools.entries)).toBe(true)
|
|
481
|
-
expect(typeof body.totals.chars).toBe('number')
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
test('agent chat-context on an empty history returns zero history entries', async () => {
|
|
485
|
-
const agentId = await spawnLmStudioAgent()
|
|
486
|
-
const r = await server.cli(['agent', 'chat-context', agentId, '--json'])
|
|
487
|
-
expect(r.exitCode).toBe(0)
|
|
488
|
-
const body = JSON.parse(r.stdout)
|
|
489
|
-
expect(body.history).toMatchObject({
|
|
490
|
-
messageEntries: 0,
|
|
491
|
-
compactionEntries: 0,
|
|
492
|
-
chars: 0,
|
|
493
|
-
})
|
|
494
|
-
// System prompt + tools still contribute to totals even on a fresh agent.
|
|
495
|
-
expect(body.totals.chars).toBeGreaterThan(0)
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
test('agent chat-compact summarizes the head via pi session.compact', async () => {
|
|
499
|
-
const agentId = await spawnLmStudioAgent()
|
|
500
|
-
seedChatHistory(server.home, agentId, 10)
|
|
501
|
-
|
|
502
|
-
// Pi's session.compact() uses the agent's current model to generate the
|
|
503
|
-
// summary — the mock has to answer one LLM call.
|
|
504
|
-
mock.push([
|
|
505
|
-
{
|
|
506
|
-
choices: [
|
|
507
|
-
{
|
|
508
|
-
message: {
|
|
509
|
-
role: 'assistant',
|
|
510
|
-
content: 'The user sent several questions; assistant replied each time.',
|
|
511
|
-
},
|
|
512
|
-
finish_reason: 'stop',
|
|
513
|
-
},
|
|
514
|
-
],
|
|
515
|
-
},
|
|
516
|
-
])
|
|
517
|
-
|
|
518
|
-
const r = await server.cli(['agent', 'chat-compact', agentId, '--force'])
|
|
519
|
-
// Pi may be stricter than us about when it bothers to compact; allow
|
|
520
|
-
// either a success summary or a "nothing to compact" refusal. The key
|
|
521
|
-
// thing is the endpoint runs without throwing.
|
|
522
|
-
if (r.exitCode === 0) {
|
|
523
|
-
expect(r.stdout).toMatch(/compacted:/)
|
|
524
|
-
} else {
|
|
525
|
-
// Non-zero exit is acceptable when pi declines to compact a small
|
|
526
|
-
// history. Surface the error reason for diagnostics.
|
|
527
|
-
expect(r.stderr + r.stdout).toMatch(/(error|nothing)/i)
|
|
528
|
-
}
|
|
529
|
-
})
|
|
530
|
-
|
|
531
|
-
test('agent chat-compact errors out on empty history', async () => {
|
|
532
|
-
const agentId = await spawnLmStudioAgent()
|
|
533
|
-
const r = await server.cli(['agent', 'chat-compact', agentId, '--force'])
|
|
534
|
-
// With no session file at all, pi either refuses or succeeds with a
|
|
535
|
-
// trivial empty compaction. We assert the command completes without a
|
|
536
|
-
// worker crash — actual exit code depends on pi's behavior for empty
|
|
537
|
-
// sessions, which can change between pi releases.
|
|
538
|
-
expect(r.stdout + r.stderr).toBeTruthy()
|
|
539
|
-
})
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { expect, test } from 'vitest'
|
|
2
|
-
import { columnize } from '../src/columnize.ts'
|
|
3
|
-
|
|
4
|
-
test('pads each column to its widest cell', () => {
|
|
5
|
-
const out = columnize([
|
|
6
|
-
['a', 'short', 'x'],
|
|
7
|
-
['ab', 'longer-word', 'y'],
|
|
8
|
-
['abc', 'x', 'z'],
|
|
9
|
-
])
|
|
10
|
-
expect(out).toEqual(['a short x', 'ab longer-word y', 'abc x z'])
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
test("doesn't pad the trailing column (keeps `| wc` friendly)", () => {
|
|
14
|
-
const out = columnize([
|
|
15
|
-
['a', 'b'],
|
|
16
|
-
['aa', 'bb'],
|
|
17
|
-
])
|
|
18
|
-
for (const line of out) {
|
|
19
|
-
expect(line.endsWith(' ')).toBe(false)
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
test('empty input returns empty output', () => {
|
|
24
|
-
expect(columnize([])).toEqual([])
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
test('single-row tables still align the first columns', () => {
|
|
28
|
-
expect(columnize([['a', 'b', 'c']])).toEqual(['a b c'])
|
|
29
|
-
})
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, expect, test } from 'vitest'
|
|
2
|
-
import { makeHome, runCli, type TestHome } from './helpers.ts'
|
|
3
|
-
|
|
4
|
-
let h: TestHome
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
h = makeHome()
|
|
7
|
-
})
|
|
8
|
-
afterEach(() => h.cleanup())
|
|
9
|
-
|
|
10
|
-
test('completion bash prints a sourceable function + complete directive', async () => {
|
|
11
|
-
const res = await runCli(['completion', 'bash'], h.home)
|
|
12
|
-
expect(res.exitCode).toBe(0)
|
|
13
|
-
expect(res.stdout).toContain('_bazilion_completion()')
|
|
14
|
-
expect(res.stdout).toContain('complete -F _bazilion_completion bazilion')
|
|
15
|
-
// Top-level subcommands are enumerated
|
|
16
|
-
expect(res.stdout).toContain('"serve')
|
|
17
|
-
expect(res.stdout).toContain('agent')
|
|
18
|
-
expect(res.stdout).toContain('backup')
|
|
19
|
-
// Nested subcommand case arm exists
|
|
20
|
-
expect(res.stdout).toContain('"agent")')
|
|
21
|
-
expect(res.stdout).toContain('"agent chat")')
|
|
22
|
-
// Flag enumeration (at least one known flag)
|
|
23
|
-
expect(res.stdout).toContain('--profile')
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
test('completion zsh wraps bashcompinit around the bash script', async () => {
|
|
27
|
-
const res = await runCli(['completion', 'zsh'], h.home)
|
|
28
|
-
expect(res.exitCode).toBe(0)
|
|
29
|
-
expect(res.stdout).toContain('bashcompinit')
|
|
30
|
-
expect(res.stdout).toContain('_bazilion_completion()')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
test('completion fish emits subcommand + guard rules', async () => {
|
|
34
|
-
const res = await runCli(['completion', 'fish'], h.home)
|
|
35
|
-
expect(res.exitCode).toBe(0)
|
|
36
|
-
expect(res.stdout).toContain('complete -c bazilion')
|
|
37
|
-
expect(res.stdout).toContain('__fish_use_subcommand')
|
|
38
|
-
expect(res.stdout).toContain('__fish_seen_subcommand_from agent')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
test('completion rejects an unknown shell', async () => {
|
|
42
|
-
const res = await runCli(['completion', 'tcsh'], h.home)
|
|
43
|
-
expect(res.exitCode).not.toBe(0)
|
|
44
|
-
expect(res.stderr + res.stdout).toMatch(/unsupported shell: tcsh/)
|
|
45
|
-
})
|